问:学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完成一切图片特效。下一篇将会详细介绍着色器代码的使用,你将会了解如何通过着色器的代码控制像素值以及像素的位置。本片就这样,相信你已经可以完成贴图了。
@张风捷特烈 2020.01.11 未允禁转
我的公众号:编程之王
联系我--邮箱:1981462002@qq.com --微信:zdl1994328
~ END ~
今天的文章[ – OpenGLES3.0 – ] 第二集 主线 – 绘制面与图片贴图分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/14390.html