babylon101

阴影


介绍

在本教程中,我们将学习如何在Babylon JS中创建阴影。阴影现在变得动态,现在它们根据光线动态生成。您可能想要访问本教程 的操场场景。

我怎样才能做到这一点?

使用babylon.js很容易产生阴影ShadowGenerator。此功能使用阴影贴图:从灯光的角度生成场景的贴图。

阴影生成器使用的两个参数是:阴影贴图的大小,以及用于阴影贴图计算的光。

var shadowGenerator = new BABYLON.ShadowGenerator(1024, light);

N接下来,您必须定义要渲染的阴影。在这里,我们想要我们的圆环的阴影,但你可以“推”你想要的任何网格:

shadowGenerator.getShadowMap().renderList.push(torus);

在babylon.js v3.1中引入,有两个新的辅助函数来处理阴影脚轮:

  • addShadowCaster(mesh, includeDescendants): Helper function to add a mesh and its descendants to the list of shadow casters
  • removeShadowCaster(mesh, includeDescendants): Helper function to remove a mesh and its descendants from the list of shadow casters

最后,您必须通过将mesh参数设置为true来定义阴影的显示位置:

ground.receiveShadows = true;

柔和的阴影

如果您想要更进一步,可以激活阴影过滤,以通过去除硬边创建更好看的阴影。

有三种过滤器可用:

泊松采样

shadowGenerator.usePoissonSampling = true;

如果将此值设置为true,则将禁用方差阴影贴图。此滤镜使用泊松采样来柔化阴影。结果更好,但速度更慢。

指数阴影贴图

shadowGenerator.useExponentialShadowMap = true;

默认情况下是这样,因为减少阴影的别名很有用。但是如果你想减少计算时间,可以随意关闭它。您还可以通过更改指数阴影贴图来控制深度值的缩放值shadowGenerator.depthScale。默认情况下,该值为50.0但如果您的世界的深度比例(MinZ和MaxZ之间的距离)很小,您可能想要更改它。

模糊指数阴影贴图

shadowGenerator.useBlurExponentialShadowMap = true;

这是更好的柔化阴影滤镜,但速度也较慢。它使用模糊的指数阴影贴图。

模糊的质量由以下属性定义:

  • shadowGenerator.blurScale: 在应用模糊后处理之前,定义用于缩小阴影贴图的比例。默认情况下,该值为2
  • shadowGenerator.blurBoxOffset: 定义用于应用模糊的框边缘的偏移量。默认情况下,该值为1(意味着该框将在两个方向上从-1变为1,从而导致模糊后处理读取9个值)。
  • shadowGenerator.useKernelBlur: 您可以决定使用内核模糊而不是框模糊。虽然有点贵,但内核模糊时阴影的质量要好得多。您可以使用shadowGenerator.blurKernel,默认值为1 来控制内核大小。

以下是模糊阴影的示例: https://www.babylonjs-playground.com/#IIZ9UU -


关闭指数阴影贴图

从Babylon.js 3.0开始,我们引入了一种新的方法来处理指数阴影贴图以处理自阴影问题:近似指数阴影贴图(CESM)。使用CESM,您可以获得准确的自阴影,但您需要定义其他参数:

  • 您必须通过设置light.shadowMinZ和提供灯光中最小的深度值范围light.shadowMaxZ。范围越小,阴影就越好。
  • 您必须确保灯光尽可能靠近阴影脚轮。

您可以使用以下命令启用CESM:

shadowGenerator.useCloseExponentialShadowMap = true;

或者如果你想要模糊阴影:

shadowGenerator.useBlurCloseExponentialShadowMap = true;

以下是CESM如何运作的示例:https://www.babylonjs-playground.com/#0TG0TB -


更紧密的过滤百分比(仅限Webgl2)

从Babylon.js 3.2开始,介绍了一种处理阴影贴图的新方法。这极大地改善了阴影的性能和设置。

PCF阴影受益于Webgl2中可用的新硬件过滤功能,并产生更平滑的泊松采样版本。当Webgl2在目标设备上不可用时,它们会回退到标准泊松采样。

您可以启用PCF:

shadowGenerator.usePercentageCloserFiltering = true;

以下是PCF如何工作的示例:https://playground.babylonjs.com/#B48X7G#1 -


由于PCF需要的资源多于小型平台上可用的资源,因此您可以filteringQuality根据您的经验使用该属性选择质量和性能之间的最佳权衡(质量越低,性能越好)。

shadowGenerator.filteringQuality = BABYLON.ShadowGenerator.QUALITY_LOW;

PCF目前仅支持Point和Directional灯。

接触硬化阴影(仅限Webgl2)

从Babylon.js 3.2开始,引入了基于PCSS阴影的接触硬化阴影。

PCSS可被视为PCF的改进版本,但尽管看起来更好,但它们的处理器价格也更高,应保留用于桌面应用程序。与PCF一样,如果代码在WebGL 1平台上运行,它们将自动回退到Poisson Sampling。

在PCSS中,当阴影远离物体投射时,阴影会变得更柔和,模拟现实生活中发生的事情。

为了获得准确的结果,您需要定义其他参数:

  • 您必须通过设置light.shadowMinZ和提供灯光中最小的深度值范围light.shadowMaxZ。范围越小,阴影就越好。
  • 您还可以使用以下参数contactHardeningLightSizeUVRatio来更改阴影软化的速度(在0和1之间)。

您可以启用PCSS:

shadowGenerator.useContactHardeningShadow = true;

以下是PCSS如何工作的示例: https://playground.babylonjs.com/#B48X7G#2 -


由于PCSS需要的资源多于小平台上可用的资源,因此您可以filteringQuality根据自己的经验使用该属性选择质量和性能之间的最佳权衡。(质量越低,性能越好)。

shadowGenerator.filteringQuality = BABYLON.ShadowGenerator.QUALITY_LOW;

以下链接可让您更好地了解阴影施法者随着阴影施法者远离接收阴影的物体而变暗的情况: https://playground.babylonjs.com/#ZT8BKT#2 -


PCSS目前仅支持Point和Directional灯。

例子

你可以在这里找到一个实例: https://playground.babylonjs.com/#B48X7G -


请在这里找到与聚光灯一起使用的各种滤光片的图片:

Hard shadows

没有过滤器

Poisson

泊松采样

ESM

指数阴影贴图

BlurESM

模糊指数阴影贴图

PCF

百分比更近的过滤

PCSS

联系硬化阴影

灯火

请记住,此阴影生成器只能与一个灯一起使用。如果要从另一个光源生成阴影,则需要创建另一个阴影生成器。

只有点,方向和聚光灯才能投射阴影。

点光源

点光源使用立方体贴图渲染,因此在启用时请务必小心,因为这可能会导致一些性能问题。您还可以访问 点光影地图游乐场场景 -


此外,BlurExponentialShadowMap并CloseBlurExponentialShadowMap没有被点光源支持(主要是因为模糊的六个面立方贴图的过于昂贵)。

为了优化渲染,如果您确定所有阴影脚轮都位于灯光的同一侧,您还可以决定使用点光源,如无限的聚光灯。要做到这一点,只需指定灯光的方向,并自动Babylon.js将使用阴影贴图的简单纹理而不是立方体贴图。

聚光灯

聚光灯使用透视投影来计算阴影贴图。

定向灯

定向灯使用正交投影。Light的位置会自动评估,以便您获得最佳的阴影贴图。您可以通过light.autoUpdateExtends关闭来控制此行为。您还可以通过修改其中一个属性来控制投影窗口的大小:

  • light.shadowOrthoScale: 默认值为0.1,表示投影窗口从最佳大小增加10%。
  • light.shadowFrustumSize: 默认情况下为Off,值为0.您可以指定一个值,该值将用于定义要使用的平截头体的平方大小。

灯光的位置以及您已推入渲染列表的网格的位置决定了阴影的显示位置。请注意,此位置的光源视点必须查看renderList中的所有网格物体; 否则可能无法渲染阴影。看这个例子 -


.

自定义投影矩阵

所有灯光都需要为阴影生成器提供投影矩阵才能构建阴影贴图。您可以通过设置light.customProjectionMatrixBuilder值来定义自己的版本:

light.customProjectionMatrixBuilder = function(viewMatrix: Matrix, renderList: Array<AbstractMesh>) {
    return BABYLON.Matrix.PerspectiveFovLH(angle, 1.0, activeCamera.minZ, this.shadowMaxZ);
}

故障排除

阴影贴图是一项很棒的技术,但并不完美。可以调整几个参数以帮助改进最终渲染。

偏压

您可能希望减少由不够精确的阴影贴图导致的阴影痤疮。为此,您可以定义偏差(默认为0.00005):

shadowGenerator.bias = 0.01;

阴影生成器将每个像素的深度与从光的角度看到的遮挡物(阴影脚轮)的深度进行比较。当我们处理低精度纹理时(当支持Babylon.js将使用浮动纹理但低端设备仅支持int纹理时),您可能希望增加遮挡物的深度以促进自阴影(一个对象投射阴影本身)。

背面渲染

您可以通过设置shadowGenerator.forceBackFacesOnly为true 来改善自阴影问题。这将强制阴影生成器将网格的背面渲染到阴影贴图。这可以明显提高整体精度并减少对偏差的需求。

提高投影矩阵精度

默认情况下,灯光的投影矩阵使用主摄像机的minZ和maxZ。但是你可能想控制它,以便通过减少minZ和maxZ之间的距离来获得更精确的阴影贴图。为此,您可以设置light.shadowMinZ和light.shadowMaxZ。

使用最佳选项进行自阴

如前所述,如果你想在自阴影对象上模糊阴影,最好的选择可能是使用近似指数阴影贴图。

挫折边缘衰落

根据您设置阴影生成器的方式,当对象靠近阴影贴图的边缘时,您可能会遇到奇怪的衰减。要优雅地解决此问题,您可以设置名为的属性frustumEdgeFalloff:

 shadowGenerator.frustumEdgeFalloff = 1.0;

你可以在这里找到一个例子: https://www.babylonjs-playground.com/#Y5IZCF -


此属性控制阴影在平截头体边缘淡出的程度。它仅用于定向和聚光灯。默认情况下,该值设置为0(无衰减)和1.0(完全衰减)。

在静态世界中冻结阴影

如果你有一个静态游戏世界(投射阴影的物体) - 没有必要每秒进行60次相同的阴影计算。仅创建一次shadowMap就足够了。这大大提高了性能,允许更高的shadowMap分辨率值。

阴影发生器可以冻结:

shadowGenerator.getShadowMap().refreshRate = BABYLON.RenderTargetTexture.REFRESHRATE_RENDER_ONCE;

要求灯光不要重新计算阴影位置:

light.autoUpdateExtends = false;

清洁骨基质重量

动画网格的骨骼重量错误或不精确可能会导致阴影或怪异阴影。在这种情况下,您可以使用以下代码加载时自动清理权重:

BABYLON.SceneLoader.CleanBoneMatrixWeights = true;

(您应该在加载场景或网格之前设置它。)

自我暗影

情况可能是Self-Shadowing在设置过程中需要最大的注意力。让我们尝试在以下场景中设置自阴影):https://playground.babylonjs.com/#FH3FM2#1 -


第一步是在场景中添加阴影生成器并将每个网格定义为脚轮和接收器(我们还强制偏置为0以突出显示生成的工件):https://playground.babylonjs.com/#FH3FM2#4 -


你可以注意到,在自阴影物体的表面上到处都出现了奇怪的图案。这称为阴影痤疮(更多信息).

幸运的是,在巴比伦,我们确实有办法解决这个问题。

偏压

正如前面的 opengl教程中所详述的那样,您可以增加偏见的值,使所有的痤疮消失:https://playground.babylonjs.com/#FH3FM2#5 -


不幸的是,这样做引入了另一种叫做彼得平移的副作用,其中阴影不再附着在它们的物体上。

PeterPanning

在这里,您可以从BabylonJS 3.2功能中获益,称为正常偏差。

正常偏差(自3.2起)

首先将偏见移回到看到彼得平移工件的极限: https://playground.babylonjs.com/#FH3FM2#6 -


正如您所注意到的,现在表面上出现了一点角膜,表面与光线方向平行:

ParallelAcnea

这是增加一点正常偏差的地方。基本上,在阴影贴图的生成过程中,这将在表面平行于光线的法线方向上插入几何体: https://playground.babylonjs.com/#FH3FM2#7 -


所有的工件现在都消失了,是时候让我们的阴影看起来很棒了。

柔和的阴影

尝试将阴影生成器更改为联系人强化: https://playground.babylonjs.com/#FH3FM2#8 -


首先,您无法看到接触硬化效果,不仅如此,您还可以再次看到阴影痤疮。注意到关于PCSS的部分,你会发现最小和最大光应设置得尽可能接近:https://playground.babylonjs.com/#FH3FM2#10 -


现在存在接触硬化效应,但是痤疮甚至更强。不幸的是,偏差被应用于归一化坐标深度(0-1),因此改变光的近和远值会影响偏差应该有多大。

因此,在看到彼得平移之前返回并将偏差更改为最大值,然后应用一些正常偏差来移除其余的痤疮,导致以下结果:https://playground.babylonjs.com/#FH3FM2#11 -


你的阴影现在很柔和,没有痤疮或彼此平移。

自定义阴影贴图着色器

从Babylon.js v4.0开始,您可以指定自己的着色器来渲染阴影贴图。要定义该着色器,您可以使用该shaddowGenerator.customShaderOption属性:

shadowGenerator.customShaderOptions = {  
  shaderName: "customShadowMap",
  uniforms: ["customWorld"]
}

唯一需要的值是shaderName。但你也可以添加:

  • attributes:用于指定着色器中所需的其他属性
  • 制服:用于指定着色器中需要的其他制服
  • 采样器:用于指定着色器中需要的其他采样器
  • 定义:用于在着色器中指定所需的其他定义

阴影贴图生成是一项复杂的任务,需要考虑几个定义(如int和float之间的阴影贴图的类型,或者需要alpha测试)。建议在此处检查当前的默认着色器:

为了更新你自己的制服,你可以依靠shadowGenerator.onBeforeShadowMapRenderObservable可观察的。每次渲染阴影贴图时都会调用它,它将为您提供当前编译的效果。

你可以在这里找到一个完整的例子https://www.babylonjs-playground.com/#IJH4VG#0 -


下一步

既然您正在成为Babylon.js的真正专业人士,也许是时候深入了解代码来操作复杂的着色器,网格或纹理。我们维基的主菜单是许多高级主题的门户。您也可以访问我们的Github页面参与此项目:https://github.com/BabylonJS/Babylon.js以及参与我们非常活跃的论坛:https://forum.babylonjs.com。到时候那里见。