目录
一、Android中的OpenGL ES(v2.0)
1、java层实现
2、java + C++实现
3、C++层实现
ES 其实应该算是封装了,不然怎么有一个libEGL.so ,再有libGLESv2.so 这些库呢?
EGL和OpenGL的关系
注:上面的关系不仅限于Android平台,IOS、Windows等其他平台也是一样的!
如上图 EGL 就是 surface 和 openGL的桥梁。
创建C++项目并导入OpenGL库
1、可以使用NDK
2、在CMakeLists.txt中导入OpenGL 相关的库
导入方式:
其作用:
EGL : EGL环境相关的库
GLESv2 :OpenGL ES 2.0的库
android : ANativeWindow 相关库
二、EGL创建流程
1、得到默认的显示设备(就是窗口) -- eglGetDisplay
2、初始化默认显示设备 -- eglInitialize
3、设置显示设备的属性
4、从系统中获取对应属性的配置 -- eglChooseConfig
5、创建EglContext -- eglCreateContext
6、创建渲染的Surface -- eglCreateWindowSurface
7、绑定EglContext和Surface到显示设备中 -- eglMakeCurrent
8、刷新数据,显示渲染场景 -- eglSwapBuffers,这个函数的作用就是sureface里面的数据提交到显示设备上去。
int WlEglHelper::initEgl(EGLNativeWindowType window) {
//1、得到默认的显示设备(就是窗口)
mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if(mEglDisplay == EGL_NO_DISPLAY)
{
LOGE("eglGetDisplay error");
return -1;
}
//2、初始化默认显示设备
EGLint *version = new EGLint[2];
if(!eglInitialize(mEglDisplay, &version[0], &version[1]))
{
LOGE("eglInitialize error");
return -1;
}
//3、设置显示设备的属性
const EGLint attribs[] = {
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_DEPTH_SIZE, 8,
EGL_STENCIL_SIZE, 8,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_NONE
};
EGLint num_config;
if(!eglChooseConfig(mEglDisplay, attribs, NULL, 1, &num_config))
{
LOGE("eglChooseConfig error 1");
return -1;
}
//4、从系统中获取对应属性的配置
if(!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_config, &num_config))
{
LOGE("eglChooseConfig error 2");
return -1;
}
//5、创建EglContext
int attrib_list[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attrib_list);
if(mEglContext == EGL_NO_CONTEXT)
{
LOGE("eglCreateContext error");
return -1;
}
//6、创建渲染的Surface
mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, window, NULL);
if(mEglSurface == EGL_NO_SURFACE)
{
LOGE("eglCreateWindowSurface error");
return -1;
}
//7、绑定EglContext和Surface到显示设备中
if(!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext))
{
LOGE("eglMakeCurrent error");
return -1;
}
LOGD("egl init success! ");
return 0;
}
int WlEglHelper::swapBuffers() {
if(mEglDisplay != EGL_NO_DISPLAY && mEglSurface != EGL_NO_SURFACE)
{ //刷新数据,显示渲染场景 -- eglSwapBuffers
if(eglSwapBuffers(mEglDisplay, mEglSurface))
{
return 0;
}
}
return -1;
}
重要变量:
1、EGLDisplay -- 显示设备
2、EGLSurface – 后端显示的surface(缓冲)
3、EGLContext – egl上下文
4、EGLConfig – egl配置
自定义SurfaceView并测试EGL环境
1、继承SurfaceView
2、得到surface
3、实例化EGL环境
4、在surface上面绘制颜色(OpenGL清屏)
EGL线程创建
surfaceCreate 只会掉用一次,surfaceChange 会在改变窗口大小的时候被调用,surfaceDraw 每次绘制的时候会调用。
三、OpenGL渲染流程
v_shader 、f_shader :是着色器语言,就是字符串哈。v_shader 定点着色器,图像要在屏幕绘制的一个坐标;f_shader 纹理着色器。
OpenGL加载shader
vertex/fragment->加载->编译->链接->program
vertex_shader:
attribute vec2 a_position;
void main(){
gl_Position = a_position;
}
注: attribute 关键字 只能在vertex中使用 , vec4表示包含四个浮点型的数据类型
gl_Position 是OpenGL里面内置的一个变量。表示定点的坐标。
片元着色器或者叫做纹理着色器 fragment_shader:
precision mediump float;
void main(){
gl_FragColor = vec4(1f,0f,0f,1f);//红、绿、蓝、α
}
注: lowp:低精度
mediump:中等精度
highp:高等精度
加载流程
1、创建shader(着色器:顶点或片元) int shader =glCreateShader(shaderType);
2、加载shader源码并编译shader glShaderSource(shader, source); glCompileShader(shader);
3、创建一个渲染程序: int program =glCreateProgram();
4、将着色器程序添加到渲染程序中: glAttachShader(program, vertexShader);
5、链接源程序: glLinkProgram(program);
四、OpenGL坐标系
以屏幕中心为原点
以屏幕左下角为原点
绘制三角形
嵌入式的OpenGL 把绘制四边形去掉的,但是可以通过绘制三角形来绘制四边形。
绘制四边形可以用下面的定点:
float vertexs[] = {
1,-1,
1,1,
-1,-1,
-1,1
};
中间两个点一定是两个三角形的共工边,才能绘制出来四边形。
五、绘制纹理
了解纹理坐标:
纹理坐标系,和屏幕坐标系一样。不管图像的大小,都会映射到0--1 的范围内
顶点着色器:vertex_shader.glsl
attribute vec4 v_Position;
attribute vec2 f_Position;
varying vec2 ft_Position;
void main() {
ft_Position = f_Position;
gl_Position = v_Position;
}
注: attribute 只能在vertex中使用
varying 用于vertex和fragment之间传递值
纹理着色器:
precision mediump float;
varying vec2 ft_Position;
uniform sampler2D sTexture;
void main() {
gl_FragColor=texture2D(sTexture, ft_Position);
}
注: uniform 用于在application中向vertex和fragment中传递值。
OpenGL ES绘制纹理过程
1、加载shader和生成program过程不变
2、创建和绑定纹理: glGenTextures(1, &textureId); glBindTexture(GL_TEXTURE_2D, textureid);
3、设置环绕和过滤方式 环绕(超出纹理坐标范围):(s==x t==y GL_REPEAT 重复)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,GL_REPEAT);
过滤(纹理像素映射到坐标点):(缩小、放大:GL_LINEAR线性)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR);
4、设置图片(byte[] data) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
5、绑定顶点坐标和纹理坐标
6、绘制图形
图像在缩放、平移的时候会用到矩阵,我们接下来学一下矩阵:
六、矩阵
初始化矩阵
单位矩阵:
vec4(x,y,z,w)
float matrix[16]={
1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1
}
参考资料: https://learnopengl-cn.github.io/01%20Getting%20started/07%20Transformations/
在定点着色器添加 矩阵变量
attribute vec4 v_Position;
attribute vec2 f_Position;
varying vec2 ft_Position;
uniform mat4 u_Matrix;
void main() {
ft_Position = f_Position;
gl_Position = v_Position * u_Matrix;
}
uniform mat4 u_Matrix;
使用
1、初始化 -- 单位矩阵
2、操作
3、glUniformMatrix4fv(u_matrix, 1, GL_FALSE, matrix);
OpenGL空间坐标系
右手坐标系:手掌对着自己,大拇指指向+x,是指直线+y,-z就垂直穿过手心。
旋转:,旋转一般都是沿着z轴旋转
角度转弧度: 弧度 = 角度 * (PI / 180.0f)
缩放:
缩放的话,一般都是沿着x,y轴进行缩放。即S3可以不写
平移:
投影矩阵-正交投影:
理解:比如我们的图片 宽:高 = 517:685 而手机的屏幕 宽:高 = 720 :1280 ,那么我们要把图片合理放在屏幕,避免失真,就需要正交投影。在视频4:3 16:9播放比价常用。
1、2D图像绘制
2、会把图像缩放到一个矩形区域内
3、控制图像长宽比不变
orthoM(float left, float right, float bottom, float top)
公式:
动态纹理的实现:
动态纹理一般用在视屏上面的水印等等
onDraw回调 中 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);