前几篇博文讲解了OpenGLES绘制多种3D图形,并赋予丰富的色彩,但是在这些3D图形绘制过程中,有一点还没有涉及,就是纹理贴图。
今天这篇博文我会用如下六张图片对立方体进行纹理贴图,实现六个面都是贴图的3D旋转立方体
//顶点坐标属性
private int vPosition;
//纹理坐标属性
private int aTextureCoord;
//转换矩阵属性
private int mvpMatrix;
//采样器
private int sampler;
//surface宽高比
private float ratio;
之前绘制混色旋转立方体的博文:《OpenGLES:绘制一个混色旋转的3D立方体》,定义了三个数组,在onDrawFrame()绘制时直接绘制索引实现立方体
今天这篇博文对立方体的实现与之前并不相同,不再使用索引绘制,只定义并实现了两个数组:
这两个数组是必须的
//顶点坐标数组
public static float[] vertexData = new float[] {
...
}
//纹理坐标数组
public static float[] textureData = new float[] {
...
}
//顶点坐标缓冲
private FloatBuffer vertexBuffer;
//纹理坐标缓冲
private FloatBuffer textureBuffer;
纹理和贴图也定了两个数组:
图片资源Id数组并不是必须的,主要是优化纹理操作时的流程代码
//纹理id数组
private int[] textureIds;
//图片资源id数组
public static int[] textureResIds = new int[]{
R.drawable.cube_texture_1,
R.drawable.cube_texture_2,
R.drawable.cube_texture_3,
R.drawable.cube_texture_4,
R.drawable.cube_texture_5,
R.drawable.cube_texture_6
};
//MVP矩阵
private float[] mMVPMatrix = new float[16];
这几个部分的代码实现2D图形绘制基本一致
可参考以前2D绘制的相关博文,里面都有详细的代码实现
不再重复展示代码
多纹理加载在之前这篇博客中有讲解过:《OpenGLES:多纹理贴图,文字水印》,其中实现了一个LoadTexture()函数,通过多次调用该函数,传入纹理Id和图片资源Id实现纹理生成和贴图绑定。
本篇博文对这个函数进行优化,只需要传入图片资源Id数组,在函数内部根据图片数量完成纹理生成和绑定,并返回生成的纹理数组
代码如下:
//纹理Id根据贴图数量在Api内部创建,加载纹理贴图后返回纹理Id数组
public static int[] LoadTexture(Context context, int[] resIds) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
Bitmap[] bitmaps = new Bitmap[resIds.length];
// 生成纹理id
final int[] textureIds = new int[resIds.length];
glGenTextures(resIds.length, textureIds, 0);
for (int i = 0; i < resIds.length; i++) {
bitmaps[i] = BitmapFactory.decodeResource(context.getResources(), resIds[i], options);
// 绑定纹理到OpenGL
glBindTexture(GL_TEXTURE_2D, textureIds[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 加载bitmap到纹理中
GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmaps[i], 0);
// 生成MIP贴图
glGenerateMipmap(GL_TEXTURE_2D);
// 取消绑定纹理
glBindTexture(GL_TEXTURE_2D, 0);
bitmaps[i].recycle();
}
return textureIds;
}
onDrawFrame()中的关键点有两处
//MVP矩阵赋值
mMVPMatrix = TransformUtils.getCubeTextureMVPMatrix(ratio);
//设置MVP变换矩阵到着色器程序/渲染器
glUniformMatrix4fv(mvpMatrix, 1, false, mMVPMatrix, 0);
getCubeTextureMVPMatrix() 函数代码:
//计算MVP变换矩阵
public static float[] getCubeTextureMVPMatrix(float ratio) {
//初始化modelMatrix, viewMatrix, projectionMatrix
float[] modelMatrix = getIdentityMatrix(16, 0); //模型变换矩阵
float[] viewMatrix = getIdentityMatrix(16, 0); //观测变换矩阵/相机矩阵
float[] projectionMatrix = getIdentityMatrix(16, 0); //投影变换矩阵
//获取modelMatrix, viewMatrix, projectionMatrix
mCubeTextureRotateAgree = (mCubeTextureRotateAgree + 0.5f) % 360;
Matrix.rotateM(modelMatrix, 0, mCubeTextureRotateAgree, -1, -1, 1); //获取模型旋转变换矩阵
Matrix.setLookAtM(viewMatrix, 0, 0, 5, 10, 0, 0, 0, 0, 1, 0); //获取观测变换矩阵,设置相机位置
Matrix.frustumM(projectionMatrix, 0, -ratio, ratio, -1, 1, 3, 20); //获取透视投影变换矩阵,正交投影:Matrix.orthoM(...)
//计算MVP变换矩阵: mvpMatrix = projectionMatrix * viewMatrix * modelMatrix
float[] tempMatrix = new float[16];
float[] mvpMatrix = new float[16];
Matrix.multiplyMM(tempMatrix, 0, viewMatrix, 0, modelMatrix, 0);
Matrix.multiplyMM(mvpMatrix, 0, projectionMatrix, 0, tempMatrix, 0);
return mvpMatrix;
}
/********* 纹理操作:激活、绑定 **********/
glActiveTexture(GL_TEXTURE);
int count = 4;
for (int i =0; i < textureIds.length; i++) {
int first = i * count;
//绑定纹理
glBindTexture(GL_TEXTURE_2D, textureIds[i]);
//绘制正方体的表面(6个面,每个面2个三角形)
glDrawArrays(GL_TRIANGLE_FAN, first, count);
}
就是最基础的着色器实现,并无特别之处
(1).cube_texture_vertex_shader.glsl
#version 300 es
layout (location = 0) in vec4 vPosition;
layout (location = 1) in vec2 aTextureCoord;
uniform mat4 mvpMatrix;
out vec2 vTexCoord;
void main() {
gl_Position = mvpMatrix * vPosition;
vTexCoord = aTextureCoord;
}
(2).cube_texture_fragtment_shader.glsl
#version 300 es
#extension GL_OES_EGL_image_external_essl3 : require
precision mediump float;
uniform sampler2D sampler;
in vec2 vTexCoord;
out vec4 outColor;
void main(){
outColor = texture(sampler,vTexCoord);
}
3D立方体纹理贴图的讲解到此就结束了
最终实现效果如本博文开头所示