当前位置: 首页 > news >正文

网站优化工作室郑州市进一步调整优化防控措施

网站优化工作室,郑州市进一步调整优化防控措施,制作网站得多少钱,网站建设合作协议文本英文原文#xff1a;https://catlikecoding.com/unity/tutorials/scriptable-render-pipeline/spotlight-shadows/ 渲染并且读取纹理从光空间#xff08;光源角度#xff09;渲染为阴影投射#xff08;shadow casters#xff09;添加一个着色器pass采样阴影贴图支持软阴影…英文原文https://catlikecoding.com/unity/tutorials/scriptable-render-pipeline/spotlight-shadows/ 渲染并且读取纹理从光空间光源角度渲染为阴影投射shadow casters添加一个着色器pass采样阴影贴图支持软阴影和硬阴影混合在单个图集中组合存储最多16个阴影贴图。 这是Unity可编程渲染管线教程的第四篇。在这篇里我们让聚光灯能投射阴影并最多同时支持16个光源的阴影。该教程基于Unity2018.3.0.f2。 1. A Spotlight With Shadows 阴影非常重要它不仅可以提升真实感还可以让物体之间的空间层次关系更加明显。没有阴影我们就很难分辨一个物体究竟是漂浮在表面上还是和表面相接触。 在这一章我们只完成聚光灯的阴影毕竟它是最简单的。我们先从支持单光源阴影开始。我们需要创建一个场景其中包含一个聚光灯以及一些游戏物体。一个平面用于接受阴影。所有的物体都使用我们之前自己创建的Lit Opaque材质。 1.1 Shadow map 阴影的渲染有很多种方法比如体阴影等这里我们使用传统的阴影贴图方法shadow map。这意味着我们的需要从光源方向渲染场景,但我们只需渲染深度信息。深度会告诉我们光线在碰撞到物体前走了多久在这距离之后的物体则处于阴影之中。 首先我们需要创建一个阴影贴图相机将会将内容渲染只该贴图为了以后能对阴影贴图采样我们需要用一个独立的渲染纹理来存储渲染结果而不是帧缓冲中在MyPipeline中添加一个RenderTextrue的字段来存储与阴影贴图的引用。 新建一个独立的函数来渲染阴影用context作为参数。首先要获取一个渲染纹理。我们通过调用 RenderTexture.GetTemporary来实现。如果有还未被清理的闲置纹理该方法则会拿来它重复利用不然就创建一个新纹理。因为我们的阴贴图几乎在每一帧都会用到所以我们可以一直重复使用同一纹理。RenderTexture.GetTemporary方法要求提供贴图的宽高深度通道的存储位数以及纹理格式等参数。我们使用固定的512x512大小并使用16位的深度通道提高精度。我们创建的是深度纹理格式是RenderTextureFormat.Shadowmap。 过滤模式设为双线性纹理环绕模式设为Clamp 阴影贴图的渲染应当先于常规场景的渲染。因此在Render方法中我们在配置常规相机操作前剔除操作之后调用RenderShadows方法。 在我们传递上下文完成渲染后要释放渲染纹理。将shadow map传给 RenderTexture.ReleaseTemporary就可以释放纹理同时清空引用。 1.2 Shadow Command Buffer 我们使用单独的command buffer完成阴影相关的工作这样在frame debugger中我们就可以看到阴影渲染和常规渲染被分成了两个部分。 影的渲染应当放在BeginSample和EndSample之间 1.3 Setting the Render Target 在渲染阴影前应当需要先让GPU渲染信息到阴影贴图中。我们可以调用CoreUtils.SetRenderTarget来实现这一点传入我们的command buffer以及shadow map作为参数即可。这个方法一开始会清理贴图所以在BeginSample之前调用来避免frame debugger里出现额外一层Render Shadows嵌套。 我们只关注深度通道为SetRenderTarget添加第三个参数 ClearFlag.Depth来指明这一点。 虽然不是必须的但我们可以对纹理的加载和储存设置更加精确的需求。因为我们会清理这个纹理所以我们并不关注它来自哪可以用RenderBufferLoadAction.DontCare来指明这一点这将使得tile-based的GPU会有更高的执行效率。因为我们随后需要采样该纹理所以需要将其存储在内存中通过RenderBufferStoreAction.Store指明这一点。 我们阴影贴图的清除操作现在能在frame debugger里看到了位于常规渲染之前。 1.4 Configuring the View and Projection Matrices 我们从光源的视角渲染场景就犹如我们将聚光灯看做是一个摄像机一样。因此我们需要提供适当的视角投影矩阵。我们可以通过剔除结果中的 ComputeSpotShadowMatricesAndCullingPrimitives方法得到该矩阵。该方法的第一个参数是光源序列因为我们只有一个光源所以就是0。视野矩阵和投影矩阵则是在后两个输出参数中。最后一个参数ShadowSplitData我们用不到但作为输出参数我们必须提供。 当我们获得了该矩阵调用阴影命令缓冲区的SetViewProjectionMatrices 方法然后执行command buffer并清理。  1.5 Rendering Shadow Casters 有了正确的矩阵信息我们现在可以渲染所有投射阴影的物体了。我们通过调用DrawShadows方法来实现。这个方法需要一个 DrawShadowsSettings 类型的引用参数。我们用剔除结果和光源索引作为参数来创建一个该实例。 只有我们把聚光灯的Shadow Type类型设为hard或者soft才有用。如果我们设为noneUnity会说这不是一个有效的投射阴影的光源。  2. Shadow Caster Pass 此时所有受光源影响的物体都应该渲染进阴影贴图中但是frame debugger告诉我们这并没有发生。因为 DrawShadows 函数会使用着色器的ShadowCaster pass但是目前我们的着色器并没有这个pass。 2.1 Shadow Include File 为了创建一个shadow-caster pass我们复制Lit.hlsl文件并重命名为ShadowCaster.hlsl。我们只需要深度信息所以移除所有和片元位置无关的东西。片元程序简单的输出0。重命名相应的方法以及导入guard define。 现在足够渲染阴影了但是有可能会出现阴影投射物和近平面相交的情况这时候就会导致阴影中有漏洞想象一下本应该在前面产生遮挡的部分因为没在近平面范围内而被舍弃为了避免这种情况我们在顶点函数中限制顶点不超出近平面。我们可以通过取裁减空间位置zw分量中较大者的来完成这一操作。为什么比较z和w就可以这牵扯到比较深入的投影矩阵知识请参考该文列出的几个文章https://blog.csdn.net/yinfourever/article/details/96481332简单科普下基础知识投影空间z值从齐次坐标转换为正常坐标后的范围为-1 到 1也就是clipPos除以w之后的结果这也是为什么比较z和w就可以确保其不超出近平面因为当点在近平面时z值为-1当点在远平面时z值为1 然而裁减空间的一些细节让情况变得复杂起来。我们往往很直观的将深度值为-1的地方想象为近平面随着距离增加值不断上升。但实际上除了OpenGL之外的 API情况与我们想象的相反在近平面上值为1。而对于OpenGL近平面则是-1。我们通过 UNITY_REVERSED_Z 和 UNITY_NEAR_CLIP_VALUE这两个宏覆盖所有情况。我们导入Common.hlsl来获取这两个宏。 2.2 A Second Pass 在我们的Lit着色器中添加ShadowCaster pass我们复制一个pass语句块并将第二个pass中Tags中的 LightMode设为ShadowCaster。接着引入 ShadowCaster.hlsl而非Lit.hlsl 。并使用对应的顶点片元函数。 现在我们的物体能够渲染进阴影贴图里了。因为物体目前只受单个光源影响所以GPU instancing的效率非常好。 选择Shadows.Draw项你就能够看到最终的阴影贴图了。因为是仅深度贴图frame debugger会为我们显示深度信息白色为近处黑色为远处。 因为阴影贴图是在聚光灯假设成相机的方式下渲染的所以它的朝向和光源是相匹配的。如果发现阴影贴图是颠倒的可能是你对光源进行了旋转导致本地空间的向上方向在世界空间反而是向下的。 3. Sampling the Shadow Map 我们现在有了包含所需要数据的阴影贴图但暂时还没有使用它。所以下一步就是采样阴影贴图 3.1 From World Space to Shadow Space 储存在深度贴图的中的深度信息是依据在渲染该贴图时所使用的聚光灯当作摄像机的裁减空间计算的我们把它叫做阴影空间。这与我们正常渲染场景所用到的坐标空间不匹配。想知道一个片元如果存储在深度贴图中深度值该是多少,我们要将片元的位置转从世界空间换到阴影空间。 首先我们得让我们的着色器可以访问阴影贴图。为此我们添加一个着色器材质变量 _ShadowMap。并在MyPipeline中持有指向它的标识符。 在RenderShadows通函数中通过SetGlobalTexture方法来将阴影贴图和全局变量相绑定。  接着我们添加一个矩阵变量用于从世界空间转换至阴影空间命名为_WorldToShadowMatrix。同样持有它的标识符。 通过阴影空间的视角矩阵和投影矩阵相乘可以得到改矩阵。用SetGlobalMatrix函数将它传给GPU。 我们又会遇到裁减空间z轴是否反向这一问题好在我们可以用SystemInfo.usesReversedZBuffer来检查如果反向那就在相乘之前修改投影矩阵的z列分量列序列号为2。直接修改原矩阵的m20至m23字段即可。 我们现在有了世界空间至阴影空间的转换矩阵。裁减空间范围是-1到1但我们的纹理坐标和深度范围在0到1。要映射至该范围就得就得再额外乘一个能在所有维度缩放和偏移 0.5个单位的转换矩阵。我们可以用Matrix4x4.TRS方法来得到想要的缩放、旋转或偏移。 但是其实这是一个simple matrix我们简单的在单位矩阵的基础上修改合适的分量即可。 3.2 Sampling Depth 在Lit.hlsl,中新增一个缓存区并在其中定义float4x4 _WorldToShadowMatrix 。 纹理资源不属于buffer的一部分我们得分开另外定义。我们可以用宏 TEXTURE2D_SHADOW来定义 _ShadowMap。 接下来我们需定义采样器状态用于采样纹理。通常我们是用的是宏SAMPLER 但是这里我们需要使用另外一个特殊的比较采样器所以使用SAMPLER_CMP。为了得到正确的采样器状态应使用sampler前缀再加上贴图名字作为参数 什么是纹理采样器 在旧的GLSL代码中我们使用sampler2D来同时定义纹理和采样器状态。但其实这是两个分开的东西都会占用资源。采样器状态可以从纹理中分离开来就为混合使用两者提供了可能。典型的例子就是多张纹理重复利用同一个采样器状态。在我们的例子里我们通过MyPipeline设置采样器状态的过滤模式为双线性以及纹理映射模式为clamping 我们使用的comparison sampler还会在双线性插值之前就为我们进行深度比较。这会比在插值之后才进行比较效果更好。 创建一个以世界位置作为参数的ShadowAttenuation方法。它会返回我们光源阴影的衰减因子。在方法里首先要做的就是将世界位置转为阴影空间位置。 就像之前转换到裁减空间一样得到的位置是定义在齐次坐标系中的。我们需要的是常规坐标所以我们让xyz分量除以w分量。 现在我们可以通过SAMPLE_TEXTURE2D_SHADOW这个宏采样阴影贴图。它需要一张贴图一个采样器状态以及对应的阴影空间位置作为参数。如果该点位置的z值比在阴影贴图中对应点的值要小就会返回1这说明他比任何投射阴影的物体离光源都要近。反之在阴影投射物后面就会返回0。因为采样器会在双线性插值之前先进行比较所以阴影边缘会混合阴影贴图的多个纹素texels。 3.3 Fading when Shadowed 让阴影产生影响只需在DiffuseLight函数中为阴影衰减添加一个参数。将它与其他的渐变因子一起作用于漫反射强度。 顶点光源现在不会有阴影所以在LitPassVertex.中将阴影衰减值设为1。  在 LitPassFragment中调用ShadowAttenuation方法并传入世界位置将返回值传给DiffuseLight函数产生阴影。 现在阴影出现了但是有非常严重的瑕疵。 4. Shadow Settings 影响阴影质量表现的因素有很多。我们暂时支持一部分阴影分辨率、深度偏移、强度、软阴影。我们可以在每个光源的检视面板对这些以及其他选项进行配置。 4.1 Shadow Map Size 虽然光源的inspector中有设置阴影分辨率的选项但这只会间接的影响深度贴图的大小真正是取决于项目设置中的quality settings至少对于Unity默认的渲染管线是这样的。我们用的是自己的渲染管线因此我们选择将阴影贴图大小的设置选项放到MyPipelineAsset中。 阴影贴图是正方形贴图我们允许设为256x256到4096x4096之间的任意二次方大小。为此我们在MyPipelineAsset中定义一个名为ShadowMapSize的枚举类型其中包含了256、512、1024、2048这几个枚举。因为枚举不能为数字所以我们加一个下划线前缀Unity编辑器在显示时会抹去下划线。我们用这个枚举类型添加一个配置字段用于设置阴影贴图尺寸。 枚举代表的整数默认从0开始。但如果枚举选项正好与相同大小的整数相通会很方便因此我们对枚举项进行显示赋值。 默认值0将无法表示任何枚举项所以我们需设置一个有效的默认值。  将该参数传入渲染管线的构造函数 在MyPipeline中添加一个变量并在构造函数中初始化。 在RenderShadows中分配渲染纹理时我们就可以使用该变量设置阴影贴图尺寸了。 4.2 Shadow Bias 阴影瑕疵的问题更详细的解释请看Rendering 7, Shadows我们用最简单的方式掩盖这些瑕疵。那就是在渲染深度贴图时在深度上添加一点偏移。这个深度偏移在每个光源中单独配置所以必须把它传给GPU。我们添加一个_ShadowBias着色器属性并记录下它的标识符。 在RenderShadows中设置视角投影矩阵后设置深度偏移。 VisibleLight中不能直接得到该信息但其中的light字段有深度偏移。 ShadowCaster.hlsl 文件中阴影Buffer中添加相应的变量。对裁减空间位置的z分量应用z分量。如果z轴是翻转的那就用减法否则用加法。 shadow bias应当尽可能小避免阴影偏移的太远引起peter-panning效果看起来影子漂浮在地面上 关于bias以及shadow map这篇博文写的非常好强烈建议读一下https://blog.csdn.net/ronintao/article/details/51649664 根本原因就是 shadow depth map 的分辨率不够因此多个 pixel 会对应 map 上的同一个点。 图中黄色箭头是照射的光线黑色长方形是实际物体表面黄色的波浪线是 shadow map中的对应值的情况。 可以看到由于map是对场景的离散取样所以黄色的线段呈阶梯状的波浪变化相对于实际场景中的情况就有一部分比实际场景中的深度要大对应着黑色线段部分着部分不会产生阴影注意图画反了一部分比实际场景中的深度要小对应着黄色线段部分这部分会产生阴影所以就出现了条纹状的阴影。 由于这种情况是物体的实际深度与自己的采样深度相比较不相等实际深度大于采样深度导致的所以可谓是自己采样的副本遮挡了自己实际的物体所以被称为 self shadowing。        解决的方法很简单其实只有实际深度大于采样深度的时候才有问题那么我们在计算实际深度的时候往灯光方向拉一点让他减小一点就可以了 4.3 Shadow Strength 我们只有单个光源并且没有任何环境光所以我们的阴影是纯黑的。但是我们可以调和一下阴影衰减的轻度让他只淡化部分光源贡献而不是完全排除。这会让我们的阴影看起来是半透明的。我们 _ShadowStrength属性表示阴影强度并记录下它的标识符。 采样阴影贴图时会用到阴影强度所以将它与世界-阴影空间矩阵和阴影贴图在一块设置。和深度偏移一样我们在 Light字段中获取该值。 在阴影缓存区添加阴影强度。在ShadowAttenuation函数中用它在1和采样得到的衰减值之间插值。 4.4 Soft Shadows  最后一个设置就是支持软硬阴影的切换。我们现在使用的是硬阴影阴影边缘的平滑过渡全靠在采样阴影贴图时使用的双线性插值。当开启平滑的软阴影时阴影和非阴影的过渡是模糊的阴影中有很大的半影区域。但不像在现实世界中半影的产生取决于光源、投射物接受阴影物体之间的空间关系在这里半影范围是固定统一的。 软阴影需要采样阴影贴图多次。次数越靠后采样点越偏离原采样位置贡献度也越低。我们使用5x5 tent filter需要九次纹理采样。为此我们要用到在Shadow/ShadowSamplingTent.hlsl文件中的一个函数方法将它导入Lit.hlsl。 tent filter需要知道阴影贴图的尺寸。该方法要求一个特定的向量四个分量分别为宽的倒数、高的倒数、宽度、高度。我们将其添加到shadow buffer。 在MyPipeline中保存相应的标识符。 在 RenderShadows函数中设置该变量。  当_SHADOWS_SOFT 关键字被定义时在ShadowAttenuation方法中我们用tent filter替换常规的阴影贴图采样。 不再是单次采样我们创建一个5x5的tent filter用来叠加九次采样结果。SampleShadow_ComputeSamples_Tent_5x5方法会给分配好每次采样的权重和UV坐标我们需要传入阴影贴图尺寸和阴影空间位置。权重和uv通过输出参数获取一个是flaot数组另一个是float2数组两者都有9个元素。 然而方法中的输出参数定义的类型为real而不是float。它不是一个实际的数字类型而是一个宏根据需要自动选择flaot或者half。我们通常可以忽略这个情况但是为了避免在某些平台出现编译错误最好还是为输出参数使用real类型。 现在我们可以在循环中使用数组里的权重和uv坐标对阴影贴图采样九次。这是一个固定次数的循环所以shader编译器会会将循环展开。我们还需要阴影空间位置的z坐标每次采样都用这三者构造一个flaot3变量作为参数。 为了设置使用软阴影创建一个定义了_SHADOWS_SOFT关键字的着色器变种。在我们的Lit着色器的默认pass中添加一个多重编译指令。我们需要两个变种一个有该关键字一个没有所以我们用下划线表示没有关键字后面跟着_SHADOWS_SOFT关键字。 最后我们在 RenderShadow函数中基于光照的shadows属性设置关键字。如果设置为LightShadows.Soft就会在我们的阴影缓冲区调用 EnableShaderKeyword方法否则调用DisableShaderKeyword方法Unity根据关键字状态决定在渲染时使用哪一个变种。 用一个bool值切换关键字很普遍我们可以用方法CoreUtils.SetKeyword代替。 5. More Lights With Shadows 目前我们只支持单光源投射阴影但是我们的管线支持最多16个光源接下来我们将实现最多支持16个聚光灯的阴影。 5.1 Shadow Data Per Light 我们目前的管线只能用单个pass完成所有的光源工作所以如果我们想支持多光源阴影我们就得确保每个光源的数据如强度等可同时访问。我们在ConfigureLights函数中收集这些数据就像设置其他的光源数据一样。所以我们将该方法移到RenderShadows前并且只在有可见光源时调用 RenderShadows。 我们用一个四维向量数组存储阴影数据,每个向量代表一个光源。在ConfigureLights函数遍历光源的循环中先将每个向量初始化为0就像之前设置衰减数据一样。 在光源是聚光灯类型的情况下获取Light脚本。如果shadows属性没有被设置为LightShadows.None,就将阴影强度存储在向量的x分量中。 我们用向量的y分量存储使用硬阴影或软阴影。1表示软阴影0表示硬阴影。 5.2 Excluding Lights 一个光源可见并且开启了阴影并不能保证一定需要阴影贴图。如果在光源的视角中并没有任何阴影的投射物或接受物自然不需要阴影贴图。我们可以调用剔除结果的 GetShadowCasterBounds函数传入一个光源索引来检查该光源是否需要阴影贴图。他会检查该光源的阴影体积是否在一个有效的范围内。如果没有我们就跳过设置阴影数据。尽管我们没有用到输出结果但是我们还是得提供一个阴影范围作为参数。 5.3 Rendering All Shadow Maps 我们把首次执行阴影缓冲区和设置阴影贴图纹理之间的代码用一个循环包括。用这个循环再次遍历所有可见光源并在光源数量超过可支持最大光源数是打断循环。将其中所有原本固定的索引0修改为迭代值变量。 跳过不需要阴影贴图的光源我们用阴影数据中的阴影强度来判断。小于等于0有可能原本的强度就这样也有可能是我们之前设0来跳过就直接用continue跳到下个迭代。 omputeSpotShadowMatricesAndCullingPrimitives方法返回是否可以生成有效的矩阵的布尔值。理论上应该和 GetShadowCasterBounds方法的结果一致但以防万一还是考虑在失败时将强度设为0并跳过此次迭代。 当我们开启多个光源的阴影只要它们的位置能够让他们产生可见的阴影frame debugger会显示我们确实渲染了多次阴影贴图。 然而阴影显示地一团糟我们还需要进一步做一些工作。 5.4 Using the Correct Shadow Data 不再是使用单一的ShadowStrength属性我们需要传入shadow data数组。 同样的我们也需要设置投影矩阵数组将其传入GPU 在shader里修改阴影缓存区使之匹配。 ShadowAttenuation 方法新增一个参数接受光源索引以便取得正确的数组元素。我们检查阴影强度是否为正数。如果不是直接将1作为衰减值返回。代替依赖_SHADOWS_SOFT关键字判断我们基于阴影数据的y分量来进行条件分支。 最后在 LitPassFragment里调用ShadowAttenuation 时传入光源索引。 5.5 Shadow Map Atlas 虽然我们现在有了正确的用于渲染阴影所需要的阴影数据和矩阵信息但是在超过一个光源有阴影时最终产生的仍然是错误的阴影。这是因为所有的阴影贴图都渲染进了同一张纹理之中多个信息混合在一起导致得到的阴影贴图没有意义。Unity轻量级渲染管线通过阴影贴图图集解决这一问题。将渲染纹理分割为多个方形区域每个光源个占据其一。我们也使用这种方法。 为什么不使用纹理数组 这是可行的但可惜使用阴影投射渲染纹理数组并不是一个普遍的做法。比如在Metal上这是可行的但是OpenGL core要求4.6的着色器等级即使生效了Unity也会打印一连串的断言错误。所以还是老老实实的用单个渲染纹理吧。 我们最多支持16个光源所以就应该把单张阴影贴图分成4x4网格的平铺块(tiles)。每个平铺块的大小应该和阴影贴图除以4的大小一样。我们要将渲染时的视口约束在这个大小所以在RenderShadows一开始创建一个Rect结构体并填充合适的值。 在我们设置视口和投影矩阵前用SetViewport函数告诉GPU使用合适的视口大小。 现在所有的阴影贴图都渲染在渲染纹理一角的单个平铺块中。下一步就是偏移每个光源的视口。我们可以依据每个平铺块的xy序号得到视口位置。Y轴偏移序号通过光源序列除以四整数除法得到。x轴偏移序号通过整数取余得到。最终视口的xy位置等于序号乘以平铺块大小。 这样的图集有一个缺点在一个平铺块边缘采样时可能会在两个平铺块之间插值从而导致错误的结果。当使用软阴影时效果会更加的糟糕因为tent filter可能会在离原始采样点偏移最多4个纹素的地方采样。相比混合附近的平铺块能够淡出阴影肯定更好。所以我们在每个平铺块周围添加一圈空值边缘让GPU写入数据时使用比平铺块略小一点的视口。这称之为裁减矩形。我们可以使用 shadowBuffer.EnableScissorRect 方法传递一个比视口略小的矩形来实现。我们需要边缘宽度为四个纹素所以这个矩形位置应该是视口位置加4大小为视口大小减8。 我们在渲染阴影后调用DisableScissorRect关闭裁剪矩形不然会影响到后面的常规渲染。 最后要做的就是调整world-to-shadow矩阵让它能采样到正确的平铺块。我们可以乘以一个有适当xy偏移的转换矩阵。shader不需要关心我们是否使用了图集。 要记住我们现在每个物体最多支持4个像素光所以你让第5个聚光源照射到平面时其中一个光源会退化为顶点光源进而无法接受该光源的阴影。16个阴影指的是整体场景总的阴影来源而不是单个物体的 6. Dynamic Tiling 使用阴影贴图图集的优点是无论有多少阴影贴图我们用的都是同一张渲染纹理所以纹理占用的内存是固定的。缺点则是每个光源只占纹理的一部分所以最终的阴影贴图分辨率会比我们想象的要低。并且最终可能有很大一部分的纹理面积没有利用。 我们可以更好的利用纹理而不是固定的将纹理分成16块。我们可以用一个变量表示平铺块的大小可以根据有多少平铺块决定值设为多大。这种方式可以确保我们至少能用到一半的纹理。 6.1 Counting Shadow Tiles 首先我们需要明确我们需要多少个平铺块。我们可以在ConfigureLights中记录我们有多少带阴影的光源。并用一个字段记录总数以便在之后使用。 6.2 Splitting the Shadow Map 接下来在RenderShadows里一开始就算好如何分割阴影贴图。我用一个整数变量来表示。如果我们只要一个平铺块就不需要分割所以split值设为1否则如果是4个以下值为2,8个以下值为3只有超过8个值才为4。 平铺块的大小可以通过阴影贴图大小除以split值得到整数除法。这意味着在除以3的时候我们会舍弃部分纹素。平铺块的缩放值应该改为1/split浮点数除法。我们使用split值计算平铺块的缩放和偏移用于调整世界-阴影矩阵。 为了在可用的空间打包阴影贴图我们需要在确实设置好一个平铺块后再递增序列。因此我们使用独立的一个变量而不是直接使用光源索引。在没我们没有跳过的迭代的末尾自递增变量。 6.3 One Tile is No Tile 最后如果我们最终只需要一个平铺块那就没必要设置视口的裁减模式了。我们只需要在有多个平铺块时需要这么做。 6.4 Shader Keywords 目前我们每个片元最多可以采样来自四个光源的阴影它们可能是软硬光源的组合。最麻烦的情况就是4个软阴影一共需采样36次。好在我们shader中的多个分支可以为我们很好的按要求采样阴影因为来自同一物体的片元最终使用的是同一条分支。但是我们可以通过分离不同的阴影组合来切换复杂度更低的备选shader。 共有四种可能的组合第一种是完全没有阴影第二种只有硬阴影第三种只有软阴影最复杂的一种就是软硬阴影的组合。我们可以使用shader变种处理所有可能的情况通过使用关键字_SHADOWS_HARD 和 _SHADOWS_SOFT。 在RenderShadows中使用两个布尔变量记录是否使用了软硬阴影我们依靠阴影信息的Y分量来判断。在循环之后使用这些布尔值切换关键字。 在shader添加另一个多重编译指令这次是_SHADOWS_HARD 在ShadowAttenuation 方法中如果两个关键字都没定义就在一开始直接返回1。这样就可以省方法的剩余部分完全的消除阴影。 为了让代码更整洁优雅我们将采样软阴影和硬阴影的代码各自分离成独立的函数。 现在我们用关键字为其他三种情况填写代码。最开始的分支在两个关键字都定义时才有。 最后如果我们不需要阴影平铺块 在MyPipeline.Render里直接跳过RenderShadows方法。我们甚至都不需要清理阴影贴图了。如果跳过了要确保两个阴影的关键字都关掉了。没有可见光时我们也要把两个关键字关掉。
http://www.zqtcl.cn/news/125978/

相关文章:

  • h5视频网站模板中国十大企业培训机构排名
  • 强的网站建设明细报价表网站建设推广新业务
  • 哪里有免费做网站wordpress 在文章前面加序号
  • 263企业邮箱登录入口首页seo公司哪家
  • 哈尔滨建设银行网站岳阳建设网站
  • 中山网页网站设计模板自己做的网站怎么让别人看见
  • 建设装饰网站出口跨境电商平台
  • 陕西网站建设公司排名WordPress图片生成文章
  • t恤定制网站wordpress 分类 seo
  • 万网网站空间多少钱一年做哪些网站流量最大
  • seo网站优化服务网站开发电脑
  • 宿迁怎样建设网站wordpress 分类目录 标签
  • 惠州双语网站建设费用seo搜索工具栏
  • 做ppt会去什么网站找图网页制作与网站建设试题
  • 如何用ai给网站做logo宝安网站制作公司
  • sem是什么职业岗位单页式网站 seo
  • 做网站用什么版本系统国外有哪些设计网站推荐
  • dz论坛怎么做视频网站吗哪些公司是wordpress
  • 在微信怎样搞做微视频网站商城小程序模板源码完整版
  • h5跟传统网站有啥区别读取wordpress最新文章
  • 网站推广120最超值的网站建设
  • 移动网站制作公司如何开公司做网站
  • 网站建设 青海试题wordpress的特点
  • 源码如何做网站宽甸县建设局网站
  • 用dw做的网页怎么连到网站上企业网站备案资料填写单
  • 中文 网站模板企业怎么建设网站
  • 建设户外腰包网站哪个网站注册域名好
  • 六安网站建设价格小学生编程网课前十名
  • 绵阳网站建设信赖辉煌wordpress多账号权限
  • 网站外链快速建设网站维护要学多久