본문 바로가기

Game/Graphics

Learn OpenGL - Advanced OpenGL : Cubemaps

link : https://learnopengl.com/Advanced-OpenGL/Cubemaps


Cubemaps


 우리는 잠시동안 2D 텍스처를 사용해 왔지만 지금까지 살펴보지 못한 더 많은 텍스처 유형이 있다. 이 튜토리얼에서는 사실


하나의 텍스처로 매핑된 여러 텍스처의 조합인 큐브 텍스처 유형에 대해 설명한다:


Indexing/Sampling from a cubemap in OpenGL


방향 벡터의 크기는 중요하지 않다. 방향이 제공되는 한 OpenGL은 방향이 도달하는 해당 텍셀을 검색하고, 제대로 샘플링 된 텍스쳐 값을 반환한다.

 큐브 맵을 첨부할 큐브 모양이 있다고 상상하면 큐브 맵을 샘플링하는 방향 벡터는 큐브의 정점 위치와 유사하다. 큐브가 원점의 중앙에 있는


한 큐브의 실제 위치 벡터를 사용해 큐브 맵을 샘플링 할 수 있다. 그런 다음 모든 정점의 텍스처 좌표를 큐브의 정점 위치로 가져올 수 있다.


결과는 큐브 맵의 적절한 개별면 텍스처에 액세스하는 텍스처 좌표이다.





Creating a cubemap


 큐브 맵은 다른 텍스처와 같은 텍스처이므로 텍스처를 생성하고 텍스처를 추가로 처리하기 전에 적절한 텍스처 대상에 바인딩한다.


지금은 GL_TEXTURE_CUBE_MAP에 바인딩한다:

unsigned int textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_CUBE_MAP, textureID); 

 큐브 맵은 6개의 텍스처로 구성되어 있기 때문에 glTexImage2D를 매개 변수를 이용해 이전 튜토리얼과 비슷한 값으로 6번 호출해야한다.


그러나 이번에는 텍스처 타겟 매개 변수를 큐브 맵의 특정 면으로 설정해야한다. 기본적으로 큐브 맵의 어느면에 대해 텍스처를 생성하는지


OpenGL에 알려야한다. 이는 큐브 맵의 각 면에 대해 glTexImage2D를 한 번 호출해야한다는 것을 의미한다.



 우리가 6개의 면을 가지고 있기 때문에 OpenGL은 특별히 cubemap의 면을 겨냥하기 위한 6개의 특별한 텍스처 타겟을 제공한다:


Texture targetOrientation
GL_TEXTURE_CUBE_MAP_POSITIVE_XRight
GL_TEXTURE_CUBE_MAP_NEGATIVE_XLeft
GL_TEXTURE_CUBE_MAP_POSITIVE_YTop
GL_TEXTURE_CUBE_MAP_NEGATIVE_YBottom
GL_TEXTURE_CUBE_MAP_POSITIVE_ZBack
GL_TEXTURE_CUBE_MAP_NEGATIVE_ZFront


 OpenGL의 많은 열거 형과 마찬가지로 배후의 int 값은 선형적으로 증가한다. 따라서 텍스처 위치의 배열이나 벡터를 가질 수 있다면


모든 텍스처 타겟을 통해 GL_TEXTURE_CUBE_MAP_POSITIVE_X로 시작하고 각 반복마다 열거 형을 1씩 증가시켜 루프 할 수 있다:

int width, height, nrChannels;
unsigned char *data;  
for(GLuint i = 0; i < textures_faces.size(); i++)
{
    data = stbi_load(textures_faces[i].c_str(), &width, &height, &nrChannels, 0);
    glTexImage2D(
        GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 
        0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data
    );
}

 여기에는 테이블에 주어진 순서대로 큐브 맵에 필요한 모든 텍스처의 위치를 포함하는 textures_faces라는 벡터가 있다.


이것은 현재 바인딩된 큐브 맵의 각 면에 대한 텍스처를 생성한다.



 큐브 맵은 다른 텍스처와 같은 텍스처이기 때문에 래핑 및 필터링 메서드도 지정한다:

glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);  

 GL_TEXTURE_WRAP_R에 겁먹지 마라. 텍스처의 3차원에 해당하는 텍스처의 R 좌표에 대한 랩핑 (wrapping) 메소드를 설정한다.


 그런 다음 큐브 맵을 사용할 객체를 그리기 전에 해당 텍스처 유닛을 활성화하고 렌더링 전에 큐브 맵을 바인딩한다. 일반적인 2D 텍스처와


비교하면 차이가 없다.



 조각 쉐이더 내에서 텍스처 함수를 사용해 샘플링하는 samplerCube 유형의 다른 샘플러를 사용해야 하지만 이번에는 vec2 대신 vec3 방향 벡터를


사용한다. 큐브 맵을 사용하는 조각 쉐이더의 예는 다음과 같다:

in vec3 textureDir; // direction vector representing a 3D texture coordinate
uniform samplerCube cubemap; // cubemap texture sampler

void main()
{             
    FragColor = texture(cubemap, textureDir);
}  

 큐브 맵으로 구현하기가 훨씬 쉬운 몇 가지 흥미로운 기술들이 있다. 그 중 하나는 skybox를 만드는 것이다.






Skybox


 skybox는 전체 장면을 포함하고 주변 환경의 6개의 이미지를 포함하는 큐브이다. 플레이어가 실제로 있는 것보다 실제 환경이 훨씬 크다는 환상을


준다. 비디오 게임에 사용되는 스카이 박스의 예로는 산, 구름, 별이 빛나는 밤하늘 등이 있다. 별이 빛나는 밤하늘 이미지를 사용하는 skybox의 예는


다음 세 번째 elder scroll game의 스크린 샷에서 볼 수 있다:


Image of morrowind with a skybox


 아마 지금까지 이 잘 맞는 큐브맵이 skybox같다는 것을 짐작할 수 있을 것이다: 


우리는 6개의 면을 가진 큐브를 가지고 있으며 면마다 텍스처가 필요하다. 이전 이미지에서 그들은 작은 상자 안에 실제로 있는 동안


플레이어가 큰 우주에 있다는 환상을 주기 위해 밤하늘의 여러 이미지를 사용했다.



 일반적으로 이러한 온라인 사이트에서 skybox를 찾을 수 있다. 예를 들어, 이 웹 사이트에는 많은 skybox를 찾을 수 있다.


이 skybox 이미지는 대개 다음 패턴을 가진다:


Image of a skybox for a cubemap in OpenGL


 그 6면을 cube로 집어 넣으면 커다란 풍경을 시뮬레이트하는 완전히 질감된 큐브를 얻을 수 있다. 일부 리소스는 이와 같은 형식으로


skybox를 제공한다. 이 경우 수동으로 6개의 면 이미지를 추출해야하지만 대부분의 경우 6개의 단일 텍스처 이미지로 제공된다.



 이 특별한 skybox는 우리가 장면에 사용할 것이며 여기에서 다운로드 할 수 있다.






Loading a skybox


 skybox는 그 자체만으로 cubemap이기 때문에 skybox를 로딩하는 것은 이전에 본 것과 너무 다르지 않다. skybox를 로드하려면


6개의 텍스처 위치 벡터를 받아들이는 다음 함수를 사용한다:

unsigned int loadCubemap(vector<std::string> faces)
{
    unsigned int textureID;
    glGenTextures(1, &textureID);
    glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);

    int width, height, nrChannels;
    for (unsigned int i = 0; i < faces.size(); i++)
    {
        unsigned char *data = stbi_load(faces[i].c_str(), &width, &height, &nrChannels, 0);
        if (data)
        {
            glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 
                         0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data
            );
            stbi_image_free(data);
        }
        else
        {
            std::cout << "Cubemap texture failed to load at path: " << faces[i] << std::endl;
            stbi_image_free(data);
        }
    }
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);

    return textureID;
}  

 함수 자체가 너무 놀라운 것은 아니다. 기본적으로 이전 섹션에서 보았던 모든 큐브 맵 코드이지만 하나의 관리 가능한 기능으로 결합되었다.



 이 함수를 호출하기 전에 큐브 맵 enums에 지정된 순서대로 벡터에 적절한 텍스처 경로를 로드한다:

vector<std::string> faces;
{
    "right.jpg",
    "left.jpg",
    "top.jpg",
    "bottom.jpg",
    "front.jpg",
    "back.jpg"
};
unsigned int cubemapTexture = loadCubemap(faces); 

 우리는 이제 skybox를 cubemapTexture가 있는 큐브 맵으로 로드했다. 우리는 이제 그것을 큐브에 바인딩해 배경으로 사용했던 선명하고


깨끗한 색상을 대체 할 수 있다.






Displaying a skybox


 skybox가 큐브에 그려지기 때문에 다른 VAO, VBO 및 다른 객체와 같은 새로운 정점 세트가 필요하다. 여기서 정점 데이터를 얻을 수 있다.



 큐브의 위치를 텍스처 좌표로 사용해 3D 큐브를 텍스처링 하는 데 사용되는 큐브 맵을 샘플링 할 수 있다. 큐브가 원점 (0,0,0)의 중심에 있을 때


각 큐브의 위치 벡터는 원점의 방향 벡터이기도 하다. 이 방향 벡터는 정확히 특정 큐브의 위치에서 해당 텍스처 값을 얻는 데 필요한 것이다.


이러한 이유로 우리는 위치 벡터를 제공하고 텍스처 좌표가 필요하지 않다.



 skybox를 렌더링하려면 너무 복잡하지 않은 새로운 쉐이더 세트가 필요하다. 하나의 정점 속성만 있기 때문에 정점 쉐이더는 아주 간단하다:

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

out vec3 TexCoords;

uniform mat4 projection;
uniform mat4 view;

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

 정점 쉐이더의 흥미로운 부분은 들어오는 위치 벡터를 조각 쉐이더의 텍스처 좌표로 설정한다는 것이다. 조각 쉐이더는 samplerCube를 샘플링하기


위해 입력으로 사용한다:

#version 330 core
out vec4 FragColor;

in vec3 TexCoords;

uniform samplerCube skybox;

void main()
{    
    FragColor = texture(skybox, TexCoords);
}

 조각 쉐이더는 비교적 간단하다. 정점 속성의 위치 벡터를 텍스처의 방향 벡터로 가져와서 큐브 맵에서 텍스처 값을 샘플링한다.



 큐브 맵 텍스처를 가지고 있기 때문에 skybox를 렌더링하는 것은 쉽다. 큐브 맵 텍스처를 단순히 바인딩하면 skybox sampler가 skybox 큐브 맵으로


자동 채워진다. skybox를 그리려면 장면의 첫 번째 객체로 그려야하고 depth writing을 비활성화해야한다. 이 방법으로 skybox는 항상 다른 모든


객체의 배경에 그려진다:

glDepthMask(GL_FALSE);
skyboxShader.use();
// ... set view and projection matrix
glBindVertexArray(skyboxVAO);
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);
glDrawArrays(GL_TRIANGLES, 0, 36);
glDepthMask(GL_TRUE);
// ... draw rest of the scene

 이걸 실행하면 어려움에 처하게 된다. skybox가 플레이어 주위를 중심으로 배치되기 때문에 플레이어가 얼마나 멀리 움직이더라도 skybox는


주변 환경이 매우 크다는 인상을 주지 않는다. 그러나 현재의 뷰 매트릭스는 회전, 스케일링, translate를 통해 모든 스카이 박스의 위치를 변환하므로


플레이어가 움직이면 큐브 맵도 움직인다. 뷰 행렬의 translate 부분을 제거해 이동이 skybox의 위치 벡터에 영향을 미치지 않도록 하고 싶다.



 basic lighting 튜토리얼에서 4x4 행렬의 왼쪽 위 3x3 행렬을 가져와서 변환 행렬의 변환 섹션을 제거하고, 효과적으로 변환 컴포넌트를 제거 할 수


있다는 것을 기억할 것이다. 뷰 매트릭스를 3x3 매트릭스로 변환하고 다시 4x4 매트릭스로 변환하면된다:ㅇ

glm::mat4 view = glm::mat4(glm::mat3(camera.GetViewMatrix()));  

 이렇게 하면 모든 변환이 제거되지만 모든 회전 변환이 유지되므로 사용자가 여전히 장면을 둘러 볼 수 있다.



 그 결과 skybox로 인해 굉장하게 보인다. 기본 컨테이너 주변을 날아다니면 즉시 장면의 사실성을 크게 향상시키는 규모 감각을 얻게 된다.


결과는 다음과 같다:


Image of a skybox in an OpenGL scene


 다른 skybox로 실험해보아라.






An optimization


 지금 우리는 장면의 다른 모든 객체를 렌더링하기 전에 skybox를 먼저 렌더링했다. 이것은 훌륭하게 작동하지만 효율적이지는 않다.


skbyox를 먼저 렌더링하면 skybox의 작은 부분만 표시되지만 화면의 각 픽셀에 대해 조각 쉐이더가 실행된다. 초기 깊이 테스트를 사용해


쉽게 파기되었을 수 있는 조각은 우리에게 귀중한 대역폭을 절약할 수 있다.



 따라서 약간의 성능 향상을 위해 skybox를 마지막으로 렌더링 할 것이다. 이렇게하면 깊이 버퍼가 모든 오브젝트의 깊이 값으로 완전히


채워지므로 초기 깊이 테스트가 통과하는 곳이라면 skybox의 단편을 렌더링해야하므로 조각 쉐이더 호출이 크게 줄어든다. 


문제는 skybox가 렌더링에 실패 할 가능성이 크다는 것이다. 왜냐하면 단지 1x1x1 크기의 큐브일 뿐이므로 대부분의 깊이 테스트는 실패한다.


깊이 테스트를 하지 않고 렌더링하는 것만으로는 해결책이 아니다. skybox는 장면의 다른 모든 오브젝트를 덮어 쓰기 때문이다.


skybox가 1.0의 최대 깊이 값을 가지기 때문에 깊이 버퍼를 트릭하여 깊이 테스트가 실패 할 때마다 앞에 다른 오브젝트가 있는지 확인해야 한다.



 coordinate systems 튜토리얼에서 우리는 vertex shader가 실행 된 후에 perspective 분할이 수행되고, gl_Position의 xyz 좌표를 w 컴포넌트로


나누다고 말했다. 또한, 깊이 테스트 튜토리얼을 통해 분할 결과의 z 구성 요소가 해당 정점의 깊이 값과 동일하다는 것을 알 수 있었다.


이 정보를 사용해 출력 위치의 z 구성 요소를 w 구성 요소와 동일하게 설정할 수 있다. 이 구성 요소는 원점 분할이 적용될 때 z 구성 요소가


w / w = 1.0으로 변환되기 때문에 항상 1.0인 z 구성 요소가 된다:

void main()
{
    TexCoords = aPos;
    vec4 pos = projection * view * vec4(aPos, 1.0);
    gl_Position = pos.xyww;
}  

 그 결과 정규화 된 장치 좌표는 항상 최대 깊이 값인 1.0과 같은 z 값을 가지게 된다. skybox는 결과적으로 볼 수 있는 객체가 없는 곳에서만


렌더링된다.



 깊이 함수는 기본 GL_LESS 대신 GL_LEQUAL로 설정해 약간 변경해야한다. 깊이 버퍼는 skybox의 1.0 값으로 채워질 것이다. skybox가 깊이 버퍼보다


작거나 같지 않은 값으로 깊이 테스트를 통과해야한다.



 보다 최적화된 버전의 소스 코드를 여기서 찾을 수 있다.







Environment mapping


 이제 우리는 전체 주변 환경을 하나의 텍스처 객체에 매핑 시켰으며 우리는 그 정보를 단순한 skybox 이상으로 사용할 수 있었다.


큐브 맵을 환경과 함께 사용하면 물체에 반사 또는 굴절 특성을 부여 할 수 있다. 이와 같은 환경 큐브 맵을 사용하는 기술을


environment mapping techniques 라고 하며 가장 널리 사용되는 기술은 반사 및 굴절이다.




Reflection


 반사는 물체가 주변 환경을 반영하는 속성이다. 물체의 색상은 보는 사람의 각도에 따라 환경과 거의 동일하다.


예를 들어, mirror는 반사 물체이다. 뷰어의 각도에 따라 주변을 반영한다.



 반사의 기본은 그렇게 어렵지 않다. 다음 이미지는 반사 벡터를 계산하고 그 벡터를 사용해 큐브 맵에서 샘플링하는 방법을 보여준다:


Image of how to calculate reflection. 


 우리는 뷰 방향 벡터 I¯에 기초해 객체의 법선 벡터 N¯을 중심으로 한 반사 벡터 R¯을 계산한다. GLSL의 내장 반사 기능을 사용해


이 반사 벡터를 계산할 수 있다. 결과 벡터 R¯은 방향 벡터로 사용되어 큐브 맵을 index/sampling 해 환경의 색상 값을 반환한다.


그 결과 개체가 skybox를 반사하는 것처럼 보인다.



 씬에 skybox 설정이 이미 있으므로 반사를 만드는 것이 그렇게 어렵지 않다. 컨테이너에서 사용하는 조각 쉐이더를 변경해 컨테이너의


반사 속성을 제공한다:

#version 330 core
out vec4 FragColor;

in vec3 Normal;
in vec3 Position;

uniform vec3 cameraPos;
uniform samplerCube skybox;

void main()
{             
    vec3 I = normalize(Position - cameraPos);
    vec3 R = reflect(I, normalize(Normal));
    FragColor = vec4(texture(skybox, R).rgb, 1.0);
}

 우리는 먼저 view/camera direction 벡터 I를 계산하고, 이를 이용해 skybox 큐브 맵에서 샘플링하기 위해 사용하는 반사 벡터 R을 다시 계산한다.


조각의 보간된 Normal 및 Position 변수가 다시 있으므로 정점 쉐이더를 조정해야한다.

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

out vec3 Normal;
out vec3 Position;

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

void main()
{
    Normal = mat3(transpose(inverse(model))) * aNormal;
    Position = vec3(model * vec4(aPos, 1.0));
    gl_Position = projection * view * model * vec4(aPos, 1.0);
}  

 우리는 법선 벡터를 사용하고 있기 때문에 다시 정상적인 행렬로 변환하려고 한다. 위치 출력 벡터는 world-space position 벡터이다.


정점 쉐이더의 이 위치 출력은 조각 쉐이더에서 뷰 방향 벡터를 계산하는데 사용된다.



 법선을 사용하기 때문에 정점 데이터를 업데이트하고 속성 포인터를 업데이트해야한다. 또한, cameraPos를 균일하게 설정해라.



 그런 다음 컨테이너를 렌더링하기 전에 큐브 맵 텍스처를 바인딩할 것이다:

glBindVertexArray(cubeVAO);
glBindTexture(GL_TEXTURE_CUBE_MAP, skyboxTexture);  		
glDrawArrays(GL_TRIANGLES, 0, 36);	  

 코드를 컴파일하고 실행하면 완벽한 미러처럼 작동하는 컨테이너를 얻을 수 있다. 주변 skybox가 컨테이너에 완벽하게 반영된다:


Image of a cube reflecting a skybox via cubemaps via environment mapping.


 반사가 전체 오브젝트에 적용되면 오브젝트는 steel 또는 chrome과 같은 고 반사 재료를 가진 것처럼 보인다. 모델 로딩 튜토리얼에 사용된


나노 스타일 모델을 로드하는 경우 suit가 크롬으로 만들어진 것처럼 보인다:


Image of a Crysis Nanosuit reflecting a skybox via cubemaps via environment mapping.


 이것은 아주 멋지게 보인다. 그러나 실제로 대부분의 모델이 완전히 반사되는 것은 아니다. 예를 들어, 모델에 또 다른 추가 수준을 제공하는


리플렉션 맵을 소개 할 수 있다. diffuse , specular 맵과 마찬가지로 reflection 맵은 조각의 반사도를 결정하기 위해 샘플링 할 수 있는


텍스처 이미지이다. 이 반사 맵을 사용해 모델의 어느 부분이 반사를 나타내고 어떤 강도로 표시되는지 확인할 수 있다. 이 튜토리얼의 실습에서


이전에 작성한 모델 로더에 반사 맵을 도입해 nanosuit 모델의 세부 사항을 크게 향상시킬 수 있다.









Refraction


 environment mapping의 또 다른 형태는 굴절이라고 하며 반사와 유사하다. 굴절은 빛이 통과하는 재료의 변화로 인한 빛의 방향 변화이다.


굴절은 빛이 직선으로 들어가지 않고 약간 구부러진 물과 같은 표면에서 흔히 볼 수 있는 것이다. 마치 물속에 있을 때 팔을 바라보는 것과 같다.



 굴절은 Snell's law에 의해 environment map이 다음과 같이 보임을 묘사한다:


Image explaining refraction of light for use with cubemaps.


 다시, veiw 벡터 I¯, 법선 벡터 N¯, 그리고 이번에는 결과적인 굴절 벡터 R을 가진다. 보시다시피, 뷰 벡터의 방향이 약간 구부러져 있다.


이 결과 벤딩된 벡터 R은 큐브 맵에서 샘플링하는데 사용된다.



 굴절은 법선 벡터, 뷰 방향 및 두 재료의 굴절률 사이의 비율을 기대하는 GLSL의 내장 굴절 기능을 사용해 쉽게 구현할 수 있다.



 굴절률은 각 재료가 고유한 굴절률을 갖는 재료의 빛 왜곡/굴곡 정도를 결정한다. 가장 일반적인 굴절률 목록은 다음 표에 나와있다:


MaterialRefractive index
Air1.00
Water1.33
Ice1.309
Glass1.52
Diamond

2.42


 우리는 빛이 통과하는 두 물질 사이의 비율을 계산하기 위해 이러한 굴절률을 사용한다. 우리의 경우 빛/뷰 레이는 공기에서 유리로 간다.


그 비율은 1.001.52 = 0.658이 된다.



 우리는 이미 큐브 맵을 바인딩하고 법선으로 정점 데이터를 제공하고 카메라 위치를 유니폼으로 설정한다. 우리가 변경해야하는


유일한 부분은 조각 쉐이더이다:

void main()
{             
    float ratio = 1.00 / 1.52;
    vec3 I = normalize(Position - cameraPos);
    vec3 R = refract(I, normalize(Normal), ratio);
    FragColor = vec4(texture(skybox, R).rgb, 1.0);
}  

 굴절률을 변경하면 완전히 다른 시각적 결과를 얻을 수 있다. 응용 프로그램을 컴파일하고 결과를 실행하는 것은 그렇게 흥미롭지 않다.


왜냐하면 우리는 단순히 기본 컨테이너를 사용하기 때문에 효과를 실제로 보여주지는 않는다. 굴절은 지금 돋보기처럼 약간 작용한다.


nanosuit 모델에서 같은 쉐이더를 사용하는 것은 우리가 찾고 있는 효과를 보여준다: 유리 같은 객체.







Dynamic environment maps


 지금 우리는 skybox로 이미지의 정적인 조합을 사용하고 있다. 실제로는 움직이는 물체가 있는 장면은 포함되지 않는다. 우리는 하나의 객체만을


사용했기 때문에 지금까지 이 사실을 알지 못했다. 주변에 여러 개의 객체가 있는 거울과 같은 경우 하늘에 있는 객체만


미러에 있는 것처럼 보이게 된다.



 프레임 버퍼를 사용하면 문제의 대상과 6가지 각도로 장면의 질감을 만들고 각각의 렌더링 반복문에 큐브 맵을 저장할 수 있다.


그런 다음 큐브 맵을 사용해 다른 모든 객체를 포함하는 사실적인 반사 및 굴절 표면을 만들 수 있다. 동적인 환경 맵핑이라고 불리는데


이는 우리가 객체의 주변 환경의 큐브 맵을 동적으로 생성하고 그것을 환경 맵으로 사용하기 때문이다.



 멋지지만 단점이 있다. 환경 맵을 사용해 객체당 6번 장면을 렌더링해야한다. 이는 응용 프로그램에 엄청난 성능 저하이다.


현대의 응용 프로그램은 skybox를 가능한 많이 사용하려고 하며 가능한 경우 동적인 환경맵을 정렬 할 수 있는 곳이라면


언제든지 큐브 맵을 사전 컴파일한다. 동적 환경 맵핑은 훌륭한 기술이지만 너무 많은 성능 저하없이 실제 렌더링 응용


프로그램에서 작동시키려면 많은 영리한 트릭과 해킹이 필요하다.






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


컴퓨터 그래픽스 강의 시간에 배웠던 내용이 나와서 이해하기 어렵지 않았다.


시각적인 결과물이 나와서 꽤나 재밌는 파트였다. 나중에 동적인 렌더링을 할 때에 어떤 트릭을 사용할지 궁금하다.