问:学OpenGL能干嘛? 答: 为所欲为。
说起OpenGLES,大家可能都敬而远之,其实它并没有想象中的那么可怕,当然也并没有那么容易 都0202年了,本系列使用OpenGLES3.0,这是一次有预谋的计
[- 多媒体 -] OpenGLES3.0 接入视频实现特效 - 引言 [ - OpenGLES3.0 - ] 第一集 主线 - 打开新世界的大门[ - OpenGLES3.0 - ] 第二集 主线 - 绘制面与图片贴图[ - OpenGLES3.0 - ] 第三集 主线 - shader着色器与图片特效[ - OpenGLES3.0 - ] 第四集 支线1 - 相机接入OpenGLES3.0实现特效[ - OpenGLES3.0 - ] 第五集 支线1 - 视频接入OpenGLES3.0实现特效[ - OpenGLES3.0 - ] 第六集 主线 - OpenGL视口详解与矩阵变换(上篇)[ - OpenGLES3.0 - ] 第七集 主线 - OpenGL视口详解与矩阵变换(下篇)[ - OpenGLES3.0 - ] 第八集 支线2 - 复杂面的绘制[ - OpenGLES3.0 - ] 第九集 支线2 - 立体图形的绘制[ - OpenGLES3.0 - ] 第十集 支线2 - OpenGLES展现建模软件3D模型这是正文的第二篇,在上一篇讲述了OpenGLES的基本使用 现在你已经能够操作着色器绘制点线了,如果你还不会,请先看第一集
1.三角形绘制三角形是OpenGL中最重要的一种图形,可以说所有的体和面都是由三角形拼组而成 所有这一节是非常重要的。
1.1 三点绘制三角形:GL_TRIANGLES目前的坐标系如下: 三个点从右上角开始逆时针,白、红、绿
//顶点数组 private final float vertexes[] = {//以逆时针顺序 1.0f, 1.0f, 0.0f, -1.0f, 1.0f, 0.0f, -1.0f, -1.0f, 0.0f, }; // 颜色数组 private final float colors[] = new float[]{ 1.0f, 1.0f, 1.0f, 1.0f,//白色 1.0f, 0.0f, 0.0f, 1.0f,//红色 0.0f, 1.0f, 0.0f, 1.0f,//绿色 };由三点进行绘制三角形,绘制时使用GLES30.GL_TRIANGLES即可
GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, vertexes.length / VERTEX_DIMENSION); 1.2 三角形三种模式比较绘制三角形有三种模式,另外两个是:GL_TRIANGLE_STRIP和GL_TRIANGLE_STRIP 下面通过四个点进行对比演示
//顶点数组 private final float vertexes[] = { //以逆时针顺序 1.0f, 1.0f, 0.0f,//原点 -1.0f, 1.0f, 0.0f, -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 0.0f, }; // 颜色数组 private final float colors[] = new float[]{ 1.0f, 1.0f, 1.0f, 1.0f,//白色 1.0f, 0.0f, 0.0f, 1.0f,//红色 0.0f, 1.0f, 0.0f, 1.0f,//绿色 0.0f, 0.0f, 1.0f, 1.0f,//蓝色 }; glDrawArrays: ---->[绘制点线]------- GLES20.GL_POINTS 绘制点 GLES20.GL_LINES 两点一线 GLES20.GL_LINE_STRIP 相邻两点一线(不连首尾) GLES20.GL_LINE_LOOP 相邻两点一线(连首尾) ---->[绘制三角形]------- GLES20.GL_TRIANGLES 三点一个(不够三个,被忽略) GLES20.GL_TRIANGLE_STRIP 相邻三点一个 GLES20.GL_TRIANGLE_FAN 第一点中心,散射到其他点矩形也就是两个三角形拼成的,所以现在绘制面的技能你已经get了。
2.圆的绘制现在你应该有所体会,OpenGL中最重要的是处理顶点和颜色的数据 圆形的绘制无非就是找到那些顶点在哪里,根据三角函数很容易求得
下面的图很好的体现了这些点的坐标是如何确定的
private void initData() { //顶点坐标数据的初始化 int verticeCount = splitCount + 2; vertexes = new float[verticeCount * 3];//坐标数据 colors = new float[verticeCount * 4];//颜色数据 float thta = 360.f / splitCount; vertexes[0] = 0; vertexes[1] = 0; vertexes[2] = 0; colors[0] = 1; colors[1] = 1; colors[2] = 1; colors[3] = 1; for (int n = 1; n <= verticeCount - 1; n++) { vertexes[n * 3] = r * cos((n - 1) * thta);//x vertexes[n * 3 + 1] = r * sin((n - 1) * thta);//y vertexes[n * 3 + 2] = 0;//z colors[4 * n] = 1; colors[4 * n + 1] = 0; colors[4 * n + 2] = 0; colors[4 * n + 3] = 1.0f; } } 复制代码3.贴图的使用没有贴图,就像肉包里没有肉馅。之前我们都是自定义颜色去给顶点着色 而贴图就是使用图形象的像素信息来给顶点着色,get贴图技能之后, 你就可以用OpenGLES 对图片进行处理和展示,甚至保存。这也是支线1的基础
3.1 贴图纹理坐标要注意,贴图的纹理坐标系是一个二维系,原点在左上角,注意和顶点系区分 下面是顶点系xoy面和纹理系的示意图,我们需要给出纹理坐标,就可以把图片贴起来:
//顶点数组 private final float vertexes[] = { //以逆时针顺序 1.0f, 1.0f, 0.0f,//原点 -1.0f, 1.0f, 0.0f, -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 0.0f, }; // 贴图坐标 private final float textureCoo[] = new float[]{ 1.0f,0.0f, 0.0f,0.0f, 0.0f,1.0f, 1.0f,1.0f, }; private static final int VERTEX_DIMENSION = 3; private static final int TEXTURE_DIMENSION = 2; 3.2 图片纹理的加载这里给出一个图片加载成贴图的工具类
//贴图工具类 public class GLTexture { /** * 资源id 加载纹理,默认重复方式:RepeatType.REPEAT * * @param ctx 上下文 * @param resId 资源id * @return 纹理id */ public static int loadTexture(Context ctx, int resId) { return loadTexture(ctx, resId, RepeatType.REPEAT); } /** * 图片加载纹理,默认重复方式:RepeatType.REPEAT * @param bitmap 图片 * @return 纹理id */ public static int loadTexture(Bitmap bitmap) { return loadTexture(bitmap, RepeatType.REPEAT); } /** * 资源id 加载纹理 * * @param ctx 上下文 * @param resId 资源id * @param repeatType 重复方式 {@link RepeatType} * @return 纹理id */ public static int loadTexture(Context ctx, int resId, RepeatType repeatType) { Bitmap bitmap = BitmapFactory.decodeResource(ctx.getResources(), resId); return loadTexture(bitmap, repeatType); } /** * bitmap 加载纹理 * * @param bitmap bitmap * @param repeatType 重复方式 {@link RepeatType} * @return 纹理id */ public static int loadTexture(Bitmap bitmap, RepeatType repeatType) { //生成纹理ID int[] textures = new int[1]; //(产生的纹理id的数量,纹理id的数组,偏移量) GLES30.glGenTextures(1, textures, 0); int textureId = textures[0]; //绑定纹理id GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureId); //采样方式MIN GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_NEAREST); GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR); int wrapS = 0; int wrapT = 0; switch (repeatType) { case NONE: wrapS = GLES30.GL_CLAMP_TO_EDGE; wrapT = GLES30.GL_CLAMP_TO_EDGE; break; case REPEAT_X: wrapS = GLES30.GL_REPEAT; wrapT = GLES30.GL_CLAMP_TO_EDGE; break; case REPEAT_Y: wrapS = GLES30.GL_CLAMP_TO_EDGE; wrapT = GLES30.GL_REPEAT; break; case REPEAT: wrapS = GLES30.GL_REPEAT; wrapT = GLES30.GL_REPEAT; break; } //设置s轴拉伸方式---重复 GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S, wrapS); //设置t轴拉伸方式---重复 GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T, wrapT); //实际加载纹理(纹理类型,纹理的层次,纹理图像,纹理边框尺寸) GLUtils.texImage2D(GLES30.GL_TEXTURE_2D, 0, bitmap, 0); bitmap.recycle(); //纹理加载成功后释放图片 return textureId; } } enum RepeatType { NONE,//不重复 REPEAT_X,//仅x轴重复 REPEAT_Y,//仅y轴重复 REPEAT//x,y重复 } 3.3 shader着色器更改主要将vsh中颜色的输入变量缓存坐标变量,在fsh中通过texture函数获取色值
---->[texture.vsh]---- #version 300 es layout (location = 0) in vec3 aPosition; layout (location = 1) in vec2 aTexCoord; uniform mat4 uMVPMatrix; out vec2 vTexCoord; void main(){ gl_Position = uMVPMatrix*vec4(aPosition.x, aPosition.y, aPosition.z, 1.0); vTexCoord = aTexCoord; } ---->[texture.fsh]---- #version 300 es precision mediump float; out vec4 outColor; in vec2 vTexCoord; uniform sampler2D sTexture; void main(){ outColor = texture(sTexture, vTexCoord); } 复制代码3.4 代码的使用主体和前面一样,这里用GLTextureTriangle类进行贴图测试
public class GLTextureTriangle { //顶点数组 private final float vertexes[] = { //以逆时针顺序 1.0f, 1.0f, 0.0f,//原点 -1.0f, 1.0f, 0.0f, -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 0.0f, }; // 贴图坐标 private final float textureCoo[] = new float[]{ 1.0f,0.0f, 0.0f,0.0f, 0.0f,1.0f, 1.0f,1.0f, }; private int program; private static final int VERTEX_DIMENSION = 3; private static final int TEXTURE_DIMENSION = 2; private FloatBuffer vertBuffer; private FloatBuffer textureCooBuffer; private int aPosition = 0;//位置的句柄 private int aTexCoord = 1;//颜色的句柄 private int uMVPMatrix;//顶点变换矩阵句柄 private int textureId;//贴图id public GLTextureTriangle(Context context) { textureId= GLTexture.loadTexture(context, R.mipmap.chaos); program = GLLoader.initProgramByAssets(context, "texture.vsh", "texture.fsh"); vertBuffer = GLBuffer.getFloatBuffer(vertexes); textureCooBuffer = GLBuffer.getFloatBuffer(textureCoo); uMVPMatrix = GLES30.glGetUniformLocation(program, "uMVPMatrix"); } public void draw(float[] mvpMatrix) { //清除颜色缓存和深度缓存 GLES30.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); // 将程序添加到OpenGL ES环境中 GLES30.glUseProgram(program); GLES30.glUniformMatrix4fv(uMVPMatrix, 1, false, mvpMatrix, 0); //启用三角形顶点的句柄 GLES30.glEnableVertexAttribArray(aPosition); //启用三角形顶点颜色的句柄 GLES30.glEnableVertexAttribArray(aTexCoord); //准备三角坐标数据 GLES30.glVertexAttribPointer( aPosition, VERTEX_DIMENSION, GLES30.GL_FLOAT, false, VERTEX_DIMENSION * 4, vertBuffer); //准备顶点颜色数据 GLES30.glVertexAttribPointer( aTexCoord, TEXTURE_DIMENSION, GLES30.GL_FLOAT, false, TEXTURE_DIMENSION * 4, textureCooBuffer); //绑定纹理 GLES30.glActiveTexture(GLES30.GL_TEXTURE0); GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureId); //绘制点 GLES30.glLineWidth(10); GLES30.glDrawArrays(GLES30.GL_TRIANGLE_FAN, 0, vertexes.length / VERTEX_DIMENSION); //禁用顶点数组 GLES30.glDisableVertexAttribArray(aPosition); GLES30.glDisableVertexAttribArray(aTexCoord); } } 3.5 多纹理贴图上面只是贴了一张图,那如何将多张图传入着色器呢?
#version 300 es precision mediump float; out vec4 outColor; in vec2 vTexCoord; uniform sampler2D uTexture; uniform sampler2D uTexture2;//多加一个纹理量 const float uT=0.5; void main(){ vec4 color= texture(uTexture, vTexCoord); vec4 color2 = texture(uTexture2, vTexCoord);//从纹理中采样出颜色值2 outColor = color*(1.0-uT) + color2*uT;// 混合两个颜色值 } 复制代码接下来在GLTextureTriangle里进行处理
---->[声明纹理id和句柄]---- private int textureId1;//贴图id private int textureId2;//贴图id private int uTexture; private int uTexture2; ---->[构造函数中加载贴图获取句柄]---- textureId1 = GLTexture.loadTexture(context, R.mipmap.girl); textureId2 = GLTexture.loadTexture(context, R.mipmap.chaos); program = GLLoader.initProgramByAssets(context, "textures.vsh","textures.fsh"); uTexture = GLES30.glGetUniformLocation(program, "uTexture"); uTexture2 = GLES30.glGetUniformLocation(program, "uTexture2"); ---->[绘制时绑定纹理,设置纹理位置]---- GLES30.glActiveTexture(GLES30.GL_TEXTURE0); GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureId1); GLES30.glUniform1i(uTexture, 0); GLES30.glActiveTexture(GLES30.GL_TEXTURE1); GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureId2); GLES30.glUniform1i(uTexture2, 1); 4. 着色器小试牛刀也许你会觉得,废了这么大半天的劲就展示了一个图片,有什么意义? 这就像你给一个不懂编程的人用计算机算出10+10=20一样,他也会觉得没什么意义 但当你演示34564*9894=341976216他就会觉得很厉害。其实本质并没有什么区别 通过着色器的编写,你就可以完成你需要的特效,比如OpenGLES3.0 接入视频实现特效 - 引言
理论上你可以通过shader完成一切图片特效。下一篇将会详细介绍着色器代码的使用,你将会了解如何通过着色器的代码控制像素值以及像素的位置。本片就这样,相信你已经可以完成贴图了。
---来自腾讯云社区的---张风捷特烈
微信扫一扫打赏
支付宝扫一扫打赏