![35f771af788e66e11ed930d0bf33bd86.png](https://i-blog.csdnimg.cn/blog_migrate/4d235041eace5e7e71f3acd5a0e7ceb5.jpeg)
翻译汇总文章:
HipHopBoy:Unity SRP 系列翻译汇总zhuanlan.zhihu.com原文链接:
Global Illuminationcatlikecoding.com![da033edfea03710220fda98a849ce7ce.png](https://i-blog.csdnimg.cn/blog_migrate/241f6b33ab668117893af9c27f95cf37.jpeg)
间接光照
- 烘焙和采样光照贴图
- 展示非直接光
- 创建自发光材质
- 通过探头和lppv(Light Probe Proxy Volume) 采样灯光
- 支持预计算实时全局照明
这是Unity SRP系列的第8篇文章。主要关于怎么支持静态和动态全局光照。
这篇教程使用 Unity 2018.3.0f2.
1 光照贴图
实时光只处理了直射光。只有直接暴露在光线下的表面才会被照亮,这是因为缺少了从表面反射到另外的表面最终到摄像机的间接光。这也被称为全局照明。我们可以把间接光烘焙到光照贴图来添加它。Rendering 16, Static Lighting 教程讲解了再unity中烘焙灯光的基础概念,但是对于老的渲染管线只能使用Enlighten lightmapper。
1.1 设置场景
当场景中只有一个直射光没有间接光的时候,可以很容易的看到所有的阴影区域颜色是接近黑色的。
![3ade2a6f628dff37366f8173b6cbcdcf.png](https://i-blog.csdnimg.cn/blog_migrate/54948a87ee74219b8d64e434aedbadb7.jpeg)
大的阴影更加显而易见
![87f9e1b047b3f634f488c20607196eb7.png](https://i-blog.csdnimg.cn/blog_migrate/07f82e7349f666239efccc56c0df7b6a.jpeg)
因为高光环境反射被添加到直射光里,所以我们还能看到阴影里面的物体。如果没有反射探头我们会看到天空盒的反射,这是明亮的。通过把Intensity Multiplier 调整到0来排除天空盒的影响。我们就会看到阴影区域是全黑的。
![69f8821c3c79059bca6fef0b7f4e894a.png](https://i-blog.csdnimg.cn/blog_migrate/04050d93925256531fcab9b5f910c323.jpeg)
![634d8868c9db90bc4655a02635fbcc4b.png](https://i-blog.csdnimg.cn/blog_migrate/6f674c3aeb4bfa8f1faeb846816d5ea0.jpeg)
1.2 烘焙光照
在场景灯光设置下的 Mixed Lighting 下勾选 Baked Global Illumination 和把Lighting Mode调为Baked Indirect 来开启烘焙间接光。这会让Unity烘焙灯光,但是我们无法直接看到。
![889ed1da91153bc0299748214d891d61.png](https://i-blog.csdnimg.cn/blog_migrate/a6239f1e9c25115c827ca7d07aa27ac5.jpeg)
我会使用默认的Lightmapping 设置,在上面做一些小的改变。默认的使用先进的Lightmapper,我会保留它。因为我有一个小的场景,我会把光照贴图的分辨率从10增加到20.我还禁用了压缩光照贴图选项,为了得到最好的质量,跳过了贴图压缩这一步。改变Directional Model为 Non-Directional ,因为只有使用法线贴图的场景才用到,这里我们不需要。
![a0fa93a68eb9ffbea994e85c09e615f6.png](https://i-blog.csdnimg.cn/blog_migrate/36701ef4bbe9a3616fa6ad0502f7cdeb.jpeg)
烘焙灯光是静态的,所以在运行模式不能改变。只有被标记为lightmap-static的游戏对象才会烘焙它们的间接光贡献。把所有的几何图形都标记为完全静态是最快的。
![896399536ea4002f3a2f3ba048d3edf0.png](https://i-blog.csdnimg.cn/blog_migrate/9a3200831afe4ffe54a11c744132692b.png)
当烘焙时,有uv重叠的话,unity会产生警告。当一个物体的uv展开时在光照贴图中太小,会引起光照信息重叠,就会发生这种情况。你可以通过调整Scale in Lightmap的参数来调整物体的比例。另外对一些默认的物体,比如球体,可以通过开启Stitch Seams 来提升烘焙光照的效果。
![6a062646abf1b39008420269f25c78fa.png](https://i-blog.csdnimg.cn/blog_migrate/41ea38a0296ab485b7697ed310f9ae2f.jpeg)
最后,为了烘焙最主要贡献的灯光,设置它的模式为Mixed。这就意味着它被用于实时光照,它的间接光照也会被烘焙。
![eff31ba32900135f4fd2ea63a27d8b3c.png](https://i-blog.csdnimg.cn/blog_migrate/dd7d7b2037ea3652c0490cc8c8a8fcc7.jpeg)
在烘焙完成之后,你可以通过Lighting window的Baked Lightmaps标签页来查看贴图。根据贴图的大小和烘焙所有静态物体所需空间的多少,你最终会得到多个贴图。
![de6a402344b125c82d6445d7b32fee65.png](https://i-blog.csdnimg.cn/blog_migrate/1054032c7fe4df7d60fb6d4041d0c71f.jpeg)
1.3 采样光照贴图
为了采样光照贴图,我们需要通知Unity让我们的贴图对我们的shader可用并且在顶点数据里面包含光照贴图的uv坐标。通过MyPipeline.Render
里面的 RendererConfiguration.PerObjectLightmaps 标志来完成。就行我们开启反射探头一样。
drawSettings.rendererConfiguration |=
RendererConfiguration.PerObjectReflectionProbes |
RendererConfiguration.PerObjectLightmaps;
当一个物体在光照贴图中被渲染时,Unity会提供需要的数据并且选一个有LIGHTMAPON关键字shader变体。所以我们必须添加一个muti-complie 在我们的shader中。
#pragma multi_compile _ _SHADOWS_SOFT
#pragma multi_compile _ LIGHTMAP_ON
通过在Lit.hlsl中添加 unity_Lightmap和和它配对使用的采样器声明来使光照贴图可用
TEXTURE2D(unity_Lightmap);
SAMPLER(samplerunity_Lightmap);
光照贴图的坐标通过第二个uv通道提供,所以在VertexInput中添加
struct VertexInput {
float4 pos : POSITION;
float3 normal : NORMAL;
float2 uv : TEXCOORD0;
float2 lightmapUV : TEXCOORD1;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
我们也必须将它们添加到VertexOutput中,但这仅在使用灯光贴图时才需要。
struct VertexOutput {
float4 clipPos : SV_POSITION;
float3 normal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float3 vertexLighting : TEXCOORD2;
float2 uv : TEXCOORD3;
#if defined(LIGHTMAP_ON)
float2 lightmapUV : TEXCOORD4;
#endif
UNITY_VERTEX_INPUT_INSTANCE_ID
};
光照贴图也有缩放和偏移,但是不应用在整个贴图中。相反,他们用来指定一个物体展开的uv在光照贴图上的位置。定义unity_LightmapST 作为UnityPerDraw buffer的一部分。因为它不符合TRANSFORM_TEX所期望的命名约定,如果需要,我们必须自己转换LitPassVertex中的坐标
CBUFFER_START(UnityPerDraw)
…
float4 unity_LightmapST;
CBUFFER_END
…
VertexOutput LitPassVertex (VertexInput input) {
…
output.uv = TRANSFORM_TEX(input.uv, _MainTex);
#if defined(LIGHTMAP_ON)
output.lightmapUV =
input.lightmapUV * unity_LightmapST.xy + unity_LightmapST.zw;
#endif
return output;
}
让我们创建一个单独的SampleLightmap函数,在给定一些UV坐标的情况下对光照贴图进行采样。在里面我们将调用Core EntityLighting文件里面的SampleSingleLightmap 函数。我们提供贴图,采样器声明和uv坐标,前两个通过TEXTURE2D_PARAM 宏传递。
float3 SampleLightmap (float2 uv) {
return SampleSingleLightmap(
TEXTURE2D_PARAM(unity_Lightmap, samplerunity_Lightmap), uv
);
}
SampleSingleLightmap需要更多的参数。下一个是UV坐标的尺度偏移变换。但是我们已经在顶点程序中做过了,所以这里我们要提供一个恒等变换。
return SampleSingleLightmap(
TEXTURE2D_PARAM(unity_Lightmap, samplerunity_Lightmap), uv,
float4(1, 1, 0, 0)
);
然后是一个布尔值,指示是否需要对光照贴图中的数据进行解码。这依赖于目标平台。如果unity使用HDR 光照贴图那么解码就不是必须的。这是UNITY_LIGHTMAP_FULL_HDR定义时的情况。
return SampleSingleLightmap(
TEXTURE2D_PARAM(unity_Lightmap, samplerunity_Lightmap), uv,
float4(1, 1, 0, 0),
#if defined(UNITY_LIGHTMAP_FULL_HDR)
false
#else
true
#endif
);
最后,我们需要提供解码指令,使照明在正确的范围。我们需要使用float4(LIGHTMAP_HDR_MULTIPLIER, LIGHTMAP_HDR_EXPONENT, 0.0, 0.0)。
return SampleSingleLightmap(
TEXTURE2D_PARAM(unity_Lightmap, samplerunity_Lightmap), uv,
float4(1, 1, 0, 0),
#if defined(UNITY_LIGHTMAP_FULL_HDR)
false,
#else
true,
#endif
float4(LIGHTMAP_HDR_MULTIPLIER, LIGHTMAP_HDR_EXPONENT, 0.0, 0.0)
);
我们对光照贴图进行采样,因为我们想要添加全局光照。我们为此创建一个全局光照函数,在这个函数里面处理细节。给它一个VertexOutput参数,这意味着需要为他定义一个结构体。如果有光照贴图,采样它,否则返回0。
struct VertexOutput {
…
};
float3 GlobalIllumination (VertexOutput input) {
#if defined(LIGHTMAP_ON)
return SampleLightmap(input.lightmapUV);
#endif
return 0;
}
在LitPassFragment的末尾调用这个函数,最初替换所有其他的照明,这样我们就可以单独看到它了。