OpenGL双着色器程序绘制双三角形问题:仅显示一个三角形
解决双三角形仅渲染一个的问题
嘿,我之前也踩过类似的OpenGL渲染坑!结合你说的用了两个VBO和着色器但只显示一个三角形的情况,大概率是着色器程序、VBO的切换逻辑没到位,或者顶点属性的绑定有问题。下面给你一步步拆解排查和修复方案:
核心问题排查方向
1. 渲染时没有正确切换着色器和VBO
很多新手容易犯的错:画完第一个三角形后,没有切换到第二个三角形的着色器程序和VBO,导致用第一个的配置去画第二个,结果要么覆盖,要么根本没画出来。
正确的渲染循环应该是每个物体单独激活对应着色器→绑定对应VBO→启用顶点属性→绘制→禁用属性,像这样:
// 绘制第一个三角形 glUseProgram(shaderProgram1); // 激活第一个着色器 glBindBuffer(GL_ARRAY_BUFFER, VBO1); // 绑定第一个VBO glEnableVertexAttribArray(posLoc1); // 启用第一个着色器的位置属性 glVertexAttribPointer(posLoc1, 3, GL_FLOAT, GL_FALSE, 0, 0); glDrawArrays(GL_TRIANGLES, 0, 3); glDisableVertexAttribArray(posLoc1); // 绘制第二个三角形 glUseProgram(shaderProgram2); // 切换到第二个着色器 glBindBuffer(GL_ARRAY_BUFFER, VBO2); // 切换到第二个VBO glEnableVertexAttribArray(posLoc2); // 启用第二个着色器的位置属性 glVertexAttribPointer(posLoc2, 3, GL_FLOAT, GL_FALSE, 0, 0); glDrawArrays(GL_TRIANGLES, 0, 3); glDisableVertexAttribArray(posLoc2);
2. 不要硬编码顶点属性位置
别假设in_position的属性位置是固定的!一定要用glGetAttribLocation分别获取每个着色器程序对应的属性位置:
// 第一个着色器链接成功后 GLint posLoc1 = glGetAttribLocation(shaderProgram1, "in_position"); // 第二个着色器链接成功后 GLint posLoc2 = glGetAttribLocation(shaderProgram2, "in_position");
如果返回-1,说明属性名写错了或者着色器没正确链接,这时候顶点数据根本传不到GPU,三角形自然不显示。
3. 确认VBO数据上传正确
检查你上传第二个VBO数据的代码,是不是绑定了正确的VBO再调用glBufferData:
// 上传第一个三角形数据 glBindBuffer(GL_ARRAY_BUFFER, VBO1); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices1), vertices1, GL_STATIC_DRAW); // 上传第二个三角形数据 glBindBuffer(GL_ARRAY_BUFFER, VBO2); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices2), vertices2, GL_STATIC_DRAW);
要是绑定错了VBO,两个三角形的顶点数据会写到同一个缓冲区里,结果就是两个三角形重叠成一个。
4. 修复Tab切换线框的逻辑
你的flag变量要加个状态锁,防止按住Tab时重复切换。优化后的输入处理函数:
void processInput(GLFWwindow *window) { if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) glfwSetWindowShouldClose(window, true); static bool tabWasPressed = false; if(glfwGetKey(window, GLFW_KEY_TAB) == GLFW_PRESS && !tabWasPressed) { tabWasPressed = true; flag = !flag; glPolygonMode(GL_FRONT_AND_BACK, flag ? GL_LINE : GL_FILL); } if(glfwGetKey(window, GLFW_KEY_TAB) == GLFW_RELEASE) { tabWasPressed = false; } }
完整可运行的代码示例
我把这些修复点整合到你的代码框架里,补充了你没贴全的部分(比如片段着色器、资源清理等):
#include <GL/glew.h> #include <GLFW/glfw3.h> #include <iostream> bool flag = false; GLuint VBO1, VBO2; GLuint shaderProgram1, shaderProgram2; GLint posLoc1, posLoc2; // 两个不重叠的三角形顶点数据 float vertices1[] = { -0.9f, -0.5f, 0.0f, -0.0f, -0.5f, 0.0f, -0.45f, 0.5f, 0.0f }; float vertices2[] = { 0.0f, -0.5f, 0.0f, 0.9f, -0.5f, 0.0f, 0.45f, 0.5f, 0.0f }; // 红色片段着色器 const char* fragShaderRed = "#version 110\n" "void main()\n" "{\n" " gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n" "}\n"; // 蓝色片段着色器 const char* fragShaderBlue = "#version 110\n" "void main()\n" "{\n" " gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);\n" "}\n"; // 编译单个着色器的工具函数 GLuint compileShader(GLenum type, const char* source) { GLuint shader = glCreateShader(type); glShaderSource(shader, 1, &source, NULL); glCompileShader(shader); // 检查编译错误 GLint success; glGetShaderiv(shader, GL_COMPILE_STATUS, &success); if(!success) { char infoLog[512]; glGetShaderInfoLog(shader, 512, NULL, infoLog); std::cerr << "Shader compile failed:\n" << infoLog << std::endl; } return shader; } // 创建并链接着色器程序 GLuint createProgram(const char* vertSource, const char* fragSource) { GLuint vertShader = compileShader(GL_VERTEX_SHADER, vertSource); GLuint fragShader = compileShader(GL_FRAGMENT_SHADER, fragSource); GLuint program = glCreateProgram(); glAttachShader(program, vertShader); glAttachShader(program, fragShader); glLinkProgram(program); // 检查链接错误 GLint success; glGetProgramiv(program, GL_LINK_STATUS, &success); if(!success) { char infoLog[512]; glGetProgramInfoLog(program, 512, NULL, infoLog); std::cerr << "Program link failed:\n" << infoLog << std::endl; } glDeleteShader(vertShader); glDeleteShader(fragShader); return program; } void framebuffer_size_callback(GLFWwindow* window, int width, int height) { glViewport(0, 0, width, height); } void processInput(GLFWwindow *window) { if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) glfwSetWindowShouldClose(window, true); static bool tabPressed = false; if(glfwGetKey(window, GLFW_KEY_TAB) == GLFW_PRESS && !tabPressed) { tabPressed = true; flag = !flag; glPolygonMode(GL_FRONT_AND_BACK, flag ? GL_LINE : GL_FILL); } if(glfwGetKey(window, GLFW_KEY_TAB) == GLFW_RELEASE) { tabPressed = false; } } int main() { // 初始化GLFW if(!glfwInit()) { std::cerr << "GLFW init failed!" << std::endl; return -1; } glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); GLFWwindow* window = glfwCreateWindow(800, 600, "Two Triangles", NULL, NULL); if(!window) { std::cerr << "Window create failed!" << std::endl; glfwTerminate(); return -1; } glfwMakeContextCurrent(window); glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); // 初始化GLEW if(glewInit() != GLEW_OK) { std::cerr << "GLEW init failed!" << std::endl; return -1; } // 创建两个VBO glGenBuffers(2, &VBO1); // 上传第一个三角形数据 glBindBuffer(GL_ARRAY_BUFFER, VBO1); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices1), vertices1, GL_STATIC_DRAW); // 上传第二个三角形数据 glBindBuffer(GL_ARRAY_BUFFER, VBO2); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices2), vertices2, GL_STATIC_DRAW); // 顶点着色器(两个三角形共用) const char* vertShader = "#version 110 \n" "attribute vec3 in_position; \n" "void main() \n" "{ \n" " gl_Position = vec4(in_position, 1.0); \n" "} \n"; // 创建两个着色器程序(不同片段着色器) shaderProgram1 = createProgram(vertShader, fragShaderRed); shaderProgram2 = createProgram(vertShader, fragShaderBlue); // 获取顶点属性位置 posLoc1 = glGetAttribLocation(shaderProgram1, "in_position"); posLoc2 = glGetAttribLocation(shaderProgram2, "in_position"); // 渲染循环 while(!glfwWindowShouldClose(window)) { processInput(window); glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); // 绘制红色三角形 glUseProgram(shaderProgram1); glBindBuffer(GL_ARRAY_BUFFER, VBO1); glEnableVertexAttribArray(posLoc1); glVertexAttribPointer(posLoc1, 3, GL_FLOAT, GL_FALSE, 0, 0); glDrawArrays(GL_TRIANGLES, 0, 3); glDisableVertexAttribArray(posLoc1); // 绘制蓝色三角形 glUseProgram(shaderProgram2); glBindBuffer(GL_ARRAY_BUFFER, VBO2); glEnableVertexAttribArray(posLoc2); glVertexAttribPointer(posLoc2, 3, GL_FLOAT, GL_FALSE, 0, 0); glDrawArrays(GL_TRIANGLES, 0, 3); glDisableVertexAttribArray(posLoc2); glfwSwapBuffers(window); glfwPollEvents(); } // 清理资源 glDeleteBuffers(2, &VBO1); glDeleteProgram(shaderProgram1); glDeleteProgram(shaderProgram2); glfwTerminate(); return 0; }
最后再检查这几点
- 确保两个三角形的顶点位置不重叠(重叠的话看起来就像一个)
- 一定要保留着色器编译/链接的错误检查,不然出问题根本找不到原因
- 绘制每个三角形时,严格遵循“激活着色器→绑定VBO→启用属性→绘制→禁用属性”的流程
这样应该就能同时显示两个不同颜色的三角形,按Tab也能正常切换线框/填充模式了!
内容的提问来源于stack exchange,提问作者Konstantinos Moulakis
相关产品推荐
相关产品推荐

