OpenGL ES中应用投影与相机视图后无法渲染三角形的问题求助
从你的描述来看,核心问题确实出在矩阵变换的配置上——当你直接使用原始顶点坐标时三角形能正常显示,说明顶点数据、着色器编译链接、绘制流程本身是没问题的,只是矩阵把三角形"移出"了可视区域。下面一步步拆解问题并给出修复方案:
1. 最关键的问题:投影矩阵的ratio未正确初始化
你在onSurfaceCreated中计算projectionMatrix时用到了ratio,但这个变量没有赋值!而且你提到onSurfaceChanged从未被调用,而ratio(宽高比)必须基于Surface的实际尺寸计算才能生成正确的投影矩阵。如果ratio是默认值(比如0),Matrix.frustumM会生成一个x轴被压缩到0的投影矩阵,直接把三角形"挤没了"。
修复:把投影矩阵计算移到onSurfaceChanged中
onSurfaceChanged是专门用来处理Surface尺寸变化的回调,必须在这里计算宽高比和投影矩阵:
override fun onSurfaceChanged(unused: GL10, width: Int, height: Int) { // 设置视口匹配Surface尺寸 GLES20.glViewport(0, 0, width, height) // 计算正确的宽高比 val ratio = width.toFloat() / height.toFloat() // 生成投影矩阵,调整near值让三角形处于可视范围内 Matrix.frustumM(projectionMatrix, 0, -ratio, ratio, -1f, 1f, 1f, 10f) }
同时要确保你的GLSurfaceView配置正确(比如在Activity中正确初始化、设置Renderer),这样onSurfaceChanged才会被触发。
2. 相机位置与裁剪平面的冲突
你当前的相机配置是:
Matrix.setLookAtM(viewMatrix, 0, 0f, 0f, -3f, 0f, 0f, 0f, 0f, 1.0f, 0.0f)
这表示相机位于(0,0,-3),看向原点(0,0,0)。而你之前的frustumM设置near=3f,意味着近裁剪平面在相机前方3单位(相机空间的z=3),而三角形刚好在原点,处于近裁剪平面的边界上,很可能被OpenGL的裁剪机制过滤掉了。
修复:调整相机位置或裁剪平面范围
两种可选方案:
- 方案A:移动相机位置:把相机移到
(0,0,3),看向原点,这样原点在相机前方3单位,设置near=1f就能让三角形处于可视范围内:
Matrix.setLookAtM(viewMatrix, 0, 0f, 0f, 3f, 0f, 0f, 0f, 0f, 1.0f, 0.0f)
- 方案B:缩小near值:保持相机位置不变,把
near改为1f,让近裁剪平面在相机前方1单位,这样原点(相机前方3单位)就处于1f-7f的可视区间内。
3. 优化draw函数的性能(可选但推荐)
你当前的draw函数每次都会调用glGetAttribLocation和glGetUniformLocation,这些查询操作比较耗时,应该把它们移到Triangle的初始化阶段,避免重复查询:
class Triangle { private lateinit var mProgram: Int private var positionHandle: Int = 0 private var mColorHandle: Int = 0 private var vPMatrixHandle: Int = 0 // 其他成员变量... init { // 编译、链接顶点/片段着色器,得到mProgram // ... // 一次性获取所有句柄 positionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition") mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor") vPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix") } fun draw(mvpMatrix: FloatArray) { GLES20.glUseProgram(mProgram) // 启用顶点属性数组并传递数据 GLES20.glEnableVertexAttribArray(positionHandle) GLES20.glVertexAttribPointer( positionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer ) // 设置颜色和变换矩阵 GLES20.glUniform4fv(mColorHandle, 1, color, 0) GLES20.glUniformMatrix4fv(vPMatrixHandle, 1, false, mvpMatrix, 0) // 绘制三角形 GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount) GLES20.glDisableVertexAttribArray(positionHandle) } }
验证步骤
- 先确认
onSurfaceChanged被正常调用,可以在方法内添加日志打印宽高值。 - 检查
ratio是否为正确的宽高比(比如竖屏时小于1,横屏时大于1)。 - 逐步恢复矩阵变换,确认三角形是否能正常显示。
内容的提问来源于stack exchange,提问作者beokay

