What are your feelings

  • Happy
  • Normal
  • Sad

实时渲染中的阴影

实时渲染中的阴影基本概念:

阴影在计算机图形学中是指由物体遮挡光源形成的暗区,它不仅增强了场景的真实感,还帮助观众理解物体与光源之间的相对位置。实时渲染中的阴影通过动态计算,以实时更新场景中的光照效果。实时渲染的阴影计算主要有以下几个基本概念:


1. 阴影的物理意义

阴影产生于光源被物体遮挡时。根据光源的类型和物体的位置,阴影的外形、强度、模糊度等都会有所变化。阴影不仅是物体与光源之间相对位置的表现,还能影响场景的亮度、对比度和深度感知。

2. 阴影的作用

  • 增强真实感:阴影可以让场景看起来更加立体和自然,避免“浮空物体”或“空洞”的不真实效果。

  • 传递深度信息:通过阴影,观众能够感知物体的形状和距离,以及物体之间的空间关系。

  • 视觉引导:阴影有助于引导观众的注意力,使其关注场景中的重要部分。


3. 阴影的类型

在实时渲染中,常见的阴影类型包括硬阴影(Hard Shadows)和软阴影(Soft Shadows),每种阴影类型有不同的效果和计算方法。

  • 硬阴影(Hard Shadows):阴影边缘清晰,通常用于模拟小型或集中的光源(例如点光源)。硬阴影的计算相对简单,但在某些情况下看起来不够自然。

  • 软阴影(Soft Shadows):阴影边缘渐变模糊,模拟的是较大或分散的光源(如太阳或云层)。软阴影提供更自然的视觉效果,但计算复杂度更高。


4. 阴影计算方法

实时渲染中,阴影的计算通常依赖于以下几种技术:

  • 阴影贴图(Shadow Mapping):通过从光源视角渲染场景的深度信息并在后续渲染时与该深度信息进行比较,判断一个像素是否位于阴影中。

  • 阴影体积(Shadow Volumes):通过构建物体的遮挡体积,计算阴影的扩展范围。适用于较复杂的场景,但计算复杂度较高。

  • 光线追踪阴影(Ray Traced Shadows):使用光线追踪技术,通过模拟光的传播路径来计算阴影。虽然可以产生非常精确的阴影效果,但计算开销较大,通常用于离线渲染或高性能硬件上。

  • 屏幕空间阴影(SSAO, Screen-Space Ambient Occlusion):这种方法主要通过屏幕空间中的局部信息,模拟光照被环境物体遮挡的效果,虽然不能提供传统阴影的效果,但可以增强场景的细节感和深度感。


5. 阴影计算的挑战

阴影的计算是实时渲染中最具挑战性的部分之一,主要面临以下难题:

  • 性能开销:阴影计算,特别是软阴影和光线追踪阴影,会显著增加渲染负担。如何在保证图像质量的同时优化性能是一个重要问题。

  • 分辨率问题:高分辨率的阴影贴图可以提供更精确的阴影效果,但会占用大量内存和计算资源。降低分辨率会导致阴影模糊或产生锯齿状的边缘。

  • 阴影失真:由于深度贴图的分辨率限制、投影畸变等原因,阴影常常会出现失真或伪影(如阴影条纹),这要求通过技术手段进行优化。

  • 动态光源和物体的影响:在动态场景中,光源和物体的位置会不断变化,阴影必须实时更新,这需要较高的计算能力。


6. 阴影的优化技术

为了在保证渲染质量的前提下提高性能,实时渲染中有多种阴影优化技术,包括:

  • 分辨率自适应:根据物体与光源的距离动态调整阴影贴图的分辨率,避免在远离光源的区域浪费计算资源。

  • 级联阴影贴图(Cascaded Shadow Maps, CSM):将场景分成多个不同的区域,近距离区域使用高分辨率阴影,远距离区域使用低分辨率阴影,以平衡性能与质量。

  • PCF(Percentage Closer Filtering):对硬阴影进行模糊处理,以产生较为平滑的阴影边缘,减少锯齿现象。

  • VSM(Variance Shadow Mapping):通过使用方差估计来平滑阴影边缘,从而在性能和质量之间找到平衡。

     

阴影类型:

阴影类型详细介绍

1. 硬阴影(Hard Shadows)

硬阴影是最简单的一种阴影类型,其特点是阴影的边缘清晰锐利,通常用于模拟较小或距离较近的光源。硬阴影的生成是通过精确的光线阻挡来实现的,常见于小型光源(例如点光源或聚光灯)产生的阴影。其优点是计算较为简单,但缺乏真实感,通常给人一种“人造”的感觉。

2. 软阴影(Soft Shadows)

软阴影的边缘具有模糊过渡,模拟的是较大或分散光源(如大太阳或漫反射的环境光)。软阴影通常是由多个光源、光照衍射或物体本身的阴影扩散效应所产生。由于光源在空间的范围较大,因此光线无法精确阻挡,导致阴影出现渐变过渡。软阴影的渲染较为复杂,需要更高的计算量,但提供了更为自然和真实的视觉效果。

3. 面积光源阴影(Area Light Shadows)

面积光源阴影是一种介于硬阴影和软阴影之间的类型,常用于模拟具有一定面积的光源,如窗户、灯具等。面积光源比点光源或聚光灯产生的阴影更柔和,但不像软阴影那样完全模糊。渲染这类阴影通常需要使用更高质量的阴影贴图或基于物理的渲染方法来模拟。

4. 体积阴影(Volumetric Shadows)

体积阴影不仅仅是对物体遮挡的光线进行计算,还考虑了光线通过物体或介质时的散射与吸收效应。常见于烟雾、雾霾、云层等环境条件下,体积阴影给场景添加了更多的深度和层次感。这类阴影通常计算量较大,但能显著增强场景的沉浸感和真实感。

5. 动态阴影(Dynamic Shadows)

动态阴影根据物体或光源的移动实时更新。在动态场景中,物体、相机或光源的运动会直接影响阴影的变化。动态阴影常用来增强交互性场景的真实感,但计算上要求较高,特别是当场景中有大量动态物体时。

6. 接触阴影(Contact Shadows)

接触阴影通常用于增强物体接触地面或其他表面时的细节。其特点是阴影边缘非常细腻,通常应用在小范围内,以便在高质量渲染中模拟细节。接触阴影一般依赖于计算高精度的局部阴影,而不考虑物体的整体阴影。

7. 硬阴影与软阴影的混合

在一些高级渲染引擎中,硬阴影和软阴影可以结合使用。例如,阴影的中心部分较为清晰,而远离物体的部分则渐渐模糊。通过这种混合方法,可以在保证阴影质量的同时降低计算量。

 

阴影类型及其实时渲染原理:

阴影类型及其实时渲染原理

阴影是实时渲染中不可或缺的一部分,它不仅增强了场景的真实感,还帮助观众理解光源和物体之间的空间关系。在现代游戏和图形应用中,阴影的计算通常是动态的,需要考虑到不同光源、物体和相机的位置。以下是几种常见阴影类型的详细介绍,以及其在实时渲染中的实现原理、数学基础与游戏引擎中的实现方式。


1. 硬阴影(Hard Shadows)

定义

硬阴影的特点是阴影边缘非常清晰,呈现出明显的对比和锐利的边界。硬阴影通常模拟小型光源或距离物体非常近的光源,类似于一个聚光灯照射到物体上的效果。它是通过光源照射到物体的边缘来创建阴影,阴影区和非阴影区之间的分界线是非常明确的。

实现原理

硬阴影的计算通常依赖于阴影贴图(Shadow Mapping)技术。此技术首先从光源的视角渲染场景的深度图(即场景中各个像素到光源的距离),然后在渲染物体时,比较从相机视角看到的场景深度和光源视角的深度图,判断该像素是否位于阴影中。

数学基础

  1. 光源视角投影矩阵:通过使用正交或透视投影矩阵,将世界空间中的物体转换到光源的视角。

  2. 深度图:从光源的角度,记录场景中每个像素的距离值。这个深度值用于判断从光源到像素之间是否被物体遮挡。

  3. 阴影比较:在渲染每个像素时,通过将其深度与深度图中的对应值进行比较,决定该像素是否处于阴影中。

局限性

  • 锯齿状边缘:硬阴影在边缘部分常常出现锯齿状的伪影,因为它没有考虑光源大小的影响。

  • 低分辨率:阴影贴图的分辨率有限,可能导致远距离的物体阴影模糊不清。

游戏引擎实现

硬阴影可以通过大多数现代游戏引擎内置的阴影贴图功能进行实现。Unity 和 Unreal Engine 都提供了基于阴影贴图的硬阴影支持,允许开发者设置光源类型(如点光源、方向光)并调节阴影质量。


2. 软阴影(Soft Shadows)

定义

软阴影是指阴影的边缘模糊渐变,模拟的是大面积的光源或光源距离较远的情形。例如,阳光照射到云层中的场景通常会产生软阴影。软阴影的特点是从物体到阴影区域的过渡是平滑的,产生更自然的效果。

实现原理

软阴影常通过多种技术实现:

  • PCF(Percentage Closer Filtering):这是一种基于阴影贴图的抗锯齿技术。PCF通过对阴影贴图中的多个相邻像素进行平均,来模糊阴影的边缘,使其过渡更加平滑。

  • Variance Shadow Mapping(VSM):通过记录光源视角的深度值的均值和方差来计算阴影,从而生成软阴影。

  • 双线性插值(Bi-Linear Interpolation):对多个阴影贴图进行插值,以获得阴影区域的渐变效果。

数学基础

  1. 深度比较与模糊:软阴影的计算不仅需要比较深度值,还涉及对多个样本的平均处理。通过取多个邻近像素的深度值,得到一个模糊效果。

  2. PCF:对多个深度样本进行平均,生成一个平滑的阴影边缘。

  3. 光源的影响:使用光源的大小来计算阴影的软化程度,光源越大,阴影的边缘越模糊。

局限性

  • 性能开销:软阴影的计算比硬阴影更为复杂,尤其是在高质量的软阴影计算中,需要较多的样本来生成更平滑的过渡,导致更高的计算成本。

游戏引擎实现

许多现代游戏引擎(如 Unreal Engine 和 Unity)都提供了软阴影的实现,通常通过PCF、VSM等方法来生成逼真的软阴影效果。


3. 面积光源阴影(Area Light Shadows)

定义

面积光源阴影模拟的是来自于一个非点光源(即大面积光源)所产生的阴影。它的特点是阴影有模糊的边缘,并且阴影的强度和方向会随光源的面积大小和位置而变化。

实现原理

面积光源阴影常常需要多重采样光源体积计算。由于光源是一个区域,而非一个点,阴影的产生取决于光源和物体之间的相对位置。

  • 蒙特卡罗方法:通过随机采样光源的多个点来模拟阴影。这些采样点的平均值用来计算最终的阴影效果。

  • 光源体积阴影:通过计算光源和物体之间的遮挡体积来生成阴影。

数学基础

  1. 光源采样:从光源中选择多个采样点,计算每个采样点对阴影的贡献。

  2. 蒙特卡罗积分:使用蒙特卡罗方法通过随机采样来估算阴影区域。

  3. 阴影体积:通过光源与物体间的遮挡计算确定阴影的范围。

局限性

  • 计算复杂:面积光源阴影的计算非常复杂,通常需要大量的采样和计算来得到真实感较强的效果。

  • 性能要求高:生成高质量的面积光源阴影对计算资源要求较高。

游戏引擎实现

现代游戏引擎如 Unreal Engine 支持面积光源阴影,通过使用多个采样点(例如,使用蒙特卡罗方法生成的随机采样)来实现较为真实的阴影效果。


4. 阴影体积(Shadow Volumes)

定义

阴影体积是一种通过计算物体周围的阴影体积来确定哪些区域处于阴影中的方法。这种方法通过构建“阴影体积”来判断一个区域是否被遮挡。

实现原理

阴影体积方法基于几何学,通过计算物体的投影区域(阴影体积),然后判断一个像素是否在这个体积内,来确定阴影区域。常见的实现方法是使用逆向光源体积来确定哪些区域处于阴影中。

数学基础

  1. 光源的投影:通过计算物体的边界与光源之间的相对位置,生成阴影体积。

  2. 体积剖分:使用体积的剖分方法来确定某一像素是否在阴影体积内。

  3. 深度比较:计算物体和阴影体积之间的深度差异,确定是否在阴影中。

局限性

  • 复杂度高:阴影体积需要大量的计算来生成体积,尤其是在大场景中,处理多个物体和光源时会显得非常复杂。

  • 性能开销:由于阴影体积需要对每个像素进行复杂的计算,它的性能开销较大。

游戏引擎实现

阴影体积的实现不如阴影贴图常见,但一些老旧的游戏引擎(如 Quake)曾广泛使用这一方法,现代引擎大多依赖阴影贴图来实现实时阴影。

 

阴影计算方法:

阴影计算方法详解

阴影是光与物体相互作用的结果,模拟阴影的计算是图形渲染中一项关键技术。实时渲染中,阴影的计算方法主要有两种:阴影贴图(Shadow Mapping)阴影体积(Shadow Volumes),此外,还有一些辅助技术如屏幕空间阴影(SSAO)光线追踪阴影等。每种方法有其适用的场景和优缺点,下面将对这些阴影计算方法进行详细介绍。


1. 阴影贴图(Shadow Mapping)

概念

阴影贴图是一种基于深度图的阴影计算方法,它通过从光源视角渲染场景的深度信息来计算阴影。这种方法广泛应用于实时渲染,尤其是游戏引擎中。阴影贴图的基本思想是:首先从光源的角度渲染场景并获取深度信息,然后在渲染每一帧时,利用这个深度信息来判断当前像素是否位于阴影中。

实现原理

  1. 生成阴影贴图

    • 首先,通过光源的位置和方向来设置光源的视图矩阵和投影矩阵。

    • 然后,使用相机的视角和投影矩阵对场景进行渲染,计算每个像素到光源的深度值,并存储到深度纹理(即阴影贴图)中。此时的阴影贴图记录的是从光源视角下,每个像素的深度信息。

  2. 在主场景渲染时使用阴影贴图

    • 当进行主场景的渲染时,首先需要将每个片元(像素)的世界坐标转换到光源的视角中,计算该位置的深度值。

    • 接着,将该深度值与阴影贴图中对应的深度值进行比较。如果当前深度值大于阴影贴图中的深度值,说明该像素被其他物体遮挡,处于阴影中;反之,则该像素不在阴影中。

优缺点

  • 优点:实现简单且计算速度较快,非常适合动态场景中的阴影渲染。

  • 缺点:阴影边缘会出现锯齿状的现象(即“鬼影”),特别是阴影贴图分辨率不足时,可能出现模糊和失真。

优化技术

  • PCF(Percentage Closer Filtering):通过对阴影贴图的多个深度样本进行模糊处理,来改善阴影边缘的锯齿效果。

  • 级联阴影贴图(Cascaded Shadow Maps,CSM):通过将场景分成多个区域,使用不同分辨率的阴影贴图来提高远近物体的阴影精度,尤其对于大范围场景特别有效。


2. 阴影体积(Shadow Volumes)

概念

阴影体积是一种基于几何的阴影计算方法。与阴影贴图不同,阴影体积通过构建物体的遮挡区域来计算阴影,主要用于点光源或非常复杂的光照情况。其基本思想是根据物体的几何信息,构建出遮挡光线的“阴影体积”,然后根据相机视角判断像素是否被阴影体积覆盖,从而决定是否在阴影中。

实现原理

  1. 计算阴影体积

    • 对于每个物体,首先确定其阴影投射的边界。这个边界由物体的形状和光源位置决定。

    • 对于每个可见物体,生成一个“阴影体积”,这个体积包含了所有被物体遮挡的区域。阴影体积通常通过扩展物体的边缘并投影到场景中生成。

  2. 确定阴影区域

    • 在渲染过程中,通过检查像素是否位于阴影体积内部,来判断该像素是否受阴影影响。

    • 如果像素在阴影体积内,表示该区域被遮挡,像素应该显示为阴影颜色;否则显示为光照颜色。

优缺点

  • 优点:适用于点光源和复杂的光照情况,能够提供准确的阴影效果,尤其在动态物体遮挡下,阴影效果更为真实。

  • 缺点:计算较为复杂,尤其是在物体较多或场景复杂时,计算性能开销较大。

优化技术

  • 分区渲染(Stenciling):使用模板缓冲区来进行遮挡测试,优化阴影体积的计算。模板缓冲区允许快速判断像素是否位于阴影体积内,极大提高了渲染效率。


3. 屏幕空间阴影(Screen Space Shadows, SSAO)

概念

屏幕空间阴影技术通过屏幕空间中的邻域信息计算阴影,主要用于提供环境光遮蔽效果(Ambient Occlusion)。这种方法不会生成实际的物理阴影,而是通过计算场景中物体之间的遮挡关系来模拟局部的阴影效果。

实现原理

  • SSAO方法通过每个像素的邻域信息来推测该像素被多少周围物体遮挡,进而给像素加上阴影效果。这个过程通常是在屏幕空间进行的,因此它的计算不依赖于具体的光源位置。

  • SSAO通过采样周围像素的深度信息、法线信息等,在局部区域创建模拟阴影,使得表面阴影看起来更为真实,尤其在细节较丰富的表面上。

优缺点

  • 优点:效果较为自然,且计算开销相对较低,适合实时渲染中的环境光遮蔽。

  • 缺点:不是真正的阴影技术,缺乏对光源和物体遮挡的精确模拟。


4. 光线追踪阴影(Ray Tracing Shadows)

概念

光线追踪阴影是一种通过追踪从光源发出的光线与物体相交来计算阴影的方法。它提供了最精确和自然的阴影效果,尤其是在处理柔软阴影、透明物体和复杂光照交互时。

实现原理

  • 光线追踪阴影通过发射光线,从光源出发,检查光线是否被场景中的物体阻挡。

  • 如果光线在与物体相交之前遇到其他物体,那么该点就处于阴影中;如果光线没有被阻挡,那么该点就不在阴影中。

  • 通过这种方式,可以精确模拟柔软阴影(如环境遮蔽)和硬阴影(如精确光源阻挡)。

优缺点

  • 优点:能够生成非常真实的阴影效果,尤其是在处理复杂光照与透明材质时。

  • 缺点:计算量大,尤其是实时光线追踪需要极高的计算资源,目前仅适用于高端硬件或预计算场景。


 

阴影优化技术:

阴影优化技术

阴影在实时渲染中是提升场景真实性的重要部分,但由于阴影计算通常需要较高的计算资源,尤其是在动态光源、复杂场景和高分辨率情况下,阴影的渲染性能成为一个重要的优化目标。以下介绍了几种常见的阴影优化技术,它们旨在减少计算量、提升渲染效率,同时保持良好的视觉效果。


1. 分辨率自适应(Resolution Adaptation)

概念

在阴影贴图中,阴影的分辨率直接影响渲染性能和质量。较高的分辨率能够提供更清晰的阴影细节,但同时也带来更大的内存占用和计算负担。通过动态调整阴影贴图的分辨率,尤其是在距离光源较远或物体较小时,可以显著提升渲染性能。

实现方法

  • 距离基准自适应:阴影分辨率与物体与光源的距离成反比。离光源较近的物体使用较高的分辨率,而远离光源的物体则使用较低的分辨率。

  • 区域自适应:根据阴影对图像质量的影响来动态调整分辨率。对于画面中的关键区域(如人物、重要物体),使用更高的分辨率,其他区域使用较低分辨率。

优点

  • 提高渲染效率

  • 减少计算负担

缺点

  • 可能导致阴影细节丢失,尤其是低分辨率阴影区域可能出现伪影或锯齿。


2. 阴影贴图级联(Cascaded Shadow Maps, CSM)

概念

级联阴影贴图(CSM)技术是对传统阴影贴图的优化,主要用于处理大范围场景中的阴影,尤其是城市规模的场景或开阔的环境。在CSM中,多个阴影贴图被使用,每个贴图对应不同的视距范围,距离较近的区域使用较高分辨率的阴影贴图,距离较远的区域使用较低分辨率的阴影贴图。

实现方法

  • 多级阴影贴图:CSM技术通常将视锥体划分为多个区域,每个区域对应一个阴影贴图。最靠近视点的区域使用高分辨率阴影贴图,而远离视点的区域使用较低分辨率。

  • 动态更新:当玩家或相机移动时,阴影贴图会随着视野的变化动态更新,以保证所有物体都能获得适当的阴影。

优点

  • 有效提高了大范围场景中的阴影质量,尤其是在远距离物体上使用低分辨率阴影时,能够避免性能瓶颈。

  • 解决了单一阴影贴图由于分辨率不一致所带来的问题,尤其是在远距离物体阴影模糊的情况。

缺点

  • 计算和内存开销增加,因为需要管理多个阴影贴图。

  • 在切换不同级别的阴影贴图时,可能会出现阴影不连续的问题,需要通过过渡技术来平滑过渡。


3. 模糊和抗锯齿(Shadow Blur and Anti-Aliasing)

概念

阴影的质量常常受到锯齿和边缘不平滑的影响,尤其是硬阴影或低分辨率阴影贴图时。为了改善阴影的质量,可以使用模糊和抗锯齿技术来平滑阴影的边缘,减轻“锯齿”效果,使阴影看起来更加自然。

实现方法

  • 高斯模糊:通过应用高斯模糊(Gaussian Blur)来平滑阴影贴图中的边缘,减轻因阴影边界过于锐利造成的视觉不适。

  • PCF(Percentage Closer Filtering):PCF是一种常见的抗锯齿技术,它通过对阴影贴图中的多个样本进行平均,从而平滑阴影边缘,减少锯齿效果。

  • VSM(Variance Shadow Maps):VSM通过存储每个像素的深度值的方差信息来进行阴影采样,相较于传统的PCF方法,VSM在计算上更高效,且能够生成更柔和的阴影。

优点

  • 可以有效改善阴影的视觉效果,减少锯齿和硬边缘。

  • 使阴影看起来更自然,特别是对于软阴影的模拟。

缺点

  • 会增加计算开销,尤其是在需要大量模糊处理或更高精度阴影时。

  • 过度模糊可能会导致阴影的细节丧失,影响最终效果。


4. 体积阴影(Volumetric Shadows)

概念

体积阴影模拟的是光线穿过透明或半透明物体时产生的阴影效果,通常用于表现雾气、烟雾、云层等现象。与传统的平面阴影不同,体积阴影考虑了光线的散射和吸收,在渲染中需要计算光线与体积物质的相互作用。

实现方法

  • 射线投射(Raymarching):通过射线投射的方式来计算光线穿过体积的过程,并在穿过过程中判断哪些区域被阴影遮挡。

  • 稀疏体积(Sparse Volumes):优化体积阴影的渲染,避免对整个体积进行计算,而是选择关键区域进行精确计算,并使用插值技术来补充其他部分。

优点

  • 提供了更为真实和动态的阴影效果,特别是在雾霾、烟雾等复杂环境中。

  • 适用于复杂的场景,如天气效果和环境光影响。

缺点

  • 计算开销较大,尤其是在实时渲染中。

  • 对性能的要求较高,需要采用优化技术来减少计算量。


5. 延迟渲染中的阴影(Deferred Shading Shadows)

概念

延迟渲染(Deferred Rendering)是一种通过分离几何信息和光照计算的渲染技术。与传统渲染方法不同,延迟渲染先在几何阶段将所有物体的基本信息(如位置、法线、颜色等)渲染到多个缓冲区中,再在光照阶段计算光源对场景的影响。阴影在延迟渲染中的实现相较于传统方法需要一些特殊处理。

实现方法

  • 延迟阴影贴图:通过在光照阶段利用多个光源的阴影贴图信息进行计算,可以在较低的计算开销下为多个光源提供阴影效果。

  • 光源剔除(Light Culling):由于延迟渲染将多个光源和阴影计算分开,因此可以通过剔除与物体不相交的光源,减少不必要的阴影计算。

优点

  • 延迟渲染可以高效处理多个光源的阴影计算,尤其是对于动态光源。

  • 适用于复杂的场景,能够同时处理大量的光源和阴影。

缺点

  • 需要大量的缓冲区和内存来存储多个光源的信息。

  • 在某些情况下,延迟渲染可能无法处理透明物体的阴影。


 

使用C#代码实现阴影的伪代码:

C# 实现阴影的伪代码

以下是一个简化版的阴影计算伪代码,基于**阴影贴图(Shadow Mapping)**技术。这个伪代码假设在 Unity 或类似的 3D 引擎中实现。主要步骤包括从光源渲染深度图、在场景渲染时应用阴影贴图进行光照计算。

1. 阴影贴图生成

从光源的角度渲染场景并生成深度贴图,这是计算阴影的第一步。

// 创建阴影贴图
Texture2D shadowMap;

// 在渲染之前设置光源的视图和投影矩阵
Matrix4x4 lightViewMatrix = GetLightViewMatrix(); // 获取光源视角矩阵
Matrix4x4 lightProjectionMatrix = GetLightProjectionMatrix(); // 获取光源投影矩阵

// 使用光源的视角渲染场景来生成深度贴图
void RenderShadowMap(Light light, Camera camera)
{
   // 将相机设置为光源的视角
   SetCameraView(lightViewMatrix);
   SetCameraProjection(lightProjectionMatrix);
   
   // 渲染场景,保存深度信息到shadowMap
   for (int i = 0; i < sceneObjects.Count; i++)
  {
       RenderObject(sceneObjects[i], shadowMap);  // 渲染物体,更新阴影贴图
  }
}

2. 场景渲染时应用阴影

在渲染主场景时,根据当前光源的阴影贴图判断每个像素是否在阴影中。如果某个像素位于阴影中,则不受光照影响。

// 渲染场景物体并应用阴影
void RenderSceneWithShadows(Camera camera)
{
   // 获取光源的视角和投影矩阵
   Matrix4x4 lightViewMatrix = GetLightViewMatrix();
   Matrix4x4 lightProjectionMatrix = GetLightProjectionMatrix();
   
   // 渲染场景中的每个物体
   for (int i = 0; i < sceneObjects.Count; i++)
  {
       Object obj = sceneObjects[i];
       
       // 获取物体世界坐标
       Matrix4x4 worldMatrix = obj.GetWorldMatrix();
       
       // 从光源的视角计算物体的裁剪坐标
       Vector4 shadowCoord = lightProjectionMatrix * lightViewMatrix * worldMatrix * obj.GetPosition();
       
       // 将物体的位置投影到阴影贴图空间
       Vector2 shadowMapTexCoord = ProjectToShadowMapSpace(shadowCoord);
       
       // 在阴影贴图中查找该位置的深度值
       float shadowDepth = ReadShadowMap(shadowMap, shadowMapTexCoord);
       
       // 获取当前物体表面点的深度值
       float currentDepth = shadowCoord.z / shadowCoord.w;
       
       // 如果当前深度大于阴影贴图中的深度,则说明该点处于阴影中
       bool isInShadow = currentDepth > shadowDepth;

       // 根据是否在阴影中,调整光照强度
       ApplyLighting(obj, isInShadow);
  }
}

3. 深度比较

在应用阴影时,计算当前像素的深度与阴影贴图中的深度值进行比较,以决定是否遮挡该像素。

// 从阴影贴图读取深度值
float ReadShadowMap(Texture2D shadowMap, Vector2 texCoord)
{
   // 使用纹理采样来获取阴影贴图中的深度值
   return shadowMap.Sample(texCoord);
}

// 将物体的裁剪坐标投影到阴影贴图空间
Vector2 ProjectToShadowMapSpace(Vector4 shadowCoord)
{
   // 计算从光源的视角到阴影贴图空间的投影
   float x = (shadowCoord.x + 1.0f) * 0.5f; // 将坐标从 -1 ~ 1 转换到 0 ~ 1
   float y = (shadowCoord.y + 1.0f) * 0.5f; // 将坐标从 -1 ~ 1 转换到 0 ~ 1
   return new Vector2(x, y);
}

// 比较当前物体深度与阴影贴图中的深度
bool IsInShadow(float currentDepth, float shadowDepth)
{
   return currentDepth > shadowDepth;
}

4. 光照计算

在进行光照计算时,如果当前像素位于阴影中,光照强度将被减少。否则,正常计算光照。

void ApplyLighting(Object obj, bool isInShadow)
{
   // 如果物体在阴影中,减少光照强度
   if (isInShadow)
  {
       obj.SetLightingIntensity(obj.GetLightingIntensity() * 0.5f); // 例如降低一半
  }
   else
  {
       obj.SetLightingIntensity(obj.GetLightingIntensity());
  }
}

5. 优化:PCF(Percentage-Closer Filtering)

在实际应用中,硬阴影会导致锯齿状的边缘。为了解决这个问题,可以使用 PCF(Percentage-Closer Filtering)来进行模糊处理,平滑阴影的过渡。

// 通过PCF平滑阴影
float PerformPCF(Vector2 texCoord, float currentDepth)
{
   float shadowSum = 0.0f;
   int filterSize = 5; // 5x5 核心
   float filterArea = filterSize * filterSize;

   // 对周围的多个样本进行深度比较
   for (int i = -2; i <= 2; i++)
  {
       for (int j = -2; j <= 2; j++)
      {
           Vector2 offset = new Vector2(i * 0.01f, j * 0.01f); // 微小偏移
           float sampleDepth = ReadShadowMap(shadowMap, texCoord + offset);
           shadowSum += (sampleDepth < currentDepth) ? 1.0f : 0.0f;
      }
  }

   // 计算平均值,作为最终的阴影值
   return shadowSum / filterArea;
}

####

以上伪代码展示了一个基于阴影贴图的阴影计算过程。其核心步骤包括:

 

  • 从光源视角生成深度图(阴影贴图)。

  • 渲染场景时使用该深度图来判断每个像素是否处于阴影中。

  • 基于该判断,调整物体的光照强度。

目录

索引