Unity中的light map

使用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 Shadowmap 的使用方法 在 Unity ,Shadowmap 是用于计算场景对象是否处于光源投射的阴影的重要工具。通过预先渲染深度信息到纹理(即 Shadowmap),可以在后续光照计算阶段高效判断片段点是否位于阴影区域。 #### 启用和配置 Shadowmap 为了启用 Shadowmap 功能,在项目设置或灯光组件属性面板内调整相应选项: - **Quality Settings**: 设置全局质量等级来控制阴影分辨率和其他参数。 - **Light Component**: 对特定光源开启 `Use Shadows` 并指定阴影类型 (Hard 或 Soft)[^1]。 ```csharp // C# 脚本示例:动态修改灯光的阴影属性 using UnityEngine; public class LightController : MonoBehaviour { public void ToggleShadows(bool enable) { GetComponent<Light>().shadows = enable ? LightShadows.Soft : LightShadows.None; } } ``` #### 自定义阴影效果 对于更复杂的阴影需求,可以通过自定义着色器进一步优化阴影表现。例如应用软阴影效果或者调节边缘过渡平滑度等特性[^3]。 ```hlsl Shader "Custom/SoftShadow" { Properties { ... } // 定义材质属性 SubShader { Tags {"RenderType"="Opaque"} Pass { Name "FORWARD" CGPROGRAM #pragma vertex vert #pragma fragment frag sampler2D _CameraDepthTexture; // 获取相机深度贴图 float4 CalculateShadow(float4 worldPos, float3 lightDir) { // 实现百分比接近滤波(PCSS),模拟柔和阴影 ... return finalColor; } ENDCG } } FallBack "Diffuse" } ``` #### 解决常见问题 当遇到与 Shadowmap 相关的问题时,可以尝试以下几种方案来进行排查和修复: - **剪裁面不匹配**:确保摄像机近远平面合理设定,防止过早剔除潜在可见几何体; - **精度不足引起伪影**:适当提高阴影映射纹理大小,减少因浮点数舍入造成的误差; - **性能瓶颈**:降低不必要的高分辨率阴影绘制频率,利用级联阴影地图(CSMs)分层管理不同距离范围内的细节程度;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值