我使用OpenGL 4.4和SDL2。我试图渲染一个带有顶点(-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)了解我最初做错了什么导致我在修改上述代码行时出现这种奇怪的结果
我一直在修补这个近一个星期。 赞赏任何见解。谢谢!
答案 0 :(得分:6)
您的代码在多边形的缠绕顺序方面存在问题。您可以为正面指定顺时针缠绕,并启用背面的剔除:
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
,与您的几何图形匹配。
禁用背面剔除始终是多边形未显示时应首先执行的操作之一。错误的绕线顺序是导致事物无法渲染的最常见原因之一,只需禁用剔除并检查是否会使几何图形显示就很容易进行分类。