Kotlin/Android/OpenGL ES:使用法线实现3D光照失败,报错无效顶点属性索引(期望索引:4294967295,最大索引:16)
这个错误的核心原因很明确:你看到的4294967295其实是**-1的无符号整数表示**,意味着glGetAttribLocation(program, "vertexNormal")返回了-1——OpenGL在你的着色器程序里找不到vertexNormal这个属性变量。导致这个问题的主要原因有两个,我们一步步拆解并解决:
问题根源分析
片段着色器未使用顶点着色器的输出,导致编译器优化删除了法线相关代码
你的顶点着色器里计算了fragColour这个varying变量,但片段着色器完全没用到它,反而用了一个无关的uniform vec4 vertexColour。GLSL编译器会自动移除所有未被使用的代码,包括vertexNormal的读取和变换逻辑,最终vertexNormal这个attribute被从程序中彻底剔除,自然找不到它的索引。顶点着色器和片段着色器的
vertexColour定义不一致
顶点着色器里vertexColour是attribute vec3(顶点属性),但片段着色器里却定义成了uniform vec4(全局变量),这不仅逻辑错误,也加剧了编译器的优化行为。
分步解决方案
1. 修正片段着色器
让片段着色器正确使用顶点着色器传递过来的fragColour变量,这样顶点着色器里的法线计算逻辑就不会被优化掉:
private val fragmentShaderCode = "precision mediump float;" + "varying vec3 fragColour;" + "void main() {" + " gl_FragColor = vec4(fragColour, 1.0);" + "}"
2. 修复顶点着色器的clamp操作
你原来的clamp(fragColour, 0.0, 1.0);没有将结果赋值回fragColour,等于无效操作,修正为:
// 替换原来的clamp行 fragColour = clamp(fragColour, 0.0, 1.0);
3. 修正绘制代码的逻辑错误
3.1 补充顶点颜色的缓冲区(假设你有对应顶点颜色的数组triangleColours)
private var vertexColourBuffer: FloatBuffer = ByteBuffer.allocateDirect(triangleColours.size * 4).run { order(ByteOrder.nativeOrder()) asFloatBuffer().apply { put(triangleColours) position(0) } }
3.2 调整绘制流程:先激活程序,再获取属性/Uniform位置
属性和Uniform的位置是和当前激活的程序绑定的,必须先调用glUseProgram再获取位置,同时修正vertexColour的处理方式(它是顶点属性,不是Uniform):
fun draw(mvpMatrix: FloatArray) { // 先激活着色器程序 glUseProgram(program) // 获取各个属性和Uniform的位置 positionHandle = glGetAttribLocation(program, "vertexPosition") val vertexColourHandle = glGetAttribLocation(program, "vertexColour") vertexNormalLocation = glGetAttribLocation(program, "vertexNormal") vPMatrixHandle = glGetUniformLocation(program, "modelView") // 设置模型视图矩阵 glUniformMatrix4fv(vPMatrixHandle, 1, false, mvpMatrix, 0) // 配置顶点位置属性 glVertexAttribPointer( positionHandle, 3, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer ) glEnableVertexAttribArray(positionHandle) // 配置顶点颜色属性 glVertexAttribPointer( vertexColourHandle, 3, GLES20.GL_FLOAT, false, 0, vertexColourBuffer ) glEnableVertexAttribArray(vertexColourHandle) // 配置法线属性 glVertexAttribPointer( vertexNormalLocation, 3, GLES20.GL_FLOAT, false, 0, normalsBuffer ) glEnableVertexAttribArray(vertexNormalLocation) // 绘制图形 glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount) // 禁用属性数组 glDisableVertexAttribArray(positionHandle) glDisableVertexAttribArray(vertexColourHandle) glDisableVertexAttribArray(vertexNormalLocation) }
额外注意事项
- 确保你的法线数组
normals的长度和顶点数量匹配(每个顶点对应一个法线向量),否则绘制时会出现异常。 - 如果你不需要顶点颜色,可以直接在顶点着色器里写死反射系数,不用保留
vertexColour属性,进一步简化代码。
内容的提问来源于stack exchange,提问作者JLK700

