Unity的Realtime GI, Probe Volumes, LOD Groups

Unity的Realtime GI, Probe Volumes, LOD Groups

RealTime GI

烘焙的光照使用lightmap处理静态物体,使用light probe处理动态物体,但是它不能处理动态的光源。对于动态光源,Unity提供了实时全局光照的支持。我们可以在Window/Rendering/Lighting Settings中开启:

在这里插入图片描述

开启之后Unity就会实时计算lightmap和light probe。当然这和当前光源的模式有关。如果当前光源为baked,此时依旧看不到realtime lightmap:

在这里插入图片描述

当前光源为realtime时,可以一睹realtime lightmap的全貌:

在这里插入图片描述

另外从中可以看出,realtime lightmap的尺寸非常的小,图中只有44*38。为了完成对realtime lightmap的采样,unity在顶点数据中提供了TEXCOORD2作为其纹理坐标:

struct VertexData {
	float4 vertex : POSITION;
	float3 normal : NORMAL;
	float4 tangent : TANGENT;
	float2 uv : TEXCOORD0;
	float2 uv1 : TEXCOORD1;
	float2 uv2 : TEXCOORD2;
};

当启用realtime lightmap时,DYNAMICLIGHTMAP_ON宏生效:

	#if defined(DYNAMICLIGHTMAP_ON)
		i.dynamicLightmapUV =
			v.uv2 * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw;
	#endif

在fragment shader中得到具体uv后,开始采样:

float3 dynamicLightDiffuse = DecodeRealtimeLightmap(
				UNITY_SAMPLE_TEX2D(unity_DynamicLightmap, i.dynamicLightmapUV)
			);

Light Probe Proxy Volumes

LPPV一般用于体积较大的物体,体积较大的物体不同位置受到的GI不同,如果使用light probe,得到的效果会不准确:

在这里插入图片描述

而开启LPPV后效果如下:

在这里插入图片描述

在这里插入图片描述

当开启LPPV时,UNITY_LIGHT_PROBE_PROXY_VOLUME宏会开启,但这个关键字是全局的,对于某个物体来说,是否使用LPPV还需要判断unity_ProbeVolumeParams这个四维向量的x分量是否为1:

在这里插入图片描述

unity提供了内置的函数SHEvalLinearL0L1_SampleProbeVolume 来采样LPPV:

				if (unity_ProbeVolumeParams.x == 1) {
					indirectLight.diffuse = SHEvalLinearL0L1_SampleProbeVolume(
						float4(i.normal, 1), i.worldPos
					);
					indirectLight.diffuse = max(0, indirectLight.diffuse);
				}

SHEvalLinearL0L1_SampleProbeVolume实现如下:

// normal should be normalized, w=1.0
half3 SHEvalLinearL0L1_SampleProbeVolume (half4 normal, float3 worldPos)
{
    const float transformToLocal = unity_ProbeVolumeParams.y;
    const float texelSizeX = unity_ProbeVolumeParams.z;

    //The SH coefficients textures and probe occlusion are packed into 1 atlas.
    //-------------------------
    //| ShR | ShG | ShB | Occ |
    //-------------------------

    float3 position = (transformToLocal == 1.0f) ? mul(unity_ProbeVolumeWorldToObject, float4(worldPos, 1.0)).xyz : worldPos;
    float3 texCoord = (position - unity_ProbeVolumeMin.xyz) * unity_ProbeVolumeSizeInv.xyz;
    texCoord.x = texCoord.x * 0.25f;

    // We need to compute proper X coordinate to sample.
    // Clamp the coordinate otherwize we'll have leaking between RGB coefficients
    float texCoordX = clamp(texCoord.x, 0.5f * texelSizeX, 0.25f - 0.5f * texelSizeX);

    // sampler state comes from SHr (all SH textures share the same sampler)
    texCoord.x = texCoordX;
    half4 SHAr = UNITY_SAMPLE_TEX3D_SAMPLER(unity_ProbeVolumeSH, unity_ProbeVolumeSH, texCoord);

    texCoord.x = texCoordX + 0.25f;
    half4 SHAg = UNITY_SAMPLE_TEX3D_SAMPLER(unity_ProbeVolumeSH, unity_ProbeVolumeSH, texCoord);

    texCoord.x = texCoordX + 0.5f;
    half4 SHAb = UNITY_SAMPLE_TEX3D_SAMPLER(unity_ProbeVolumeSH, unity_ProbeVolumeSH, texCoord);

    // Linear + constant polynomial terms
    half3 x1;
    x1.r = dot(SHAr, normal);
    x1.g = dot(SHAg, normal);
    x1.b = dot(SHAb, normal);

    return x1;
}

unity_ProbeVolumeParams是一个四维向量,x分量表示该物体是否启用LPPV,y分量为0表示在世界空间进行计算,为1表示在LPPV的模型空间进行计算,z分量表示采样的体积纹理在u方向上的纹素大小。unity_ProbeVolumeWorldToObject定义了从世界空间转换到LPPV模型空间的变换矩阵。unity_ProbeVolumeSizeInv是LPPV长宽高的倒数。 unity_ProbeVolumeMin是LPPV左下角的x、y、z坐标。

该函数首先根据传入点的坐标计算出归一化的纹理坐标,由于这里球谐函数的系数和probe遮挡信息被打包到了不同分段中,即前1/4为红色分量,1/4到1/2为绿色分量,1/2到3/4为蓝色分段,因此需要对得到的纹理坐标进行压缩,乘以0.25,并且使用clamp函数限制纹理坐标采样的范围为第1个纹素到第1/4个纹理宽度的纹素之间,这样保证不会漏采样rgb分段的信息。

LOD Groups

Unity原生支持LOD,它使用LOD Group这个component来进行控制:

在这里插入图片描述

在这里插入图片描述

这里的百分比表示物体的包围盒在屏幕空间中所占的比例,当比例下降到60%以下就会切换到LOD1,当比例低于10%时,就会被完全剔除。不过有时候会出现默认LOD的距离已经调节到100%了,但是距离太远或是太近的情况,这时可以在Quality Settings中调整LOD Bias解决:

在这里插入图片描述

如果fade mode设置成了cross fade,那么不同LOD过渡时两个LOD对应的几何体都会被渲染,我们可以使用Unity内置的UnityApplyDitherCrossFade函数进行平滑过渡:

    sampler2D unity_DitherMask;
    void UnityApplyDitherCrossFade(float2 vpos)
    {
        vpos /= 4; // the dither mask texture is 4x4
        float mask = tex2D(unity_DitherMask, vpos).a;
        float sgn = unity_LODFade.x > 0 ? 1.0f : -1.0f;
        clip(unity_LODFade.x - mask * sgn);
    }

unity_DitherMask是一个4x4的纹理,使用RenderDoc可以抓到它长啥样:

在这里插入图片描述

可以发现,这个texture只有在a通道上有值。vpos为屏幕空间中像素的坐标,x和y取值范围类似[0, screenWidth],[0, screenHeight]。除以4就是对取值范围进行缩放处理,换言之就是将unity_DitherMask纹理进行放大,使其更明显。

unity_LODFade是一个四维向量,它的x分量是一个介于[-1,1]的值,在平滑过渡的过程中,LOD0从1过渡到0,LOD1从-1过渡到0:

在这里插入图片描述

在这里插入图片描述

使用这种方式,得到的LOD过渡效果如下:

在这里插入图片描述

如果你觉得我的文章有帮助,欢迎关注我的微信公众号
在这里插入图片描述

Reference

[1] Realtime GI, Probe Volumes, LOD Groups

[2] Unity3D的全局光照和阴影:下篇

[3] 标准管线下的unity_LODFade操作

猜你喜欢

转载自blog.csdn.net/weixin_45776473/article/details/124013611
LOD