Unity Shader打包:multi_compile与shader_feature

本文记录了在Unity中使用shader_feature进行优化时遇到的显示异常问题。通过分析和排查,发现由于shader_feature与multi_compile在变体打包上的区别,导致LIGHTMAP_ON和SCENE_SPOTLIGHT关键词未正确打包,进而引发渲染错误。解决方案是将这些关键词改回multi_compile,强调在优化时需注意Unity内置关键字的特殊性。

摸鱼指南NOTE1:开始记录一些在游戏开发维护过程遇到的问题,以及这些问题背后的原因和解决方法。
这篇主要是记录排查问题的一些思路,可以考虑考虑如何更高效地找到问题所在,也可以更好地摸鱼^^

问题:shader_feature优化导致显示异常

事情的起因是一次优化,为了控制shader变体的数量,减少打包出来的shader体积,所以将shader中的几个宏定义从multi_compile改成了shader_feature。

修改前:

#pragma multi_compile _ LIGHTMAP_ON
#pragma multi_compile _NORMALMAP
#pragma multi_compile _PARAMETERMAP
#pragma multi_compile _ SCENE_SPOTLIGHT_DIFFUSE_ON

修改后:

#pragma shader_feature _ LIGHTMAP_ON
#pragma shader_feature _NORMALMAP
#pragma shader_feature _PARAMETERMAP
#pragma shader_feature _ SCENE_SPOTLIGHT_DIFFUSE_ON

在本地Editor测试一遍之后觉得毫无问题,但是提交打包后却发现,修改过后的材质显示的不那么对了OTL,以下两张图分别为修改前后的效果,可见修改后丢了部分信息。

修改前的路

<think>我们已经在之前的回答中实现了通过脚本控制URP管线设置来启用高精度深度纹理(SampleHighPrecisionDepth)。现在,用户特别询问了如何使用`#pragmamulti_compile`来实现最高画质深度设置的着色器代码部分。根据用户提供的引用内容,特别是引用[1][3][4][5],我们知道`#pragmamulti_compile`用于生成多个着色器变体,每个变体对应不同的关键字组合。这样,在运行时可以通过启用或禁用关键字来切换不同的功能。在之前的着色器代码中,我们已经使用了`#pragmamulti_compile__HIGH_PRECISION_DEPTH`来生成两个变体:一个没有定义_HIGH_PRECISION_DEPTH(即普通精度),另一个定义了_HIGH_PRECISION_DEPTH(即高精度深度)。这样,在脚本中通过`Shader.EnableKeyword("_HIGH_PRECISION_DEPTH")`来启用高精度深度采样路径。下面,我将提供一个更完整的示例,展示如何在Unity着色器中使用`#pragmamulti_compile`来支持高精度深度纹理,并给出如何从脚本中控制该关键字的代码。###着色器代码示例(重点展示multi_compile的使用)```hlslShader"Custom/HighPrecisionDepthSampler"{Properties{_MainTex("Texture",2D)="white"{}_DepthSensitivity("DepthSensitivity",Range(0.001,0.1))=0.01}SubShader{Tags{"RenderType"="Opaque""RenderPipeline"="UniversalPipeline"}Pass{HLSLPROGRAM#pragmavertexvert#pragmafragmentfrag//关键点:使用multi_compile生成两个变体:一个带_HIGH_PRECISION_DEPTH,一个不带#pragmamulti_compile__HIGH_PRECISION_DEPTH#include"Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"#include"Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"structAttributes{float4positionOS:POSITION;float2uv:TEXCOORD0;};structVaryings{float2uv:TEXCOORD0;float4positionCS:SV_POSITION;};TEXTURE2D(_MainTex);SAMPLER(sampler_MainTex);float_DepthSensitivity;Varyingsvert(Attributesinput){Varyingsoutput;output.positionCS=TransformObjectToHClip(input.positionOS.xyz);output.uv=input.uv;returnoutput;}//根据是否启用_HIGH_PRECISION_DEPTH关键字,选择不同的深度采样方式floatSampleDepth(float2uv){#ifdefined(_HIGH_PRECISION_DEPTH)//高精度路径:使用更高精度的深度值计算//注意:这里假设已经通过脚本设置了高精度的深度纹理格式(如RFloat)floatdepth=SampleSceneDepth(uv);//可以在这里进行更高精度的计算,例如使用更大的范围或者更复杂的解码returnLinear01Depth(depth,_ZBufferParams)*_DepthSensitivity;#else//普通精度路径floatdepth=SampleSceneDepth(uv);returnLinear01Depth(depth,_ZBufferParams);#endif}half4frag(Varyingsinput):SV_Target{half4color=SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex,input.uv);floatdepth=SampleDepth(input.uv);//这里仅作为示例,将深度值叠加到颜色上returncolor*depth;}ENDHLSL}}}```###脚本控制关键字的代码在之前的脚本中,我们已经包含了启用关键字的代码:```csharp//启用高精度深度关键字Shader.EnableKeyword("_HIGH_PRECISION_DEPTH");//禁用则使用//Shader.DisableKeyword("_HIGH_PRECISION_DEPTH");```###注意事项1.**变体数量控制**:使用`#pragmamulti_compile`会生成多个着色器变体。如果变体过多,会增加包体大小和内存占用。因此,应仅对必要的功能使用。2.**关键字命名**:确保关键字(如`_HIGH_PRECISION_DEPTH`)在整个项目中唯一,避免其他着色器冲突。3.**硬件支持**:在启用高精度深度之前,应检查硬件是否支持高精度深度纹理格式(如`RenderTextureFormat.RFloat`),如不支持,则不应启用该关键字。###性能优化建议如果同时存在多个影响画质的关键字,可以考虑使用`shader_feature`代替`multi_compile`。`shader_feature`在最终打包时只会包含实际使用的变体,而`multi_compile`会包含所有变体。但是,如果关键字需要在运行时动态切换,则必须使用`multi_compile`。例如,如果高精度深度设置只在编辑器中调整,并且不会在运行时改变,则可以使用:```hlsl#pragmashader_feature__HIGH_PRECISION_DEPTH```###相关设置整合在URP管线的配置脚本中,我们不仅要设置着色器关键字,还需要设置管线资源以支持高精度深度纹理。这包括:-启用深度纹理:`urpAsset.supportsCameraDepthTexture=true;`-设置深度纹理的格式(在URP中,深度纹理的格式通常由管线内部管理,但我们可以通过设置`depthPrimingMode`为`Disabled`来避免深度预通道,从而可能获得更高精度的深度纹理)###总结通过结合`#pragmamulti_compile`在着色器中定义高精度深度路径,并在脚本中根据画质设置启用或禁用对应的关键字,我们可以实现最高画质的深度设置。同时,注意硬件兼容性和性能优化。§§相关问题§§1.如何减少由multi_compile导致的着色器变体数量过多的问题?2.在URP管线中,如何设置深度纹理的格式为32位浮点数?3.如何通过图形设置界面(GraphicsSettings)来管理多个画质等级的高精度深度开关?</think>以下是根据您的需求,在 Unity 中通过 `#pragma multi_compile` 实现最高画质深度设置的完整代码示例: ### 着色器代码(使用 multi_compile 控制高精度深度) ```hlsl Shader "Custom/HighPrecisionDepth" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalRenderPipeline" } Pass { HLSLPROGRAM #pragma vertex vert #pragma fragment frag // 核心:定义高精度深度变体 #pragma multi_compile _ _HIGH_PRECISION_DEPTH #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl" struct Attributes { float4 positionOS : POSITION; float2 uv : TEXCOORD0; }; struct Varyings { float2 uv : TEXCOORD0; float4 positionCS : SV_POSITION; }; TEXTURE2D(_MainTex); SAMPLER(sampler_MainTex); Varyings vert(Attributes input) { Varyings output; output.positionCS = TransformObjectToHClip(input.positionOS.xyz); output.uv = input.uv; return output; } // 高精度深度采样函数 float SampleHighPrecisionDepth(float2 uv) { #if defined(_HIGH_PRECISION_DEPTH) // 高精度路径:使用32位浮点深度 return SampleSceneDepth(uv); #else // 默认路径:使用16位深度 return Linear01Depth(SampleSceneDepth(uv), _ZBufferParams); #endif } half4 frag(Varyings input) : SV_Target { float depth = SampleHighPrecisionDepth(input.uv); return half4(depth, depth, depth, 1.0); } ENDHLSL } } } ``` ### C# 控制脚本(启用高精度深度) ```csharp using UnityEngine; using UnityEngine.Rendering; public class DepthQualityController : MonoBehaviour { [Header("深度精度设置")] public bool enableHighPrecisionDepth = true; void Start() { ConfigureDepthQuality(); } void ConfigureDepthQuality() { // 设置全局关键字 if (enableHighPrecisionDepth) { // 启用高精度深度变体 Shader.EnableKeyword("_HIGH_PRECISION_DEPTH"); // 配置渲染管线使用32位深度纹理 ConfigureRenderPipelineForHighPrecision(); } else { Shader.DisableKeyword("_HIGH_PRECISION_DEPTH"); } } void ConfigureRenderPipelineForHighPrecision() { // URP 配置 var urpAsset = GraphicsSettings.currentRenderPipeline as UniversalRenderPipelineAsset; if (urpAsset != null) { urpAsset.supportsCameraDepthTexture = true; // 确保使用高精度深度格式 #if UNITY_2022_1_OR_NEWER urpAsset.depthPrimingMode = DepthPrimingMode.Disabled; urpAsset.depthTextureMode = DepthTextureMode.Depth; #endif } // 内置管线配置 #if !UNITY_2022_1_OR_NEWER Camera.main.depthTextureMode |= DepthTextureMode.Depth; #endif } // 运行时切换质量 public void SetHighPrecisionDepth(bool enabled) { enableHighPrecisionDepth = enabled; ConfigureDepthQuality(); Debug.Log($"高精度深度模式: {enabled}"); } } ``` ### 关键点说明 1. **多编译变体控制**: ```hlsl #pragma multi_compile _ _HIGH_PRECISION_DEPTH ``` 这行代码会生成两个着色器变体:一个包含 `_HIGH_PRECISION_DEPTH` 关键字的版本(高精度),和一个不包含的版本(默认精度)[^3][^4]。 2. **条件深度采样**: ```hlsl #if defined(_HIGH_PRECISION_DEPTH) return SampleSceneDepth(uv); // 32位精度 #else return Linear01Depth(...); // 16位精度 #endif ``` 3. **运行时控制**: ```csharp // 启用高精度 Shader.EnableKeyword("_HIGH_PRECISION_DEPTH"); // 禁用高精度 Shader.DisableKeyword("_HIGH_PRECISION_DEPTH"); ``` 4. **变体管理建议**: ```hlsl // 替代方案:使用shader_feature减少变体数量 #pragma shader_feature _HIGH_PRECISION_DEPTH ``` 使用 `shader_feature` 而非 `multi_compile` 可以确保最终构建只包含实际使用的变体[^3]。 ### 性能优化建议 1. **变体数量控制**: ```csharp // 在质量设置中定义变体组合 public enum DepthQuality { Low, // _LOW_PRECISION Medium, // _MEDIUM_PRECISION High // _HIGH_PRECISION_DEPTH } ``` 2. **按平台自动配置**: ```csharp void AutoConfigureDepthQuality() { if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D12 || SystemInfo.graphicsDeviceType == GraphicsDeviceType.Metal) { Shader.EnableKeyword("_HIGH_PRECISION_DEPTH"); } else { Shader.DisableKeyword("_HIGH_PRECISION_DEPTH"); } } ``` ### 使用示例 ```csharp // 在游戏设置菜单中调用 public void OnQualitySettingChanged(int qualityLevel) { var depthController = FindObjectOfType<DepthQualityController>(); depthController.SetHighPrecisionDepth(qualityLevel == 2); } ``` > **注意**:高精度深度会显著增加显存使用和带宽需求,特别是在移动设备上。建议在高端设备上启用,在低端设备上回退到默认精度[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值