Unity GI Shader implementation

I shared an analysis of the global illumination of unity before, which mentioned some things that need to be implemented in the Shader, so I will add it in this article.
To realize the shader implementation of the global GI, we can analyze and view the built-in Lit of unity.

There are many ways to bake, and it is especially important to choose the right way to bake and use the right type of light source.

First of all, let's implement the most basic baked lighting display, which is to display the Light Map texture. What needs to be set is that all the models that need to be baked are set to static, the lighting is set to Baked, and the model opens the second set of LightMap UV.
insert image description here
Preparations are over.

Pure baking, no dynamic

The lighting is all static, and the lighting mode will not work, because it is the same as which one it chooses to bake, and bakes direct light, indirect light and shadows to the Light Map.
Then start writing the shader, first you need to open the macro

#pragma multi_compile _ LIGHTMAP_ON //光照贴图支持

Get the UV of LightMap

float2 staticLightmapUV : TEXCOORD1;

LightMapUV is the second set

output.staticLightmapUV = input.staticLightmapUV * unity_LightmapST.xy + unity_LightmapST.zw;

Then after calculating the UV offset, pass it to the fragment shader
. In the fragment shader, we only need to make a macro judgment with the previous SH to modify the indirect light diffuse reflection.

#if defined(LIGHTMAP_ON)
    float4 encodedIrradiance = SAMPLE_TEXTURE2D(unity_Lightmap,samplerunity_Lightmap,input.staticLightmapUV);
    Irradiance = DecodeLightmap(encodedIrradiance, float4(LIGHTMAP_HDR_MULTIPLIER, LIGHTMAP_HDR_EXPONENT, 0.0h, 0.0h));
#else
 Irradiance = SampleSH(WorldNormal);//SH,Light Probe
#endif

The first is to judge the macro, and then read the LightMap texture. The read texture is encoded, so it needs to be decoded in one step. This is a built-in function of unity, so you can directly use the variable of the internal value to decode. If LIGHTMAP_ON is not currently enabled, we can also obtain indirect light diffuse reflection according to the previous SH method.

In this mode, the dynamic model receives the diffuse lighting of the Light Probe, because there is no main light source, and there is no main light source for rendering in real-time rendering.
insert image description here
Next, let's be compatible with the Lightmap direction map.

#pragma multi_compile _ DIRLIGHTMAP_COMBINED //方向贴图

First, set the macro to determine whether to enable the orientation map.
Then judge the direction map in LightMap and set the direction map

half3 Irradiance = half3(0,0,0);
#if defined(LIGHTMAP_ON)
    float4 encodedIrradiance = SAMPLE_TEXTURE2D(unity_Lightmap,samplerunity_Lightmap,input.staticLightmapUV);
    Irradiance = DecodeLightmap(encodedIrradiance, float4(LIGHTMAP_HDR_MULTIPLIER, LIGHTMAP_HDR_EXPONENT, 0.0h, 0.0h));
    #if defined(DIRLIGHTMAP_COMBINED)
        float4 direction = SAMPLE_TEXTURE2D(unity_LightmapInd,samplerunity_Lightmap,input.staticLightmapUV);
        half3 LightDir = direction * 2.0f - 1.0f;
        half halfLambert = dot(WorldNormal,LightDir) * 0.5 + 0.5;
        Irradiance = Irradiance * halfLambert / max(1e-4,direction.w);
    #endif
#else
 Irradiance = SampleSH(WorldNormal);//SH,Light Probe
#endif

Note that unity_Lightmap is the built-in LightMap, unity_LightmapInd is the name of the built-in direction map, and samplerunity_Lightmap is a public sampler.
So, there is a question, we have the direction of the light, the normal direction and the direction of the viewing angle, can we calculate the highlight effect of the LightMap model, the answer is yes. Whether it is blinnPhong or GGX highlight data is enough, it can definitely be calculated directly.
Here is sample code:

#if !defined(_DIRHIGHLIGHT_OFF)
    half BlinnPhong = pow(saturate(dot(WorldNormal,normalize(LightDir + ViewDir))),30); //计算出高光强度
    Irradiance = Irradiance + Irradiance * BlinnPhong; //高光和漫反射相加
#endif

Features:

  1. The lighting in the scene is provided by Light Map, Light Probe, and Refletion Probe. There is no real-time light source, saving performance.
  2. All objects lack dynamic highlights. Static objects can barely use direction maps to obtain orientation calculations. Dynamic objects can only be realized in other ways.
  3. Dynamic objects cannot cast shadows.

Summary: Pure baking mode, due to its excellent performance, is generally used in games with fixed viewing angles on mobile terminals. Dynamic objects are implemented using other methods such as globally setting the direction of a light. Dynamic objects have no shadows, and generally use false shadows, such as flat shadows, which are more suitable for mobile terminals.
Recommended use: 2.5D top view game on mobile.

Baking with Mixed lighting

As I said before, after using the Mixed mixed light source, an additional set of shadow maps, that is, static shadow maps, will be generated, and then there are different differences according to the lighting mode we set.

Subtractive mode

Mixed light is invalid for LightMap objects. Light.distanceAttenuation is used to shield Mixed light sources. If it is an object using LightMap, this value will be 0 to avoid repeated coloring.
In this mode, the direct light, indirect light, and shadow of static objects are baked to LightMap and LightProbe, and dynamic objects will still be illuminated by Mixed light sources for real-time calculation. This method is a very reasonable setting method. Static objects are fixed and can be calculated with LightMap. Dynamic objects just use Mixed lighting for real-time rendering, and diffuse reflection uses LightProbe. However, there are still a few issues that need to be resolved. If they can be resolved, this will be the best solution for most mobile platforms:

  1. Dynamic objects cannot cast shadows on static objects.
    solution:
//SUBTRACTIVE模式下的混合光照,用于处理实时光照和光照贴图的混合
#if defined(LIGHTMAP_ON) && defined(_MIXED_LIGHTING_SUBTRACTIVE)
    #if defined(_MAIN_LIGHT_SHADOWS_SCREEN)
        float4 positionCS = TransformWorldToHClip(WorldPos);
        float4 ShadowCoord = ComputeScreenPos(positionCS);
    #else
        float4 ShadowCoord = TransformWorldToShadowCoord(WorldPos);
    #endif
    float4 ShadowMask = float4(1.0, 1.0, 1.0, 1.0);
    Light mainLight = GetMainLight(ShadowCoord, WorldPos, ShadowMask);
    Irradiance = SubtractDirectMainLightFromLightmap(mainLight, WorldNormal, Irradiance);
#endif

This method is to deal with the radiance of the indirect light source. As can be seen from the name SubtractDirectMainLightFromLightmap, it is to subtract the main light source from the light map, which belongs to the trick method.
First of all, it is necessary to obtain the main light source of balanced light. This method is only suitable for the main light source of balanced light.
The above macro _MIXED_LIGHTING_SUBTRACTIVE is not declared in unity, it only needs to be judged.
For its implementation principle, we can check the source code to learn:
insert image description here
From the effect point of view, it is acceptable on the mobile side:
insert image description here
2. The real-time lighting of dynamic objects cannot be blocked by static objects.
Solution: When obtaining real-time light sources, use macros to distinguish them.

//ShadowMask是用来处理静态投影和动态投影的结合
#if defined(SHADOWS_SHADOWMASK) && defined(LIGHTMAP_ON)
    half4 ShadowMask = SAMPLE_SHADOWMASK(input.staticLightmapUV);
#elif !defined(LIGHTMAP_ON)
    half4 ShadowMask = unity_ProbesOcclusion;
#else
    half4 ShadowMask = half4(1, 1, 1, 1);
#endif

The above is the way to obtain ShadowMask. If you use light baking, you can directly obtain the texture of ShadowMask (if any). If there is no LIGHTMAP_ON, it is real-time rendering, then get the occlusion relationship on the Light Probe, we can also view the occlusion relationship through the editor:
insert image description here
set to display all Light Probes, and then display the occlusion, you will find that the Light Probe in the shadow is obvious, such as in The Light Probe in the bright light should be dark.
insert image description here
Unity's built-in processing is a function called MainLightShadow:
insert image description here
this method is the most recommended method on the mobile platform. Static objects simply obtain direct light, indirect light and shadows from Light Map, and can obtain them from dynamic objects in real time. Real-time shadows for blending. Dynamic objects can calculate direct light in real time, and can update the relationship of being occluded by the scene in real time through the baked Light Probe, achieving a balance between performance and effect.
There are still some issues: characters being occluded by shadows are not as clear as realtime occlusions, and the blend between realtime rendered shadows and baked shadows is not as good. If it is a mobile platform, it is barely enough. After all, other methods are more expensive.

ShadowMask mode

If the target is a host or pc, you don’t have to be so tight. You can use the ShadowMask mode. The difference between this mode and the above Subtrative is that the shadow will not be baked directly to the LightMap when baking, but a ShadowMask texture will be generated separately. , for shadow occlusion relations. In this way, instead of using that trick to calculate the shadow, the two shadows are directly fused, and the influence of the baked shadow can also be obtained on the dynamic object. But you will find that the volume of the baked LightMap will double, which is also a disadvantage.
insert image description here
You will find that each texture has a corresponding shadowmask. There are two modes in the ShadowMask mode. As I said before, you
insert image description here
can set the ShadowMask mode in the quality. ShadowMask is a static object that uses shadowmask textures to render shadows. Blends well with dynamic objects. But Distance Shadowmask, it will render twice, that is, both real-time shadows and shadowmask shadows are rendered, and finally a fusion.
insert image description here
The picture above shows the batches of Distance Shadowmask's rendered shadows.
insert image description here
The picture above shows batches in shadowmask mode. It is obvious that dozens of passes are missing. So, again, better effects require more performance.
Using Distance Shadowmask, your static object will perform two shadow renderings (shadowmask texture and real-time rendering) and finally merge, and shadowCasterPass rendering is also required.
Compared with the Subtrative mode, Shadowmask solves the problem of fusion of dynamic shadows and static shadows, at the cost of doubling the size of the lightmap.

Baked Indirect mode

In this mode, both the indirect light and the baked light are baked on the LightMap, and the direct light is still rendered in real time. We only need to get LightMap in the indirect light to replace SH as before.

Render LightMap in real time

Realtime Lighting and Mixed Lighting can be shared. Realtime mainly solves the diffuse reflection and ejection effect of real-time light sources, while baked ones can directly process Baked light sources and Area Light.
insert image description here
How to mix, we can see the built-in code of unity, which is compatible with dynamic LightMap and static LightMap, and judge whether your lightmap needs to be calculated through macros. There is no overlap between them before, because the dynamic LightMap is the diffuse reflection information of the stored dynamic lighting, and the static LightMap is the diffuse reflection information of the stored static lighting. So they can be added together. We also calculated the static LightMap before, so how to get the UV of the dynamic LightMap.

First of all, dynamicLightmapUV must be obtained, which requires a third set of uvs.

float2 dynamicLightmapUV : TEXCOORD2;

Vertex shader calculates UVs

output.dynamicLightmapUV = input.dynamicLightmapUV * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw;

Get dynamic lighting LightMap:

float4 encodedIrradiance = SAMPLE_TEXTURE2D(unity_DynamicLightmap,samplerunity_DynamicLightmap,input.dynamicLightmapUV);
Irradiance = DecodeLightmap(encodedIrradiance, float4(LIGHTMAP_HDR_MULTIPLIER, LIGHTMAP_HDR_EXPONENT, 0.0h, 0.0h));

In this way, you can implement it yourself. If there is no custom requirement, I recommend that you just use the SampleLightmap function directly.

This two-pronged approach is the best effect. It is recommended to turn on the highest effect. After all, two baking systems are used, which consume both performance and memory, but the effect will be much better, because your dynamic light source Both static and static light sources get indirect light diffuse calculations, making the effect more realistic.
Recommendation: Host and computer with high-definition enabled.

Plug-in recommendation

Bakery replaces the built-in baking GI, which is more efficient.
Magic Lightmap Switcher can realize the switching of LightMap textures.
Magic Light Probes can help you easily and quickly arrange light probes LightProbe

Guess you like

Origin blog.csdn.net/qq_30100043/article/details/130411800