我一直在关注this tutorial以学习OpenGL。我有一些有用的东西,但只有我使用全局变量:
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>
// float vertices[] = {
// -0.5f, -0.5f, 0.0f,
// 0.5f, -0.5f, 0.0f,
// 0.0f, 0.5f, 0.0f
// };
float rectVertices[] = {
0.5f, 0.5f, 0.0f, // Top Right
0.5f, -0.5f, 0.0f, // Bottom Right
-0.5f, -0.5f, 0.0f, // Bottom Left
-0.5f, 0.5f, 0.0f // Top Left
};
uint rectIndices[] = {
0, 1, 3, // First Triangle
1, 2, 3 // Second Triangle
};
const GLchar* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 position;\n"
"void main() {\n"
"gl_Position = vec4(position.x, position.y, position.z, 1.0);\n"
"}\n";
const GLchar* fragmentShaderSource = "#version 330 core\n"
"out vec4 color;\n"
"void main() {\n"
"color = vec4(1.0, 0.5, 0.2, 1.0);\n"
"}\n";
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode) {
if(key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
}
void finalize() {
glfwTerminate();
}
void programExit(int code) {
finalize();
exit(code);
}
void enforce(int success, const char* msg, const char* info) {
if (!success) {
std::cerr << msg << info << std::endl;
exit(-1);
}
}
// Initialise
// GLFW and OpenGL
void initialiseGLFW(int vMajor, int vMinor) {
// Initialising GLFW
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, vMajor);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, vMinor);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
}
// Window Creation
GLFWwindow* createWindow(int width, int height, const char* title) {
GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", nullptr, nullptr);
enforce(window != nullptr, "Failed to create GLFW window", nullptr);
return window;
}
void initialiseGLEW(bool experimental) {
glewExperimental = experimental;
enforce(glewInit() == GLEW_OK, "Failed to initialise GLEW", nullptr);
}
GLFWwindow* initialise(int width, int height, const char* title) {
initialiseGLFW(3, 3);
// Creating GLFW Window
GLFWwindow* window = createWindow(width, height, title);
glfwMakeContextCurrent(window);
glfwSetKeyCallback(window, key_callback);
// Initialising GLEW
initialiseGLEW(true);
glViewport(0, 0, 800, 600);
return window;
}
// Creating Shaders
void checkCompilationError(uint shader) {
int success;
char infoLog[512];
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
glGetShaderInfoLog(shader, 512, nullptr, infoLog);
enforce(success, "Shader compilation error: ", infoLog);
}
void checkLinkingError(uint program) {
int success;
char infoLog[512];
glGetProgramiv(program, GL_LINK_STATUS, &success);
glGetProgramInfoLog(program, 512, nullptr, infoLog);
enforce(success, "Program linking error: ", infoLog);
}
uint compileShader(GLenum type, const char* source, ushort count, int* lengths) {
uint shader = glCreateShader(type);
glShaderSource(shader, count, &source, lengths);
glCompileShader(shader);
checkCompilationError(shader);
return shader;
}
uint createProgram(uint vShader, uint fShader) {
uint program = glCreateProgram();
glAttachShader(program, vShader);
glAttachShader(program, fShader);
glLinkProgram(program);
checkLinkingError(program);
return program;
}
// Subprocedure specific to this program
uint initialiseShaders(const char* vsSource, const char* fsSource) {
// Initialising shaders
uint vShader = compileShader(GL_VERTEX_SHADER, vsSource, 1, nullptr);
uint fShader = compileShader(GL_FRAGMENT_SHADER, fsSource, 1, nullptr);
// Link program
GLuint shaderProgram = createProgram(vShader, fShader);
// clean up
glDeleteShader(vShader);
glDeleteShader(fShader);
return shaderProgram;
}
void configureVBO(float* vertices) {
for (int i = 0; i < 12; i++) std::cout << vertices[i] << std::endl;
glBufferData(
GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW
);
// Tell OpenGL how to interpret the vertices
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*) 0);
glEnableVertexAttribArray(0);
}
void configureEBO(uint* indices) {
for (int i = 0; i < 6; i++) std::cout << indices[i] << std::endl;
glBufferData(
GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW
);
}
uint intialiseVAO(float* vertices, uint* indices) {
uint VAO, VBO, EBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
configureVBO(vertices);
if (indices != nullptr) {
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
configureEBO(indices);
}
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
return VAO;
}
void execGameLoop(GLFWwindow* window, uint shaderProgram, uint VAO) {
while(!glfwWindowShouldClose(window)) {
glfwPollEvents();
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Set the program to be used
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
glfwSwapBuffers(window);
}
}
int main() {
// Config
int width = 800, height = 800;
const char* title = "Learn OpenGL";
// Initialise GLFW and GLEW
GLFWwindow* window = initialise(width, height, title);
// Initialise Shader program
uint shaderProgram = initialiseShaders(vertexShaderSource, fragmentShaderSource);
// Configuring VAO, VBO and EBO
uint VAO = intialiseVAO(rectVertices, rectIndices);
execGameLoop(window, shaderProgram, VAO);
finalize();
return 0;
}
我的问题特别与:
有关void configureVBO(float* vertices) {
for (int i = 0; i < 12; i++) std::cout << vertices[i] << std::endl;
glBufferData(
GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW
);
// Tell OpenGL how to interpret the vertices
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*) 0);
glEnableVertexAttribArray(0);
}
void configureEBO(uint* indices) {
for (int i = 0; i < 6; i++) std::cout << indices[i] << std::endl;
glBufferData(
GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW
);
}
在使用rectVertices/Indices
全局变量时按预期工作,但在作为参数传递时不起作用。每个方法中的for循环打印数组,它们包含我期望的值。为什么OpenGL在使用全局变量时绘制矩形,但在使用局部参数时却不是这样?
答案 0 :(得分:4)
void configureVBO(float* vertices) {
for (int i = 0; i < 12; i++) std::cout << vertices[i] << std::endl;
glBufferData(
GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW
);
// Tell OpenGL how to interpret the vertices
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*) 0);
glEnableVertexAttribArray(0);
}
这里的问题是sizeof(vertices)
没有做你认为它做的事情。它没有告诉你数组中有多少个顶点 - 相反,它告诉你vertices
类型的变量占用了多少字节。由于vertices
是指针,因此根据您的系统,该数字可能为4或8 - 但重要的是,它与您实际打算上传的顶点数无关。
相反,您需要做的是告诉OpenGL您希望缓冲区有多大(以字节为单位)。您按number of vertices * sizeof(vertex_type)
计算出来。因此,在您的情况下,12 * sizeof(float)
。
通常,您需要在函数中包含第二个参数,其中包括顶点数,或者使用std::vector<float>
来保存顶点数据。例如,使用vector
,这将成为:
void configureVBO(const std::vector<float>& vertices) {
for (auto v : vertices) std::cout << v << "\n";
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float),
vertices.data(), GL_STATIC_DRAW);
// etc...
}
使用全局变量时代码工作的原因与C ++将数组传递给函数的方式有关(反过来,它继承自C)。当你传递一个数组时,函数收到的只是一个指针到数组的开头 - 有关数组长度的信息已经丢失,所以sizeof(vertices)
可以&#39 ;告诉你数组实际上有多长。但是,如果使用全局变量,那么即使在float vertices[12]
函数内,编译器也可以看到定义configureVBO
,因此在这种情况下sizeof()
将执行您期望的操作。
对于开始使用C和C ++的人来说,这是一个非常常见的混淆。我真的建议使用std::vector
代替(由于很多原因,这只是一个),但它也值得一读数组和指针如何在C和C ++中工作。