跳轉到內容

OpenGL 程式設計/高階/陰影

來自華夏公益教科書,為開放世界提供開放書籍

在 OpenGL 中繪製陰影可能是一項具有挑戰性的任務,但有幾種技術可以用來實現令人信服的效果。以下是在 OpenGL 中繪製陰影的一些常見技術

  1. 陰影貼圖:陰影貼圖是一種在即時 3D 圖形中渲染陰影的常用技術。它涉及從光源的角度渲染場景並將深度值儲存在紋理中。然後使用該紋理來確定在從相機的角度渲染場景時,畫素是否在陰影中。
  2. 陰影體積:陰影體積是另一種渲染陰影的技術,它透過建立一個包圍物體並與場景中的其他物體相交的體積來工作。然後使用該體積來確定畫素是否在陰影中。
  3. 陰影蒙版:陰影蒙版是用於在 OpenGL 中渲染軟陰影的技術。它們的工作原理是渲染一個代表陰影形狀的黑白蒙版,然後使用該蒙版來調製場景中物體的亮度。
  4. 投影紋理:投影紋理可以用於透過將代表陰影的紋理投影到場景中的物體上來模擬陰影的外觀。這種技術可以用來建立軟陰影或模擬由場景中不存在的物體投射的陰影的外觀。

每種技術都有其自身的優缺點,最佳方法將取決於專案的具體要求。透過嘗試這些技術並將它們調整到您的需要,您可以在 OpenGL 應用程式中建立令人信服且視覺上吸引人的陰影。


以下是如何使用著色器和幀緩衝區在 OpenGL 中實現陰影貼圖的示例

首先,設定幀緩衝區和深度紋理

GLuint fbo, depthTex;

glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);

glGenTextures(1, &depthTex);
glBindTexture(GL_TEXTURE_2D, depthTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTex, 0);
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
glBindFramebuffer(GL_FRAMEBUFFER, 0);

接下來,建立陰影貼圖著色器程式

GLuint shadowProgram = createShaderProgram("shadow.vert", "shadow.frag");

GLint shadowViewLoc = glGetUniformLocation(shadowProgram, "view");
GLint shadowProjLoc = glGetUniformLocation(shadowProgram, "proj");
GLint shadowModelLoc = glGetUniformLocation(shadowProgram, "model");

然後,從光的角度渲染場景到深度紋理

glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glViewport(0, 0, width, height);
glClear(GL_DEPTH_BUFFER_BIT);

glUseProgram(shadowProgram);
glUniformMatrix4fv(shadowViewLoc, 1, GL_FALSE, glm::value_ptr(lightView));
glUniformMatrix4fv(shadowProjLoc, 1, GL_FALSE, glm::value_ptr(lightProj));

for (auto& object : objects) {
  glUniformMatrix4fv(shadowModelLoc, 1, GL_FALSE, glm::value_ptr(object.model));
  object.mesh->render();
}

glBindFramebuffer(GL_FRAMEBUFFER, 0);

最後,從相機的角度渲染場景,使用深度紋理來確定畫素是否在陰影中

glViewport(0, 0, width, height);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glUseProgram(program);
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(proj));
glUniformMatrix4fv(lightViewLoc, 1, GL_FALSE, glm::value_ptr(lightView));
glUniformMatrix4fv(lightProjLoc, 1, GL_FALSE, glm::value_ptr(lightProj));
glUniform1i(shadowMapLoc, 0);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, depthTex);

for (auto& object : objects) {
  glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(object.model));
  object.mesh->render();
}

這只是一個基本的示例,有許多方法可以最佳化和改進此程式碼。但是,它應該讓您瞭解如何在 OpenGL 中使用著色器和幀緩衝區實現陰影貼圖。

華夏公益教科書