Android OpenGL ES 入门系列(五) --- 应用投影和相机视角
程序员文章站
2023-12-31 16:58:04
...
转载请注明出处
本文出自Hansion的博客
本章介绍如何创建投影和相机视图,并应用于GLSurfaceView中绘制的形状上。
在OpenGL ES环境中,利用投影和相机视角可以让绘制对象的显示更接近人眼看到的样子,这是一种视角模拟,它是通过对绘制对象坐标进行数学变换实现的。
投影(Projection):基于GLSurfaceView 的宽和高来调整绘制对象的坐标。如果没有该计算,绘制对象和GLSurfaceView的宽高比例不同,就会发生变形。投影转换通常只在OpenGL View的比例刚被建立或改变onSurfaceChanged()中计算
相机视角(Camera View):基于虚拟相机的位置调整绘制对象的坐标。而实际上OpenGL ES并没有定义相机对象,它是提供了一些方法通过转换绘制对象的显示来模拟相机视角。
定义一个投影
投影变换的数据是在 GLSurfaceView.Renderer 类的 onSurfaceChanged() 方法中计算出来的。
以下代码首先获取到GLSurfaceView的宽高,并使用Matrix.frustumM()方法填充一个投影变换矩阵
public class MyGLRenderer implements GLSurfaceView.Renderer {
...
// mMVPMatrix is an abbreviation for "Model View Projection Matrix"
private final float[] mMVPMatrix = new float[16];
private final float[] mProjectionMatrix = new float[16];
private final float[] mViewMatrix = new float[16];
...
@Override
public void onSurfaceChanged(GL10 gl10, int width, int height) {
//glViewport用于告诉OpenGL应把渲染之后的图形绘制在窗体的哪个部位、大小
GLES20.glViewport(0, 0, width, height);
//计算宽高比
float ratio = (float) width / height;
//填充投影矩阵 mProjectionMatrix
Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
}
...
}
对绘制对象仅使用投影变换,往往会导致看不到显示的东西,所以还需进行相机视角转换使绘制对象显示在屏幕上。
定义一个相机视角
下面的代码在 onDrawFrame()方法中使用 Matrix.setLookAtM() 方法计算相机视角变换,再与之前计算的投影矩阵结合起来,然后将组合后的变换矩阵传递给绘制对象:
@Override
public void onDrawFrame(GL10 gl10) {
// glClear表示清除缓冲 传入GL_COLOR_BUFFER_BIT指要清除颜色缓冲
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
// 设置相机视角
Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
// 计算投影和视图变换
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
// 绘制形状
mTriangle.draw(mMVPMatrix);
}
应用投影和相机视角转换
我们在Triangle中之前定义的顶点着色器vertexShaderCode中增加一个变量uMVPMatrix
然后修改draw方法,使其能接收组合后的变换矩阵,并应用于绘制对象上:
public class Triangle {
//顶点着色器
private final String vertexShaderCode =
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"void main() {" +
" gl_Position = uMVPMatrix * vPosition;" +
"}";
// 用于访问和设置视图转换
private int mMVPMatrixHandle;
...
public void draw(float[] mvpMatrix) {
...
// 获取片元着色器的vColor成员
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
// 得到绘制对象的变换矩阵句柄
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
// 将投影和视图转换传递给着色器
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
// 设置绘制颜色
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
// 绘制形状
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
// 禁用顶点属性数组
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
}
运行结果
此时就是按着正确的比例显示图形了。
参考: