一、问题背景
在unity shader开发过程中,有些时候时候我们会使用unity一些内置变量,来实现一些受unity执行过程控制的效果。比如,使用"_Time"来实现一些受时间控制的效果。但是这些变量往往会受时间缩放(Time.timeScale)影响,这在一些特殊场景中可能并不是想要的,比如在战斗过程中,我们可能加速了战斗,但不希望战斗中的一些特效也受时间缩放影响,同时也不想过多调整Shader,这时候对这些内置变量的个性化控制就很有必要了。
二、解决方案
可以在shader中使用关键字配合宏命令将相关内置变量个性化赋值,并将这些设置单独抽出到独立的hlsl文件,以便在有类似需求的shader中方便快捷引入。最后通过外部使用C#来设置这些变量,并在对应材质上开启指定keywords即可。如下:
-
个性化内置变量赋值,定义在TimeUitl.hlsl中:
c#ifndef TIMEUTILS_INCLUDE #define TIMEUTILS_INCLUDE #pragma multi_compile _ USE_UNSCALEDTIME #ifdef USE_UNSCALEDTIME float4 _UnscaledTime; #define CUST_SETUPTIME _Time=_UnscaledTime; #else #define CUST_SETUPTIME #endif #endif -
在需要个性化控制的shader中include进该文件,并在需要控制的顶点着色器或者片元着色器中添加需要的宏命令,如下:
c
Shader "test"
{
Properties
{
_MainTexture("MainTexture", 2D) = "white" {}
_MaskTexture("MaskTexture", 2D) = "white" {}
_MaskTex_UVSpeed("MaskTex_UVSpeed", Vector) = (1,1,0,0)
[Toggle(USE_UNSCALEDTIME)]USE_UNSCALEDTIME("Use Unscaled Time", Float) = 0//开关控制是否使用未缩放时间
}
SubShader
{
Tags
{
"RenderType"="Transparent" "Queue"="Transparent" "RenderPipeline"="UniversalPipeline"
}
Blend SrcAlpha One
Pass
{
Tags
{
"LightMode"="UniversalForward"
}
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 4.5
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Include/TimeUtils.hlsl"//include个性化变量控制文件
sampler2D _MainTexture;
sampler2D _MaskTexture;
float4 _MaskTex_UVSpeed;
struct appdata
{
float4 vertex : POSITION;
float4 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float4 pos : SV_POSITION;
float4 uv : TEXCOORD1;
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert(appdata v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_TRANSFER_INSTANCE_ID(v, o);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.uv.xy = v.texcoord.xy;
o.pos = TransformObjectToHClip(v.vertex.xyz);
return o;
}
half4 frag(v2f i, float vface : VFACE) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(i);
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
CUST_SETUPTIME//添加指定宏命令
float4 mainTex = tex2D(_MainTexture, i.uv);
float4 finalColor = mainTex;
float2 maskUV = i.uv.xy * _MaskTex_UVSpeed.xy + _Time.y * _MaskTex_UVSpeed.zw;
float maskTex = tex2D(_MaskTexture, maskUV).r;
finalColor.a *= maskTex;
return finalColor;
}
ENDHLSL
}
}
}
-
在C#脚本中传入需要个性化变量值,如下:
C#using UnityEngine; public class CustumBuiltInPropertiesSetting : MonoBehaviour { private static readonly int unscaledTimeProperty = Shader.PropertyToID("_UnscaledTime"); void Update() { //Unscaled Time var unscaledTime = Application.isPlaying ? Time.unscaledTime : Time.time; var unscaledTimeVector = unscaledTime * new Vector4(1f / 20f, 1f, 2f, 3f); Shader.SetGlobalVector(unscaledTimeProperty, unscaledTimeVector); } }三、实现效果如下:
-
未开启USE_UNSCALEDTIME情况如下:


-
开启USE_UNSCALEDTIME情况如下:

-
最后,所有类似需求均可参照上述设计,即可方便快捷的控制shader中的一些unity内置变量。
示例工程如下:UnscaledTime