问:学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
代码语言:javascript复制目前的坐标系如下: 三个点从右上角开始逆时针,白、红、绿
//顶点数组
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,//绿色
};
代码语言:javascript复制由三点进行绘制三角形,绘制时使用
GLES30.GL_TRIANGLES
即可
GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, vertexes.length / VERTEX_DIMENSION);
1.2 三角形三种模式比较
代码语言:javascript复制绘制三角形有三种模式,另外两个是:
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,//蓝色
};
代码语言:javascript复制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中最重要的是处理顶点和颜色的数据 圆形的绘制无非就是找到那些顶点在哪里,根据三角函数很容易求得
代码语言:javascript复制下面的图很好的体现了这些点的坐标是如何确定的
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 贴图纹理坐标
代码语言:javascript复制要注意,贴图的纹理坐标系是一个二维系,原点在左上角,注意和顶点系区分 下面是
顶点系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 图片纹理的加载
代码语言:javascript复制这里给出一个图片加载成贴图的工具类
//贴图工具类
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着色器更改
代码语言:javascript复制主要将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 代码的使用
代码语言:javascript复制主体和前面一样,这里用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 多纹理贴图
代码语言:javascript复制上面只是贴了一张图,那如何将多张图传入着色器呢?
#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;// 混合两个颜色值
}
复制代码
代码语言:javascript复制接下来在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完成一切图片特效。下一篇将会详细介绍着色器代码的使用,你将会了解如何通过着色器的代码控制像素值以及像素的位置。本片就这样,相信你已经可以完成贴图了。