Computer Graphics/ShaderPixel

ShaderPixel - 11. another world 추가

surkim 2024. 11. 22. 20:19

과제의 A shader applied on a surface, evoking 'another world' in 3D, quite like a portal or a window. Pay attention to the perspective, the rendering must like a window in real life 항목을 구현하였다.

 

이번 구현은 "다른 세계"를 창처럼 표현하기 위해 기존에 사용했던 skybox, texture, simple shader를 활용하였다. 프레임을 만드는 것은 비교적 간단했지만, 자연스러운 시점 처리에 많은 고민이 필요했다.

구현의 기본 원리는 간단하다:

  1. 다른 세계 렌더링: 프레임 버퍼에 다른 세계를 렌더링한다.
  2. 현실 세계 렌더링: 렌더링된 프레임 버퍼 텍스처를 플레인에 적용하여 창을 표현한다.

다른 세계를 프레임 버퍼에 렌더링

먼저, 프레임 버퍼를 활성화한 상태에서 다른 세계를 렌더링한다. 다른 세계는 skyboxdino 오브젝트들로 구성된다.

// start another world
m_anotherWorldFramebuffer->Bind();
auto& colorAttachmentAW = m_anotherWorldFramebuffer->GetColorAttachment(0);
glViewport(0, 0, m_width, m_height);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

// 렌더링된 skybox
glDepthFunc(GL_LEQUAL);
m_skyboxProgram->Use();
m_skyboxProgram->SetUniform("projection", anotherWorldProjection);
m_skyboxProgram->SetUniform("view", anotherWorldView);
m_skyboxProgram->SetUniform("cubeMap", 0);
m_anotherWorldCubeMap->Bind();
m_box->Draw(m_skyboxProgram.get());
glDepthFunc(GL_LESS);

// 다른 세계의 오브젝트 렌더링
glActiveTexture(GL_TEXTURE0);
m_dinoTexture->Bind();
for (int i = 0; i < 10; i++) {
    for (int j = 0; j < 10; j++) {
        m_textureProgram->Use();
        model = glm::mat4(1.0f);
        model = glm::translate(model, glm::vec3(-10.0f + j * 4.0f, 0.0f, 7.5f + i * 4.0f));
        model = glm::scale(model, glm::vec3(0.5f));
        model = glm::rotate(model, glm::radians(180.0f), glm::vec3(0.0f, 1.0f, 0.0f));
        m_textureProgram->SetUniform("transform", anotherWorldProjection * anotherWorldView * model);
        m_textureProgram->SetUniform("tex", 0);
        m_dinoModel->Draw(m_textureProgram.get());
    }
}
// end another world

다른 세계를 창으로 렌더링

렌더링된 프레임 버퍼 텍스처를 플레인에 적용하여 창을 표현한다. 창의 경계선은 simple shader를 활용해 강조하였다.

// start another world
m_textureProgram->Use();
model = glm::mat4(1.0f);
model = glm::translate(model, m_anotherWorldPos);
model = glm::scale(model, glm::vec3(2.0f));
model = glm::rotate(model, glm::radians(180.f), glm::vec3(0.0f, 1.0f, 0.0f));
m_textureProgram->SetUniform("transform", projection * view * model);
glActiveTexture(GL_TEXTURE0);
colorAttachmentAW->Bind();
m_textureProgram->SetUniform("tex", 0);
m_plane->Draw(m_textureProgram.get());

// 창의 경계선
m_simpleProgram->Use();
model = glm::mat4(1.0f);
model = glm::translate(model, m_anotherWorldPos);
model = glm::scale(model, glm::vec3(1.2f, 1.0f, 1.0f));
model = glm::rotate(model, glm::radians(180.f), glm::vec3(0.0f, 1.0f, 0.0f));
m_simpleProgram->SetUniform("transform", projection * view * model);
m_simpleProgram->SetUniform("color", glm::vec4(1.0f, 0.8f, 0.6f, 1.0f));
m_pictureFrame->Draw(m_simpleProgram.get());
// end another world

시점 처리

시점 처리를 위해 현실 세계의 카메라 위치를 기반으로 "다른 세계"의 카메라 위치는 고정인 체로 방향만을 계산하였다.

auto anotherWorldCameraFront = glm::normalize(m_anotherWorldPos - m_cameraPos);
auto anotherWorldProjection = glm::perspective(glm::radians(45.0f),
    (float)m_width / (float)m_height, 0.01f, 150.0f);
auto anotherWorldView = glm::lookAt(
    m_anotherWorldPos,
    m_anotherWorldPos + anotherWorldCameraFront,
    m_cameraUp);

결과