Shader随笔Baked&GI
Global Bake
这里是简化Lighting.cginc的UnityGI_Base函数
以及AutoLight.cginc的LightingLambert
来实现bake贴图采样(没开灯光)
其中,Mixed是重点
Directional Mode是重点
Light组件的Mode需要调成Mixed
完成以上内容可以在使用unity标准Shader的情况下看的bake情况
自定义cginc文件
直接看pass
Baked光照贴图采样技术
Pass { Tags {"LightMode"="ForwardBase"} CGPROGRAM #pragma vertex vert #pragma fragment frag //需要使用这两个宏定义 #pragma multi_compile DIRLIGHTMAP_COMBINED #pragma multi_compile LIGHTMAP_ON #include "UnityCG.cginc" #include "CGINC/MyCGInc.cginc" struct appdata { float4 vertex : POSITION; #if defined(LIGHTMAP_ON) float4 texcoord1 : TEXCOORD1; #endif float3 normal : NORMAL; }; struct v2f { float4 pos : SV_POSITION; float3 worldNormal : NORMAL; float3 worldPos : TEXCOORD0; #if defined(LIGHTMAP_ON) fixed4 lightmapUV : TEXCOORD1; #endif }; v2f vert (appdata v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.worldNormal = mul(unity_ObjectToWorld, v.normal); o.worldPos = mul(unity_ObjectToWorld, v.vertex); #if defined(LIGHTMAP_ON) o.lightmapUV.xy = v.texcoord1 * unity_LightmapST.xy + unity_LightmapST.zw; #endif return o; } fixed4 frag (v2f i) : SV_Target { SurfaceOutput o; UNITY_INITIALIZE_OUTPUT(SurfaceOutput, o); o.Albedo = 1; o.Normal = i.worldNormal; UnityGI gi; UNITY_INITIALIZE_OUTPUT(UnityGI, gi); gi.light.color = _LightColor0; gi.light.dir = _WorldSpaceLightPos0; UnityGIInput giInput; UNITY_INITIALIZE_OUTPUT(UnityGIInput, giInput); giInput.light = gi.light; giInput.worldPos = i.worldPos; giInput.worldViewDir = normalize(_WorldSpaceCameraPos - i.worldPos); giInput.atten = 0.5;//先默认给一个,后续需要计算光照衰减 #if defined(LIGHTMAP_ON) giInput.lightmapUV = i.lightmapUV; #endif //对烘焙贴图进行采样 UnityGI_Base(giInput,1.0h,i.worldNormal, gi); //计算一个简单的Lambert光照模型 return LightingLambert(o, gi); } ENDCG }
这些gi啊giInput啊什么什么的,都来根据Unity内部实现使用的
来自于Lighting.cginc
同时,自定义的cginc文件也是改编自Lighting.cginc
1.SurfaceOutput是Lighting.cginc定义的struct,我们可以在自定义cginc中也定义一个
这里我定义了一个简略版,就是把需要的数据留着,不用的就删了
2.UnityGI和UnityGIInput也都是struct,由于东西太多,我这使用的#include "UnityLightingCommon.cginc"
这样,该cginc和该pass就可以使用这两个struct了
3.LightingLambert来自于Lighting.cginc,是实现一个简单的Lambert光照模型
4.UnityGI_Base来自于Lighting.cginc引入的UnityGlobalIllumination.cginc
这里使用的是改编版,只留下了一个宏定义
UnityGI_Base原版
inline UnityGI UnityGI_Base(UnityGIInput data, half occlusion, half3 normalWorld) { UnityGI o_gi; ResetUnityGI(o_gi); // Base pass with Lightmap support is responsible for handling ShadowMask / blending here for performance reason #if defined(HANDLE_SHADOWS_BLENDING_IN_GI) half bakedAtten = UnitySampleBakedOcclusion(data.lightmapUV.xy, data.worldPos); float zDist = dot(_WorldSpaceCameraPos - data.worldPos, UNITY_MATRIX_V[2].xyz); float fadeDist = UnityComputeShadowFadeDistance(data.worldPos, zDist); data.atten = UnityMixRealtimeAndBakedShadows(data.atten, bakedAtten, UnityComputeShadowFade(fadeDist)); #endif o_gi.light = data.light; o_gi.light.color *= data.atten; #if UNITY_SHOULD_SAMPLE_SH o_gi.indirect.diffuse = ShadeSHPerPixel(normalWorld, data.ambient, data.worldPos); #endif #if defined(LIGHTMAP_ON) // Baked lightmaps half4 bakedColorTex = UNITY_SAMPLE_TEX2D(unity_Lightmap, data.lightmapUV.xy); half3 bakedColor = DecodeLightmap(bakedColorTex); #ifdef DIRLIGHTMAP_COMBINED fixed4 bakedDirTex = UNITY_SAMPLE_TEX2D_SAMPLER (unity_LightmapInd, unity_Lightmap, data.lightmapUV.xy); o_gi.indirect.diffuse += DecodeDirectionalLightmap (bakedColor, bakedDirTex, normalWorld); #if defined(LIGHTMAP_SHADOW_MIXING) && !defined(SHADOWS_SHADOWMASK) && defined(SHADOWS_SCREEN) ResetUnityLight(o_gi.light); o_gi.indirect.diffuse = SubtractMainLightWithRealtimeAttenuationFromLightmap (o_gi.indirect.diffuse, data.atten, bakedColorTex, normalWorld); #endif #else // not directional lightmap o_gi.indirect.diffuse += bakedColor; #if defined(LIGHTMAP_SHADOW_MIXING) && !defined(SHADOWS_SHADOWMASK) && defined(SHADOWS_SCREEN) ResetUnityLight(o_gi.light); o_gi.indirect.diffuse = SubtractMainLightWithRealtimeAttenuationFromLightmap(o_gi.indirect.diffuse, data.atten, bakedColorTex, normalWorld); #endif #endif #endif #ifdef DYNAMICLIGHTMAP_ON // Dynamic lightmaps fixed4 realtimeColorTex = UNITY_SAMPLE_TEX2D(unity_DynamicLightmap, data.lightmapUV.zw); half3 realtimeColor = DecodeRealtimeLightmap (realtimeColorTex); #ifdef DIRLIGHTMAP_COMBINED half4 realtimeDirTex = UNITY_SAMPLE_TEX2D_SAMPLER(unity_DynamicDirectionality, unity_DynamicLightmap, data.lightmapUV.zw); o_gi.indirect.diffuse += DecodeDirectionalLightmap (realtimeColor, realtimeDirTex, normalWorld); #else o_gi.indirect.diffuse += realtimeColor; #endif #endif o_gi.indirect.diffuse *= occlusion; return o_gi; }
我看着就是一个简单的bake贴图采样,不过和法线有关
MyCGInc.cginc
#ifndef MYCGINC_CGINCLUDE #define MYCGINC_CGINCLUDE #include "UnityLightingCommon.cginc" struct SurfaceOutput { fixed3 Albedo; fixed3 Normal; }; inline fixed4 LightingLambert (SurfaceOutput s, UnityGI gi) { fixed4 c; fixed diff = max (0, dot (s.Normal, gi.light.dir)); c.rgb = s.Albedo * gi.light.color * diff; #if defined(LIGHTMAP_ON) c.rgb += s.Albedo * gi.indirect.diffuse; #endif return c; } inline void UnityGI_Base(UnityGIInput data, half occlusion, half3 normalWorld, inout UnityGI gi) { #if defined(LIGHTMAP_ON) half4 bakedColorTex = UNITY_SAMPLE_TEX2D(unity_Lightmap, data.lightmapUV.xy); half3 bakedColor = DecodeLightmap(bakedColorTex); #ifdef DIRLIGHTMAP_COMBINED fixed4 bakedDirTex = UNITY_SAMPLE_TEX2D_SAMPLER (unity_LightmapInd, unity_Lightmap, data.lightmapUV.xy); gi.indirect.diffuse += DecodeDirectionalLightmap (bakedColor, bakedDirTex, normalWorld); #endif #endif //将衰减应用于灯光颜色在 gi.light.color *= data.atten; //环境光遮蔽 gi.indirect.diffuse *= occlusion; } #endif
Global Bake之Non-Directional
近距离采用实时阴影,远距离采用Baked阴影 的技术
1.Light组件的Mode需要调成Mixed
2.Bake设置的LightingMode要调成Shadowmask
3.Bake设置的Directional Mode要调成Non-Directional
4.ProjectSettings-Quality-Shadows-Shadowmask Mode要调成Distance Shadowmask
5.ProjectSettings-Quality-Shadows-ShadowDistance调成你想要的值,这里是 5
效果:Bake的光照PosY是30,实时的光照PosY调了个46
距离小于ShadowDistance就用的实时阴影,大于就用bake阴影
此时上面是实时,下面是bake
继续远离
代码部分:
原先的自定义cginc没有实现该技术
该技术来自于UnityGlobalIllumination.cginc的HANDLE_SHADOWS_BLENDING_IN_GI宏定义,
就是将自定义cginc,修改成了使用Lighting.cginc的Bake阴影技术和AutoLight.cginc的实时阴影技术
代码部分
主pass
Pass { Tags {"LightMode"="ForwardBase"} CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_fwdbase #include "UnityCG.cginc" #include "Lighting.cginc" #include "AutoLight.cginc" struct appdata { float4 vertex : POSITION; #if defined(LIGHTMAP_ON) float4 texcoord1 : TEXCOORD1; #endif float3 normal : NORMAL; }; struct v2f { float4 pos : SV_POSITION; float3 worldNormal : NORMAL; float3 worldPos : TEXCOORD0; #if defined(LIGHTMAP_ON) fixed4 lightmapUV : TEXCOORD1; #endif UNITY_LIGHTING_COORDS(2,3) }; v2f vert (appdata v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.worldNormal = mul(unity_ObjectToWorld, v.normal); o.worldPos = mul(unity_ObjectToWorld, v.vertex); #if defined(LIGHTMAP_ON) o.lightmapUV.xy = v.texcoord1 * unity_LightmapST.xy + unity_LightmapST.zw; #endif UNITY_TRANSFER_LIGHTING(o, v.texcoord1.xy); return o; } fixed4 frag (v2f i) : SV_Target { SurfaceOutput o; UNITY_INITIALIZE_OUTPUT(SurfaceOutput, o); o.Albedo = 1; o.Normal = i.worldNormal; UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos); UnityGI gi; UNITY_INITIALIZE_OUTPUT(UnityGI, gi); gi.light.color = _LightColor0; gi.light.dir = _WorldSpaceLightPos0; UnityGIInput giInput; UNITY_INITIALIZE_OUTPUT(UnityGIInput, giInput); giInput.light = gi.light; giInput.worldPos = i.worldPos; giInput.worldViewDir = normalize(_WorldSpaceCameraPos - i.worldPos); giInput.atten = atten; #if defined(LIGHTMAP_ON) || defined(DYNAMACLIGHTMAP_ON) giInput.lightmapUV = i.lightmapUV; #endif //对烘焙贴图进行采样 LightingLambert_GI(o,giInput,gi); //计算一个简单的Lambert光照模型 return LightingLambert(o, gi); } ENDCG }
阴影Pass
pass { Tags{ "LightMode"="ShadowCaster" } CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_shadowcaster #include "UnityCG.cginc" struct a2v { float4 vertex: POSITION; half3 normal: NORMAL; }; struct v2f { V2F_SHADOW_CASTER; }; v2f vert(a2v v) { v2f o; TRANSFER_SHADOW_CASTER_NORMALOFFSET(o); return o; } fixed4 frag(v2f i) : SV_Target { SHADOW_CASTER_FRAGMENT(i); } ENDCG }
自发光Bake Pass
修改版
自发光BakePass
//只有bake才会走这个pass pass { Name "Meta" Tags { "LightMode"="Meta" } Cull off CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma target 2.0 #include "UnityCG.cginc" #include "UnityMetaPass.cginc" fixed4 _Color; struct v2f { float4 pos: SV_POSITION; }; v2f vert(appdata_full v) { v2f o; UNITY_INITIALIZE_OUTPUT(v2f,o); o.pos = UnityMetaVertexPosition(v.vertex, v.texcoord1.xy, v.texcoord2.xy, unity_LightmapST, unity_DynamicLightmapST); return o; } fixed4 frag(v2f i):SV_Target { UnityMetaInput metaIN; UNITY_INITIALIZE_OUTPUT(UnityMetaInput, metaIN); metaIN.Albedo = 1; metaIN.Emission = _Color; return UnityMetaFragment(metaIN); } ENDCG }
然后需要修改本材质的Lightmap Flags
开启该物体的debug模式,在材质里找Lightmap Flags,设置为2
原因:MaterialGlobalIlluminationFlags - Unity 脚本 API
也可以在最后加上CustomEditor "LegacyIlluminShaderGUI"
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!