light map in Unity

Using light map, Information about static light sources can be (color, shadow, direction, etc.) are stored on the texture. When rendering static objects, it is not necessary to perform multiple light pass es, but directly sample and calculate from the texture. Using light map can realize indirect lighting and global lighting at a low cost. Turn on light map in Unity, first set the required light source mode to Baked, and then turn on Baked in Lighting Settings Global Illumination:

The light map after baking can be previewed in Lighting Settings:

The uv of the sampled light map is stored in the second set of texture coordinates, TEXCOORD1. We can sample the light map in the following way:

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;
    ...
}

We sample the light map to get the diffuse information of indirect illumination. There is no special information because the special is related to the camera's angle of view, and the camera's running angle cannot be known when baking the light map.

When baking a light map, Unity will look for the pass of "LightMode" = "Meta" in the shader to bake the Albedo and Emission of the object surface and make them participate in indirect lighting. For example, the following figure shows the effect of the scene without meta pass:

After using meta pass:

It can be seen that the green floor makes other objects in the scene dyed green through indirect lighting, which is in line with the effect of the real world. You can refer to the built-in unitymetapass Cginc to write this 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_ The metafragmentcontrol built-in variable indicates whether the current output is albedo or emission, the x component is albedo, and the y component is emission.

In order to make indirect lighting support normal map, you can set to bake the direction information of the light source when baking the light map. It is also set in Lighting Settings:

Unity will generate an additional light map recording direction information:

With the direction information, we can sample the light map in the following way:

float4 lightmapDirection = UNITY_SAMPLE_TEX2D_SAMPLER(unity_LightmapInd, unity_Lightmap, i.lightmapUV);
indirectLight.diffuse = DecodeDirectionalLightmap(indirectLight.diffuse, lightmapDirection, i.normal);

Decodedirectionalllightmap is a built-in API provided by Unity. It uses the semi Lambert diffuse model to calculate the 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 is the vector whose component is between [0,1]. After subtracting 0.5, it happens to be between [- 0.5,0.5], so there is no need to multiply 0.5 after calculating the dot product. Finally, add 0.5 to make the value of halfLambert between [0,1].

Finally, let's see if we can use the direction information to the difference of light map rendering effect:

Reference

[1] Static Lighting

[2] Have you ever encountered this pit - the use of baking self luminous metapass

If you think my article is helpful, please pay attention to my WeChat official account (the way of game development of older animals and animals).

Keywords: Unity Computer Graphics

Added by cuboidgraphix on Sat, 25 Dec 2021 09:53:24 +0200