Notice
Recent Posts
Recent Comments
Link
«   2024/04   »
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
Archives
Today
Total
관리 메뉴

게임공장

[Learn OpenGL 번역] 3-2. 조명 - 기본 조명 본문

OpenGL

[Learn OpenGL 번역] 3-2. 조명 - 기본 조명

짱승_ 2018. 7. 15. 19:57

기본 조명

조명/기본 조명

  실생활의 조명은 매우 복잡하고 프로세싱 파워로 계산하기 어려울 정도의 많은 요소들로 이루어져 있습니다. 그러므로 OpenGL에서의 조명은 처리되기 쉽고 실세계의 사물과 비슷하게 보이는 모델을 사용하여 실세계에 대한 근사치를 기반으로 이루어져있습니다. 이 조명 모델들은 우리가 이해한 빛의 물리학을 기반으로 이루어져 있습니다. 이 모델들 중 하나는 Phong lighting model이라고 불립니다. Phong 모델은 3 가지의 요소로 이루어져 있습니다: ambient lighting(주변광), diffuse lighting(분산광), specular lighting(반사광). 아래에서 이 조명 요소들이 실제로 어떻게 보이는지 볼 수 있습니다.



  • Ambient lighting: 어두운 때일지라도 일반적으로 world의 어딘가엔 어떠한 조명이 존재(달, 멀리 떨어져 있는 조명)하므로 오브젝트는 대부분 완전히 어두워지지 않습니다. 이를 시뮬레이션해보기 위해 우리는 오브젝트에 어떠한 색상을 주는 ambient 조명 상수를 사용합니다.
  • Diffuse lighting: 조명 오브젝트가 가지고 있는 방향이 있는 조명을 오브젝트에 비추어 시뮬레이션 합니다. 이 것은 조명 모델에서 시각적으로 가장 중요한 요소입니다. 오브젝트의 많은 부분이 광원을 마주보고 있을수록 더 밝아집니다.
  • Specular lighting: 빛나는 오브젝트에서 볼 수 있는 밝은 지점을 시뮬레이션 합니다. 반사 하이라이트는 종종 오브젝트의 컬러보다는 조명의 컬러에 더 치우쳐집니다.

  시각적으로 흥미로운 Scene을 생성하기 위해 최소한 이 3 가지의 조명 요소들을 시뮬레이션해야 합니다. 우리는 가장 간단한 ambient lighting으로 시작해보도록 하겠습니다.

Ambient lighting

  빛은 일반적으로 하나의 광원으로부터 오는 것이 아니라 우리 주변에 산재해 있는 많은 광원들로부터 옵니다. 그들이 직접적으로 보이지 않을지라도 말이죠. 빛의 특성들 중 하나는 근처에 있지 않은 지점에 도달하기 위해 어려 방향으로 퍼지고 산란한다(튄다)는 것입니다. 따라서 빛은 면에서 반사될 수 있고 오브젝트에 간접적으로 영향을 줍니다. 이 것을 고려한 알고리즘을 global illumination 알고리즘이라고 하고 이는 비용이 많이 들고 매우 복잡합니다.


  우리는 복잡하고 비용이 많이 드는 알고리즘을 좋아하지 않기 때문에 global illumination 의 아주 간단한 모델을 사용하여 시작할 것입니다. 이 것의 이름은 ambient lighting입니다. 이전 섹션에서 보셨다시피 오브젝트 fragment의 최종 컬러에 추가할 작은 상수 (빛) 컬러를 사용합니다. 따라서 직접적인 광원이 없다고 하더라도 항상 퍼지는 빛이 있는 것처럼보이게 만듭니다.


  Scene에 ambient lighting을 추가하는 것은 정말 쉽습니다. 빛의 컬러를 정하고 이를 작은 상수 ambient 요소와 곱합니다. 그리고 이를 오브젝트의 컬러와 곱하여 fragment의 컬러로 이 값을 사용합니다.


void main()
{
    float ambientStrength = 0.1;
    vec3 ambient = ambientStrength * lightColor;

    vec3 result = ambient * objectColor;
    FragColor = vec4(result, 1.0);
}  

  이제 프로그램을 실행시키면 조명의 첫 번째 단계가 성공적으로 적용되었음을 볼 수 있습니다. 이 오브젝트는 상당히 어둡지만 ambient lighting이 적용되었기 때문에 완전히 어둡지는 않습니다(다른 shader를 사용하기 때문에 빛 큐브는 영향을 받지 않습니다). 이는 다음과 같이 보일 것입니다.


Diffuse lighting

  Ambient lighting은 그자체로 아주 흥미로운 결과를 만들디 못합니다. 하지만 diffuse lighting은 오브젝트에 중요한 시각적인 효과를 주기 시작할 것입니다. Diffuse lighting은 해당 오브젝트의 광선에 정렬된 fragment가 광원과 가까이 있을수록 그 오브젝트는 밝아집니다. diffuse lighting에 대해 좀 쉽게 이해를 하기 위해 다음의 이미지를 보세요.



  오브젝트의 하나의 fragment를 향해 광선을 쏘고 있는 광원이 왼쪽에 있습니다. 우리는 광선과 fragment 사이의 각이 필요합니다. 광선이 오브젝트의 면에 수직으로 향한다면 빛은 아주 많이 영향을 끼칠것입니다. 광선과 fragment 사이의 각을 측정하기 위해서는 법선 벡터(normal vector)라고 불리는 것을 사용합니다. 법선 벡터는 fragment 면에 대해 수직인 벡터입니다(여기에서 노란색 화살표로 표현된 벡터). 이 벡터는 나중에 얻어보도록 하겠습니다. 두 벡터 사이의 각은 내적을 통해 쉽게 계산될 수 있습니다.


  두 유닛 벡터 사이의 각이 작을수록 내적은 1와 가까워진다는 사실을 변환 강좌에서 언급했던 것을 기억하실 것입니다. 두 벡터 사이의 각이 90도일때 내적은 0이 됩니다. θ 에 동일하게 적용해본다면 θ 의 값이 클수록 빛의 영향을 더 적게 받게됩니다.   두 벡터 사이의 각에 대한 cosine(만) 구하기 위해서 우리는 단위 벡터(길이가 1인 벡터)를 사용할 것이므로 모든 벡터들을 정규화할 필요가 있습니다. 그렇지 않으면 내적은 그냥 cosine한 값보다 더 큰 값을 리턴할 것입니다(변환 강좌를 보세요).

  따라서 내적의 결과로 fragment의 컬러에 대한 빛의 영향을 계산하기 위해 사용할 스칼라를 얻을 수 있습니다. 결과적으로 빛을 향한 방향에 따라 다르게 빛나는 fragment를 만들 수 있습니다.


  그래서 diffuse lighring을 계산하기위해 무엇이 필요할까요?


  • Normal vector(법선 벡터): vertex의 면에 수직인 벡터
  • The directed light ray(방향을 가진 광선): 빛의 위치에서 fragment 위치로 향하는 방향 벡터입니다. 이 광선을 계산하기 위해 빛의 위치 벡터와 fragment의 위치 벡터가 필요합니다.

법선 벡터

  법선 벡터는 vertex의 면에 수직인 (단위) 벡터입니다. vertex는 그 자체로 면을 가지고 있지 않기 때문에(단지 공간 내부에 하나의 점일 뿐입니다) vertex의 면을 알아내기 위해 주변의 vertex들을 사용하여 법선 벡터를 구합니다. 큐브의 모든 vertex들에 대한 법선 벡터를 계산하기 위해 외적을 사용하여 약간의 트릭을 사용할 수 있습니다. 하지만 3D 큐브는 복잡한 도형이 아니기 때문에 우리는 간단히 법선 벡터를 vertex data에 수작업으로 넣을 수 있습니다. 수정된 vertex data는 여기에서 확인할 수 있습니다. 이 법선들이 실제로 큐브의 평면에 대해 수직인 벡터들인지 생각해보세요(큐브는 6개의 평면을 가지고 있습니다).


  vertex 배열에 데이터를 추가했기 때문에 lighting vertex shader를 수정해야 합니다.


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

  이제 우리는 각 vertex들에 법선 벡터를 추가하였고 vertex shader를 수정하고 vertex attribute pointer 또한 수정하였습니다. 램프는 같은 vertex 배열의 데이터를 사용하지만 lmap shader에서는 새롭게 추가된 법선 벡터를 사용하지 않습니다. lamp shader나 attribute 구성을 수정할 필요가 없고 vertex attribute pointer에 새로운 vertex 배열의 크기를 반영해야 합니다.


glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);

  우리는 오직 각 vertex의 처음 3개의 실수 값을 사용하고 나머지 3개의 실수 값은 무시하므로 stride 파라미터를float 크기의 6배로 수정합니다.

  lamp shader가 완전하게 사용하지 않는 vertex 데이터를 사용하는 것은 비효율적으로 보일 수 있습니다. 하지만 vertex 데이터는 컨테이너 오브젝트 덕분에 이미 GPU의 메모리 안에 저장되어 있기 때문에 GPU 메모리에 새로운 데이터를 저장할 필요가 없습니다. 이는 실제로 lmap에 대한 새로운 VBO를 할당한느 것보다 효율적입니다.

  모든 빛에 대한 계산은 fragment shader에서 완료되므로 vertex shader의 법선벡터를 fragment shader에 전달해야 합니다.


out vec3 Normal;

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

  남은 일은 fragment shader에 해당하는 입력 변수를 선언하는 것입니다.


in vec3 Normal;  

Diffuse color 계산

  이제 우리는 각각의 vertex에 대한 법선 벡터를 가지고 있지만 여전히 fragment 위치 벡터와 빛의 위치 벡터가 필요합니다 빛의 위치는 하나의 정적인 변수이기 때문에 fragment shader에 uniform으로 간단히 선언할 수 있습니다.


uniform vec3 lightPos;  

  그런 다음 게임 루프 안에서 uniform을 업데이이트 합니다(또는 이 값이 바뀌지 않기 때문에 게임루프 밖에서). 광원의 위치로서 이전 강좌에서 선언한 lightPos 벡터를 사용합니다.


lightingShader.setVec3("lightPos", lightPos);  

  우리가 필요한 마지막 하나는 실제 fragment의 위치입니다. 우리는 모든 빛에 대한 계산을 world space에서 할 것이므로 world space에 있는 vertex 위치가 필요합니다. 우리는 이를 vertex 위치 attribute를 오직(view, projection 행렬을 제외한) model 행렬과 곱하여 world space 좌표로 변환하는 것만으로 수행할 수 있습니다. 이는 vertex shader에서 쉽게 수행될 수 있으므로 출력 변수를 선언하고 world space 좌표를 계산해봅시다.


out vec3 FragPos;  
out vec3 Normal;
  
void main()
{
    gl_Position = projection * view * model * vec4(aPos, 1.0);
    FragPos = vec3(model * vec4(aPos, 1.0));
    Normal = aNormal;
}

  그리고 마지막으로 해당 입력 변수를 fragment shader에 추가 합니다.


in vec3 FragPos;  

  이제 필요한 모든 변수들이 설정되었고 fragment shader에서 빛에 대한 계산을 시작할 수 있습니다.


  계산을 하기 위해 먼저 필요한 것은 광원과 fragment의 위치 사이의 방향 벡터입니다. 빛의 방향 벡터는 빛의 위치 벡터와 fragment의 위치 벡터의 차라고 언급했었습니다. 변환 강좌에서 보셨다시피 우리는 이를 두 개의 벡터에 대한 뺄셈을 수행해서 계산할 수 있습니다. 또한 관련있는 모든 벡터들이 단위 벡터로 변하기를 원하므로 법선 벡터와 최종 방향 벡터를 정규화 합니다.


vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);  
  빛을 계산할 때 우리는 일반적으로 벡터의 크기에 대해 생각하지 않습니다. 오직 그들의 방향에 대해서 생각할 뿐입니다. 우리가 방향에 대해서만 신경쓰기 때문에 거의 모든 계산들은 단위 벡터들로 수행되어 집니다. 대부분의 계산을 간단히 할 수 있기 때문입니다(내적 처럼). 그래서 빛을 계산할 때 관련있는 벡터들을 정규화하였는지 확인하여 그들이 모두 단위 벡터인지 확인하세요. 벡터 정규화를 빼먹는 것은 흔히 발생하는 실수입니다.

  다음 우리는 normlightDir 벡터를 내적하여 실제 diffuse 효과를 계산하기 원합니다. 그러면 diffuse 요소를 얻기 위해 결과 값은 빛의 컬러와 곱해집니다. 최종적으로 두 벡터 사이의 각이 클수록 diffuse 요소는 어두워집니다.


float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * lightColor;

  두 개의 벡터 사이의 각이 90도 보다 크다면 내적의 결과는 실제로 음수가 되고 음의 diffuse 요소를 가지게 됩니다. 이러한 이유 때문에 우리는 max 함수를 사용합니다. 이 함수는 두 개의 파라미터 중 가장 큰 값을 리턴합니다. 그래서 diffuse 요소(와 컬러)가 음의 값을 가질 수 없게 합니다. 빛에서 음의 값을 가지는 컬러는 실제로 정의되지 않으므로 피하는 것이 좋습니다. 여러분이 괴짜 예술가가 아닌 이상 말이죠.


  이제 우리는 ambient와 diffuse 컴포넌트를 가지고 있습니다. 이 두개의 컴포넌트에 설정된 컬러와 오브젝트의 컬러를 곱하여 최종 fragment의 컬러를 결정합니다.


vec3 result = (ambient + diffuse) * objectColor;
FragColor = vec4(result, 1.0);

  응용 프로그램(과 shader)이 성공적으로 컴파일되었으면 다음과 같은 화면을 볼 수 있습니다.



  diffuse lighting과 함께 큐브가 실제 큐브처럼 보임을 알 수 있습니다. 머릿속으로 법선 벡터를 시각화 한 후 큐브를 둘러 보고 법선 벡터와 빛의 방향 벡터 사이의 각이 클수록 fragment는 어두워지는 것을 확인해보세요.


  문제가 생겼다면 여기에서 소스 코드를 확인하세요.

마지막 하나

  현재로서는 법선 벡터를 그냥 그대로 vertex shader에서 fragment shader로 보내기만 했습니다. 하지만 fragment shader에서 수행해왔던 계산들은 모두 world space 좌표에서 수행되므로 법선 벡터를 world space 좌표로 변환해야 하지 않을까요?. 기본적으로는 맞습니다. 하지만 이를 단순히 model 행렬과 곱하는 것이 아니라 좀 복잡합니다.


  제일 먼저 법선 벡터는 오직 방향 벡터입니다. 공간에서 특정한 위치를 나타내지 않습니다. 또한 법선 벡터는 동차 좌표(위치 벡터의 w 요소)를 가지고 있지 않습니다. 이는 이동 변환하는 것은 법선 벡터에서 아무런 효과가 없다는 것을 의미합니다. 그리고 법선 벡터에 model 행렬을 곱하고 싶다면 model 행렬의 좌측 상단의 3x3 행렬을 취하여 이동 행렬의 일부를 지워야 합니다(우리는 법선 벡터의 w 요소를 0으로 설정할 수 있고 4x4 행렬과 곱할 수도 있습니다. 이는 이동을 지우는 것과 같습니다). 우리가 법선 벡터에 적용할 것은 스케일과 회전 변홥입니다.


  두 번째로 model 행렬이 불균일 스케일을 수행하면 vertex들이 수정되어 법선 벡터가 더 이상 해당 면과 수직이 되지 않습니다. 그래서 우리는 이 model 행렬로 법선 벡터를 변환할 수 없습니다. 다음 이미지는 불균일 스케일을 수행하는 model 행렬로 법선 벡터를 변환했을 때를 보여줍니다.



  불균일 스케일(균일 스케일은 그들의 방향이 변하지 않고 단지 크기만 변하기 때문에 법선에 영향을 끼치지 않습니다. 크기는 정규화를 통해 다시 원래의 상태로 되돌릴 수 있습니다)을 적용할때마다 법선 벡터는 해당 면에 더 이상 수직하지 않아 빛을 왜곡하게 됩니다.


  이를 해결하는 트릭은 특별히 법선 벡터에 맞춰서 만들어진 다른 model 행렬을 사용하는 것입니다. 이 행렬은 법선 행렬이라고 불리며 법선 벡터를 잘못된 형태로 스케일링하는 효과를 지워주는 선형대수학 연산들을 사용합니다. 이 행렬이 실제로 어떻게 계산되는지 알고싶다면 이 을 보는 것을 권장합니다.


  이 법선 행렬은 'model 행렬의 좌측 상단 모서리의 역행렬에 대한 전치행렬'로 정의될 수 있습니다. 휴, 길고 복잡한 말입니다. 여러분이 이 뜻을 정확히 이해하지 못해도 걱정하지 마세요. 우리는 아직 역행렬과 전치행렬에 대해 아직 다루지 않았습니다. 대부분의 자료에서 법선 행렬을 이 연산들을 model-view 행렬에 적용하는 것으로 정의합니다. 하지만 우리는 world space(view space가 아닌)에서 작업하기 때문에 오직 model 행렬만 사용합니다.


  Vertex shader에서 우리는 모든 행렬 유형에서 작동하는 inverse, transpose 함수를 사용하여 우리 스스로 법선 행렬을 생성할 수 있습니다. 또한 우리는 행렬을 3x3 행렬로 변환하므로 이동 속성을 잃지만 vec3 법선 벡터와 곱셈을 할 수 있다는 것을 알아두세요.


Normal = mat3(transpose(inverse(model))) * aNormal;  

  Diffuse lighting 섹션에서 조명은 괜찮습니다. 우리는 오브젝트에 어떠한 스케일 연산도 수행하지 않았기 때문입니다. 그래서 실제로 법선 행렬을 사용할 필요가 없고 법선을 모델 행렬과 곱하기만 하면 됩니다. 하지만 불균일 스케일을 수행한다면 법선 벡터에 이 법선 행렬을 반드시 곱해주어야합니다.

  역행렬로 변환하는 것은 비용이 많이 드는 연산입니다. scene의 각각의 vertex에 대해 수행되어야 하기 때문에 역행렬로 변환하는 연산을 비하도록 하세요. 공부 목적으로는 괜찮지만 효율적인 응용 프로그램을 위해서는 CPU에서 법선 행렬을 계산하고 이 것을 렌더링 하기 전에 uniform을 통해 shader로 전달해야 합니다(model 행렬처럼).

Specular Lighting

  여러분이 지치지 않았다면 specular 하이라이트를 추가하여 Phong lighting model을 끝낼 수 있습니다.


  Diffuse lighting과 마찬가지로 specular lighring은 빛의 방향 벡터와 오브젝트의 법선 벡터를 기반으로 하지만 플레이어가 fragment를 바라보고 있는 방향에 대한 view 방향도 기반으로 합니다. Specular lighring은 빛의 반사 특성을 기반으로 합니다. 오브젝트의 면을 거울이라고 생각하면 이 면에서 반사되어진 빛을 보는 specular lighting은 가장 센 빛일것입니다. 다음의 그림에서 이 효과를 볼 수 있습니다.



  우리는 법선 벡터 주위로 빛의 방향을 반사하여 반사 벡터를 계산합니다. 그런 다음 이 반사 벡터와 view 방향 사이의 거리를 계산합니다. 두 벡터 사이의 각이 가까울수록 specular light의 강도는 강해집니다. 최종 효과는 오브젝트를 통해 반사된 빛의 방향을 바라볼 때 약간의 하이라이트를 보는 것입니다.


  View 벡터는 specular lighting에 필요한 하나의 추가적인 변수입니다. 우리는 viewer의 world space 위치와 fragment들의 위치를 사용하여 이 변수를 계산할 수 있습니다. 그런 다음 specular의 세기를 계산하고 이를 빛의 컬러와 곱하고 이 를 ambient, diffuse 요소에 추가합니다.

  우리는 world space에서 lighting 계산을 선택했습니다. 하지만 대부분의 사람들은 view space 에서 lighting을 수행하는 것을 선호하는 경향이 있습니다. view space 에서 계산을 하는 것의 장점은 viewer의 위치가 항상 (0,0,0)이므로 항상 뷰어의 위치를 쉽게 알 수 있습니다. 하지만 world space에서의 lighting 계산이 학습 목적으로는 더 직관적이라고 생각합니다. 여러분이 view space에서 lighting을 꼐산하고 싶다면 모든 관련된 벡터들을 view 행렬을 사용하여 변환시켜야 합니다(법선 행렬도 수정해야 한다는 것을 잊지 마세요).

  Viewer의 world space 좌표를 얻기 위해서 간단히 카메라 오브젝트(이는 당연히 viewer입니다)의 위치 벡터를 사용하면 됩니다. 이제 fragment shader에 다른 uniform을 추가하고 해당 카메라 위치 벡터를 fragment shader에 전달해봅시다.


uniform vec3 viewPos;

lightingShader.setVec3("viewPos", camera.Position); 

  이제 우리는 필요한 모든 변수를 가졌으므로 specular 세기를 계산할 수 있습니다. 먼저 specular 하이라이트에 효과가 너무 세지 않을 정도의 중간-밝기의 컬러로 specular 세기 값을 정의합니다.


float specularStrength = 0.5;

  이 것을 1.0f로 설정하면 아주 밝은 specular 요소를 얻을 수 있고 이는 coral 큐브에 대해 지나치게 셉니다. 다음 강좌에서 이 모든 lighting 세기에 대한 적절한 세팅에 대해서 오브젝트에 어떻게 영향을 끼치는지에 대해 다룰 것입니다. 그 다음 view 방향 벡터와 해당 반사 벡터를 계산합니다.


vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);  

  lightDir 벡터의 부호를 -로 바꿨다는 것을 알아두세요. reflect 함수는 첫 번째 벡터로 광원으로부터 fragment 위치로 향하는 벡터를 원하는데 lightDir 벡터는 현재 fragment에서 광원으로 향하는 벡터 입니다(lightDir 벡터를 계산할 때 뺄셈의 순서에 따라 달라집니다). 정확한 reflect 벡터를 얻기 위해 먼저 lightDir 벡터의 부호를 반대로 합니다. 두 번째 파라미터는 법선 벡터를 필요로 하므로 우리는 정규화된 norm 벡터를 넘겨줍니다.


  그런 다음 남은 할일은 실제로 specular 컴포넌트를 계산하는 것입니다. 이는 다음과 같은 공식으로 수행될 수 있습니다.


float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
vec3 specular = specularStrength * spec * lightColor;  

  먼저 view 방향과 reflect 방향을 내적합니다(그리고 이 것이 음수가 되지 않도록 확인합니다). 그런 다음 이 것을 32제곱 해줍니다. 이 32값은 하이라이트는 shininess(빛남의 정도) 값입니다. 오브젝트의 shininess 값이 높을수록 빛을 주변에 퍼지게 하지 않고 적절하게 반사합니다. 따라서 하이라이트가 작아집니다. 아래에서 shininess 값에 따른 시각적 효과를 볼 수 있습니다.



  우리는 specular 컴포넌트가 너무 많이 차지하지 않기를 원하므로 32로 유지시킵니다. 남은 단 한가지 할 일은 이 컴포넌트를 ambient와 diffuse 컴포넌트에 추가하고 오브젝트 컬러의 결과값에 곱하여 혼합하는 것입니다.


vec3 result = (ambient + diffuse + specular) * objectColor;
FragColor = vec4(result, 1.0);

  이제 Phong lighting model 의 모든 lighting 컴포넌트들을 계산했습니다. 여러분의 시점에 기반하여 다음과 같이 보일 것입니다.



  여기에서 완전한 소스 코드를 볼 수 있습니다.

  lighting shader들의 초창기에는 개발자들은 vertex shader에서 Phong lighting model을 구현하곤 했었습니다. 이를 vertex shader에서 구현하는 것의 장점은 fragment 보다 적은 vertex의 수가 적으므로 (비용이 많이 드는) lighting 계산들이 덜 자주 사용되기 때문에 효율적이다는 것입니다. 하지만 vertex shader의 최종 컬러 값은 오직 vertex만의 lighting 컬러입니다. 그러면 fragment의 컬러 값은 보간된 lighting 컬러가 됩니다. 이 결과는 아주 많은 양의 vertex들이 사용되지 않는다면 lighting이 현실적으로 보이지 않게됩니다.



  Phong lighting model이 vertex shader에서 구현되었을 때 이를 Phong shading 대신 Gouraud shading이라고 부릅니다. 보간때문에 lighting이 뭔가 아쉬워보입니다. Phong shading은 좀 더 부드러운 lighting을 보여줍니다.

  이제 shader가 얼마나 강력한지 알아야 합니다. 약간의 정보와 함께 shader는 우리의 모든 오브젝트에 대한 fragment 컬러에 lighting이 얼마나 영향을 끼칠지 계산할 수 있습니다. 다음 강좌에서 lighting 모델을 가지고 우리가 할 수 있는 것들에 대해 좀 더 깊이 다루어 볼 것입니다.

연습

  • 지금 당장 광원은 움직이지 않는 지루하고 정적인 광원입니다. sin이나 cos 함수를 사용하여 광원이 시간이 지남에 따라 scene 주위를 움직이게 해보세요. 시간이 지남에 따라 lighting이 바뀌는 것을 보면 Phong lighting model에 대해 쉽게 이해할 수 있을 것입니다: 해답
  • 다른 ambient, diffuse, specular 세기를 가지고 놀아보세요. 그리고 이것들이 결과에 어떻게 영향을 끼치는지 살펴보세요. 또한 shininess 값으로도 실험을 해보세요. 왜 이러한 값이 이러한 시각적인 결과를 낳는지 이해해보세요.
  • world space 대신 view space에서 Phong shading을 수행해 보세요: 해답
  • Phong shading 대신 Gouraud shading을 구현해보세요. 여러분이 제대로 했다면 뭔가 아쉬운 것을 볼 수 있어야 합니다. 왜 이렇게 이상하게 보이는지 이유를 생각해보세요: 해답



출처 : https://learnopengl.com, Joey de Vries

반응형