OpenGL
文章目录
- OpenGL
- OpenGL 、OpenGL ES简介
- OpenGL
- OpenGL ES
- OpenGLES 3.0
- 图形API汇总
- 着色器与管线
- 着色器程序Shader
- 顶点着色器VertexShader
- 片元着色器FragmentShader
- 管线
- 内置变量与内置函数
- 内置变量
- 内置函数总结
- 渲染宿主
- 渲染窗口宿主
- GLSurfaceView和TextureView的区别
- EGL
- 什么是EGL
- 自己写一个TextureEGLHelper来管理EGL和渲染线程
- 关键代码
- 创建管理线程
- 启动线程并且通知EGL初始化环境
- 初始化EGL环境
- 在相机绘制的SurfaceTexture FrameAvailable的时候通知渲染
- drawFrame()开始渲染画面
- 注意销毁释放
- OpenGL ES 基本使用
- 写一个Renderer渲染器
- 自定义一个Renderer渲染器,传入上述TextureEGLHelper使用
- 自定义ITextureRenderer接口
- 写一个CameraTextureRenderer完成ITextureRenderer接口
- 几个glsl着色器代码
- 顶点着色器
- 片元着色器
- 分屏
- 灵魂出窍
- 灵魂抖动
- 二值化
OpenGL 、OpenGL ES简介
OpenGL
- OpenGL是一种图形应用程序编程接口(Application Programming Interface,API)。它是一种可以对图形硬件设备特性进行访问的软件库,OpenGL被设计为一个现代化的、硬件无关的接口,因此我们可以在不考虑计算机操作系统或窗口系统的前提下,在多种不同的图形硬件系统上,完全通过软件的方式实现OpenGL的接口。
OpenGL ES
- OpenGL® ES is a royalty-free, cross-platform API for rendering advanced 2D and 3D graphics on embedded and mobile systems - including consoles, phones, appliances and vehicles. It consists of a well-defined subset of desktop OpenGL suitable for low-power devices, and provides a flexible and powerful interface between software and graphics acceleration hardware.
- 是OpenGL的子集,本质上是编程接口规范。
- OpenGL与OpenGL ES的主要区别,在于OpenGL ES主要针对嵌入式设备使用
OpenGLES 3.0
- OpenGLES 3.0 实际上是 OpenGLES 2.0 的扩展版本,向下兼容 OpenGLES 2.0 ,但不兼容 OpenGLES 1.0 。
图形API汇总
- OpenGL(Open Graphics Library):是一个跨编程语言、跨平台的编程图形程序接口,它将计算机的资源抽象称为一个个OpenGL的对象,对这些资源的操作抽象为一个个的OpenGL指令。
- OpenGL ES(OpenGL for Embedded Systems):是OpenGL三维图形API的子集,针对手机、PDA和游戏主机等嵌入式设备而设计的,去除了许多不必要和性能较低的API接口。
- DirectX:是由很多API组成的,DirectX并不是一个单纯的图形API。最重要的是DirectX是属于Windows上一个多媒体处理API。并不支持Windows以外的平台,所以不是跨平台框架。按照性质分类,可分为四大部分,显示部分、声音部分、输入部分和网络部分。
- Metal:Apple为游戏开发者推出了新的平台技术,该技术能够为3D图像提高10倍的渲染性能。Metal是Apple为解决3D渲染而推出的框架。
着色器与管线
着色器程序Shader
- 将固定渲染管线架构变成为可编程渲染管线。因此,OpenGL在实际调用绘制函数之前,还需要指定一个由shader编译成的着色器程序。常见的着色器主要有顶点着色器(VertexShader)、片段/片元着色器(FragmentShader)/像素着色器(PixelShader)、几何着色器(GeometryShader)、曲面细分着色器(TessellationShader)。直到OpenGL ES3.0,依然只支持了顶点着色器和片段着色器这两个最基础的着色器
- OpenGL在处理shader时,和其他编译器一样。通过编译、链接等步骤,生成了着色器程序(glProgram),着色器程序同时包含了顶点着色器和片元着色器的运算逻辑。在OpenGL进行绘制的时候,首先由顶点着色器对传入的顶点数据进行运算。在通过图元装配,将顶点转换为图元。然后进行光栅化,将图元这种矢量图形,转换为栅格化数据。最后,将栅格化数据传入片元着色器中进行运算。片元着色器会对栅格化数据中的每一个像素进行运算,并决定像素的颜色。
顶点着色器VertexShader
- 一般用来处理图形每个顶点变换(旋转/平移/投影等)
- 顶点着色器时OpenGL中用于计算顶点属性的程序。顶点着色器是逐顶点运算的程序,也就是说每个顶点数据都会执行一次。当然这是并行,并且顶点着色器运算过程中无法访问其他顶点数据
- 一般来说典型的需要计算的顶点属性主要包括顶点坐标变换、逐顶点光照运算等等。顶点坐标由自身坐标系转换到规范化坐标系的运算,就是在这里发生的
片元着色器FragmentShader
- 一般用来处理图形中每个像素点颜色计算和填充
- 片元着色器是OpenGL中用于计算片段(像素)颜色的程序,片元着色器是逐像素运算的程序,也就说每个像素都会执行一次片元着色器,当然也是并行的
管线
- OpenGL实现了我们通常所说的渲染管线(redering pipeline),它是一系列数据处理过程,并将应用程序的数据转换到最终渲染的图像。
- 顶点着色、细分着色(它本身包含两个着色器)以及最后的几何着色,然后再经过图元装配和剪切后,将它们送到光栅化单元(rasterizer)。光栅化单元负责对所有剪切区域(clipping region)内的图元生成片元数据,然后对每个生成的片元都执行一个片元着色器.
内置变量与内置函数
内置变量
gl_Position: 用于vertex shader, 写顶点位置;被图元收集、裁剪等固定操作功能所使用;
其内部声明是:highp vec4 gl_Position;
gl_PointSize: 用于vertex shader, 写光栅化后的点大小,像素个数;
其内部声明是:mediump float gl_Position;
gl_FragColor: 用于Fragment shader,写fragment color;被后续的固定管线使用;
mediump vec4 gl_FragColor;
gl_FragData: 用于Fragment shader,是个数组,写gl_FragData[n] 为data n;被后续的固定管线使用;
mediump vec4 gl_FragData[gl_MaxDrawBuffers];
gl_FragColor和gl_FragData是互斥的,不会同时写入;
gl_FragCoord: 用于Fragment shader,只读, Fragment相对于窗口的坐标位置 x,y,z,w; 这个是固定管线图元差值后产生的;z 是深度值; mediump vec4 gl_FragCoord;
gl_FrontFacing: 用于判断 fragment是否属于 front-facing primitive;只读;
bool gl_FrontFacing;
gl_PointCoord: 仅用于 point primitive; mediump vec2 gl_PointCoord;
内置函数总结
radians(degree) : 角度变弧度;
degrees(radian) : 弧度变角度;
sin(angle), cos(angle), tan(angle)
asin(x): arc sine, 返回弧度 [-PI/2, PI/2];
acos(x): arc cosine,返回弧度 [0, PI];
atan(y, x): arc tangent, 返回弧度 [-PI, PI];
atan(y/x): arc tangent, 返回弧度 [-PI/2, PI/2];
pow(x, y): x的y次方;
exp(x): 指数, log(x):
exp2(x): 2的x次方, log2(x):
sqrt(x): x的根号; inversesqrt(x): x根号的倒数
abs(x): 绝对值
sign(x): 符号, 1, 0 或 -1
floor(x): 底部取整
ceil(x): 顶部取整
fract(x): 取小数部分,函数形状跟mod相似,锯齿形状。
mod(x, y): 取模, x - y*floor(x/y),函数形状跟fract相似,如果y=1.0,那么mod和fract函数的形状就相同了。
min(x, y): 取最小值
max(x, y): 取最大值
clamp(x, min, max): min(max(x, min), max); 根据输入的x,返回介于 min 与 max 之间的值,当 x < min时,返回min,当 x > max 时,返回 max.
mix(x, y, a): x, y的线性混叠, x(1-a) + y*a;
step(edge, x): 第一个是极限或阈值,第二个是我们想要检测或通过的值。对任何小于阈值edge的值,返回 0.0,大于阈值edge的值,则返回 1.0。
smoothstep(edge0, edge1, x): 当给定一个范围的上下限和一个数值,这个函数会在已有的范围内给出插值。前两个参数规定转换的开始和结束点,第三个是给出一个值用来插值。 x<=edge0时为0.0, x>=edge1时为1.0
length(x): 向量长度
distance(p0, p1): 两点距离, length(p0-p1);
dot(x, y): 点积,各分量分别相乘 后 相加,对于向量x, y,返回的结果是 sum = ∑(xi * yi) 乘积之和。关于点积的数学知识请参考《线性代数》等相关书籍.
cross(x, y): 差积,x[1]*y[2]-y[1]*x[2], x[2]*y[0] - y[2]*x[0], x[0]*y[1] - y[0]*x[1]
normalize(x): 归一化, length(x)=1;
faceforward(N, I, Nref): 如 dot(Nref, I)< 0则N, 否则 -N
reflect(I, N): I的反射方向, I -2*dot(N, I)*N, N必须先归一化
refract(I, N, eta): 折射,k=1.0-eta*eta*(1.0 - dot(N, I) * dot(N, I)); 如k<0.0 则0.0,否则 eta*I - (eta*dot(N, I)+sqrt(k))*N
matrixCompMult(matX, matY): 矩阵相乘, 每个分量 自行相乘, 即 r[i][j] = x[i][j]*y[i][j];
矩阵线性相乘,直接用 *
lessThan(vecX, vecY): 向量 每个分量比较 x < y
lessThanEqual(vecX, vecY): 向量 每个分量比较 x<=y
greaterThan(vecX, vecY): 向量 每个分量比较 x>y
greaterThanEqual(vecX, vecY): 向量 每个分量比较 x>=y
equal(vecX, vecY): 向量 每个分量比较 x==y
notEqual(vecX, vexY): 向量 每个分量比较 x!=y
any(bvecX): 只要有一个分量是true, 则true
all(bvecX): 所有分量是true, 则true
not(bvecX): 所有分量取反
texture2D(sampler2D, coord): texture lookup
texture2D(sampler2D, coord, bias): LOD bias, mip-mapped texture
texture2DProj(sampler2D, coord):
texture2DProj(sampler2D, coord, bias):
texture2DLod(sampler2D, coord, lod):
texture2DProjLod(sampler2D, coord, lod):
textureCube(samplerCube, coord):
textureCube(samplerCube, coord, bias):
textureCubeLod(samplerCube, coord, lod):
渲染宿主
渲染窗口宿主
在App端它仍在View 层次中,但在Server端(WMS(WindowManagerService)()和SF)中,它与宿主窗口是分离的。这样的好处是对这个Surface的渲染可以放到单独线程去做,渲染时可以有自己的GL context。这对于一些游戏、视频等性能相关的应用非常有益,因为它不会影响主线程对事件的响应。但它也有缺点,因为这个Surface不在View hierachy中,它的显示也不受View的属性控制,所以不能进行平移,缩放等变换,也不能放在其它ViewGroup中,一些View中的特性也无法使用。
GLSurfaceView和TextureView的区别
- GLSurfaceView:
前面的一些博客都是基于GLSurfaceView来实现渲染的,因为它内部自带了EGL的管理以及渲染线程。另外它定义了用户需要实现的Render接口,其中EglHelper和GLThread分别实现了上面提到的管理EGL环境和渲染线程的工作。
缺点:GLSurfaceView将OpenGL绑定到一起,换言之,GLSurfaceView一但销毁,伴随的OpenGL也一起销毁了,一个OpenGL只能渲染一个GLSurfaceView。
- TextureView:
和SurfaceView不同,它不会在WMS中单独创建窗口,而是作为一个普通View,因此可以和其它普通View一样进行移动,旋转,缩放,动画等变化.
缺点: 使用这个控件需要自己来实现EGL管理和渲染线程,当然了,虽然麻烦了一点,但是也更为灵活。
EGL
什么是EGL
EGL 是 OpenGL ES 渲染 API 和本地窗口系统(native platform window system)之间的一个中间接口层,它主要由系统制造商实现。EGL提供如下机制:
- 与设备的原生窗口系统通信
- 查询绘图表面的可用类型和配置
- 创建绘图表面
- 在OpenGL ES 和其他图形渲染API之间同步渲染
- 管理纹理贴图等渲染资源
Android中使用EGL绘图的一般步骤:
- 1、获取 EGL Display 对象:eglGetDisplay()
- 2、初始化与 EGLDisplay 之间的连接:eglInitialize()
- 3、获取 EGLConfig 对象:eglChooseConfig()
- 4、创建 EGLContext 实例:eglCreateContext()
- 5、创建 EGLSurface 实例:eglCreateWindowSurface()
- 6、连接 EGLContext 和 EGLSurface:eglMakeCurrent()
- 7、使用 OpenGL ES API 绘制图形:gl_*()
- 8、切换 front buffer 和 back buffer 送显:eglSwapBuffer()
- 双缓冲(前后台缓冲 SurfaceView中交换前后台,提高渲染效率,避免刷新率过高出现闪烁现象)
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kkiwkSuO-1603105574497)(https://www.pianshen.com/images/235/92bd6b7857c8ccd62015e0c8e0c32ca3.png)]
- 9、断开并释放与 EGLSurface 关联的 EGLContext 对象:eglRelease()
- 10、删除 EGLSurface 对象
- 11、删除 EGLContext 对象
- 12、终止与 EGLDisplay 之间的连接
自己写一个TextureEGLHelper来管理EGL和渲染线程
关键代码
创建管理线程
public class TextureEGLHelper extends HandlerThread implements SurfaceTexture.OnFrameAvailableListener {
@IntDef({EGLMessage.MSG_INIT, EGLMessage.MSG_RENDER, EGLMessage.MSG_DESTROY})
@Retention(RetentionPolicy.SOURCE)
public @interface EGLMessage {
int MSG_INIT = 100;
int MSG_RENDER = 200;
int MSG_DESTROY = 300;
}
private HandlerThread mHandlerThread;
private Handler mHandler;
...
private final class TextureHandler extends Handler {
public TextureHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case EGLMessage.MSG_INIT:
//用的是OpenGL ES 3.0
initEGLContext(3);
return;
case EGLMessage.MSG_RENDER:
//开始渲染
drawFrame();
return;
case EGLMessage.MSG_DESTROY:
//销毁
return;
default:
return;
}
}
}
...
}
MSG_INIT的时候初始化EGL环境,
MSG_RENDERER的时候即绘制画面的时候,
MSG_DESTROY的时候销毁
启动线程并且通知EGL初始化环境
public void initEgl(TextureView textureView, int textureId) {
mTextureView = textureView;
mOESTextureId = textureId;
//启动线程
mHandlerThread = new HandlerThread("Renderer Thread");
mHandlerThread.start();
mHandler = new TextureHandler(mHandlerThread.getLooper());
//线程中初始化
mHandler.sendEmptyMessage(EGLMessage.MSG_INIT);
}
初始化EGL环境
/**
* 初始化EGL环境
*
* @param clientVersion
*/
private void initEGLContext(int clientVersion) {
//获取默认显示设备
mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
throw new RuntimeException("eglGetDisplay error: " + EGL14.eglGetError());
}
//存放EGL版本号
int[] version = new int[2];
version[0] = 3;
if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) {
throw new RuntimeException("eglInitialize error: " + EGL14.eglGetError());
}
//配置列表
int[] attributes = {
EGL14.EGL_BUFFER_SIZE, 32,
EGL14.EGL_RED_SIZE, 8,
EGL14.EGL_GREEN_SIZE, 8,
EGL14.EGL_BLUE_SIZE, 8,
EGL14.EGL_ALPHA_SIZE, 8,
EGL14.EGL_RENDERABLE_TYPE, 4,
EGL14.EGL_SURFACE_TYPE, EGL14.EGL_WINDOW_BIT,
EGL14.EGL_NONE
};
int[] numConfigs = new int[1];
//EGL选择配置
if (!EGL14.eglChooseConfig(mEGLDisplay, attributes, 0, configs, 0, configs.length, numConfigs, 0)) {
throw new RuntimeException("eglChooseConfig error: " + EGL14.eglGetError());
}
//获取TextureView内置的SurfaceTexture作为EGL的绘图表面,也就是跟系统屏幕打交道
SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();
if (surfaceTexture == null) {
throw new RuntimeException("surfaceTexture is null");
}
//创建EGL显示窗口
final int[] surfaceAttributes = {EGL14.EGL_NONE};
mEglSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, configs[0], surfaceTexture, surfaceAttributes, 0);
//创建上下文环境
int[] contextAttributes = {
EGL14.EGL_CONTEXT_CLIENT_VERSION, clientVersion,
EGL14.EGL_NONE
};
mEGLContext = EGL14.eglCreateContext(mEGLDisplay, configs[0], EGL14.EGL_NO_CONTEXT, contextAttributes, 0);
if (mEGLDisplay == EGL14.EGL_NO_DISPLAY || mEGLContext == EGL14.EGL_NO_CONTEXT) {
throw new RuntimeException("eglCreateContext fail error: " + EGL14.eglGetError());
}
if (!EGL14.eglMakeCurrent(mEGLDisplay, mEglSurface, mEglSurface, mEGLContext)) {
throw new RuntimeException("eglMakeCurrent error: " + EGL14.eglGetError());
}
//加载渲染器
try {
mTextureRenderer = (ITextureRenderer)(mRendererClass.getConstructor(Integer.class).newInstance(mOESTextureId));
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
mTextureRenderer.onSurfaceCreated();
}
在相机绘制的SurfaceTexture FrameAvailable的时候通知渲染
@Override
public void onFrameAvailable(SurfaceTexture surfaceTexture) {
if (mHandler != null) {
//通知子线程渲染
mHandler.sendEmptyMessage(EGLMessage.MSG_RENDER);
}
}
drawFrame()开始渲染画面
/**
* 绘制帧画面,双缓冲 送显
*/
private void drawFrame() {
if (mTextureRenderer != null) {
//指定mEGLContext为当前系统的EGL上下文
EGL14.eglMakeCurrent(mEGLDisplay, mEglSurface, mEglSurface, mEGLContext);
//调用渲染器绘制
mTextureRenderer.onDrawFrame(mOESSurfaceTexture);
//交换缓冲区,android使用双缓冲机制,所以我们绘制的都是在后台缓冲区,通过交换将后台缓冲区变为前台显示区,下一帧的绘制仍然在后台缓冲区
EGL14.eglSwapBuffers(mEGLDisplay, mEglSurface);
}
}
注意销毁释放
/**
* 销毁
* 释放
*/
public void onDestroy() {
if (mHandlerThread != null) {
mHandlerThread.quitSafely();
}
if (mHandler != null) {
mHandler.removeCallbacksAndMessages(null);
}
}
完整代码:
public class TextureEGLHelper extends HandlerThread implements SurfaceTexture.OnFrameAvailableListener {
private static final String TAG = "TextureEGLHelper";
@IntDef({EGLMessage.MSG_INIT, EGLMessage.MSG_RENDER, EGLMessage.MSG_DESTROY})
@Retention(RetentionPolicy.SOURCE)
public @interface EGLMessage {
int MSG_INIT = 100;
int MSG_RENDER = 200;
int MSG_DESTROY = 300;
}
private HandlerThread mHandlerThread;
private Handler mHandler;
private TextureView mTextureView;
private int mOESTextureId;
/**
* 显示设备
*/
private EGLDisplay mEGLDisplay = EGL14.EGL_NO_DISPLAY;
/**
* EGL上下文
*/
private EGLContext mEGLContext = EGL14.EGL_NO_CONTEXT;
/**
* 描述帧缓冲区配置参数
*/
private EGLConfig[] configs = new EGLConfig[1];
/**
* EGL绘图表面
*/
private EGLSurface mEglSurface;
/**
* 自定义的SurfaceTexture
* 用来接受Camera数据作二次处理
*/
private SurfaceTexture mOESSurfaceTexture;
//private MeiYanRenderer mTextureRenderer;
private ITextureRenderer mTextureRenderer;
private Class mRendererClass;
public TextureEGLHelper(String name) {
super(name);
}
public TextureEGLHelper(String name, int priority) {
super(name, priority);
}
private final class TextureHandler extends Handler {
public TextureHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case EGLMessage.MSG_INIT:
//用的是OpenGL ES 3.0
initEGLContext(3);
return;
case EGLMessage.MSG_RENDER:
//开始渲染
drawFrame();
return;
case EGLMessage.MSG_DESTROY:
//销毁
return;
default:
return;
}
}
}
public TextureEGLHelper() {
super("TextureEGLHelper");
}
public void initEgl(TextureView textureView, int textureId) {
mTextureView = textureView;
mOESTextureId = textureId;
//启动线程
mHandlerThread = new HandlerThread("Renderer Thread");
mHandlerThread.start();
mHandler = new TextureHandler(mHandlerThread.getLooper());
//线程中初始化
mHandler.sendEmptyMessage(EGLMessage.MSG_INIT);
}
public void changeRenderer(int resId){
((CameraTextureRenderer)mTextureRenderer).changeShader(resId);
}
/**
* 初始化EGL环境
*
* @param clientVersion
*/
private void initEGLContext(int clientVersion) {
//获取默认显示设备
mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
throw new RuntimeException("eglGetDisplay error: " + EGL14.eglGetError());
}
//存放EGL版本号
int[] version = new int[2];
version[0] = 3;
if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) {
throw new RuntimeException("eglInitialize error: " + EGL14.eglGetError());
}
//配置列表
int[] attributes = {
EGL14.EGL_BUFFER_SIZE, 32,
EGL14.EGL_RED_SIZE, 8,
EGL14.EGL_GREEN_SIZE, 8,
EGL14.EGL_BLUE_SIZE, 8,
EGL14.EGL_ALPHA_SIZE, 8,
EGL14.EGL_RENDERABLE_TYPE, 4,
EGL14.EGL_SURFACE_TYPE, EGL14.EGL_WINDOW_BIT,
EGL14.EGL_NONE
};
int[] numConfigs = new int[1];
//EGL选择配置
if (!EGL14.eglChooseConfig(mEGLDisplay, attributes, 0, configs, 0, configs.length, numConfigs, 0)) {
throw new RuntimeException("eglChooseConfig error: " + EGL14.eglGetError());
}
//获取TextureView内置的SurfaceTexture作为EGL的绘图表面,也就是跟系统屏幕打交道
SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();
if (surfaceTexture == null) {
throw new RuntimeException("surfaceTexture is null");
}
//创建EGL显示窗口
final int[] surfaceAttributes = {EGL14.EGL_NONE};
mEglSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, configs[0], surfaceTexture, surfaceAttributes, 0);
//创建上下文环境
int[] contextAttributes = {
EGL14.EGL_CONTEXT_CLIENT_VERSION, clientVersion,
EGL14.EGL_NONE
};
mEGLContext = EGL14.eglCreateContext(mEGLDisplay, configs[0], EGL14.EGL_NO_CONTEXT, contextAttributes, 0);
if (mEGLDisplay == EGL14.EGL_NO_DISPLAY || mEGLContext == EGL14.EGL_NO_CONTEXT) {
throw new RuntimeException("eglCreateContext fail error: " + EGL14.eglGetError());
}
if (!EGL14.eglMakeCurrent(mEGLDisplay, mEglSurface, mEglSurface, mEGLContext)) {
throw new RuntimeException("eglMakeCurrent error: " + EGL14.eglGetError());
}
//加载渲染器
try {
mTextureRenderer = (ITextureRenderer)(mRendererClass.getConstructor(Integer.class).newInstance(mOESTextureId));
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
if (mTextureRenderer == null) {
mTextureRenderer = new CameraTextureRenderer(mOESTextureId);
}
// mTextureRenderer = new MeiYanRenderer(mOESTextureId);
mTextureRenderer.onSurfaceCreated();
}
public void setRenderer(Class rendererClz){
this.mRendererClass = rendererClz;
}
public ITextureRenderer getRenderer(){
return mTextureRenderer;
}
public void onSurfaceChanged(int width, int height) {
//设置视口
mTextureRenderer.onSurfaceChanged(width, height);
}
/**
* 绘制帧画面,双缓冲 送显
*/
private void drawFrame() {
if (mTextureRenderer != null) {
//指定mEGLContext为当前系统的EGL上下文
EGL14.eglMakeCurrent(mEGLDisplay, mEglSurface, mEglSurface, mEGLContext);
//调用渲染器绘制
mTextureRenderer.onDrawFrame(mOESSurfaceTexture);
//交换缓冲区,android使用双缓冲机制,所以我们绘制的都是在后台缓冲区,通过交换将后台缓冲区变为前台显示区,下一帧的绘制仍然在后台缓冲区
EGL14.eglSwapBuffers(mEGLDisplay, mEglSurface);
}
}
@Override
public void onFrameAvailable(SurfaceTexture surfaceTexture) {
if (mHandler != null) {
//通知子线程渲染
mHandler.sendEmptyMessage(EGLMessage.MSG_RENDER);
}
}
public SurfaceTexture loadOESTexture() {
//加载自定义的SurfaceTexture传递给相机
mOESSurfaceTexture = new SurfaceTexture(mOESTextureId);
mOESSurfaceTexture.setOnFrameAvailableListener(this);
return mOESSurfaceTexture;
}
public SurfaceTexture getCurrentOESTexture(){
return mOESSurfaceTexture;
}
/**
* 销毁
* 释放
*/
public void onDestroy() {
if (mHandlerThread != null) {
mHandlerThread.quitSafely();
}
if (mHandler != null) {
mHandler.removeCallbacksAndMessages(null);
}
}
}
OpenGL ES 基本使用
写一个Renderer渲染器
自定义一个Renderer渲染器,传入上述TextureEGLHelper使用
这里是自定义的,如果用GLSurfaceView,自定义的Renderer则完成GLSurfaceView.Renderer接口
自定义ITextureRenderer接口
public interface ITextureRenderer {
/**
* Surface创建时候
*/
void onSurfaceCreated();
/**
* 横竖屏切换时候
*/
void onSurfaceChanged(int width, int height);
/**
* 绘制帧
*/
void onDrawFrame(SurfaceTexture surfaceTexture);
}
写一个CameraTextureRenderer完成ITextureRenderer接口
public class CameraTextureRenderer implements ITextureRenderer {
private static final String TAG = "CameraTextureRenderer";
private FloatBuffer mVertexBuffer;
/**
* OES纹理ID
*/
private int mOESTextureId = -1;
/**
* 程序
*/
private int mShaderProgram = -1;
private int aPositionLocation = -1;
private int aTextureCoordLocation = -1;
private int uTextureMatrixLocation = -1;
private int uTextureSamplerLocation = -1;
int mTextureCoordOffsetLocation = -1;
int mProgressLocation = -1;
public static final String POSITION_ATTRIBUTE = "aPosition";
public static final String TEXTURE_COORD_ATTRIBUTE = "aTextureCoord";
public static final String TEXTURE_MATRIX_UNIFORM = "uTextureMatrix";
public static final String TEXTURE_SAMPLER_UNIFORM = "uTextureSampler";
private static final int POSITION_SIZE = 2;
private static final int TEXTURE_SIZE = 2;
private static final int STRIDE = (POSITION_SIZE + TEXTURE_SIZE) * 4;
/**
* 前两个为顶点坐标
* 后两个为纹理坐标
* 只有三角形 正方形要两个三角形画
*/
private static final float[] VERTEX_DATA = {
1.0f, 1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 0.0f, 1.0f,
-1.0f, -1f, 0.0f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, 0f, 0.0f,
1.0f, -1.0f, 1.0f, 0.0f
};
/**
* 变换矩阵
*/
private float[] transformMatrix = new float[16];
public CameraTextureRenderer(int OESTextureId) {
mOESTextureId = OESTextureId;
mVertexBuffer = loadVertexBuffer(VERTEX_DATA);
}
public FloatBuffer loadVertexBuffer(float[] vertexData) {
FloatBuffer buffer = ByteBuffer.allocateDirect(vertexData.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
buffer.put(vertexData, 0, vertexData.length).position(0);
return buffer;
}
boolean needChange = false;
int targetResId;
public void changeShader(int targetResId) {
needChange = true;
this.targetResId = targetResId;
}
/**
*真正切换渲染器,在onDrawFrame的时候切换
*/
private void realChange(){
int vertexShader = ShaderUtils.compileVertexShader(ResReadUtils.readResource(R.raw.vertex_texture_shader));
int fragmentShader = ShaderUtils.compileFragmentShader(ResReadUtils.readResource(targetResId));
mShaderProgram = ShaderUtils.linkProgram(vertexShader, fragmentShader);
//开始使用程序
GLES30.glUseProgram(mShaderProgram);
aPositionLocation = GLES30.glGetAttribLocation(mShaderProgram, CameraTextureRenderer.POSITION_ATTRIBUTE);
aTextureCoordLocation = GLES30.glGetAttribLocation(mShaderProgram, CameraTextureRenderer.TEXTURE_COORD_ATTRIBUTE);
uTextureMatrixLocation = GLES30.glGetUniformLocation(mShaderProgram, CameraTextureRenderer.TEXTURE_MATRIX_UNIFORM);
uTextureSamplerLocation = GLES30.glGetUniformLocation(mShaderProgram, CameraTextureRenderer.TEXTURE_SAMPLER_UNIFORM);
mTextureCoordOffsetLocation = GLES30.glGetUniformLocation(mShaderProgram, "uTextureCoordOffset");
mProgressLocation = GLES30.glGetUniformLocation(mShaderProgram, "progress");
needChange = false;
}
@Override
public void onSurfaceCreated() {
//首先是顶点着色器
int vertexShader = ShaderUtils.compileVertexShader(ResReadUtils.readResource(R.raw.vertex_texture_shader));
//其次是片元着色器
// final int fragmentShader = ShaderUtils.compileFragmentShader(ResReadUtils.readResource(R.raw.fragment_grid_texture_shader));
// final int fragmentShader = ShaderUtils.compileFragmentShader(ResReadUtils.readResource(R.raw.fragment_soul_texture_shader));
// final int fragmentShader = ShaderUtils.compileFragmentShader(ResReadUtils.readResource(R.raw.fragment_shake_texture_shader));
// final int fragmentShader = ShaderUtils.compileFragmentShader(ResReadUtils.readResource(R.raw.fragment_binaryzation_2_texture_shader));
int fragmentShader = ShaderUtils.compileFragmentShader(ResReadUtils.readResource(R.raw.fragment_normal_texture_shader));
//将顶点着色器和片元着色器编译并连接到一个Program 程序中
mShaderProgram = ShaderUtils.linkProgram(vertexShader, fragmentShader);
//使用该程序
GLES30.glUseProgram(mShaderProgram);
//获取程序中着色器的变量位置,用于后续将应用程序中的变量,通过类似GLSL30.glUniform1f()的方法传入.glsl着色器代码中去
aPositionLocation = GLES30.glGetAttribLocation(mShaderProgram, CameraTextureRenderer.POSITION_ATTRIBUTE);
aTextureCoordLocation = GLES30.glGetAttribLocation(mShaderProgram, CameraTextureRenderer.TEXTURE_COORD_ATTRIBUTE);
uTextureMatrixLocation = GLES30.glGetUniformLocation(mShaderProgram, CameraTextureRenderer.TEXTURE_MATRIX_UNIFORM);
uTextureSamplerLocation = GLES30.glGetUniformLocation(mShaderProgram, CameraTextureRenderer.TEXTURE_SAMPLER_UNIFORM);
mTextureCoordOffsetLocation = GLES30.glGetUniformLocation(mShaderProgram, "uTextureCoordOffset");
mProgressLocation = GLES30.glGetUniformLocation(mShaderProgram, "progress");
}
@Override
public void onSurfaceChanged(int width, int height) {
GLES30.glViewport(0, 0, width, height);
final float aspectRatio = width > height ?
(float) width / (float) height :
(float) height / (float) width;
if (width > height) {
//横屏
Matrix.orthoM(transformMatrix, 0, -aspectRatio, aspectRatio, -1f, 1f, -1f, 1f);
} else {
//竖屏
Matrix.orthoM(transformMatrix, 0, -1f, 1f, -aspectRatio, aspectRatio, -1f, 1f);
}
}
private float mProgress = 0.0f;
private int mFrames = 0;
private static final int mMaxFrames = 8;
private static final int mSkipFrames = 4;
@Override
public void onDrawFrame(SurfaceTexture surfaceTexture) {
//在onDrawFrame中进行Renderer切换(或者说 Program切换)
if (needChange){
realChange();
}
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
//动画的进度,可以通过帧进度来表现
mProgress = (float) mFrames / mMaxFrames;
if (mProgress > 1f) {
mProgress = 0f;
}
mFrames++;
if (mFrames > mMaxFrames + mSkipFrames) {
mFrames = 0;
}
surfaceTexture.updateTexImage();
//获得纹理矩阵
surfaceTexture.getTransformMatrix(transformMatrix);
//绑定Texture
GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
GLES30.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mOESTextureId);
//传入数据
GLES30.glUniform1i(uTextureSamplerLocation, 0);
GLES30.glUniformMatrix4fv(uTextureMatrixLocation, 1, false, transformMatrix, 0);
float textureCoordOffset = 0.01f * mProgress;
GLES30.glUniform1f(mTextureCoordOffsetLocation, textureCoordOffset);
float progress = mProgress;
GLES30.glUniform1f(mProgressLocation, progress);
mVertexBuffer.position(0);
GLES30.glEnableVertexAttribArray(aPositionLocation);
GLES30.glVertexAttribPointer(aPositionLocation, 2, GLES30.GL_FLOAT, false, STRIDE, mVertexBuffer);
mVertexBuffer.position(2);
GLES30.glEnableVertexAttribArray(aTextureCoordLocation);
GLES30.glVertexAttribPointer(aTextureCoordLocation, 2, GLES30.GL_FLOAT, false, STRIDE, mVertexBuffer);
GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, 6);
}
}
代码中通过 GLES30.glUniform()的方法,将数据传入.glsl的 uniform变量中
几个glsl着色器代码
顶点着色器
#version 300 es
layout (location = 0) in vec4 aPosition;
layout (location = 1) in vec4 aTextureCoord;
//纹理矩阵
uniform mat4 uTextureMatrix;
out vec2 vTextureCoord;
void main() {
gl_Position = aPosition;
gl_PointSize = 10.0;
vTextureCoord = (uTextureMatrix * aTextureCoord).xy;
}
片元着色器
分屏
#version 300 es
//外部纹理
#extension GL_OES_EGL_image_external_essl3 : require
precision mediump float;
uniform samplerExternalOES uTextureSampler;
in vec2 vTextureCoord;
out vec4 vFragColor;
const float horizontal = 2.0;//(1) 封装 横竖屏
const float vertical = 1.0;
void main()
{
float horizontalCount = max(horizontal, 1.0);// (2)参数控制,分屏必须是1个以上的屏幕
float verticalCount = max(vertical, 1.0);
float ratio = verticalCount / horizontalCount;// (3)
vec2 originSize = vec2(1.0, 1.0);
vec2 newSize = originSize;
if (ratio > 1.0) {
newSize.y = 1.0 / ratio;
} else {
newSize.x = ratio;
}
//注意 裁剪 因为分屏会导致小格子的长宽比例和原图像的长宽不同
vec2 offset = (originSize - newSize) / 2.0;// (4)计算新的图像在原始图像中的偏移量。因为我们的图像要居中裁剪,所以要计算出裁剪后的偏移。比如 (2.0 / 3.0, 1.0) 的图像,对应的 offset 是 (1.0 / 6.0, 0.0)
vec2 position = offset + mod(vTextureCoord * min(horizontalCount, verticalCount), newSize);// (5)将原始的纹理坐标,乘上 horizontalCount 和 verticalCount 的较小者,然后对新的尺寸进行求模运算。
vFragColor = texture(uTextureSampler, position);// (6)通过新的计算出来的纹理坐标,从纹理中读出相应的颜色值输出。
}
灵魂出窍
#version 300 es
//外部纹理
#extension GL_OES_EGL_image_external_essl3 : require
precision mediump float;
uniform samplerExternalOES uTextureSampler;
in vec2 vTextureCoord;
out vec4 vFragColor;
uniform float progress;
void main()
{
float duration = 0.7;
float maxAlpha = 0.4;
float maxScale = 1.8;
float alpha = maxAlpha * (1.0 - progress);
float scale = 1.0 + (maxScale - 1.0) * progress;
float weakX = 0.5 + (vTextureCoord.x - 0.5) / scale;
float weakY = 0.5 + (vTextureCoord.y - 0.5) / scale;
vec2 weakTextureCoords = vec2(weakX,weakY);
vec4 weakMask= texture(uTextureSampler,weakTextureCoords);
vec4 mask = texture(uTextureSampler,vTextureCoord);
vFragColor = mask * (1.0 - alpha) + weakMask * alpha;
// vFragColor = mix( mask * (1.0 - alpha) ,weakMask * alpha,0.5);
}
灵魂抖动
#version 300 es
//外部纹理
#extension GL_OES_EGL_image_external_essl3 : require
precision mediump float;
uniform samplerExternalOES uTextureSampler;
in vec2 vTextureCoord;
out vec4 vFragColor;
uniform float uTextureCoordOffset;
void main()
{
//理解为 颜色取的是相对当前像素右上角或者左下角某个另外一个像素的颜色
vec4 blue = texture(uTextureSampler, vTextureCoord);
vec4 green = texture(uTextureSampler, vec2(float(vTextureCoord.x + uTextureCoordOffset), float(vTextureCoord.y + uTextureCoordOffset)));
vec4 red = texture(uTextureSampler, vec2(float(vTextureCoord.x - uTextureCoordOffset), float(vTextureCoord.y - uTextureCoordOffset)));
vFragColor = vec4(red.r,green.g,blue.b,blue.a);
}
二值化
#version 300 es
//外部纹理
#extension GL_OES_EGL_image_external_essl3 : require
precision mediump float;
uniform samplerExternalOES uTextureSampler;
in vec2 vTextureCoord;
out vec4 vFragColor;
void main()
{
//对xyz操作
vec4 color = texture(uTextureSampler, vTextureCoord);
// 黑白滤镜
float gray =(0.299*color.r + 0.587*color.g + 0.184*color.b);
if (gray > 0.5f){
gray = 0.9f;
} else {
gray = 0.1f;
}
vFragColor = vec4(gray, gray, gray, color.a);
}