Shadow mapping(陰影貼圖)是一種常用的實時陰影生成技術,廣泛應用於計算機圖形學、遊戲引擎和其他實時渲染系統中。它用於建立場景中物體的陰影,從而增加畫面的真實感和深度感。
陰影貼圖的基本原理
陰影貼圖技術的基本思想是使用一個深度圖(depth map)來記錄光源視角下場景中每個畫素到光源的距離。然後,在渲染場景時,透過比較畫素到光源的距離與深度圖中的值來確定該畫素是否在陰影中。
陰影貼圖的步驟
-
從光源視角渲染深度圖:
- 首先,從光源的視角渲染整個場景,並將每個畫素到光源的距離儲存在深度圖中。這一步稱為深度圖生成(depth map generation)。
-
從相機視角渲染場景:
- 其次,從相機的視角渲染場景。對於每個畫素,計算該畫素到光源的距離,並將其與深度圖中的對應值進行比較。
- 如果該畫素到光源的距離大於深度圖中的值,則該畫素在陰影中;否則,該畫素不在陰影中。
陰影貼圖的實現細節
以下是一些實現陰影貼圖的關鍵細節:
-
深度圖生成:
- 使用光源的視角進行正交或透視投影。
- 渲染場景並記錄每個畫素到光源的深度。
-
深度比較:
- 在相機視角下渲染時,將每個畫素的世界座標轉換到光源的視角。
- 透過光源的投影矩陣計算該畫素在深度圖中的座標。
- 比較該畫素到光源的距離與深度圖中的值,確定是否在陰影中。
-
處理偽影:
- 陰影走樣(Shadow Acne): 由於深度精度限制,可能會出現鋸齒狀陰影。通常透過偏移深度值(biasing)來減輕。
- 彼得平滑(PCF,Percentage-Closer Filtering): 透過對多個鄰近深度值進行平均處理,可以平滑陰影邊緣。
- 陰影偽影(Shadow Map Aliasing): 解析度不足的深度圖可能導致鋸齒狀陰影邊緣。透過提高深度圖解析度或使用級聯陰影貼圖(Cascaded Shadow Maps)來減輕。
程式碼示例
以下是一個基本的虛擬碼示例,展示瞭如何使用 OpenGL 實現陰影貼圖:
// 1. 從光源視角生成深度圖 glViewport(0, 0, shadowWidth, shadowHeight); glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO); glClear(GL_DEPTH_BUFFER_BIT); glm::mat4 lightProjection, lightView; glm::mat4 lightSpaceMatrix; // 設定光源的投影矩陣和檢視矩陣 lightProjection = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane); lightView = glm::lookAt(lightPos, glm::vec3(0.0f), glm::vec3(0.0, 1.0, 0.0)); lightSpaceMatrix = lightProjection * lightView; // 渲染場景到深度圖 shader.setMat4("lightSpaceMatrix", lightSpaceMatrix); renderScene(shader); glBindFramebuffer(GL_FRAMEBUFFER, 0); // 2. 從相機視角渲染場景,並使用深度圖進行陰影檢測 glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glm::mat4 view = camera.GetViewMatrix(); glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f); shader.setMat4("projection", projection); shader.setMat4("view", view); // 傳遞光源的空間矩陣和深度圖到著色器 shader.setMat4("lightSpaceMatrix", lightSpaceMatrix); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, depthMap); shader.setInt("shadowMap", 1); renderScene(shader);
總結
陰影貼圖是一種強大且靈活的實時陰影生成技術,透過記錄和比較深度值,能夠在三維場景中生成動態陰影。雖然實現細節可能比較複雜,但透過合理處理深度比較和偽影問題,可以實現高質量的陰影效果。
軟陰影(Soft Shadows)是指陰影的邊緣是平滑的,而不是硬邊的。這種陰影更接近於現實生活中的陰影,因為在現實世界中,光源通常不是點光源,而是具有一定面積的面光源或體積光源,導致陰影邊緣出現漸變。
軟陰影的生成
生成軟陰影的方法有很多,包括:
- 面積光源取樣(Area Light Sampling): 透過對光源的多個點進行取樣,生成陰影的柔和邊緣。
- 陰影貼圖的模糊(Shadow Map Blurring): 對陰影貼圖進行模糊處理,使得陰影邊緣變得柔和。
- PCF(Percentage-Closer Filtering): 對多個鄰近深度值進行平均處理,可以平滑陰影邊緣。
陰影貼圖與軟陰影
陰影貼圖(Shadow Mapping)是生成陰影的基礎技術,透過記錄從光源視角下場景中每個畫素到光源的距離,來判斷畫素是否在陰影中。透過新增一些額外的處理,陰影貼圖技術也可以用來生成軟陰影。
使用陰影貼圖生成軟陰影的方法
以下是幾種常見的方法,用於在陰影貼圖的基礎上生成軟陰影:
使用陰影貼圖生成軟陰影的方法
以下是幾種常見的方法,用於在陰影貼圖的基礎上生成軟陰影:
-
PCF(Percentage-Closer Filtering):
- 在計算陰影時,對當前畫素周圍的多個深度值進行取樣,然後對這些深度值進行平均處理,以生成平滑的陰影邊緣。
float shadow = 0.0; vec2 texelSize = 1.0 / textureSize(shadowMap, 0); for(int x = -1; x <= 1; ++x) { for(int y = -1; y <= 1; ++y) { float pcfDepth = texture(shadowMap, fragPosLightSpace.xy + vec2(x, y) * texelSize).r; shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0; } } shadow /= 9.0;
-
VSM(Variance Shadow Maps):
- 使用方差陰影貼圖技術,透過儲存深度和深度的平方,使用方差來估計深度的分佈,從而實現軟陰影。
float ChebyshevUpperBound(float Moments[2], float t) { // One-tailed inequality valid if t > Moments[0] float p = t - Moments[0]; float variance = Moments[1] - (Moments[0] * Moments[0]); variance = max(variance, 0.00002); float d = p * p / variance; return variance / (variance + d); }
-
面積光源取樣(Area Light Sampling):
- 對光源進行多個取樣,模擬面積光源的效果,從而生成軟陰影。這個方法比較昂貴,因為需要進行多次取樣。
float shadow = 0.0; int samples = 16; vec3 random = vec3(rand(), rand(), rand()); for(int i = 0; i < samples; ++i) { vec3 offset = gridSamplingDisk(i, samples, random); float closestDepth = texture(shadowMap, fragPosLightSpace.xy + offset.xy).r; shadow += currentDepth - bias > closestDepth ? 1.0 : 0.0; } shadow /= float(samples);
總結
軟陰影是指具有平滑邊緣的陰影,能夠提供更逼真的渲染效果。陰影貼圖是生成陰影的一種基礎技術,透過一些擴充套件和處理,可以在陰影貼圖的基礎上生成軟陰影。常見的方法包括PCF、VSM和麵積光源取樣等。透過這些技術,可以實現不同程度和質量的軟陰影效果,以滿足不同的渲染需求。