본문 바로가기

컴퓨터그래픽스

[OpenGL 공부] Textures (2)

학습을 위해 참고한 사이트

https://learnopengl.com/Getting-started/Textures

 

LearnOpenGL - Textures

Textures Getting-started/Textures We learned that to add more detail to our objects we can use colors for each vertex to create some interesting images. However, to get a fair bit of realism we'd have to have many vertices so we could specify a lot of colo

learnopengl.com

Textures (1) : 텍스쳐 매핑 결과

 

지난 글에서 텍스쳐 매핑을 다루었지만 한 가지 의문점이 있었는데, 프래그먼트 셰이더에서 텍스쳐 객체를 저장해 주는 역할인 sampler2D 타입 변수가 uniform으로 선언되었음에도 불구하고, 해당 uniform에 값을 지정해 주기 위한 함수인 glUniform을 사용하지 않았다. 이유는 이후 서술할 텍스쳐 유닛(Texture Unit)과 연관되어 있는데, 결과적으로 말하면 사용하려는 텍스쳐를 하나만 사용하는 경우, 기본적으로 OpenGL이 0번에 해당하는 텍스쳐 유닛을 자동으로 설정해 주기 때문에 렌더링이 정상적으로 된다고 볼 수 있다.

텍스쳐 유닛(Texture Unit)

텍스쳐는 하나 이상의 위치에 바인딩될 수 있고, 이러한 독립적인 바인딩 위치를 텍스쳐 유닛이라고 한다.

쉽게 말해서 다중 텍스쳐를 처리하기 위한 매커니즘을 OpenGL에서 제공해 준다고 보면 된다.

 

코드로 이해하기 위해서, 마찬가지로 LearnOpenGL 사이트에서 제공하는 다른 텍스쳐 파일을 준비한다. *. png 파일의 경우 투명 또는 반투명, 즉 알파값에 대한 처리가 가능한 형식의 파일을 의미한다.

awesomeface.png

 

텍스쳐가 하나 더 추가되었으므로, 프래그먼트 셰이더에서도 추가된 텍스쳐를 위한 sampler 변수를 추가한다.

uniform sampler2D texture1;
uniform sampler2D texture2;

이제 프래그먼트 셰이더는 두 개의 텍스쳐를 다루기 때문에, 단일 텍스쳐를 다루는 경우와 달리 각각의 샘플러가 어떠한 텍스쳐를 사용하는지 알지 못한다. 따라서 셰이더 프로그램에게 어느 텍스쳐 유닛을 사용할 것인지 알려주어야 한다.

Shader ourShader("shaders/vertShader.glsl", "shaders/fragShader.glsl");
ourShader.use();		// glUseProgram(ProgramID);
glUniform1i(glGetUniformLocation(ourShader.ID, "texture1"), 0);
glUniform1i(glGetUniformLocation(ourShader.ID, "texture2"), 1);

해당 작업은 셰이더 파일을 읽어와 컴파일 및 연결하는 일련의 과정을 수행하여 glUseProgram 함수를 통해 정상적으로 셰이더 프로그램을 활성화시킨 이후에 가능해진다. 위의 코드는 셰이더 프로그램 내부에서 샘플러 변수인 texture1, texture2에 대해서 각각 0번과 1번 텍스쳐 유닛을 사용할 것을 명시해 주는 코드이다. 

 

이제 셰이더는 텍스쳐를 참조하기 위해서 어떤 텍스쳐 유닛에서 정보를 가져올 지 알게 되었지만, 각각의 텍스쳐 유닛이 실제로 어떤 텍스쳐 정보를 가지는지는 모르기 때문에 다음의 코드를 렌더링 기능을 수행하는 함수에 추가해주어야 한다.

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture2);

/* draw process ... */
// glDrawElements();

텍스쳐 유닛의 바인드 위치는 GL_TEXTURE0, GL_TEXTURE1, ... 으로 나타낼 수 있다.  glActiveTexture 함수는 해당 바인드 위치의 텍스쳐 유닛을 활성화시키고, glBindTexture 함수를 통해 활성화된 텍스쳐 유닛에 사용할 텍스쳐를 바인딩한다. 즉, 여러 개의 텍스쳐를 각각의 텍스쳐 유닛에 바인딩해 주면 프래그먼트 셰이더에서 텍스쳐를 참조할 때, 샘플러 변수에 명시된 텍스쳐 유닛 번호에 따라 어떤 텍스쳐를 사용할 것인지 결정하게 된다.

 

두 텍스쳐의 정상적인 렌더링을 확인하기 위해 프래그먼트 셰이더의 출력을 결정하는 색상값을 다음과 같이 설정한다.

/* fragment shader */
#version 330 core
out vec4 FragColor;

in vec3 ourColor;
in vec2 texCoord;

uniform sampler2D texture1;
uniform sampler2D texture2;

void main()
{
	FragColor = mix(texture(texture1, texCoord), 
					texture(texture2, texCoord), 0.2);	
}

FragColor를 결정할 때, GLSL의 mix 함수를 사용하도록 한다. 해당 함수는 texture1, texture2 각각의 기본 출력 비율을 1이라고 두었을 때, texture1을 0.8, texture2를 0.2의 비율로 출력하도록 한다.

 

출력 결과는 다음과 같다.

 

전체 코드

더보기
/* texture.cpp */

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

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#include "shader.h"

GLuint renderingProgram;

using namespace std;

// callback 함수
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
	glViewport(0, 0, width, height);
}

void processInput(GLFWwindow* window)
{
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
	{
		std::cout << "Pressed ESC" << std::endl;
		glfwSetWindowShouldClose(window, true);
	}
}

float vertices[] = {
	// positions			// colors				// texture coords
	 0.5f,  0.5f, 0.0f,		1.0f, 0.0f, 0.0f,		1.0f, 1.0f,
	 0.5f, -0.5f, 0.0f,		0.0f, 1.0f, 0.0f,		1.0f, 0.0f,
	-0.5f, -0.5f, 0.0f,		0.0f, 0.0f, 1.0f,		0.0f, 0.0f,
	-0.5f,  0.5f, 0.0f,		1.0f, 1.0f, 1.0f,		0.0f, 1.0f
};

unsigned int indices[] = {
	0, 1, 3,
	1, 2, 3
};

int main() {
	// GLFW 초기화
	glfwInit();
	// GLFW 설정
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

	// 윈도우 객체 생성, 모든 window 데이터를 보유
	GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);
	if (window == NULL)
	{
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(window);
	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

	// GLAD가 OpenGL 함수 포인터를 관리
	// OS 마다 다른 OpenGL 함수 포인터의 주소를 로드하기 위해 GLAD 함수를 거침
	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
	{
		std::cout << "Failed to initialize GLAD" << std::endl;
		return -1;
	}

	unsigned int VBO, VAO, EBO;

	glGenBuffers(1, &VBO);
	glGenBuffers(1, &EBO);
	glGenVertexArrays(1, &VAO);

	glBindVertexArray(VAO);

	glBindBuffer(GL_ARRAY_BUFFER, VBO);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

	// position attribute
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
	glEnableVertexAttribArray(0);
	//// color attribute
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
	glEnableVertexAttribArray(1);
	// texture coords attribute
	glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
	glEnableVertexAttribArray(2);

	//texture load & settings
	unsigned int texture1, texture2;

	glGenTextures(1, &texture1);
	glBindTexture(GL_TEXTURE_2D, texture1);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	int width, height, nrChannels;
	unsigned char* data = stbi_load("wall.jpg", &width, &height, &nrChannels, 0);
	if (data != 0)
	{
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
		glGenerateMipmap(GL_TEXTURE_2D);
	}
	else
	{
		std::cout << "Failed to load texture" << std::endl;
	}
	stbi_image_free(data);

	glGenTextures(1, &texture2);
	glBindTexture(GL_TEXTURE_2D, texture2);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	stbi_set_flip_vertically_on_load(true);
	data = stbi_load("awesomeface.png", &width, &height, &nrChannels, 0);
	if (data != 0)
	{
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
		glGenerateMipmap(GL_TEXTURE_2D);
	}
	else
	{
		std::cout << "Failed to load texture" << std::endl;
	}
	stbi_image_free(data);

	Shader ourShader("shaders/vertShader.glsl", "shaders/fragShader.glsl");
	ourShader.use();
	glUniform1i(glGetUniformLocation(ourShader.ID, "texture1"), 0);
	glUniform1i(glGetUniformLocation(ourShader.ID, "texture2"), 1);

	while (!glfwWindowShouldClose(window)) {
		// 입력 감지
		processInput(window);

		// 렌더링 명령 ...
		glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT);
		
		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, texture1);
		glActiveTexture(GL_TEXTURE1);
		glBindTexture(GL_TEXTURE_2D, texture2);

		glBindVertexArray(VAO);
		glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

		// 렌더링 명령 ...

		glfwSwapBuffers(window);
		glfwPollEvents();
	}

	glfwTerminate();
	return 0;
}
/* vertShader.glsl */

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord;

out vec3 ourColor;
out vec2 texCoord;

void main() 
{
	gl_Position = vec4(aPos, 1.0);
	ourColor = aColor;
	texCoord = aTexCoord;
}
/* fragShader.glsl */

#version 330 core
out vec4 FragColor;

in vec3 ourColor;
in vec2 texCoord;

uniform sampler2D texture1;
uniform sampler2D texture2;

void main()
{
	FragColor = mix(texture(texture1, texCoord), 
					texture(texture2, texCoord), 0.2);	
}

 

 

'컴퓨터그래픽스' 카테고리의 다른 글

[OpenGL 공부] Transformations  (0) 2024.01.20
[OpenGL 공부] Textures (exercises)  (0) 2024.01.16
[OpenGL 공부] Textures (1)  (1) 2024.01.13
[OpenGL 공부] Shader  (0) 2024.01.13
[OpenGL 공부] Hello Triangle  (1) 2024.01.06