카테고리 없음

OpenGL tutorial 1 (Window, triangle)

ksh950510 2023. 7. 18. 21:43
728x90

그저께 부터 보기 시작했는데 가장 잘 정리한것 같다.

이 유튜버는 2년전 부터 opengl 관련 영상을 업로드를 시작했으며, 영어발음이나 자막 등이 깔끔하게 전달된다.

https://www.youtube.com/watch?v=XpBGwZNyUh0&list=PLPaoO-vpZnumdcb4tZc4x5Q-v7CkrQ6M-&index=1 

필요한 것만 정리해볼 예정이며, 코드와 그 옆에 주석을 달아 설명

정말 개인적인 공부용이므로 영어공부를 병행할 예정 유튜브를 보는걸 추천함

 


tutorial 1

#include<iostream>
#include<glad/glad.h>
#include<GLFW/glfw3.h>

int main()
{
	glfwInit();

	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); // Inform opengl version (4.4) actually the version is 4.6, but glfw isnt updated
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); 
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Inform glfw profile (we can change with the cmake option)

	GLFWwindow* window = glfwCreateWindow(800, 800, "OPGT", NULL, NULL);
	if (window == NULL) // check window is working?
	{
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(window); // context being a sort of object that holds the whole of opengl

	gladLoadGL(); // load the needed configurations

	glViewport(0, 0, 800, 800); // set viewport left bottom to right top
	glClearColor(0.07f, 0.13f, 0.17f, 1.0f); // To specify initialized color 
	glClear(GL_COLOR_BUFFER_BIT); // set back buffer with the color we want
	glfwSwapBuffers(window); // swap front and back buffer

	while (!glfwWindowShouldClose(window)) // Main while loop, check GLFW close permit
	{
		glfwPollEvents(); //glfw process all the pooled events such as the window appearing
	}

	glfwDestroyWindow(window);
	glfwTerminate();
	return 0;
}

 

tutorial 2

 

VERTEX DATA got the vertices information

though they're not mathematical vertices. Since each vertex having a position, and color or texture coordinates

 

1. Vertex Shader takes the positions of all the vertices and transforms them.

2. Shape Assembler takes all the positionsm, and connects them according to a primitive.

* primitive: just a shape such as a point, line or triangle.
Each primitive interprets the data differently (take 3 points and draw a triangle between them or take two points for a line)

3. Geometry Shader can add vertices and create new primitives out of already existing primitives.

4. Rasterization phase: all the perfect mathematical shapes get transformed into actual pixels. (bunch of squares)

5. Fragment shader adds colors to the pixels. (such as the lighting or the textures or shadows. important shader!)

6. Tests and blending: we might have multiple colors for just one pixel because multiple objects overlapping

(consider the transparent's degree, set final color)

 

But, the opengl didnt provide the Vertex and fragment shader

So, we have to set own shader like this

// Vertex Shader source code
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";
//Fragment Shader source code
const char* fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
"   FragColor = vec4(0.8f, 0.3f, 0.02f, 1.0f);\n"
"}\n\0";

Shaders are an OpenGL object (in the background(memory))

we can only access them by references aka value (!infact all OpenGL objects are accessed by reference!)

 

opengl coordinate system: x pointing to the right and y pointing up.

also the coordinate system is normalized, which means the leftmost part of the window for x is -1 and rightmost is 1

(the lowermost part of the window is -1, uppermost part of the window is 1.)

 

* Why use GLfloat?

normal floats and GLfloats may differ in size that OpenGL uses (GLuint too, GLuint is unsigned int)

 

Sending stuff between the CPU and the GPU is kind of slow.

then, we want to send it into big batches called buffers (위에 설명한 front and back buffer가 아님)

it called VBO(Vertex Buffer Object) is buffer for transfer cpu to gpu big batch unit (VBO is normally array) 

it packed an object with our Vertex Data but OpenGL doesn't know where to ever find it.

in order to do that, VAO(Vertex Array Object) stores pointers to one or more VBOs

and tells OpenGL how to interpret them.

it exist in order to quickly be able to switch between different VBOs.

In code, we have to generate VAO before the VBO (ordering is very important!)

 

Binding: make a certain object the current object.

whenever we fire a function that would modify that type of object, it modifies the current object(aka the binded object)

 

#include<iostream>
#include<glad/glad.h>
#include<GLFW/glfw3.h>

// Vertex Shader source code
const char* vertexShaderSource = "#version 440 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";
//Fragment Shader source code
const char* fragmentShaderSource = "#version 440 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
"   FragColor = vec4(0.8f, 0.3f, 0.02f, 1.0f);\n"
"}\n\0";

int main()
{
	glfwInit();

	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); // Inform opengl version (4.4) actually the version is 4.6, but glfw isnt updated
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); 
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Inform glfw profile (we can change with the cmake option)

	GLFWwindow* window = glfwCreateWindow(800, 800, "OPGT", NULL, NULL);
	if (window == NULL) // check window is working?
	{
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}

	GLfloat vertices[] = // for 2D triangle we set left corner, right corner, top corner
	{
		-0.5f, -0.5f * float(sqrt(3)) / 3, 0.0f,
		0.5f, -0.5f * float(sqrt(3)) / 3, 0.0f,
		0.0f, 0.5f * float(sqrt(3)) * 2 / 3, 0.0f,
	};


	glfwMakeContextCurrent(window); // context being a sort of object that holds the whole of opengl

	gladLoadGL(); // load the needed configurations

	glViewport(0, 0, 800, 800); // set viewport left bottom to right top

	GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
	glShaderSource(vertexShader, 1, &vertexShaderSource, NULL); // give reference value, specify only use 1 string, point source code
	glCompileShader(vertexShader); //compile the shader for GPU

	GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
	glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL); // give reference value, specify only use 1 string, point source code
	glCompileShader(fragmentShader);

	GLuint shaderProgram = glCreateProgram();
	glAttachShader(shaderProgram, vertexShader);
	glAttachShader(shaderProgram, fragmentShader);
	glLinkProgram(shaderProgram);

	glDeleteShader(vertexShader); // they are already in the program it self
	glDeleteShader(fragmentShader);


	GLuint VAO, VBO; // 

	glGenVertexArrays(1, &VAO); // only one object, pointing it to the reference
	glGenBuffers(1, &VBO); // same setting

	glBindVertexArray(VAO);
	glBindBuffer(GL_ARRAY_BUFFER, VBO);

	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); //type, total size of the data in bytes, actual data, use of this data
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); // communicate vertex shader from the outside 
		// (index, size, type, normalized, stride, *pointer(offset))
	glEnableVertexAttribArray(0); // (position of vertex attribute)

	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindVertexArray(0);


	while (!glfwWindowShouldClose(window)) // Main while loop, check GLFW close permit
	{
		glClearColor(0.07f, 0.13f, 0.17f, 1.0f); // To specify initialized color 
		glClear(GL_COLOR_BUFFER_BIT); // set back buffer with the color we want
		glUseProgram(shaderProgram);
		glBindVertexArray(VAO); // now only one VAO, so it's not nessecary but good to get used to this
		glDrawArrays(GL_TRIANGLES, 0, 3); // type of primitive, starting index, amount
		glfwSwapBuffers(window); // swap front and back buffer

		glfwPollEvents(); //glfw process all the pooled events such as the window appearing
	}

	glDeleteVertexArrays(1, &VAO);
	glDeleteBuffers(1, &VBO);
	glDeleteProgram(shaderProgram);

	glfwDestroyWindow(window);
	glfwTerminate();
	return 0;
}

EBO(Element Buffer Object)

To bind OpenGL object to OpenGL context(called target), we use GL_ARRAY_BUFFER (in VBO)

the GL_ELEMENT_ARRAY_BUFFER is used to indicate the buffer you're presenting contains the indices of each element in the "other" (GL_ARRAY_BUFFER) buffer.