눈팅하는 게임개발자 블로그

OpenGL Lighting 3-1 Color 본문

공부한거/OpenGL

OpenGL Lighting 3-1 Color

Palamore 2020. 10. 11. 13:49

원문 사이트

learnopengl.com/Lighting/Colors

 

LearnOpenGL - Colors

Colors Lighting/Colors We briefly used and manipulated colors in the previous chapters, but never defined them properly. Here we'll discuss what colors are and start building the scene for the upcoming Lighting chapters. In the real world, colors can take

learnopengl.com

번역 사이트

heinleinsgame.tistory.com/14

 

[Learn OpenGL 번역] 3-1. 조명 - 색

색 조명/색 이전 강좌에서 OpenGL 에서 색을 가지고 어떻게 동작하는지 간단히 언급했었습니다. 하지만 지금까지는 오직 면의 색만 다루었습니다. 여기에서 우리는 색이란 무엇인지 광범위하게 ��

heinleinsgame.tistory.com

 

Color

컴퓨터 상으로 색을 표현하기 위해서 

Red, Green, Blue의 3가지 요소를 사용한다.

이 3가지 값(0.0~1.0 사이)을 조합하여 거의 모든 색을 표현할 수 있다.

예를 들면 coral 색을 얻기 위해 다음과 같은 컬러 벡터를 정의할 수 있다.

glm::vec3 coral(1.0f, 0.5f, 0.31f);  

실제 세계에서 보는 색은 실제 사물이 가지고 있는 색이 아니라 사물에 반사된 색상이다.

예를 들어 여러가지 다른 컬러들이 조합된 결과인 태양의 색은 하얀색으로 보이며.

파란색 물체에 흰색 빛을 비추었을 경우 파란색을 제외한 모든 빛이 흡수되어 파란색만이 반사되어

파란색으로 보이게 되는 것이다.

다음 이미지는 coral색을 가진 물체가 빛을 반사하는 것을 보여주는 이미지이다.

흰색 태양빛이 모든 색들의 집합이고 사물은 해당 색들 중 일부를 흡수하게 된다는 것을 알 수 있다.

 

해당 반사 규칙은 컴퓨터 그래픽 상에서도 적용된다. OpenGL에 광원을 정의할 때 해당 광원에 색을 설정할 수 있다.

광원을 흰색 색상으로 설정하고, 광원의 색상 값과 오브젝트의 색상 값을 곱하면 결과로 나온 색상은 오브젝트에

반사된 색상이 된다.

 

다시 위의 Coral 색상을 가진 오브젝트를 어떻게 Coral 색상으로 인식할 수 있게 되는지(연산을 하는지) 알아보자.

두 개의 색상 벡터를 요소마다 곱하여 최종 색상 벡터를 얻는다.

glm::vec3 lightColor(1.0f, 1.0f, 1.0f);
glm::vec3 toyColor(1.0f, 0.5f, 0.31f);
glm::vec3 result = lightColor * toyColor; // = (1.0f, 0.5f, 0.31f);

toyColor의 색상이 오브젝트가 가지고 있는 고유한 색상 값에 따라 흰색 빛의 큰 부분을 흡수하고 여러 red, green, blue

값을 반사시키는 것처럼 된다.

이것이 실생활에서 색상을 나타내는 방법이다. 

이를 이용해 오브젝트의 색상을 광원으로부터 반사된 각 색상 요소들의 양으로 정의할 수 있다.

다음은 녹색 빛을 사용하는 경우이다.

glm::vec3 lightColor(0.0f, 1.0f, 0.0f);
glm::vec3 toyColor(1.0f, 0.5f, 0.31f);
glm::vec3 result = lightColor * toyColor; // = (0.0f, 0.5f, 0.0f);

결과로 나온 색상인 result는 red, blue 값을 가지고 있지 않다.

또한 빛의 green 값의 반을 흡수하고 반을 반사하였다.

이렇게 되면 해당 오브젝트는 어두운 녹색으로 보이게 된다.

 

glm::vec3 lightColor(0.33f, 0.42f, 0.18f);
glm::vec3 toyColor(1.0f, 0.5f, 0.31f);
glm::vec3 result = lightColor * toyColor; // = (0.33f, 0.21f, 0.06f);

위의 경우에는 또 다른 독특한 색상이 만들어진다.

이와 같이 빛의 색상을 조절하여 예상치 못한 다른 색상들을 얻어낼 수도 있다.

 

A lighting scene

광범위하게 색상을 사용하여 실세계 조명을 시뮬레이션함으로써 흥미로운 효과를 만들어 볼 것이다.

이제부터는 광원을 사용할 것이기 때문에 해당 광원을 눈으로 확인 가능한 오브젝트로써 나타내야 한다.

그리고 해당 조명의 빛을 반사할 최소 하나 이상의 오브젝트가 필요하다.

 

제일 먼저 필요한 것은 빛을 만드는 오브젝트이고, 이전 강좌의 컨테이너를 사용할 것이다.

또한 해당 광원이 어디에 위치하고 있는지 보여줄 조명 오브젝트가 필요하다.

간단하게 해당 광원을 정육면체로 표현하기로 한다.

 

해당 광원(정육면체)을 그릴 vertex shader를 작성한다.

컨테이너의 vertex 위치를 변경하지 않고, 텍스처를 사용하지 않으므로 단순한 vertex shader가 된다.

#version 330 core
layout (location = 0) in vec3 aPos;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
    gl_Position = projection * view * model * vec4(aPos, 1.0);
} 

 

vertex 데이터와 attribute pointer들을 새로운 vertex shader에 맞게 수정한다.

정육면체를 만들기 위해 특별히 해당 램프를 위한 VAO를 생성해야 한다.

정육면체를 동일한 VAO로 생성하고 간단히 model 행렬을 수정하는 것으로 사용해도 되지만

다른 오브젝트들을 수정할 때마다 광원 정육면체에 영향을 끼치지 않도록 하기 위해서

광원 정육면체 만의 새로운 VAO를 만든다.

unsigned int lightVAO;
glGenVertexArrays(1, &lightVAO);
glBindVertexArray(lightVAO);
// VBO를 바인딩 바인딩하기만 하면 됩니다. 컨테이너의 VBO 데이터는 이미 정확한 데이터를 가지고 있습니다.
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// vertex attribute를 설정합니다(램프를 위한 위치 데이터만).
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);

해당 코드는 비교적 간단하다.

이제 광원 컨테이너를 생성하였으니

fragment shader를 작성한다.

#version 330 core
out vec4 FragColor;
  
uniform vec3 objectColor;
uniform vec3 lightColor;

void main()
{
    FragColor = vec4(lightColor * objectColor, 1.0);
}

이 fragment shader는 objectColor와 lightColor를 uniform 변수로 받는다.

이후 처음 coral 오브젝트에 했던 것처럼 lightColor와 objectColor를 곱한다.

오브젝트의 색상을 마지막 섹션의 coral 색상으로 설정하고 조명은 흰색으로 설정한다.

// 먼저 해당 shader program을 'use'해야 한다는 것을 잊지마세요(uniform을 설정하기 위해).
lightingShader.use();
lightingShader.setVec3("objectColor", 1.0f, 0.5f, 0.31f);
lightingShader.setVec3("lightColor",  1.0f, 1.0f, 1.0f);

 

이제 광원 큐브가 조명 계산에 영향을 받지 않도록 만들어주어야 한다.

광원 큐브를 나머지 것들과 격리시켜야 한다.

이를 위한 두 번째 shader가 필요하다.

vertex 셰이더는 그대로 두고 fragment shader만 수정할 수 없는 흰색 컬러로 정의해서 광원 색상을 흰색으로 유지한다.

#version 330 core
out vec4 FragColor;

void main()
{
    FragColor = vec4(1.0); // 4 개의 모든 벡터의 값을 1.0으로 설정합니다.
}

 

오브젝트를 그릴 때 방금 정의한 lighting shader를 이용하여 여러 다른 오브젝트들을.

그리고 광원 shader를 사용하여 광원을 그린다.

현실적인 결과를 얻기 위해 점진적으로 lighting shader를 수정해 나갈 것이다.

 

광원의 주 목적은 빛이 어디로부터 오는지를 보여주기 위함이다.

일반적으로 광원의 위치를 scene의 어딘가에 정의하며

단순히 위치를 나타낼 뿐 보이는 것에 있어서 아무런 의미를 갖지 않는다.

따라서 실제 광원을 보여주기 위해 실제 광원과 동일한 위치에 광원 큐브를 그린다.

lightingCube shader를 사용하여 광원 오브젝트를 그림으로써 이를 수행한다.

world space 좌표에서 광원의 위치를 나타내는 vec3 타입의 전역 변수를 선언한다.

glm::vec3 lightPos(1.2f, 1.0f, 2.0f);

 

다음 이 광원 큐브를 그리기 전에 위치로 이동시킨다. 또한 크기를 약간 축소시켜 너무 큰 공간을 차지하지 않도록 한다.

model = glm::mat4();
model = glm::translate(model, lightPos);
model = glm::scale(model, glm::vec3(0.2f)); 

 

광원 큐브를 위한 최종 드로잉 코드는 다음과 같다.

lampShader.use();
// model, view, projection 행렬 unifrom을 설정하세요.
...
// 램프 오브젝트를 그립니다.
glBindVertexArray(lightVAO);
glDrawArrays(GL_TRIANGLES, 0, 36);	

모든 코드 조각들을 적절한 위치에 삽입하면 조명을 실험하기 위한 OpenGL 응용 프로그램이 구성된다.

결과는 다음과 같다.