找回密码
 立即注册
查看: 410|回复: 0

Unity中的light map

[复制链接]
发表于 2022-1-12 08:57 | 显示全部楼层 |阅读模式
使用light map,可以将静态光源的信息(颜色,阴影,方向等)存储到texture上,渲染静态的物体时,无需进行多个light pass,直接从texture中进行采样计算即可。使用light map可以用较低的成本实现间接光照和全局光照。在Unity中开启light map,首先将需要的光源mode设置为Baked,然后在Lighting Settings里开启Baked Global Illumination:


烘培之后的light map可以在Lighting Settings进行预览:


采样light map的uv存储在第二套纹理坐标中,即TEXCOORD1。我们可以使用如下的方式对light map进行采样:
struct VertexData {
    float4 vertex : POSITION;
    float3 normal : NORMAL;
    float4 tangent : TANGENT;
    float2 uv : TEXCOORD0;
    float2 uv1 : TEXCOORD1;
};

struct Interpolators {
    float4 pos : SV_POSITION;
    float4 uv : TEXCOORD0;
    float3 normal : TEXCOORD1;

    float3 tangent : TEXCOORD2;
    float2 lightmapUV : TEXCOORD3;
    SHADOW_COORDS(4)
};

Interpolators MyVertexProgram (VertexData v) {
    ...
    i.lightmapUV = v.uv1 * unity_LightmapST.xy + unity_LightmapST.zw;
    return i;
}

FragmentOutput MyFragmentProgram (Interpolators i) {
    ...
    UnityIndirect indirectLight;
    indirectLight.diffuse = DecodeLightmap(UNITY_SAMPLE_TEX2D(unity_Lightmap, i.lightmapUV));
    indirectLight.specular = 0;
    ...
}我们采样light map来得到间接光照的diffuse信息。没有specular信息是因为specular与相机的视角有关,而在烘焙light map时是无法得知相机运行时视角的。
在烘焙light map时,Unity会在shader中寻找"LightMode" = "Meta"的pass,来让烘焙物体表面的Albedo和Emission,使其参与到间接光照中,例如下图是没有meta pass时场景的效果:


使用meta pass后:


可以看出,绿色的地板通过间接光照,使场景中其他物体也染上了绿色,这是符合真实世界的效果。可以参考Unity内置的UnityMetaPass.cginc来编写这个pass:
float4 UnityMetaVertexPosition(float4 vertex, float2 uv1, float2 uv2, float4 lightmapST, float4 dynlightmapST)
{
#if !defined(EDITOR_VISUALIZATION)
    if (unity_MetaVertexControl.x)
    {
        vertex.xy = uv1 * lightmapST.xy + lightmapST.zw;
        // OpenGL right now needs to actually use incoming vertex position,
        // so use it in a very dummy way
        vertex.z = vertex.z > 0 ? 1.0e-4f : 0.0f;
    }
    if (unity_MetaVertexControl.y)
    {
        vertex.xy = uv2 * dynlightmapST.xy + dynlightmapST.zw;
        // OpenGL right now needs to actually use incoming vertex position,
        // so use it in a very dummy way
        vertex.z = vertex.z > 0 ? 1.0e-4f : 0.0f;
    }
    return mul(UNITY_MATRIX_VP, float4(vertex.xyz, 1.0));
#else
    ...
#endif
}

half4 UnityMetaFragment (UnityMetaInput IN)
{
    half4 res = 0;
#if !defined(EDITOR_VISUALIZATION)
    if (unity_MetaFragmentControl.x)
    {
        res = half4(IN.Albedo,1);

        // d3d9 shader compiler doesn't like NaNs and infinity.
        unity_OneOverOutputBoost = saturate(unity_OneOverOutputBoost);

        // Apply Albedo Boost from LightmapSettings.
        res.rgb = clamp(pow(res.rgb, unity_OneOverOutputBoost), 0, unity_MaxOutputValue);
    }
    if (unity_MetaFragmentControl.y)
    {
        half3 emission;
        if (unity_UseLinearSpace)
            emission = IN.Emission;
        else
            emission = GammaToLinearSpace(IN.Emission);

        res = half4(emission, 1.0);
    }
#else
    ...
#endif
    return res;
}unity_MetaFragmentControl内置变量表示当前输出的是albedo还是emission,x分量是albedo,y分量是emission。
为了让间接光照也支持normal map,在烘焙light map时可以设置将光源的方向信息也一并进行烘焙。同样也是在Lighting Settings里进行设置:


Unity会额外生成一张记录方向信息的light map:


有了方向信息之后,我们可以使用如下的方式对light map进行采样:
float4 lightmapDirection = UNITY_SAMPLE_TEX2D_SAMPLER(unity_LightmapInd, unity_Lightmap, i.lightmapUV);
indirectLight.diffuse = DecodeDirectionalLightmap(indirectLight.diffuse, lightmapDirection, i.normal);DecodeDirectionalLightmap是Unity提供的内置API,使用半兰伯特漫反射模型计算diffuse:
inline half3 DecodeDirectionalLightmap (half3 color, fixed4 dirTex, half3 normalWorld)
{
    // In directional (non-specular) mode Enlighten bakes dominant light direction
    // in a way, that using it for half Lambert and then dividing by a "rebalancing coefficient"
    // gives a result close to plain diffuse response lightmaps, but normalmapped.

    // Note that dir is not unit length on purpose. Its length is "directionality", like
    // for the directional specular lightmaps.

    half halfLambert = dot(normalWorld, dirTex.xyz - 0.5) + 0.5;

    return color * halfLambert / max(1e-4h, dirTex.w);
}dirTex为分量在[0, 1]之间的向量,减去0.5之后恰好就是[-0.5,0.5]之间,这样就不用在计算点积之后再去乘0.5了,最后加上0.5使halfLambert的值在[0, 1]之间。
最后来看看是否使用方向信息light map渲染效果的差异:




Reference

[1] Static Lighting
[2] 这个坑你遇到过吗——烘焙自发光metapass的使用
如果你觉得我的文章有帮助,欢迎关注我的微信公众号(大龄社畜的游戏开发之路)^-^

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-16 15:30 , Processed in 0.090383 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表