使用两个VAO/VBO渲染不同对象时,顶点着色器该如何编写?
问题解决:双VAO/VBO渲染对象的顶点着色器修正
你的核心错误是把两个对象的渲染逻辑都塞进了同一个顶点着色器main函数,顶点着色器是针对单次绘制调用的每个顶点执行的,最后一行的gl_Position赋值会完全覆盖前面的,导致只有第二个对象的计算逻辑生效,而第一个对象的VAO并没有提供vertexPositionMotorcycle这类属性,自然无法正确渲染。
下面是两种可行的修正方案:
方案1:共用同一套着色器(推荐)
这是最常用的方式,因为两个对象的渲染逻辑本质是一致的,只是模型矩阵、纹理等参数不同。
修改后的顶点着色器
只保留一套通用变量,不要给变量加对象后缀:
#version 330 core layout (location = 0) in vec3 vertexPosition; layout (location = 1) in vec3 vertexNormal; layout (location = 2) in vec3 vertexColor; layout (location = 3) in vec2 vertexTexCoord; uniform mat4 model_matrix; uniform mat4 normal_matrix; uniform mat4 projection_view_matrix; out vec3 worldSpacePosition; out vec3 worldSpaceNormal; out vec3 objectColor; out vec2 shaderTexCoord; void main() { worldSpacePosition = (model_matrix * vec4(vertexPosition, 1.0f)).xyz; worldSpaceNormal = (normal_matrix * vec4(vertexNormal, 0.0f)).xyz; // 法线用0.0f避免平移影响 objectColor = vertexColor; shaderTexCoord = vertexTexCoord; gl_Position = projection_view_matrix * vec4(worldSpacePosition, 1.0f); }
对应Render代码调整
每次绘制前更新Uniform参数,绑定对应纹理和VAO:
// 绘制第一个对象(卡车) // 1. 设置纹理:绑定卡车纹理到纹理单元0,传递给着色器的sampler glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, truckTexture); glUniform1i(glGetUniformLocation(shaderProgram, "objectTexture"), 0); // 2. 更新Uniform参数为卡车的矩阵 glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model_matrix"), 1, GL_FALSE, &truckModelMatrix[0][0]); glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "normal_matrix"), 1, GL_FALSE, &truckNormalMatrix[0][0]); // 投影视图矩阵如果两个对象共用,只需要设置一次,不用重复设置 // glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "projection_view_matrix"), 1, GL_FALSE, &projViewMatrix[0][0]); // 3. 绑定VAO并绘制 glBindVertexArray(vao1); glDrawArrays(GL_TRIANGLES, 0, indicesNo1); // 绘制第二个对象(摩托车) // 1. 设置纹理:绑定摩托车纹理到纹理单元0 glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, motorcycleTexture); glUniform1i(glGetUniformLocation(shaderProgram, "objectTexture"), 0); // 2. 更新Uniform参数为摩托车的矩阵 glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model_matrix"), 1, GL_FALSE, &motorcycleModelMatrix[0][0]); glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "normal_matrix"), 1, GL_FALSE, &motorcycleNormalMatrix[0][0]); // 3. 绑定VAO并绘制 glBindVertexArray(vao2); glDrawArrays(GL_TRIANGLES, 0, indicesNo2);
方案2:使用两套独立的着色器程序
如果两个对象的渲染逻辑差异极大(比如一个是卡通渲染,一个是PBR),可以为每个对象单独编写着色器程序。
- 为卡车编写一套顶点/片段着色器,编译链接成
truckShaderProgram - 为摩托车编写另一套顶点/片段着色器,编译链接成
motorcycleShaderProgram
Render代码调整为:
// 绘制卡车 glUseProgram(truckShaderProgram); // 设置卡车的纹理、矩阵Uniform glBindVertexArray(vao1); glDrawArrays(GL_TRIANGLES, 0, indicesNo1); // 绘制摩托车 glUseProgram(motorcycleShaderProgram); // 设置摩托车的纹理、矩阵Uniform glBindVertexArray(vao2); glDrawArrays(GL_TRIANGLES, 0, indicesNo2);
关于gl_InstanceID的说明
gl_InstanceID是用于实例化绘制(比如glDrawArraysInstanced)的,当你需要一次性绘制大量相同模型(比如一片树林)时,用它来区分不同实例的参数。而你现在是两次独立的glDrawArrays调用,每次调用都是处理单个对象,完全不需要用到gl_InstanceID。
内容的提问来源于stack exchange,提问作者noobcloud
相关产品推荐
相关产品推荐

