Unity 3d Sprite Shader(如何通过多个灯光将最大亮度限制为 1)
Unity 3d Sprite Shader (How do I limit Max Brightness to 1 with Multiple Lights Hitting)
我正在 Unity 中创建视频游戏。每个 sprite 都使用 Material 的 Sprite Renderer 进行渲染,CornucopiaShader.shader。我遇到的问题是我想将精灵的最大亮度(或颜色)限制为精灵的正常图像,而不管有多少点光源照射它的功率,灯光的强度,以及统一场景中的环境光。当打在精灵上的灯光强度低于最大亮度水平时,我希望它像普通的发光精灵一样工作,如果没有灯光打到它,它就会变成黑色,如果打到它的强度为 0.5,它就会半亮等等,以及介于两者之间的一切都像往常一样。
问题 1: 总之,如果三盏灯以 5 的强度打在精灵上,我希望精灵看起来只是正常亮度 1,而不是被光冲出白色。
由于玩家可以像纸片马里奥一样旋转并切换侧面,因此当前的着色器代码以这种方式工作,而且当前从背面照射的光线也应该照亮两侧,就像它当前在着色器中所做的那样。
问题 2: 但是我遇到的另一个问题,就像我包含的四张图片中看到的那样,当我翻转播放器时,强度会发生变化。
这两个问题我连续3天都在想办法搞不懂。
Picture 1
Picture 2
Picture 3
Picture 4
Shader "Custom/CornucopiaShader" {
Properties{
_MainCol("Main Tint", Color) = (1,1,1,1)
_MainTex("Main Texture", 2D) = "white" {}
_Cutoff("Alpha cutoff", Range(0,0.5)) = 0.5
}
SubShader{
Tags {"Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" "PreviewType" = "Plane"}
Cull Off
ZWrite Off
LOD 200
ColorMask RGB
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma surface surf SimpleLambert alphatest:_Cutoff addshadow fullforwardshadows alpha:blend
#pragma target 3.0
#include "RetroAA.cginc"
sampler2D _MainTex;
float4 _MainTex_TexelSize;
fixed4 _MainCol;
half4 LightingSimpleLambert(SurfaceOutput s, half3 lightDir, half atten)
{
half4 c;
c.rgb = s.Albedo * _MainCol.rgb * (atten)* _LightColor0.rgb;
c.a = s.Alpha;
return c;
}
struct Input {
float2 uv_MainTex;
};
void surf(Input IN, inout SurfaceOutput o) {
fixed4 c = RetroAA(_MainTex, IN.uv_MainTex, _MainTex_TexelSize);
o.Albedo = lerp(c.rgb, c.rgb, c.a);
o.Alpha = c.a;
}
ENDCG
}
Fallback "Transparent/Cutout/VertexLit"
}
#include "UnityCG.cginc"
#pragma target 3.0
fixed4 RetroAA(sampler2D tex, float2 uv, float4 texelSize){
float2 texelCoord = uv*texelSize.zw;
float2 hfw = 0.5*fwidth(texelCoord);
float2 fl = floor(texelCoord - 0.5) + 0.5;
float2 uvaa = (fl + smoothstep(0.5 - hfw, 0.5 + hfw, texelCoord - fl))*texelSize.xy;
return tex2D(tex, uvaa);
}
你不能用表面着色器真正做到这一点,但你可以用顶点片段着色器非常有效地做到这一点。 Unity 将 4 个最近的点光源存储在一组向量中,以用于逐顶点(非重要)光源。幸运的是,这些也可以在片段着色器中访问,因此您可以使用它们一次为所有 4 盏灯着色!当您将所有灯光加在一起时,请确保它们的强度不能超过 1。这是我为您拼凑的快速着色器:
Shader "Unlit/ToonTest"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Name "FORWARD"
Tags { "LightMode" = "ForwardBase" "RenderType" = "TransparentCutout" "Queue"="AlphaTest"}
Cull Off
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
half3 normal : NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
float3 worldPos : TEXCOORD1;
float3 ambient : TEXCOORD2;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.ambient = ShadeSH9(mul(unity_ObjectToWorld, float4(v.normal, 0.0 ))); // Ambient from spherical harmonics
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
float3 Shade4Lights (
float4 lightPosX, float4 lightPosY, float4 lightPosZ,
float3 lightColor0, float3 lightColor1, float3 lightColor2, float3 lightColor3,
float4 lightAttenSq,
float3 pos)
{
// to light vectors
float4 toLightX = lightPosX - pos.x;
float4 toLightY = lightPosY - pos.y;
float4 toLightZ = lightPosZ - pos.z;
// squared lengths
float4 lengthSq = 0;
lengthSq += toLightX * toLightX;
lengthSq += toLightY * toLightY;
lengthSq += toLightZ * toLightZ;
// don't produce NaNs if some vertex position overlaps with the light
lengthSq = max(lengthSq, 0.000001);
// attenuation
float4 atten = 1.0 / (1.0 + lengthSq * lightAttenSq);
float4 diff = atten; //ndotl * atten;
// final color
float3 col = 0;
col += lightColor0 * diff.x;
col += lightColor1 * diff.y;
col += lightColor2 * diff.z;
col += lightColor3 * diff.w;
return col;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
half3 intensity = Shade4Lights(unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0, unity_LightColor[0], unity_LightColor[1], unity_LightColor[2], unity_LightColor[3], unity_4LightAtten0, i.worldPos);
intensity = min((half3)1, i.ambient + intensity);
col.rgb *= intensity;
clip(col.a - 0.5);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}
"Shade4Lights" 函数是 Unity "Shade4PointLights" 的修改版本,移除了漫射朗伯光照(仅衰减)。您还必须将 RetroAA 函数添加到纹理采样中。您的截止值是 "clip" 函数内的“- 0.5” - 如果需要,您可以公开它。如果您需要此着色器的阴影投射,您可以 copy/paste 来自 Unity 标准着色器的阴影通道(您可以从他们的页面下载源代码)。对于阴影接收,您需要向着色器添加几行代码 - 再次检查源代码。
您可以在此处阅读有关内置着色器变量的更多信息:
https://docs.unity3d.com/Manual/SL-UnityShaderVariables.html
我正在 Unity 中创建视频游戏。每个 sprite 都使用 Material 的 Sprite Renderer 进行渲染,CornucopiaShader.shader。我遇到的问题是我想将精灵的最大亮度(或颜色)限制为精灵的正常图像,而不管有多少点光源照射它的功率,灯光的强度,以及统一场景中的环境光。当打在精灵上的灯光强度低于最大亮度水平时,我希望它像普通的发光精灵一样工作,如果没有灯光打到它,它就会变成黑色,如果打到它的强度为 0.5,它就会半亮等等,以及介于两者之间的一切都像往常一样。 问题 1: 总之,如果三盏灯以 5 的强度打在精灵上,我希望精灵看起来只是正常亮度 1,而不是被光冲出白色。
由于玩家可以像纸片马里奥一样旋转并切换侧面,因此当前的着色器代码以这种方式工作,而且当前从背面照射的光线也应该照亮两侧,就像它当前在着色器中所做的那样。 问题 2: 但是我遇到的另一个问题,就像我包含的四张图片中看到的那样,当我翻转播放器时,强度会发生变化。
这两个问题我连续3天都在想办法搞不懂。
Picture 1
Picture 2
Picture 3
Picture 4
Shader "Custom/CornucopiaShader" {
Properties{
_MainCol("Main Tint", Color) = (1,1,1,1)
_MainTex("Main Texture", 2D) = "white" {}
_Cutoff("Alpha cutoff", Range(0,0.5)) = 0.5
}
SubShader{
Tags {"Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" "PreviewType" = "Plane"}
Cull Off
ZWrite Off
LOD 200
ColorMask RGB
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma surface surf SimpleLambert alphatest:_Cutoff addshadow fullforwardshadows alpha:blend
#pragma target 3.0
#include "RetroAA.cginc"
sampler2D _MainTex;
float4 _MainTex_TexelSize;
fixed4 _MainCol;
half4 LightingSimpleLambert(SurfaceOutput s, half3 lightDir, half atten)
{
half4 c;
c.rgb = s.Albedo * _MainCol.rgb * (atten)* _LightColor0.rgb;
c.a = s.Alpha;
return c;
}
struct Input {
float2 uv_MainTex;
};
void surf(Input IN, inout SurfaceOutput o) {
fixed4 c = RetroAA(_MainTex, IN.uv_MainTex, _MainTex_TexelSize);
o.Albedo = lerp(c.rgb, c.rgb, c.a);
o.Alpha = c.a;
}
ENDCG
}
Fallback "Transparent/Cutout/VertexLit"
}
#include "UnityCG.cginc"
#pragma target 3.0
fixed4 RetroAA(sampler2D tex, float2 uv, float4 texelSize){
float2 texelCoord = uv*texelSize.zw;
float2 hfw = 0.5*fwidth(texelCoord);
float2 fl = floor(texelCoord - 0.5) + 0.5;
float2 uvaa = (fl + smoothstep(0.5 - hfw, 0.5 + hfw, texelCoord - fl))*texelSize.xy;
return tex2D(tex, uvaa);
}
你不能用表面着色器真正做到这一点,但你可以用顶点片段着色器非常有效地做到这一点。 Unity 将 4 个最近的点光源存储在一组向量中,以用于逐顶点(非重要)光源。幸运的是,这些也可以在片段着色器中访问,因此您可以使用它们一次为所有 4 盏灯着色!当您将所有灯光加在一起时,请确保它们的强度不能超过 1。这是我为您拼凑的快速着色器:
Shader "Unlit/ToonTest"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Name "FORWARD"
Tags { "LightMode" = "ForwardBase" "RenderType" = "TransparentCutout" "Queue"="AlphaTest"}
Cull Off
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
half3 normal : NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
float3 worldPos : TEXCOORD1;
float3 ambient : TEXCOORD2;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.ambient = ShadeSH9(mul(unity_ObjectToWorld, float4(v.normal, 0.0 ))); // Ambient from spherical harmonics
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
float3 Shade4Lights (
float4 lightPosX, float4 lightPosY, float4 lightPosZ,
float3 lightColor0, float3 lightColor1, float3 lightColor2, float3 lightColor3,
float4 lightAttenSq,
float3 pos)
{
// to light vectors
float4 toLightX = lightPosX - pos.x;
float4 toLightY = lightPosY - pos.y;
float4 toLightZ = lightPosZ - pos.z;
// squared lengths
float4 lengthSq = 0;
lengthSq += toLightX * toLightX;
lengthSq += toLightY * toLightY;
lengthSq += toLightZ * toLightZ;
// don't produce NaNs if some vertex position overlaps with the light
lengthSq = max(lengthSq, 0.000001);
// attenuation
float4 atten = 1.0 / (1.0 + lengthSq * lightAttenSq);
float4 diff = atten; //ndotl * atten;
// final color
float3 col = 0;
col += lightColor0 * diff.x;
col += lightColor1 * diff.y;
col += lightColor2 * diff.z;
col += lightColor3 * diff.w;
return col;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
half3 intensity = Shade4Lights(unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0, unity_LightColor[0], unity_LightColor[1], unity_LightColor[2], unity_LightColor[3], unity_4LightAtten0, i.worldPos);
intensity = min((half3)1, i.ambient + intensity);
col.rgb *= intensity;
clip(col.a - 0.5);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}
"Shade4Lights" 函数是 Unity "Shade4PointLights" 的修改版本,移除了漫射朗伯光照(仅衰减)。您还必须将 RetroAA 函数添加到纹理采样中。您的截止值是 "clip" 函数内的“- 0.5” - 如果需要,您可以公开它。如果您需要此着色器的阴影投射,您可以 copy/paste 来自 Unity 标准着色器的阴影通道(您可以从他们的页面下载源代码)。对于阴影接收,您需要向着色器添加几行代码 - 再次检查源代码。
您可以在此处阅读有关内置着色器变量的更多信息:
https://docs.unity3d.com/Manual/SL-UnityShaderVariables.html