일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 디자드
- 파이썬서버
- EnhancedInput
- 프메
- Ajax
- 프린세스메이커
- 언리얼뮤지컬
- 스터디
- 마인크래프트뮤지컬
- 스마일게이트
- 레베카
- 미니프로젝트
- 정글사관학교
- 알고풀자
- Jinja2
- node
- 으
- Enhanced Input System
- 데이터베이스
- flask
- Bootstrap4
- 언리얼
- R
- Unseen
- VUE
- 카렌
- 언리얼프로그래머
- JWT
- Express
- 게임개발
- Today
- Total
Showing
[Open GL] opengl-tutorial 2 first triangle 본문
Tutorial 2 : 첫 삼각형 (opengl-tutorial.org)
* opengl-tutorial.org)을 보고 학습한 개인학습 기록 포스팅입니다.
1. 버텍스 정의
삼각형을 만들기 위해 세개의 3D 점이 필요하다
// 3 버텍스들을 표현하는 3 벡터들의 배열
static const GLfloat g_vertex_buffer_data[] = {
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
};
가장 처음 버틱스는 (-1,-1,0)이다.
변환(트랜스폼;Transform) 하기 전에는, 화면상에는 (-1,-1)로 표시된다.
(-1,-1) 은 화면 좌측 최하단. (1,-1) 은 화면 우측 최하단. (0,1) 은 중앙 최상단
따라서 삼각형은 화면 대부분을 차지하게 된다.
// Include standard headers
#include <stdio.h>
#include <stdlib.h>
// Include GLEW
#include <GL/glew.h>
// Include GLFW
#include <GLFW/glfw3.h>
GLFWwindow* window;
// Include GLM
#include <glm/glm.hpp>
using namespace glm;
#include <common/shader.hpp>
int main( void )
{
// Initialise GLFW
if( !glfwInit() )
{
fprintf( stderr, "Failed to initialize GLFW\n" );
getchar();
return -1;
}
glfwWindowHint(GLFW_SAMPLES, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be needed
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// Open a window and create its OpenGL context
window = glfwCreateWindow( 1024, 768, "Tutorial 02 - Red triangle", NULL, NULL);
if( window == NULL ){
fprintf( stderr, "Failed to open GLFW window. If you have an Intel GPU, they are not 3.3 compatible. Try the 2.1 version of the tutorials.\n" );
getchar();
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
// Initialize GLEW
glewExperimental = true; // Needed for core profile
if (glewInit() != GLEW_OK) {
fprintf(stderr, "Failed to initialize GLEW\n");
getchar();
glfwTerminate();
return -1;
}
// Ensure we can capture the escape key being pressed below
glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);
// Dark blue background
glClearColor(0.0f, 0.0f, 0.4f, 0.0f);
GLuint VertexArrayID;
glGenVertexArrays(1, &VertexArrayID);
glBindVertexArray(VertexArrayID);
// Create and compile our GLSL program from the shaders
GLuint programID = LoadShaders( "SimpleVertexShader.vertexshader", "SimpleFragmentShader.fragmentshader" );
static const GLfloat g_vertex_buffer_data[] = {
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
};
GLuint vertexbuffer;
// 버퍼를 하나 생성
glGenBuffers(1, &vertexbuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
// 버텍스들을 OpenGL로 넘겨줍니다
glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);
do{
// Clear the screen
glClear( GL_COLOR_BUFFER_BIT );
// Use our shader
glUseProgram(programID);
// 1rst attribute buffer : vertices
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glVertexAttribPointer(
0, // attribute 0. No particular reason for 0, but must match the layout in the shader.
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);
// Draw the triangle !
glDrawArrays(GL_TRIANGLES, 0, 3); // 3 indices starting at 0 -> 1 triangle
glDisableVertexAttribArray(0);
// Swap buffers
glfwSwapBuffers(window);
glfwPollEvents();
} // Check if the ESC key was pressed or the window was closed
while( glfwGetKey(window, GLFW_KEY_ESCAPE ) != GLFW_PRESS &&
glfwWindowShouldClose(window) == 0 );
// Cleanup VBO
glDeleteBuffers(1, &vertexbuffer);
glDeleteVertexArrays(1, &VertexArrayID);
glDeleteProgram(programID);
// Close OpenGL window and terminate GLFW
glfwTerminate();
return 0;
}
위의 상태에서 쉐이더를 통해 색감을 입힐 수 있다.
2. VAO
일찍이 Vertex Array Object를 생성하고 이를 현재 객체로 설정했다.
GLuint VertexArrayID;
glGenVertexArrays(1, &VertexArrayID);
glBindVertexArray(VertexArrayID);
창이 생성된 후(= OpenGL 컨텍스트 생성 후)와 다른 OpenGL 호출 전에 이 작업을 수행했다.
3. 셰이더
// Create and compile our GLSL program from the shaders
GLuint programID = LoadShaders( "SimpleVertexShader.vertexshader", "SimpleFragmentShader.fragmentshader" );
가능한 가장 간단한 구성에는 두 개의 셰이더가 필요하다.
하나는 각 정점에 대해 실행되는 Vertex Shader이고 다른 하나는 각 샘플에 대해 실행되는 Fragment Shader이다.
셰이더는 OpenGL의 일부인 GLSL: GL Shader Language라는 언어로 프로그래밍된다.
C나 Java와 달리 GLSL은 런타임에 컴파일되어야 한다. 즉, 애플리케이션을 시작할 때마다 모든 셰이더가 다시 컴파일된다.
LoadShaders 코드
GLuint LoadShaders(const char * vertex_file_path,const char * fragment_file_path){
// Create the shaders
GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER);
GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
// Read the Vertex Shader code from the file
std::string VertexShaderCode;
std::ifstream VertexShaderStream(vertex_file_path, std::ios::in);
if(VertexShaderStream.is_open()){
std::stringstream sstr;
sstr << VertexShaderStream.rdbuf();
VertexShaderCode = sstr.str();
VertexShaderStream.close();
}else{
printf("Impossible to open %s. Are you in the right directory ? Don't forget to read the FAQ !\n", vertex_file_path);
getchar();
return 0;
}
// Read the Fragment Shader code from the file
std::string FragmentShaderCode;
std::ifstream FragmentShaderStream(fragment_file_path, std::ios::in);
if(FragmentShaderStream.is_open()){
std::stringstream sstr;
sstr << FragmentShaderStream.rdbuf();
FragmentShaderCode = sstr.str();
FragmentShaderStream.close();
}
GLint Result = GL_FALSE;
int InfoLogLength;
// Compile Vertex Shader
printf("Compiling shader : %s\n", vertex_file_path);
char const * VertexSourcePointer = VertexShaderCode.c_str();
glShaderSource(VertexShaderID, 1, &VertexSourcePointer , NULL);
glCompileShader(VertexShaderID);
// Check Vertex Shader
glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result);
glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
if ( InfoLogLength > 0 ){
std::vector<char> VertexShaderErrorMessage(InfoLogLength+1);
glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]);
printf("%s\n", &VertexShaderErrorMessage[0]);
}
// Compile Fragment Shader
printf("Compiling shader : %s\n", fragment_file_path);
char const * FragmentSourcePointer = FragmentShaderCode.c_str();
glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer , NULL);
glCompileShader(FragmentShaderID);
// Check Fragment Shader
glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result);
glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
if ( InfoLogLength > 0 ){
std::vector<char> FragmentShaderErrorMessage(InfoLogLength+1);
glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]);
printf("%s\n", &FragmentShaderErrorMessage[0]);
}
// Link the program
printf("Linking program\n");
GLuint ProgramID = glCreateProgram();
glAttachShader(ProgramID, VertexShaderID);
glAttachShader(ProgramID, FragmentShaderID);
glLinkProgram(ProgramID);
// Check the program
glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result);
glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
if ( InfoLogLength > 0 ){
std::vector<char> ProgramErrorMessage(InfoLogLength+1);
glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]);
printf("%s\n", &ProgramErrorMessage[0]);
}
glDetachShader(ProgramID, VertexShaderID);
glDetachShader(ProgramID, FragmentShaderID);
glDeleteShader(VertexShaderID);
glDeleteShader(FragmentShaderID);
return ProgramID;
}
(1) VertexShader
#version 330 core
// Input vertex data, different for all executions of this shader.
layout(location = 0) in vec3 vertexPosition_modelspace;
void main(){
gl_Position.xyz = vertexPosition_modelspace;
gl_Position.w = 1.0;
}
두 번째 줄은 입력 데이터를 선언한다.
layout(location = 0) in vec3 vertexPosition_modelspace;
이 줄을 자세히 설명하겠습니다.
- "vec3"은 GLSL의 3개 구성 요소로 구성된 벡터이다. 이는 삼각형을 선언하는 데 사용한 glm::vec3과 유사하지만 다르다.
- "layout(location = 0)"은 vertexPosition_modelspace 속성을 제공하는 데 사용하는 버퍼를 나타낸다 . 각 정점은 위치, 하나 또는 여러 색상, 하나 또는 여러 텍스처 좌표, 기타 여러 가지 등 다양한 속성을 가질 수 있다. OpenGL은 색상이 무엇인지 모르고 단지 vec3만 볼 뿐이다. 따라서 어떤 버퍼가 어떤 입력에 해당하는지 알려주어야 한다. glVertexAttribPointer의 첫 번째 매개변수와 동일한 값으로 레이아웃을 설정하여 이를 수행한다. 값 "0" 자체는 중요하지 않습니다. 12일 수 있습니다(그러나 glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &v) 이하). 중요한 것은 양쪽의 숫자가 동일하다는 것이다.
glVertexAttribPointer(
0, // attribute 0. No particular reason for 0, but must match the layout in the shader.
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);
- "vertexPosition_modelspace"는 다른 이름을 가질 수 있으며 정점 셰이더의 각 실행에 대한 정점 위치가 포함된다.
- "in"은 이것이 일부 입력 데이터임을 의미한다.("out" 키워드도 있음)
주요 기능은 정점의 위치를 버퍼에 있던 대로 설정하는 것뿐이다. 따라서 (1,1)을 주면 삼각형의 정점 중 하나가 화면 오른쪽 상단에 있게 된다. 입력 위치에 대해 좀 더 흥미로운 계산도 수행 가능하다.
gl_Position.xyz = vertexPosition_modelspace;
gl_Position.w = 1.0;
}
gl_Position은 몇 가지 내장 변수 중 하나입니다. *이 변수에 값을 할당해야만 한다. 다른 선택 사항들도 존재한다.
(2) FragmentShader
각 조각의 색상을 빨간색으로 설정한다.
(4x AA를 사용하기 때문에 한 픽셀에 4개의 조각이 있다)
#version 330 core
out vec3 color;
void main(){
color = vec3(1,0,0);
}
예, vec3(1,0,0)은 빨간색을 의미한다. 이는 컴퓨터 화면에서 색상이 빨간색, 녹색, 파란색의 순서로 표시되고, 따라서 (1,0,0)은 완전 빨간색, 녹색도 파란색도 없음을 의미한다.
최종
LoadShaders 함수를 가져와서,
#include <common/shader.hpp>
메인 루프 전에 LoadShaders 함수를 호출한다.
// Create and compile our GLSL program from the shaders
GLuint programID = LoadShaders( "SimpleVertexShader.vertexshader", "SimpleFragmentShader.fragmentshader" );
이제 메인 루프 내부에서 먼저 화면을 지운다.
이전 glClearColor(0.0f, 0.0f, 0.4f, 0.0f) 호출로 인해 배경색이 진한 파란색으로 변경된다.
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
셰이더를 사용하고 싶다고 OpenGL에 알린다.
// Use our shader
glUseProgram(programID);
// Draw triangle...
'Open GL' 카테고리의 다른 글
[Open GL] LearnOpenGL hello_triangle_exercise 1, 2, 3 (0) | 2023.11.21 |
---|---|
[Open GL] LearnOpenGL many Triangles with Uniform Variables (0) | 2023.11.21 |
[GLSL] 쉐이더 프로세서의 인풋,아웃풋 레지스터 (0) | 2023.11.17 |
[Open GL] Tutorial 4 : A Colored Cube 예제풀이 (1) | 2023.10.30 |
[Open GL] opengl-tutorial matrices (1) | 2023.10.28 |