Android OpenGL透视投影
首先申明下,本文为笔者学习《OpenGL ES应用开发实践指南》的笔记,并加入笔者自己的理解和归纳总结。
1、透视除法
OpenGL会把每个gl_Position的x、y和z分量都除以它的w分量。当w分量用来表示距离的时候,会使得较远处的物体被移动到距离渲染区域中心更近的地方。
添加w分量创建三维图
private static final int POSITION_COMPONENT_COUNT = 4;
float[] tableVerticesWithTriangles = {
// 中心点
0f, 0f, 0f, 1.5f, 1f, 1f, 1f,
// 四个角
-0.5f, -0.8f, 0f, 1f, 0.7f, 0.7f, 0.7f,
0.5f, -0.8f, 0f, 1f, 0.7f, 0.7f, 0.7f,
0.5f, 0.8f, 0f, 2f, 0.7f, 0.7f, 0.7f,
-0.5f, 0.8f, 0f, 2f, 0.7f, 0.7f, 0.7f,
-0.5f, -0.8f, 0f, 1f, 0.7f, 0.7f, 0.7f,
// 直线
-0.5f, 0f, 0f, 1.5f, 1f, 0f, 0f,
0.5f, 0f, 0f, 1.5f, 1f, 0f, 0f,
// 点
0f, -0.25f, 0f, 1.25f, 0, 0, 1,
0f, 0.25f, 0f, 1.75f, 1, 0, 0
};显示如下
2、视椎体
视椎体是一个立方体,其远端比近端大,从而使其变成一个被截断的金字塔。视椎体有一个焦点,当你用透视投影观察一个场景时,那个场景看上去就像你的头被放在了焦点处。焦点和视椎体小端的距离被称为焦距。
3、定义透视投影
Matrix的perspectiveM方法可以生成一个透视投影
perspectiveM(float[] m, int offset,
float fovy, float aspect, float near, float far)
float[] m:目标数组,存储正交投影矩阵int mOffset:结果矩阵起始的偏移量float fovy:视眼角度float aspect:屏幕的宽高比,等于宽度/高度float near:到近处平面的距离,必须是正值。float far:到远处平面的距离,必须是正值且大于到近处平面距离。
投影矩阵,a代表焦距
a = 1.0f / Math.tan((fovy * Math.PI / 180.0f) / 2.0f)
4、绘制着色器
(1) 使用投影矩阵
Matrix.perspectiveM(projectionMatrix, 0, 45, (float)width / (float)height, 1, 10);(2) 在z轴上移动物体,先定义一个modelMatrix模型
Matrix.setIdentityM(modelMatrix, 0);
Matrix.translateM(modelMatrix, 0, 0, 0, -2.8f);(3) 利用模型移动物体
Matrix.multiplyMM(modelProjectionMatrix, 0, projectionMatrix, 0, modelMatrix, 0);
(4) OpenGLPerspectiveShaderRender类
在onSurfaceChanged方法,定义透视矩阵,并使用模型矩阵把数据显示出来。
private class OpenGLPerspectiveShaderRender implements GLSurfaceView.Renderer {
private final static String A_POSITION = "a_Position";
private final static String A_COLOR = "a_Color";
private final static String U_MATRIX = "u_Matrix";
private static final int POSITION_COMPONENT_COUNT = 2;
private static final int COLOR_COMPONENT_COUNT = 3;
private static final int BYTES_PER_FLOAT = 4;
private static final int STRIDE = (POSITION_COMPONENT_COUNT + COLOR_COMPONENT_COUNT)
* BYTES_PER_FLOAT;
private float[] projectionMatrix = new float[16];
private float[] modelMatrix = new float[16];
private float[] modelProjectionMatrix = new float[16];
private FloatBuffer vertexData;
private int mProgramId;
private int aPositionLocation, aColorLocation, uMatrixLocation;
OpenGLPerspectiveShaderRender() {
float[] tableVerticesWithTriangles = {
// 中心点
0f, 0f, 1f, 1f, 1f,
// 四个角
-0.5f, -0.8f, 0.7f, 0.7f, 0.7f,
0.5f, -0.8f, 0.7f, 0.7f, 0.7f,
0.5f, 0.8f, 0.7f, 0.7f, 0.7f,
-0.5f, 0.8f, 0.7f, 0.7f, 0.7f,
-0.5f, -0.8f, 0.7f, 0.7f, 0.7f,
// 直线
-0.5f, 0f, 1f, 0f, 0f,
0.5f, 0f, 1f, 0f, 0f,
// 点
0f, -0.25f, 0, 0, 1,
0f, 0.25f, 1, 0, 0
};
vertexData = ByteBuffer
.allocateDirect(tableVerticesWithTriangles.length * BYTES_PER_FLOAT)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
vertexData.put(tableVerticesWithTriangles);
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
mProgramId = useProgram(R.raw.ortho_vertex_shader, R.raw.ortho_fragment_shader);
// 获取Attribute位置
aPositionLocation = GLES20.glGetAttribLocation(mProgramId, A_POSITION);
aColorLocation = GLES20.glGetAttribLocation(mProgramId, A_COLOR);
uMatrixLocation = GLES20.glGetUniformLocation(mProgramId, U_MATRIX);
vertexData.position(0);
GLES20.glVertexAttribPointer(aPositionLocation, POSITION_COMPONENT_COUNT,
GLES20.GL_FLOAT, false, STRIDE, vertexData);
GLES20.glEnableVertexAttribArray(aPositionLocation);
vertexData.position(POSITION_COMPONENT_COUNT);
GLES20.glVertexAttribPointer(aColorLocation, COLOR_COMPONENT_COUNT,
GLES20.GL_FLOAT, false, STRIDE, vertexData);
GLES20.glEnableVertexAttribArray(aColorLocation);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
GLES20.glViewport(0, 0, width, height);
// 创建透视投影
Matrix.perspectiveM(projectionMatrix, 0, 45, (float)width / (float)height, 1, 10);
// 定义模型矩阵
Matrix.setIdentityM(modelMatrix, 0);
// z轴平移-2.8
Matrix.translateM(modelMatrix, 0, 0, 0, -2.8f);
// 把投影矩阵和模型矩阵相乘
Matrix.multiplyMM(modelProjectionMatrix, 0, projectionMatrix, 0, modelMatrix, 0);
}
@Override
public void onDrawFrame(GL10 gl) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
// 给着色器传递正交投影矩阵
GLES20.glUniformMatrix4fv(uMatrixLocation, 1, false, modelProjectionMatrix, 0);
// 绘制三角形
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, 6);
// 绘制直线
GLES20.glDrawArrays(GLES20.GL_LINES, 6, 2);
// 绘制点
GLES20.glDrawArrays(GLES20.GL_POINTS, 8, 1);
// 绘制点
GLES20.glDrawArrays(GLES20.GL_POINTS, 9, 1);
}
}显示如下
5、增加旋转
旋转矩阵,绕x轴、y轴和z轴旋转所用矩阵的定义
在modelMatrix进行平移以后,对模型进行旋转操作
Matrix.rotateM(modelMatrix, 0, -60, 1f, 0f, 0f);显示如下