본문 바로가기

Game/Graphics

Learn OpenGL - Lighting : Colors

link : https://learnopengl.com/Lighting/Colors


Colors


 우리는 이전 튜토리얼에서 OpenGL의 색상 작업 방법에 대해 간략하게 언급했지만 지금까지는 색상의 표면만 다루었다.


여기서 우리는 색상이 무엇인지를 광범위하게 논의하고, 다가올 Lighting 튜토리얼을 위한 장면을 구축할 것이다.



 실세계에서 색상은 각 객체가 고유한 색상을 갖는 임의의 알려진 색상 값을 실제로 취할 수 있다.


디지털 세계에서 (무한한) 실제 색상을 (제한된) 디지털 값으로 매핑해야하므로 모든 실제 색상을 표현할 수 있다.


색상은 일반적으로 RGB로 축약된 빨강, 녹색, 파랑 구성 요소를 사용해 디지털 방식으로 표현된다. 그 3가지 값의


다른 조합을 사용해 거의 모든 색상을 표현할 수 있다. 예를 들어 coral 색상을 얻으려면 다음과 같이 색상 벡터를


정의한다:

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

  실생활에서 볼 수 있는 색은 물체가 실제로 가지고 있는 색이 아니라 물체에서 반사되는 색이다. 객체에 의해


흡수 (거부) 되지 않는 색상은 우리가 인식하는 색상이다. 예를 들어, 태양의 빛은 다양한 색상의 합계 인 흰색


빛으로 인식된다. 따라서 파란색 장난감에 흰색 비을 비추면 파란색을 제외한 모든 흰색 색상의 하위 색상을 흡수한다.


장난감은 파란색 값을 흡수하지 않기 때문에 반영되고, 반사된 빛이 눈에 들어와서 장난감이 파란색처럼 보인다.


다음 이미지는 coral 색깔의 장난감에서 다양한 강도로 여러 색상을 반영하는 경우 이를 보여준다:



 흰색 햇빛은 실제로 모든 보이는 색상의 모음이며 그 물체는 그 색상의 많은 부분을 흡수한다는 것을 알 수 있다.


그것은 단지 물체의 색을 나타내는 색을 반영하고, 그 조합은 우리가 인지하는 것이다.



 이러한 색상 반영 규칙은 그래픽 영역에 직접 적용된다. OpenGL에서 광원을 정의할 때 이 광원에 색상을 지정하려고 한다.


이전 단락에서 우리는 흰색을 가졌으므로 광원에 흰색을 주도록 하겠다. 광원의 색상에 물체의 색상 값을 곱하면 결과 색상은


물체이 반사된 색상이다. 우리의 장난감을 다시 살펴보고 그래픽 영역에서 지각 가능한 색상을 계산하는 방법을 살펴 보자.


두 색상 벡터 모두에 대해 component-wise 곱셈을 수행해 결과 컬러 벡터를 검색한다:

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);

 장난감의 색상은 흰색 빛의 많은 부분을 흡수하지만 고유한 색상 값을 기반으로 여러 빨강, 초록 및 파랑 값을 반영한다.


이것은 실제 생활에서 색상이 어떻게 작동하는지 표현한 것이다. 따라서 우리는 물체의 색을 광원에서 반사하는 각 색 구성 요소의


양으로 정의할 수 있다. 녹색 불빛을 사용하면 어떻게 될까?

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);

 우리가 볼 수 있듯이, 장난감에는 적색 및 청색 빛이 흡수되거나 반사되지 않는다. 장난감은 또한 빛의 녹색 값의 절반을 흡수하지만,


여전히 빛의 녹색 값의 절반을 반영한다. 우리가 지각하는 장난감의 색깔은 진녹색이다. 녹색 빛을 사용하면 녹색 성분만 반사되어


인식 될 수 있다. 적색과 청색이 감지되지 않는다. 그 결과 coral 물체가 갑자기 짙은 녹색 물체가 된다. 그 결과 coral 물체가


갑자기 짙은 녹색이된다. 짙은 olive-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


 다가올 튜토리얼에서는 색상을 광범위하게 사용해 실제 조명을 시뮬레이션해 흥미로운 비주얼을 만들어 낼 것이다.


이제 우리는 장면에서 시각적인 객체로 표시할 광원을 사용하고 조명을 시뮬레이트하기 위해 하나 이상의 객체를


추가할 것이다.



 우리가 가장 먼저 해야할 일은 조명을 켜는 것이다. 이전 튜토리얼의 악명 높은 컨테이너 큐브를 사용한다.


3D 장면에서 광원이 어디에 있는지를 알려주기 위해 light 오브젝트가 필요할 것이다. 간단하게 하기 위해


우리는 광원을 큐브와 함께 나타낼 것이다. (우리는 이미 정점 데이터를 가지고 있다)



 따라서 정점 버퍼 객체를 채우고 정점 속성 포인터와 그 이상한 것들을 모두 설정하는 것이 쉽지 않을 것이다.


이러한 항목에 여전히 어려움이 있다면 계속하기 전에 가능한 경우 이전 튜토리얼을 검토하고 연습을 해보는 것이 좋다.



 그래서, 우리가 실제로 필요로 할 첫 번째 것은 컨테이너를 그리는 정점 쉐이더이다. 컨테이너의 정점 위치는 동일하게


유지되지만 (이번에는 텍스처 좌표가 필요하지 않음) 코드가 새롭지 않아야한다. 우리는 마지막 튜토리얼에서


정점 쉐이더의 버전 다운된 버전을 사용할 것이다:

#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);
} 

 새 정점 쉐이더에 해당하는 정점 데이터와 애트리뷰트 포인터를 업데이트해야한다.



 램프 큐브를 만드려고 하기 때문에 램프 용으로 특별히 새로운 VAO를 생성하려고 한다. 또한, 동일한 VAO를 사용해 램프를


표현한 다음 모델 행렬에서 일부 변형을 수행할 수 있지만 다음 튜토리얼에서는 컨테이너 객체의 정점 데이터 및 특성 포인터를


자주 변경하므로 이 옵션은 필요하지 않다. 램프 오브젝트에 전파하기 위해 변경되므로 새로운 VAO를 생성한다:

unsigned int lightVAO;
glGenVertexArrays(1, &lightVAO);
glBindVertexArray(lightVAO);
// we only need to bind to the VBO, the container's VBO's data already contains the correct data.
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// set the vertex attributes (only position data for our lamp)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);

 코드는 비교적 간단해야한다. 이제 우리는 컨테이너와 램프 큐브를 만들었다. 정의할 부분이 하나 남아있고, 그것은 조각 쉐이더이다:

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

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

 조각 쉐이더는 유니폼 변수의 객체 색상과 밝은 색상을 모두 허용한다. 여기서 우리는 이 튜토리얼의 시작 부분에서 설명한 것처럼


빛의 색상에 대상의 (반사된) 색상을 곱한다. 다시 말하지만 이 쉐이더는 이해하기 쉬워야한다. 하얀 색으로 개체의 색을 마지막


섹션의 coral 색으로 설정해보자:

// don't forget to 'use' the corresponding shader program first (to set the uniform)
lightingShader.use();
lightingShader.setVec3("objectColor", 1.0f, 0.5f, 0.31f);
lightingShader.setVec3("lightColor",  1.0f, 1.0f, 1.0f);

 주목해야 할 것은 정점과 조각 쉐이더를 변경하기 시작할 때 램프 큐브도 바뀌고, 이것이 우리가 원하는 바가 아니라는 것이다.


램프 객체의 색상이 다음 튜토리얼의 조명 계산에 의해 영향을 받는 것을 원하지 않지만 램프를 나머지와 격리시켜야한다.


우리는 램프가 다른 색상 변경에 영향을 받지 않고 일정한 밝은 색상을 유지하기를 원한다.



 이를 달성하기 위해 실제로 조명 쉐이더를 변경하지 않아도 되므로 조명을 그리는데 사용할 쉐이더 세트를 실제로 만들어야 한다.


정점 쉐이더는 현재 정점 쉐이더와 동일하므로 램프의 정점 쉐이더에 대한 소스 코드를 간단히 복사할 수 있다.


램프의 조각 쉐이더는 램프에 일정한 흰색을 정의해 램프의 색상을 밝게 유지한다:

#version 330 core
out vec4 FragColor;

void main()
{
    FragColor = vec4(1.0); // set all 4 vector values to 1.0
}

 객체를 그리려면 방금 정의한 조명 쉐이더를 사용해 컨테이너 객체 (또는 다른 많은 객체)를 그려야하고, 


램프를 그릴 때 램프의 쉐이더를 사용한다. 튜토리얼을 진행하는 동안 조명 쉐이더를 점차적으로 업데이트해 좀 더 사실적인


결과를 천천히 얻을 것이다.



 램프 큐브의 주 목적은 빛이 어디에서 오는가를 보여주는 것이다. 우리는 일반적으로 광원의 위치를 장면의 어딘가에 정의하지만,


이것은 단순히 시각적 의미가 없는 위치이다. 실제 램프를 표시하기 위해 광원의 동일한 위치에 램프 큐브를 그린다.


램프 쉐이더로 램프 개체를 그리면 씬의 조명 상태에 관계없이 램프 큐브가 항상 흰색으로 유지된다.



 따라서 광원 좌표계에서 광원의 위치를 나타내는 전역 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();
// set the model, view and projection matrix uniforms
...
// draw the lamp object
glBindVertexArray(lightVAO);
glDrawArrays(GL_TRIANGLES, 0, 36);			

 따라서 적절한 위치에 모든 코드 조각을 주입하면 조명을 실험하기 위해 제대로 구성된 OpenGL 응용 프로그램이 만들어진다.


모든 것이 컴파일되면 다음과 같이 보일 것이다:



 지금 당장은 많이 보지는 않지만 앞으로의 튜토리얼에서 더 재밌을 것이라고 약속한다.





** ** ** ** ** ** ** ** ** **


흠... 파트가 바뀌면서 코드가 달라지니 프로젝트 폴더를 나눌까 하다가 혼자 공부하는 것이니 주석을 잘 옮겨두고 한 프로젝트 파일로 하기로 결정했다.


큐브 램프 ㅋㅓ엽