Texture
이번 글에서는 객체에 2D 이미지를 입힐 수 있는 '텍스쳐' 라는 기능에 대해 정리한다.
택스쳐는 어떤 이미지 위에 도형 틀을 그리는 것과 비슷하다.
이를 텍스쳐를 도형에 매핑한다고 하는데, 텍스쳐를 도형(가령 삼각형)에 매핑하기 위해서는, 도형의 vertex가 texture의 어느 위치에 해당하는지를 지정해야 한다.
이때 텍스쳐 좌표계를 통해서 위치를 지정할 수 있는데, 텍스쳐 좌표계는 x, y 축에서 0~1사이의 값을 가진다.
기준점은 왼쪽 아래를 원점으로 한다.
그리고 이 텍스쳐 좌표계를 통해 텍스쳐의 색상을 가져올 수도 있는데, 이를 sampling 이라고 한다.
Texture 생성
// Create Texture & Bind
unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
텍스쳐는 VBO, VAO 와 유사하게 정수형 변수에 생성한 텍스쳐를 저장한다.
그리고 생성한 텍스쳐는 현재 컨텍스트에 바인딩해둔다.
이 과정을 통해 텍스처를 먼저 만들고, 이 텍스쳐에 이미지를 바인딩하여 객체에 그려낼 수 있다.
Texture 로 사용할 이미지 가져오기
텍스쳐로 사용할 이미지를 가져올 때는 stb_image.h 헤더를 사용한다.
// Texture Image Loading
int width, height, component_count;
unsigned char* data = stbi_load("container.jpg", &width, &height, &component_count, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
stbi_image_free(data);
이미지를 가져오는 것은 stbi_load() 함수를 이용한다.
첫번째 인자는 이미지 파일명, 2, 3번째 인자는 이미지 크기 정보를 저장할 변수를 연결하고
4번째 인자는 텍스쳐를 구성하는 component의 개수를 저장하는 변수를 연결한다.
마지막은 필요한 컴포넌트의 숫자를 적으면 된다.
가져온 이미지 데이터로 2D 텍스쳐 이미지를 만들기 위해 glTexImage2D 함수를 사용한다.
각 인자의 설명은 GPT의 설명으로 대체한다.
다음은 glGenerateMipmap 함수를 사용하여 mipmap을 자동으로 생성한다.
mipmap은 텍스처를 여러 단계의 해상도로 미리 축소해놓은 이미지 세트이다.
이를 사용하는 이유는 어떤 오브젝트가 멀리 멀어졌을 때, 기존 고해상도 이미지를 그대로 사용하는 게 아니라 이미지의 해상도를 미리 적절하게 낮춰두고 이를 가져다 사용함으로써 성능을 최적화할 수 있기에 유용하다.
이 함수는 현재 바인딩 된 텍스쳐의 미피맵 체인을 생성하고, 원본 텍스쳐 이미지 (미피맵 레벨 0)부터 각 단계마다 해상도를 절반으로 줄인 이미지를 계산하고 저장한다. 이 과정은 이미지의 크기가 1이되어 더 이상 줄일 수 없을 때까지 반복된다.
바인딩 된 텍스쳐가 존재해야하기 때문에 뒤에 호출한다.
도형 안에 이미지 그리기
glBindTexture(GL_TEXTURE_2D, texture);
shader.use();
glBindTexture(GL_TEXTURE_2D, texture);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);
렌더링 루프에서 위 코드를 실행하면 된다.
glBindTexture() 함수로 렌더링하기 전 새롭게 다시 텍스처를 바인딩 해준다.
(사실 위에서 이미 한번 바인딩 했기 때문에, 텍스쳐가 바뀌는게 아니라면 안해줘도 상관은 없다)
셰이더 코드 작성
float vertice[] = {
// position // texture coords
-0.5, -0.5, 1.0, 0.0f, 0.0f,
0.5, -0.5, 1.0, 1.0f, 0.0f,
0, 0.5, 1.0, 1.0f, 1.0f
};
도형의 각 정점이 텍스쳐 이미지 상에 어떤 위치에 존재하는지 좌표를 맞춰준다.
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 5, (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 5, (void*)(sizeof(float) * 3));
glEnableVertexAttribArray(1);
이에 맞게 속성값도 다시 설정해준다.
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;
out vec2 TexCoord;
void main()
{
gl_Position = vec4(aPos, 1.0);
TexCoord = aTexCoord;
}
Vertex Shader는 위와 같이 작성하였다.
TexCoord는 프래그먼트 셰이더로 넘겨야 하므로 그대로 출력해주었다.
#version 330 core
out vec4 FragColor;
in vec2 TexCoord;
uniform sampler2D ourTexture;
void main()
{
FragColor = texture(ourTexture, TexCoord);
}
출력할 색상값을 전달할 FragColor에 텍스쳐를 전달한다.
이때 uniform 변수를 사용한다.
변수의 타입은 sampler2D 타입이고, 변수명은 자유롭게 지어도 된다.
사용하는 텍스쳐가 1개일 경우에는 바인딩된 텍스쳐를 자동으로 위 유니폼 변수에 넘겨준다.
GPT의 설명은 위와 같다.
실행 결과
최종 코드는 아래와 같다.
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include "Shader.h"
// 안쓰면 LINK : warning LNK4098: 'MSVCRT' defaultlib가 다른 라이브러리와 충돌합니다. /NODEFAULTLIB:library를 사용하십시오. 경고 발생
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
float vertice[] = {
// position // texture coords
-0.5, -0.5, 1.0, 0.0f, 0.0f,
0.5, -0.5, 1.0, 1.0f, 0.0f,
0, 0.5, 1.0, 1.0f, 1.0f
};
int main() {
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow(800, 600, "hello texture", NULL, NULL);
glfwMakeContextCurrent(window);
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
unsigned int VBO, VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertice), vertice, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 5, (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 5, (void*)(sizeof(float) * 3));
glEnableVertexAttribArray(1);
Shader shader("4.1.texture.vs", "4.1.texture.fs");
// Create Texture & Bind
unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
// Texture Image Loading
int width, height, component_count;
unsigned char* data = stbi_load("container.jpg", &width, &height, &component_count, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
stbi_image_free(data);
while (!glfwWindowShouldClose(window)) {
glClearColor(0.5, 0.5, 0.5, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glBindTexture(GL_TEXTURE_2D, texture);
shader.use();
glBindTexture(GL_TEXTURE_2D, texture);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
}
그리고 실행한 화면은 아래와 같다.
원래 사각형 이미지인데, 해당 이미지의 오른쪽 위 꼭짓점을 삼각형의 가운데 윗 꼭짓점에 할당했다.
그랬더니 이미지가 알아서 잘 찌그러지게 렌더링 되었다.
'CS > HCI 윈도우즈프로그래밍' 카테고리의 다른 글
[OpenGL] 12. Shader (8) - 여러 Texture 생성하기 (0) | 2024.04.14 |
---|---|
[OpenGL] 11. Shader (7) - Texture Wrapping, Filtering, MipMaps (1) | 2024.04.12 |
[OpenGL] 9. Shader (5) - 직사각형에 실시간으로 변하는 색상 입히기 (GLFW, GLAD) (0) | 2024.04.09 |
[OpenGL] 8. Shader (4) - GLSL (0) | 2024.04.09 |
[OpenGL] 7. Shader (3) - EBO (Element Buffer Object) (0) | 2024.04.08 |