First contact with OpenGL(1)--Windows and Triangles
I'm trying to learn something about OpenGL these days, and planing to create a technical demo based on OpenGL as a part of my portfolio. I would record my learning experience here.
OpenGL is a graphic API and I use it based on C++, Xcode and GLFW, GLAD, and the most important reference tutorial of my learning is the website https://learnopengl-cn.github.io/(Chinese version of https://learnopengl.com/).
After configuring environment of OpenGL(actually the tutorial have just mentioned the way of configuring environment with windows OS and Visual Studio, so it really took a long time for me to find out how to do the same thing with Mac OS and Xcode), the first step of creating an OpenGL program is creating a window. We can use codes following to initiate a window in MacOS:
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
And then use those codes to create a window object:
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
Then before we call any OpenGL functions, as the tutorial have accentuated, we have to initiate GLAD to manage pointers of OpenGL functions:
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
Before we start rendering a window, we still have to designate the size of the viewport, which is the rendering size of the window, and register a call back function to ensure when the size of the window has been changed, the size of the viewport can get changed automatically:
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
Finally we can use the while(1) loop to start a rendering loop:
while (!glfwWindowShouldClose(window))
{
glfwSwapBuffers(window);
glfwPollEvents();
}
Do not forget to add codes following to free up the storage at the end of the main() function:
glfwTerminate();
return 0;
Then we can compile the file and can get a black window as following:
Then we can try to render the first graphic in our program, a triangle. The first step is to save our vertex's coordinate in the Vertex Buffer Object(VBO), and in order to do so we have to generate a VBO at first, and bind a GL_ARRAY_BUFFER to it, then copy the vertex's coordinate to the VBO:
float vertices[] = {
0f, 0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
};
unsigned int VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
Since then we have finished the initiation of VBO, and in order to render the triangle, we still have to generate our shader. Here are two types of frequently used shader in OpenGL, vertex shader and fragment shader, and the former one is used to draw vertex, the last one is used to draw the color between vertex. Shaders in OpenGL are programmed by OpenGL Shading Language(GLSL), and I would code them in the C++ program with string, and compile them while I running my C++ program.(But it is hard to debug).
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0";
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\n\0";
int vertexShader = glCreateShader(GL_VERTEX_SHADER);glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
Then we can create a shader program object, and link those shaders to it, and use this shader program in our program:
int shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
glad_glUseProgram(shaderProgram);
We still need to explain to the OpenGL the format of our vertex input, for we can use almost any format in this input. We use the following function:
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), (void*)(0));
glEnableVertexAttribArray(0);
In the glVertexAttribPointer() function, we should know that the first parameter is the location of vertex, as we have designated in the vertex shader before, it is 0, and the second parameter is the size of our vertex, vec3 actually, and the forth parameter is the space between two set of vertex attributes, for each vertex is a vec3, this parameter should be 3 * sizeof(float). After that we still need one more step before shader a triangle, we need to create and initiate a Vertex Array Object(VAO), while actually I do not really understand why the attributes of vertex should be stored separately in VAO and VBO, but OpenGL requires to do so.
unsigned int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
Then finally we can get our triangle like this:
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
And then use those codes to create a window object:
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
Then before we call any OpenGL functions, as the tutorial have accentuated, we have to initiate GLAD to manage pointers of OpenGL functions:
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
Before we start rendering a window, we still have to designate the size of the viewport, which is the rendering size of the window, and register a call back function to ensure when the size of the window has been changed, the size of the viewport can get changed automatically:
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
Finally we can use the while(1) loop to start a rendering loop:
while (!glfwWindowShouldClose(window))
{
glfwSwapBuffers(window);
glfwPollEvents();
}
Do not forget to add codes following to free up the storage at the end of the main() function:
glfwTerminate();
return 0;
Then we can compile the file and can get a black window as following:
Then we can try to render the first graphic in our program, a triangle. The first step is to save our vertex's coordinate in the Vertex Buffer Object(VBO), and in order to do so we have to generate a VBO at first, and bind a GL_ARRAY_BUFFER to it, then copy the vertex's coordinate to the VBO:
float vertices[] = {
0f, 0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
};
unsigned int VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
Since then we have finished the initiation of VBO, and in order to render the triangle, we still have to generate our shader. Here are two types of frequently used shader in OpenGL, vertex shader and fragment shader, and the former one is used to draw vertex, the last one is used to draw the color between vertex. Shaders in OpenGL are programmed by OpenGL Shading Language(GLSL), and I would code them in the C++ program with string, and compile them while I running my C++ program.(But it is hard to debug).
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0";
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\n\0";
int vertexShader = glCreateShader(GL_VERTEX_SHADER);glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
Then we can create a shader program object, and link those shaders to it, and use this shader program in our program:
int shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
glad_glUseProgram(shaderProgram);
We still need to explain to the OpenGL the format of our vertex input, for we can use almost any format in this input. We use the following function:
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), (void*)(0));
glEnableVertexAttribArray(0);
In the glVertexAttribPointer() function, we should know that the first parameter is the location of vertex, as we have designated in the vertex shader before, it is 0, and the second parameter is the size of our vertex, vec3 actually, and the forth parameter is the space between two set of vertex attributes, for each vertex is a vec3, this parameter should be 3 * sizeof(float). After that we still need one more step before shader a triangle, we need to create and initiate a Vertex Array Object(VAO), while actually I do not really understand why the attributes of vertex should be stored separately in VAO and VBO, but OpenGL requires to do so.
unsigned int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
Then finally we can get our triangle like this:


评论
发表评论