防止透明区域被投影着色器着色
Prevent transparent areas being shaded by projection shader
我正在尝试制作贴花着色器以在 Unity 中与投影仪一起使用。这是我整理的内容:
Shader "Custom/color_projector"
{
Properties {
_Color ("Tint Color", Color) = (1,1,1,1)
_MainTex ("Cookie", 2D) = "gray" {}
}
Subshader {
Tags {"Queue"="Transparent"}
Pass {
ZTest Less
ColorMask RGB
Blend One OneMinusSrcAlpha
Offset -1, -1
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f {
float4 uvShadow : TEXCOORD0;
float4 pos : SV_POSITION;
};
float4x4 unity_Projector;
float4x4 unity_ProjectorClip;
v2f vert (float4 vertex : POSITION)
{
v2f o;
o.pos = UnityObjectToClipPos (vertex);
o.uvShadow = mul (unity_Projector, vertex);
return o;
}
sampler2D _MainTex;
fixed4 _Color;
fixed4 frag (v2f i) : SV_Target
{
fixed4 tex = tex2Dproj (_MainTex, UNITY_PROJ_COORD(i.uvShadow));
return _Color * tex.a;
}
ENDCG
}
}
}
这在大多数情况下都很有效:
但是,每当它投射到一个透明表面(或多个表面)上时,它似乎都会为每个表面渲染额外的时间。在这里,我使用带有透明区域的草纹理打破了草和铺路之间的界限:
我尝试了多种混合和选项以及所有 ZTesting 选项。这是我能看到的最好的了。
通过阅读我了解到这可能是因为透明着色器没有写入深度缓冲区。我尝试添加 ZWrite On
并尝试在主要传递之前进行传递:
Pass {
ZWrite On
ColorMask 0
}
但两者都没有任何效果。
如何修改此着色器,使其只在最近的几何体上投影一次纹理?
想要的结果(照片处理过):
问题出在投影仪的工作方式上。基本上,他们会第二次渲染视野内的所有网格,但使用不同的着色器除外。在您的情况下,这意味着地面和有草的平面都将被渲染两次并相互叠加。我认为可以通过两个步骤解决此问题;
首先,将以下内容添加到透明(草)着色器的标签中:
"IgnoreProjector"="True"
然后,将投影仪的渲染队列从 "Transparent" 更改为 "Transparent+1"。这意味着地面将首先渲染,然后是草地边缘,最后投影仪将投影到地面上(除了出现在顶部,因为它是最后渲染的)。
至于混合,我想你想要常规的 alpha 混合:
Blend SrcAlpha OneMinusSrcAlpha
如果您使用延迟渲染,另一种选择是使用 deferred decals。这些都比投影仪更便宜,而且通常更容易使用。
我正在尝试制作贴花着色器以在 Unity 中与投影仪一起使用。这是我整理的内容:
Shader "Custom/color_projector"
{
Properties {
_Color ("Tint Color", Color) = (1,1,1,1)
_MainTex ("Cookie", 2D) = "gray" {}
}
Subshader {
Tags {"Queue"="Transparent"}
Pass {
ZTest Less
ColorMask RGB
Blend One OneMinusSrcAlpha
Offset -1, -1
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f {
float4 uvShadow : TEXCOORD0;
float4 pos : SV_POSITION;
};
float4x4 unity_Projector;
float4x4 unity_ProjectorClip;
v2f vert (float4 vertex : POSITION)
{
v2f o;
o.pos = UnityObjectToClipPos (vertex);
o.uvShadow = mul (unity_Projector, vertex);
return o;
}
sampler2D _MainTex;
fixed4 _Color;
fixed4 frag (v2f i) : SV_Target
{
fixed4 tex = tex2Dproj (_MainTex, UNITY_PROJ_COORD(i.uvShadow));
return _Color * tex.a;
}
ENDCG
}
}
}
这在大多数情况下都很有效:
但是,每当它投射到一个透明表面(或多个表面)上时,它似乎都会为每个表面渲染额外的时间。在这里,我使用带有透明区域的草纹理打破了草和铺路之间的界限:
我尝试了多种混合和选项以及所有 ZTesting 选项。这是我能看到的最好的了。
通过阅读我了解到这可能是因为透明着色器没有写入深度缓冲区。我尝试添加 ZWrite On
并尝试在主要传递之前进行传递:
Pass {
ZWrite On
ColorMask 0
}
但两者都没有任何效果。
如何修改此着色器,使其只在最近的几何体上投影一次纹理?
想要的结果(照片处理过):
问题出在投影仪的工作方式上。基本上,他们会第二次渲染视野内的所有网格,但使用不同的着色器除外。在您的情况下,这意味着地面和有草的平面都将被渲染两次并相互叠加。我认为可以通过两个步骤解决此问题;
首先,将以下内容添加到透明(草)着色器的标签中:
"IgnoreProjector"="True"
然后,将投影仪的渲染队列从 "Transparent" 更改为 "Transparent+1"。这意味着地面将首先渲染,然后是草地边缘,最后投影仪将投影到地面上(除了出现在顶部,因为它是最后渲染的)。
至于混合,我想你想要常规的 alpha 混合:
Blend SrcAlpha OneMinusSrcAlpha
如果您使用延迟渲染,另一种选择是使用 deferred decals。这些都比投影仪更便宜,而且通常更容易使用。