概述
给立方体6个面进行纹理贴图,首先要能绘制立方体,然后对立方体每对表面进行纹理贴图。这里使用glDrawArrays方法直接绘制每个面的6个顶点,每个面6个定点组成2个三角形。
顶点着色器:
vertex_cubic_texture_shader.glsl
#version 300 es
layout (location = 0) in vec4 vPosition;
layout (location = 1) in vec2 aTextureCoord;
uniform mat4 vMatrix;
out vec2 vTexCoord;
void main() {
gl_Position = vMatrix * vPosition;
gl_PointSize = 10.0;
vTexCoord = aTextureCoord;
}
片元着色器:
fragment_cubic_texture_shader.glsl
#version 300 es
precision mediump float;
uniform sampler2D uTextureUnit;
in vec2 vTexCoord;
out vec4 vFragColor;
void main() {
vFragColor = texture(uTextureUnit,vTexCoord);
}
渲染器器代码
/** 立方体每个面贴不同的纹理图,以glDrawArrays方式进行绘制
*
*
* **/
public class CubicMultiSixTextureRotateRenderer implements GLSurfaceView.Renderer {
private FloatBuffer textureBuffer;
private FloatBuffer vertexBuffer;
private ShortBuffer vertexIndexBuffer;
// 立方体8个顶点坐标
private float[] vertexPoints = new float[]{
-1.0f,1.0f,1.0f, //正面左上0
-1.0f,-1.0f,1.0f, //正面左下1
1.0f,-1.0f,1.0f, //正面右下2
1.0f,1.0f,1.0f, //正面右上3
-1.0f,1.0f,-1.0f, //反面左上4
-1.0f,-1.0f,-1.0f, //反面左下5
1.0f,-1.0f,-1.0f, //反面右下6
1.0f,1.0f,-1.0f, //反面右上7
};
/**
* 立方体每个面(2个三角形)的组成顶点的索引
* 需注意:每个面的第3个点和第5个点坐标一样,是为了和下方的纹理坐标进行一一对应,所采用的排列方式。
*/
private static final short[] vertexIndexs = {
0,3,2,0,2,1, //正面
0,1,5,0,5,4, //左面
0,3,7,0,7,4, //上面
6,7,4,6,4,5, //后面
6,7,3,6,3,2, //右面
6,5,1,6,1,2 //下面
};
//每个面6个点,3个点组成一个三角形
private static final float[] vertexSixTotal={
//正面
-1.0f,1.0f,1.0f, //正面左上0
1.0f,1.0f,1.0f, //正面右上3
1.0f,-1.0f,1.0f, //正面右下2
-1.0f,1.0f,1.0f, //正面左上0
1.0f,-1.0f,1.0f, //正面右下2
-1.0f,-1.0f,1.0f, //正面左下1
//左面
-1.0f,1.0f,1.0f, //正面左上0
-1.0f,-1.0f,1.0f, //正面左下1
-1.0f,-1.0f,-1.0f, //反面左下5
-1.0f,1.0f,1.0f, //正面左上0
-1.0f,-1.0f,-1.0f, //反面左下5
-1.0f,1.0f,-1.0f, //反面左上4
//上面
-1.0f,1.0f,1.0f, //正面左上0
1.0f,1.0f,1.0f, //正面右上3
1.0f,1.0f,-1.0f, //反面右上7
-1.0f,1.0f,1.0f, //正面左上0
1.0f,1.0f,-1.0f, //反面右上7
-1.0f,1.0f,-1.0f, //反面左上4
//后面
1.0f,-1.0f,-1.0f, //反面右下6
1.0f,1.0f,-1.0f, //反面右上7
-1.0f,1.0f,-1.0f, //反面左上4
1.0f,-1.0f,-1.0f, //反面右下6
-1.0f,1.0f,-1.0f, //反面左上4
-1.0f,-1.0f,-1.0f, //反面左下5
//右面
1.0f,-1.0f,-1.0f, //反面右下6
1.0f,1.0f,-1.0f, //反面右上7
1.0f,1.0f,1.0f, //正面右上3
1.0f,-1.0f,-1.0f, //反面右下6
1.0f,1.0f,1.0f, //正面右上3
1.0f,-1.0f,1.0f, //正面右下2
//下面
1.0f,-1.0f,-1.0f, //反面右下6
-1.0f,-1.0f,-1.0f, //反面左下5
-1.0f,-1.0f,1.0f, //正面左下1
1.0f,-1.0f,-1.0f, //反面右下6
-1.0f,-1.0f,1.0f, //正面左下1
1.0f,-1.0f,1.0f, //正面右下2
};
//每个面6个点坐标,先按每个面都用同一个纹理处理
/** 纹理坐标个数要与定点坐标个数一致,顶点数组是36个,纹理坐标个数也必须一一对应是36个
* 且对应的坐标转换也要一致,下方每个面的纹理坐标都一样,所以对应的前面顶点坐标组成三角形
* 时,坐标也要跟转换后的纹理坐标一一对应。
* **/
float textureVertex[] = {
0.0f,0.0f,
1.0f,0.0f,
1.0f,1.0f,
0.0f,0.0f,
1.0f,1.0f,
0.0f,1.0f,
0.0f,0.0f,
1.0f,0.0f,
1.0f,1.0f,
0.0f,0.0f,
1.0f,1.0f,
0.0f,1.0f,
0.0f,0.0f,
1.0f,0.0f,
1.0f,1.0f,
0.0f,0.0f,
1.0f,1.0f,
0.0f,1.0f,
0.0f,0.0f,
1.0f,0.0f,
1.0f,1.0f,
0.0f,0.0f,
1.0f,1.0f,
0.0f,1.0f,
0.0f,0.0f,
1.0f,0.0f,
1.0f,1.0f,
0.0f,0.0f,
1.0f,1.0f,
0.0f,1.0f,
0.0f,0.0f,
1.0f,0.0f,
1.0f,1.0f,
0.0f,0.0f,
1.0f,1.0f,
0.0f,1.0f,
};
//相机矩阵
private final float[] mViewMatrix = new float[16];
//投影矩阵
private final float[] mProjectMatrix = new float[16];
//最终变换矩阵
private final float[] mMVPMatrix = new float[16];
//旋转矩阵 [20200623] 进行物体旋转 要与其他矩阵相乘,最终保存到mMVPMatrix中
private final float[] rotationMatrix = new float[16];
private final float[] scaleMatrix = new float[16];
private final float[] tempMatrix = new float[16];
private final float[] tempScaleMatrix = new float[16];
//渲染程序
private int mProgram;
//累计旋转过的角度
private float angle =0;
//缩放比例
private volatile float scale = 0.3f;
//是否处于持续放大状态
private volatile boolean isOnScaleSmall = false;
//上次执行缩放比例变化的时间
private long lastZoomTime = System.currentTimeMillis();
//纹理id列表
private int[] textureIds;
public CubicMultiSixTextureRotateRenderer() {
textureBuffer = ByteBuffer.allocateDirect(textureVertex.length *4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
textureBuffer.put(textureVertex);
textureBuffer.position(0);
//分配内存空间,每个浮点型占4字节空间
vertexIndexBuffer = ByteBuffer.allocateDirect(vertexIndexs.length * 4)
.order(ByteOrder.nativeOrder())
.asShortBuffer();
//传入指定的数据
vertexIndexBuffer.put(vertexIndexs);
vertexIndexBuffer.position(0);
vertexBuffer = ByteBuffer.allocateDirect(vertexSixTotal.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
//要把所有6个面的数据都塞进去
vertexBuffer.put(vertexSixTotal);
vertexBuffer.position(0);
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
//开启深度测试
GLES30.glEnable(GLES20.GL_DEPTH_TEST);
GLES30.glClearColor(0.5f,0.5f,0.5f,1.0f);
//编译顶点着色程序
String vertexShaderStr = ResReadUtils.readResource(R.raw.vertex_cubic_texture_shader);
int vertexShaderId = ShaderUtils.compileVertexShader(vertexShaderStr);
//编译片段着色程序
String fragmentShaderStr = ResReadUtils.readResource(R.raw.fragment_cubic_texture_shader);
int fragmentShaderId = ShaderUtils.compileFragmentShader(fragmentShaderStr);
//连接程序
mProgram = ShaderUtils.linkProgram(vertexShaderId, fragmentShaderId);
//在OpenGLES环境中使用程序
GLES30.glUseProgram(mProgram);
//加载纹理
int[] resList = {R.drawable.hzw1,R.drawable.hzw2,R.drawable.hzw3,R.drawable.hzw4,R.drawable.hzw5,R.drawable.hzw6};
textureIds = TextureUtils.loadTextures(AppCore.getInstance().getContext(),resList);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
GLES30.glViewport(0, 0, width, height);
float ratio = (float) width/height;
//设置透视投影
Matrix.frustumM(mProjectMatrix,0,-ratio,ratio,-1,1,3,20);
//设置相机位置
Matrix.setLookAtM(mViewMatrix,0,5,5,10.0f,//摄像机坐标
0f,0f,0f,//目标物的中心坐标
0f,1.0f,0.0f);//相机方向
//接着是摄像机顶部的方向了,如下图,很显然相机旋转,up的方向就会改变,这样就会会影响到绘制图像的角度。
//例如设置up方向为y轴正方向,upx = 0,upy = 1,upz = 0。这是相机正对着目标图像
//计算变换矩阵
Matrix.multiplyMM(tempMatrix,0,mProjectMatrix,0,mViewMatrix,0);
//先初始化为单位正交矩阵
Matrix.setIdentityM(rotationMatrix,0);
//旋转角度
Matrix.rotateM(rotationMatrix,0,0,0,0,1);
//透视矩阵*相机矩阵*旋转矩阵
Matrix.multiplyMM(mMVPMatrix,0,tempMatrix,0,rotationMatrix,0);
//进行初始旋转操作
Matrix.setIdentityM(tempMatrix,0);
Matrix.translateM(tempMatrix,0,0.25f,0,0);
Matrix.multiplyMM(mMVPMatrix,0,mMVPMatrix,0,tempMatrix,0);
// Matrix.rotateM(mViewMatrix,0,45,0,0,1);
}
@Override
public void onDrawFrame(GL10 gl) {
GLES20.glClear(GLES30.GL_COLOR_BUFFER_BIT| GLES30.GL_DEPTH_BUFFER_BIT);
//先初始化为单位正交矩阵
Matrix.setIdentityM(rotationMatrix,0);
//旋转角度
Matrix.rotateM(rotationMatrix,0,angle,0,1,0);//angle每次绘制完进行自增
//透视矩阵*相机矩阵*旋转矩阵 native层代码,估计数据放进去时做了拷贝,所以输出矩阵也可以使用mMVPMatrix保存运算后结果
// Matrix.multiplyMM(mMVPMatrix,0,mMVPMatrix,0,rotationMatrix,0); //解法错误
//正解---如果要进行自动旋转、平移、缩放等操作。则每次mMVPMatrix矩阵要重新走一遍流程进行计算,而不是在上一次mMVPMatrix的基础上进行矩阵相乘
Matrix.multiplyMM(tempMatrix,0,mProjectMatrix,0,mViewMatrix,0);
Matrix.setIdentityM(scaleMatrix,0);
//设置缩放比例
Matrix.scaleM(scaleMatrix,0,scale,scale,scale);
//执行缩放
Matrix.multiplyMM(tempScaleMatrix,0,tempMatrix,0,scaleMatrix,0);
//执行旋转
Matrix.multiplyMM(mMVPMatrix,0,tempScaleMatrix,0,rotationMatrix,0);
//左乘矩阵
int uMaxtrixLocation = GLES30.glGetUniformLocation(mProgram,"vMatrix");
// 将前面计算得到的mMVPMatrix(frustumM setLookAtM 通过multiplyMM 相乘得到的矩阵) 传入vMatrix中,与顶点矩阵进行相乘
GLES30.glUniformMatrix4fv(uMaxtrixLocation,1,false,mMVPMatrix,0);
int aPositionLocation = GLES30.glGetAttribLocation(mProgram,"vPosition");
GLES30.glEnableVertexAttribArray(aPositionLocation);
//x y z 所以数据size 是3
GLES30.glVertexAttribPointer(aPositionLocation,3,GLES30.GL_FLOAT,false,0, vertexBuffer);
int aTextureLocation = GLES20.glGetAttribLocation(mProgram,"aTextureCoord");
//纹理坐标数据 x、y,所以数据size是 2
GLES30.glVertexAttribPointer(aTextureLocation, 2, GLES30.GL_FLOAT, false, 0, textureBuffer);
//启用纹理颜色句柄
GLES30.glEnableVertexAttribArray(aTextureLocation);
//启用纹理
GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
//每个面6个顶点数据,使用不同的纹理贴图
for (int i = 0; i < textureIds.length; i++) {
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureIds[i]);
GLES30.glDrawArrays(GLES30.GL_TRIANGLES,i*6,6);
}
//禁止顶点数组的句柄
GLES30.glDisableVertexAttribArray(aPositionLocation);
GLES30.glDisableVertexAttribArray(aTextureLocation);
//每次绘制旋转1度
angle += 1;
//每隔500ms进行一次缩放比例变化
long timeNow = System.currentTimeMillis();
if (timeNow - lastZoomTime <500){
return;
}else{
lastZoomTime = timeNow;
}
if (!isOnScaleSmall){
scale += 0.1;
if (scale >=0.9){
isOnScaleSmall = true;
}
}else{
scale -= 0.1;
if (scale <= 0.3){
isOnScaleSmall = false;
}
}
}
}
实际效果如下图所示:
代码:
https://github.com/godtrace12/DOpenglTest.git
主要参考:
https://blog.csdn.net/byhook/article/details/83747146
https://blog.csdn.net/gongxiaoou/article/details/89463784
https://blog.csdn.net/byhook/article/details/83990792
https://blog.csdn.net/jklwan/article/details/104010383