带有 OpenGL 4.4 的 SDL2:三角形未正确渲染

Posted

技术标签:

【中文标题】带有 OpenGL 4.4 的 SDL2:三角形未正确渲染【英文标题】:SDL2 with OpenGL 4.4: Triangle Not Rendering Properly 【发布时间】:2015-08-08 05:57:41 【问题描述】:

我正在使用带有 SDL2 的 OpenGL 4.4。我正在尝试使用顶点(-1、-1、0)、(1、-1、0)、(0、1、0)渲染一个简单的三角形。然而,当我认为我做的一切都正确时,什么都没有画出来。

我从我的项目中提取并重组了相关代码:

#include <cerrno>
#include <cstring>
#include <exception>
#include <fstream>
#include <iostream>
#include <string>
#include <GL/glew.h>
#include <GL/glu.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_opengl.h>

void init();
void cleanUp();
std::string loadShader(std::string filepath);
void checkShaderSuccess(GLuint shader);

SDL_Window* win;
SDL_GLContext glContext;
GLuint program, vertShader, fragShader, vao, vbo;

class GenError: public std::exception 
 public:
        GenError():
                exception(), msg("") 

        GenError(const std::string& m):
                exception(), msg(m) 

        virtual ~GenError() throw() 

        virtual const char* what() const throw() 
                return msg.c_str();
        
 private:
        std::string msg;
;

int main() 
        init();

        program = glCreateProgram();
        if (program == 0) 
                throw GenError("Shader creation failed: "
                                  "Could not find valid memory location in "
                                  "constructor");
        

        vertShader = glCreateShader(GL_VERTEX_SHADER);
        fragShader = glCreateShader(GL_FRAGMENT_SHADER);
        if (vertShader == 0 || fragShader == 0) 
                std::string m;
                m += "Shader creation failed: "
                        "Could not find valid memory location when "
                        "adding shader: ";
                m += (char *)gluErrorString(glGetError());
                throw GenError(m);
        

        std::cout << "Creating vertex shader..." << std::endl;
        std::string data = loadShader("./shaders/basicVertex.vs");
        const GLchar* data_c = data.c_str();
        glShaderSource(vertShader, 1, &data_c, NULL);
        glCompileShader(vertShader);
        checkShaderSuccess(vertShader);
        glAttachShader(program, vertShader);
        std::cout << "Vertex shader created" << std::endl;

        std::cout << "Creating fragment shader..." << std::endl;
        data = loadShader("./shaders/basicFragment.fs");
        data_c = data.c_str();
        glShaderSource(fragShader, 1, &data_c, NULL);
        glCompileShader(fragShader);
        checkShaderSuccess(fragShader);
        glAttachShader(program, fragShader);
        std::cout << "Fragment shader created" << std::endl;

        glLinkProgram(program);

        GLint success;
        glGetProgramiv(program, GL_LINK_STATUS, &success);
        if (success == GL_FALSE) 
                GLint logLen = 0;
                glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLen);
                GLchar programLog[logLen];
                glGetProgramInfoLog(program, logLen, &logLen, programLog);
                std::string m;
                m += "Failed to link program: ";
                m += (char *)gluErrorString(glGetError());
                m += ": ";
                m += (char *)programLog;
                throw GenError(m);
        

        glValidateProgram(program);

        glGetProgramiv(program, GL_VALIDATE_STATUS, &success);
        if (success == GL_FALSE) 
                GLint logLen = 0;
                glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLen);
                GLchar programLog[logLen];
                glGetProgramInfoLog(program, logLen, &logLen, programLog);
                std::string m;
                m += "Failed to validate program: ";
                m += (char *)gluErrorString(glGetError());
                m += ": ";
                m += (char *)programLog;
                throw GenError(m);
        

        glGenVertexArrays(1, &vao);
        glBindVertexArray(vao);
        glGenBuffers(1, &vbo);

        const GLfloat verts[] = 
                -1.0f, -1.0f, 0.0f,
                 1.0f, -1.0f, 0.0f,
                 0.0f,  1.0f, 0.0f
        ;
        glBindBuffer(GL_ARRAY_BUFFER, vbo);
        glBufferData(GL_ARRAY_BUFFER,
                     sizeof(verts),
                     verts,
                     GL_STATIC_DRAW );

        SDL_Event ev;
        bool running = true;
        while (true) 
                while (SDL_PollEvent(&ev)) 
                        if (ev.type == SDL_WINDOWEVENT &&
                            ev.window.event == SDL_WINDOWEVENT_CLOSE) 
                                std::cout << "Closing window..." << std::endl;
                                running = false;
                                break;
                        
                

                if (!running) break;

                glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
                glUseProgram(program);

                glEnableVertexAttribArray(0);
                glBindBuffer(GL_ARRAY_BUFFER, vbo);
                glVertexAttribPointer(0,
                                      3,
                                      GL_FLOAT,
                                      GL_FALSE,
                                      3*sizeof(GLfloat),
                                      (GLvoid*)0 );

                glDrawArrays(GL_TRIANGLES, 0, 3);
                glDisableVertexAttribArray(0);

                SDL_GL_SwapWindow(win);
        
        std::cout << "Window closed" << std::endl;

        glDeleteBuffers(1, &vbo);
        glDeleteVertexArrays(1, &vao);
        glDeleteProgram(program);
        glDeleteShader(vertShader);
        glDeleteShader(fragShader);

        cleanUp();

        return 0;


void init() 
        std::cout << "Initializing..." << std::endl;

        if (SDL_Init(SDL_INIT_VIDEO) != 0) 
                std::string m;
                m.append("Error initializing SDL2: ");
                m.append(SDL_GetError());
                throw GenError(m);
        

        SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
        SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
        SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
        SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
        SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 4);

        win = SDL_CreateWindow("Triangle Test",
                               SDL_WINDOWPOS_UNDEFINED,
                               SDL_WINDOWPOS_UNDEFINED,
                               800, 600,
                               SDL_WINDOW_OPENGL );
        if (win == NULL) 
                throw GenError(SDL_GetError());
        

        glContext = SDL_GL_CreateContext(win);
        if (glContext == NULL) 
                std::string m;
                m.append("Error associating window with OpenGL: SDL Error: ");
                m.append(SDL_GetError());
                throw GenError(m);
        

        glewExperimental = GL_TRUE;
        GLenum glewErr = glewInit();
        if (glewErr != GLEW_OK) 
                std::string m;
                m.append("Error initializing OpenGL GLEW extension: ");
                m.append((const char*)glewGetErrorString(glewErr));
                throw GenError(m);
         else 
                /* GLEW does not play nice with OpenGL 4.4.
                 * GLEW thinks OpenGL 4.4 is "pretentious" and
                 * "entitled". GLEW likes to throw an invalid
                 * enumerant error the next time glGetError is
                 * called after GLEW's initialization.
                 * glGetError must be envoked to discard this
                 * faulty error. GLEW makes my code look sloppy.
                 * We do not like GLEW. We tolerate GLEW.
                 */
                GLenum junk = glGetError();
        

        glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
        glFrontFace(GL_CW);
        glCullFace(GL_BACK);
        glEnable(GL_CULL_FACE);
        glEnable(GL_DEPTH_TEST);

        glEnable(GL_FRAMEBUFFER_SRGB);

        if(SDL_GL_SetSwapInterval(1) < 0) 
                std::cerr << "Warning: Unable to set VSync! "
                          << "SDL Error: "
                          << SDL_GetError() << std::endl;
        

        GLenum error = glGetError();
        if (error != GL_NO_ERROR) 
                std::string m;
                m.append("Error initializing OpenGL: OpenGL Error: ");
                m.append(reinterpret_cast<const char*>(gluErrorString(error)));
                throw GenError(m);
        

        std::cout << "Initialized" << std::endl;


void cleanUp() 
        std::cout << "Cleaning up..." << std::endl;
        SDL_GL_DeleteContext(glContext);
        SDL_DestroyWindow(win);
        SDL_Quit();
        std::cout << "Cleaned" << std::endl;


std::string loadShader(std::string filepath) 
        std::ifstream shaderFile(filepath.c_str());
        if (!shaderFile.is_open()) 
                std::cerr << "Could not load shader: "
                          << "Error opening "
                          << filepath
                          << ": " << std::strerror(errno)
                          << std::endl;
                return std::string("");
        

        std::string content, line;
        while (std::getline(shaderFile, line)) 
                content += line + '\n';
        

        shaderFile.close();

        return content;


void checkShaderSuccess(GLuint shader) 
        GLint success;
        glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
        if (success == GL_FALSE) 
                GLint logLen = 0;
                glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLen);
                GLchar shaderLog[logLen];
                glGetShaderInfoLog(shader, logLen, &logLen, shaderLog);
                std::string m;
                m += "Shader compilation failed: ";
                m += (char *)gluErrorString(glGetError());
                m += ": ";
                m += (char *)shaderLog;
                glDeleteShader(shader);
                throw GenError(m);
        

...没有错误捕获(为了更快的浏览):

#include <cerrno>
#include <cstring>
#include <exception>
#include <fstream>
#include <iostream>
#include <string>
#include <GL/glew.h>
#include <GL/glu.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_opengl.h>

void init();
void cleanUp();
std::string loadShader(std::string filepath);

SDL_Window* win;
SDL_GLContext glContext;
GLuint program, vertShader, fragShader, vao, vbo;

int main() 
        init();

        program = glCreateProgram();

        vertShader = glCreateShader(GL_VERTEX_SHADER);
        fragShader = glCreateShader(GL_FRAGMENT_SHADER);

        std::cout << "Creating vertex shader..." << std::endl;
        std::string data = loadShader("./shaders/basicVertex.vs");
        const GLchar* data_c = data.c_str();
        glShaderSource(vertShader, 1, &data_c, NULL);
        glCompileShader(vertShader);
        glAttachShader(program, vertShader);
        std::cout << "Vertex shader created" << std::endl;

        std::cout << "Creating fragment shader..." << std::endl;
        data = loadShader("./shaders/basicFragment.fs");
        data_c = data.c_str();
        glShaderSource(fragShader, 1, &data_c, NULL);
        glCompileShader(fragShader);
        glAttachShader(program, fragShader);
        std::cout << "Fragment shader created" << std::endl;

        glLinkProgram(program);

        glGenVertexArrays(1, &vao);
        glBindVertexArray(vao);
        glGenBuffers(1, &vbo);

        const GLfloat verts[] = 
                -1.0f, -1.0f, 0.0f,
                 1.0f, -1.0f, 0.0f,
                 0.0f,  1.0f, 0.0f
        ;
        glBindBuffer(GL_ARRAY_BUFFER, vbo);
        glBufferData(GL_ARRAY_BUFFER,
                     sizeof(verts),
                     verts,
                     GL_STATIC_DRAW );

        SDL_Event ev;
        bool running = true;
        while (true) 
                while (SDL_PollEvent(&ev)) 
                        if (ev.type == SDL_WINDOWEVENT &&
                            ev.window.event == SDL_WINDOWEVENT_CLOSE) 
                                std::cout << "Closing window..." << std::endl;
                                running = false;
                                break;
                        
                

                if (!running) break;

                glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
                glUseProgram(program);

                glEnableVertexAttribArray(0);
                glBindBuffer(GL_ARRAY_BUFFER, vbo);
                glVertexAttribPointer(0,
                                      3,
                                      GL_FLOAT,
                                      GL_FALSE,
                                      3*sizeof(GLfloat),
                                      (GLvoid*)0 );

                glDrawArrays(GL_TRIANGLES, 0, 3);
                glDisableVertexAttribArray(0);

                SDL_GL_SwapWindow(win);
        
        std::cout << "Window closed" << std::endl;

        glDeleteBuffers(1, &vbo);
        glDeleteVertexArrays(1, &vao);
        glDeleteProgram(program);
        glDeleteShader(vertShader);
        glDeleteShader(fragShader);

        cleanUp();

        return 0;


void init() 
        std::cout << "Initializing..." << std::endl;

        SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
        SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
        SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
        SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
        SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 4);

        win = SDL_CreateWindow("Triangle Test",
                               SDL_WINDOWPOS_UNDEFINED,
                               SDL_WINDOWPOS_UNDEFINED,
                               800, 600,
                               SDL_WINDOW_OPENGL );

        glContext = SDL_GL_CreateContext(win);

        glewExperimental = GL_TRUE;
        GLenum glewErr = glewInit();

        glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
        glFrontFace(GL_CW);
        glCullFace(GL_BACK);
        glEnable(GL_CULL_FACE);
        glEnable(GL_DEPTH_TEST);

        glEnable(GL_FRAMEBUFFER_SRGB);

        std::cout << "Initialized" << std::endl;


void cleanUp() 
        std::cout << "Cleaning up..." << std::endl;
        SDL_GL_DeleteContext(glContext);
        SDL_DestroyWindow(win);
        SDL_Quit();
        std::cout << "Cleaned" << std::endl;


std::string loadShader(std::string filepath) 
        std::ifstream shaderFile(filepath.c_str());

        std::string content, line;
        while (std::getline(shaderFile, line)) 
                content += line + '\n';
        

        shaderFile.close();

        return content;

...我的顶点着色器(GLSL):

#version 440

layout (location = 0) in vec3 position;

void main() 
     gl_Position = vec4(0.5 * position, 1.0);

...和我的片段着色器:

#version 440

out vec4 fragColor;

void main() 
     fragColor = vec4(0.0, 1.0, 1.0, 1.0);

现在很奇怪,当我从这里更改 C++ 代码中的第 148 行(带有错误捕获)时...

3*sizeof(GLfloat),

...到这个(换句话说,改变步幅)...

3*sizeof(GLdouble),

...编译和运行会生成一个三角形,其顶点为 (-1, -1, 0), (0, 0, 0), (0, 1, 0)。第二个顶点显然变得模糊了。我得到的是不等腰三角形,而不是等腰三角形。

我想 1) 弄清楚如何修复我的程序,使其显示具有指定顶点的三角形,以及 2) 了解我最初做错了什么,导致我在修改上述代码行时出现了如此奇怪的结果.

我已经对此进行了将近一周的修改。 任何见解表示赞赏。谢谢!

【问题讨论】:

您可以尝试不启用剔除吗?删除glEnable(GL_CULL_FACE) 调用。据我所知,您为三角形方向指定顺时针方向,但绘制一个逆时针三角形。所以它会被剔除。 @reto-koradi 成功了!注释掉 glEnable(GL_CULL_FACE) 使三角形可见。重新启用面相关多边形剔除后,我切换了三角形的第二个和第三个顶点以吸引逆时针剔除偏差(或任何技术术语)。所以 ((-1, -1, 0), (1, -1, 0), (0, 1, 0)) 变成了 ((-1, -1, 0), (0, 1, 0), ( 1, -1, 0))。这样做会导致我的三角形按需要渲染。非常感谢!如果没有人超过我,我稍后会尝试为我的问题添加官方答案。 糟糕。我的意思是顺时针不是逆时针。 【参考方案1】:

您的代码在多边形的缠绕顺序方面存在问题。您为正面指定顺时针缠绕,并启用背面的剔除:

glFrontFace(GL_CW);
glCullFace(GL_BACK);

但是三角形的缠绕顺序是逆时针的:

const GLfloat verts[] = 
    -1.0f, -1.0f, 0.0f,
     1.0f, -1.0f, 0.0f,
     0.0f,  1.0f, 0.0f
;

这意味着三角形将被剔除。

使用逆时针缠绕是 OpenGL 的主要标准,也是默认设置。所以最好的选择是您只需删除这行代码:

glFrontFace(GL_CW);

这将使GL_CCW 的值与您的几何形状相匹配。

禁用背面剔除始终是当多边形不显示时您应该做的第一件事。缠绕顺序错误是导致事物无法渲染的最常见原因之一,只需禁用剔除并检查是否会显示几何图形,就可以很容易地进行分类。

【讨论】:

以上是关于带有 OpenGL 4.4 的 SDL2:三角形未正确渲染的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL 不绘制到 SDL2 窗口

SDL2 OpenGL C++ 移动使用 VBO 和 FBO 绘制的精灵

SDL2 丢失 OpenGL 上下文或未定义函数

如何让 OpenGL-ES 在带有 SDL2 的 Raspberry Pi 上工作?

OpenGL网格错误的位置

带有 Gtk+ 的 OpenGL,尽管背景已清除,但未绘制形状