OpenGL学习(一)——画一个三角形
作为一个刚接触opengl的程序员,我想的地方开始介绍glew和glfw。介绍一些基本的函数。
先贴代码。来自Joey de Vries的《Learn OpenGL》
#include <iostream>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
const GLchar* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 position;\n"
"void main()\n"
"{\n"
"gl_Position = vec4(position.x, position.y, position.z, 1.0);\n"
"}\0";
const GLchar* fragmentShaderSource = "#version 330 core\n"
"out vec4 color;\n"
"void main()\n"
"{\n"
"color = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\n\0";
int main()
{
glfwInit();
//create a window
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", nullptr,
nullptr);
if (window == nullptr)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
// Set the required callback functions
glfwSetKeyCallback(window, key_callback);
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK)
{
std::cout << "Failed to initialize GLEW" << std::endl;
return -1;
}
int width, height;
glfwGetFramebufferSize(window, &width, &height);
glViewport(0, 0, width, height);
//vertex shader
GLuint vertexShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER);
//将vertex shader的代码赋给对象,并进行编译
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
//check whether compile ok
GLint success;
GLchar infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
//if not success, print the error
if(!success)
{
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" <<
infoLog << std::endl;
}
//fragment shader
GLuint fragmentShader;
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
//creating a program object
GLuint shaderProgram;
shaderProgram = glCreateProgram();
//attach previous shaders and link them
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
//check whether link ok
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if(!success) {
glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
}
//delete the shader
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
GLfloat firstTriangle[] = {
-0.9f, -0.5f, 0.0f, // Left
-0.0f, -0.5f, 0.0f, // Right
-0.45f, 0.5f, 0.0f, // Top
};
GLfloat secondTriangle[] = {
0.0f, -0.5f, 0.0f, // Left
0.9f, -0.5f, 0.0f, // Right
0.45f, 0.5f, 0.0f // Top
};
GLuint VBOs[2], VAOs[2];
glGenVertexArrays(2, VAOs); // We can also generate multiple VAOs or buffers at the same time
glGenBuffers(2, VBOs);
//first triangle
glBindVertexArray(VAOs[0]);
glBindBuffer(GL_ARRAY_BUFFER, VBOs[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(firstTriangle), firstTriangle,GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0);
glEnableVertexAttribArray(0);
glBindVertexArray(0);
//second triangle
glBindVertexArray(VAOs[1]);
glBindBuffer(GL_ARRAY_BUFFER, VBOs[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(secondTriangle), secondTriangle,GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0);
glEnableVertexAttribArray(0);
glBindVertexArray(0);
while(!glfwWindowShouldClose(window))
{
glfwPollEvents();
// Render
// Clear the colorbuffer
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Draw our first triangle
// Activate shader (same shader for both triangles)
glUseProgram(shaderProgram);
// Draw first triangle using the data from the first VAO
glBindVertexArray(VAOs[0]);
glDrawArrays(GL_TRIANGLES, 0, 3);
// Then we draw the second triangle using the data from the second VAO
glBindVertexArray(VAOs[1]);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
glfwSwapBuffers(window);
}
// Properly de-allocate all resources once they've outlived their purpose
glDeleteVertexArrays(2, VAOs);
glDeleteBuffers(2, VBOs);
glfwTerminate();
return 0;
}
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);
}
总体想法
将数据传入VBO,然后将VBO绑定到VAO中,在主循环中绑定VAO并进行绘画即可。
主要函数介绍
glBufferData is a function specifically targeted to copy user-defined data into the currently bound buffer. Its first argument is the type of the buffer we want to copy data into: the vertex buffer object currently bound to the GL_ARRAY_BUFFER target. The second argument specifies the size of the data (in bytes) we want to pass to the buffer; a simple sizeof of the vertex data suffices. The third parameter is the actual data we want to send.
The fourth parameter specifies how we want the graphics card to manage the given data. This can take 3 forms:
• GL_STATIC_DRAW: the data will most likely not change at all or very rarely.
• GL_DYNAMIC_DRAW: the data is likely to change a lot.
• GL_STREAM_DRAW: the data will change every time it is drawn.
glBufferData函数的第一个参数是buffer的类型,第二个是数据的大小,第三个是数据,第四个是我们想如何让显卡处理这些数据。
The function glVertexAttribPointer has quite a few parameters so let’s carefully walk through them:
• The first parameter specifies which vertex attribute we want to configure. Remember that we specified the location of the position vertex attribute in the vertex shader with layout (location = 0).This sets the location of the vertex attribute to 0 and since we want to pass data to this vertex attribute, we pass in 0.
• The next argument specifies the size of the vertex attribute. The vertex attribute is a vec3 so it is composed of 3 values.
• The third argument specifies the type of the data which is GL_FLOAT (a vec* in GLSL consists of floating point values).
• The next argument specifies if we want the data to be normalized. If we set this to GL_TRUE all the data that has a value not between 0 (or -1 for signed data) and 1 will be mapped to those values. We leave this at GL_FALSE.
• The fifth argument is known as the stride and tells us the space between consecutive vertex attribute sets. Since the next set of position data is located exactly 3 times the size of a GLfloat away we specify that value as the stride. Note that since we know that the array is tightly packed (there is no space between the next vertex attribute value) we could’ve also specified the stride as 0 to let OpenGL determine the stride (this only works when values are tightly packed). Whenever we have more vertex attributes we have to carefully define the spacing between each vertex attribute but we’ll get to see more examples of that later on.
• The last parameter is of type GLvoid* and thus requires that weird cast. This is the offset of where the position data begins in the buffer. Since the position data is at the start of the data array this value is just 0. We will explore this parameter in more detail later on
函数glVertexAttribPointers的第一个参数表示vertex shader的layout, 第二个参数表示vertex的大小,第三个表示data的类型。第四个表示数据是否被正规化,第五个参数表示stride的长度,第六个表示buffer的开头位置,其余不是很懂。