You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

阴影映射:如何计算平行光的视图矩阵与投影矩阵

阴影映射:如何计算平行光的视图矩阵与投影矩阵

嘿,我刚好之前在实时渲染里折腾过平行光的阴影映射,这个最优视图投影矩阵的计算确实是新手容易卡壳的地方,我来给你一步步掰扯明白~

首先先呼应下你提到的阴影映射核心流程:

  • 从光源视角渲染阴影图
  • 着色时把世界空间点P投影到光源空间,采样阴影图判断是否被遮挡

你现在卡壳的第一步,核心就是构建一个刚好适配场景中需要阴影区域的视图投影矩阵(也就是视图矩阵和投影矩阵的乘积)。平行光的情况和点光源不一样——它没有“位置”只有方向,而且得用正交投影,不能用透视投影。

先搞定平行光的视图矩阵

平行光的视图矩阵作用是把世界空间的点转换到「光源的局部空间」——简单说就是让光源的“照射方向”对应这个空间的-z轴(只要你自己的坐标系规则统一就行)。构建方法其实很直观:

  • 先确定你的平行光照射方向,比如从上往下照的话方向就是vec3(0, -1, 0)(这里要注意:我们要的是光源“看向场景”的方向,如果你定义的lightDir是从场景指向光源,记得取反成光源指向场景的方向)。
  • 计算光源空间的三个正交基向量:
    • 右向向量:找一个和lightDir垂直的向量,比如如果lightDir不是上下方向,就用cross(lightDir, vec3(0, 1, 0))再归一化;如果lightDir刚好是纯上下方向,就换成cross(lightDir, vec3(1, 0, 0))。
    • 上向向量:用cross(右向向量, lightDir)再归一化,这样三个基向量就两两正交了。
  • 视图矩阵就是由这三个基向量组成的旋转矩阵(因为平行光没有位置偏移,所以矩阵的平移部分全为0)。如果你用GLM这类数学库,也可以偷懒用glm::lookAt:随便选一个在光源方向上的点当eye(比如vec3(0, 100, 0)),center设为eye + lightDir(也就是看向场景的方向),up传刚才算的上向向量,生成的矩阵和我们手动构建的旋转矩阵是等价的。

然后是关键的「最优正交投影矩阵」

为什么要强调“最优”?因为如果投影范围拉得太大,阴影图的分辨率会被浪费,阴影会模糊;范围太小又会把需要阴影的物体裁掉,导致阴影缺失。所以我们要精准包围所有需要生成阴影的区域:

  1. 先把相机视锥体的8个角点全部列出来(就是相机能看到的空间的8个顶点)。
  2. 用刚才的视图矩阵,把这8个角点全部转换到光源空间。
  3. 在光源空间里,计算这8个点的最小/最大x、y、z值,得到一个轴对齐包围盒(AABB)。
  4. 用这个包围盒的边界来构建正交投影矩阵:比如用GLM的话就是glm::ortho(minX, maxX, minY, maxY, minZ, maxZ),这里的min/max就是刚才算的包围盒极值。

最后组合成视图投影矩阵

把投影矩阵和视图矩阵相乘就得到最终的lightViewProjMatrix——这里要注意矩阵乘法的顺序!如果你的数学库是列主序(比如GLM、DXMath),顺序得是投影矩阵 × 视图矩阵,因为列主序下变换是右乘,我们要先做视图变换(转到光源空间)再做投影变换(转到裁剪空间),所以矩阵顺序要反过来写。

给你提两个避坑小技巧

  • 计算包围盒的时候,可以把范围稍微扩大1%左右,能有效避免浮点精度问题导致的“阴影 acne”(就是那些莫名其妙的阴影斑点)。
  • 新手阶段先只考虑相机视锥体内的物体,等跑通基础效果再优化:比如如果场景里有在视锥体外但会投射阴影到视锥体内的物体,那得把这些物体也加入包围盒计算,但先搞定核心流程再说~

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.08 12:38:15