Hi there!
Right now I’m trying to write a shader (or shaders) which can perform two tasks:
- Run an alpha test on a render texture input (or raw camera input).
- Set all pixels which were not cut in the alpha test to a precise, uniform color and alpha, then outputs as an unlit texture.
I’d like this shader to run on a plane which follows the main camera around OR on a camera itself. The finished output will be visible in front of most of the playable area, meaning that seeing through the plane is critical (a final alpha of 1 on the output texture is not an option - our player and the environment will be completely covered up). This output is a representation of water that our players can drown in.
I’ve tried a plethora of different example shaders (including the vegetation shader from older Unity docs http://wiki.unity3d.com/index.php/VegetationTwoPass), and my trouble implementing this may just be my ineptitude with writing shaders (I’m brand new to it).
Would anyone be willing to help me write such a shader? Or does anyone know an existing shader which already accomplishes these tasks? Thanks so much!
Vertex-fragment shaders will give you better results here than surface shaders, but either way…
Use this general logic wherever you’re sampling your texture.
float4 color = tex2D(_texture, uv);
if (color.a < 0.1) discard;
color = _Color;
You will need a texture and color property in your shader. The discard keyword will cancel drawing the current pixel, so if you get past that statement, you know it’s a pixel you want to keep so you can set it’s color accordingly. You will need to disable unity’s built-in alpha test if you’ve enabled it for the shader (though in most cases use theirs, it’s more hardware accelerated).
Thanks for the reply!
I gave this shader code a shot, and the result was so slow that it crashed Unity on my powerful desktop:
Shader "Custom/JordanAlphaTest" {
Properties {
_Color ("Main Color", Color) = (.5, .5, .5, .5)
_MainTex ("Diffuse (RGB) Alpha (A)", 2D) = "white" {}
_Cutoff ("Base Alpha cutoff", Range (0,1)) = .5
}
SubShader {
Tags { "RenderType"="TransparentCutout" }
LOD 200
Cull Off Lighting Off
CGPROGRAM
#pragma surface surf Lambert
sampler2D _MainTex;
fixed4 _Color;
half _Cutoff;
struct Input {
float2 uv_MainTex;
};
void surf (Input IN, inout SurfaceOutput o) {
fixed4 c = tex2D(_MainTex, IN.uv_MainTex);
if (c.a < _Cutoff) discard;
o.Albedo = _Color.rgb;
o.Alpha = _Color.a;
}
ENDCG
}
}
What am I doing wrong here? _MainTex is correctly set as a 256x256 render texture, and the material containing this shader is applied to a stretched plane.
Discard is very slow in general, and even more so in a surface shader. Try something like this:
Shader "Custom/JordanAlphaTest" {
Properties {
_MainTex ("Diffuse (RGB) Alpha (A)", 2D) = "white" {}
_Cutoff ("Base Alpha cutoff", Range (0,1)) = .5
}
SubShader {
Tags { "RenderType"="TransparentCutout" "Queue" = "Transparent" }
Pass{
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
sampler2D _MainTex;
half _Cutoff;
struct vertIn{
float4 vertex : SV_POSITION;
half2 texcoord : TEXCOORD0;
};
struct vertOut{
float4 vertex : SV_POSITION;
half2 texcoord : TEXCOORD0;
};
vertOut vert(vertIn i)
{
vertOut o;
o.vertex = mul (UNITY_MATRIX_MVP, i.vertex);
o.texcoord = i.texcoord;
return o;
}
fixed4 frag(vertOut i) : COLOR
{
fixed4 c = tex2D(_MainTex, i.texcoord);
c.a *= step(_Cutoff, c.a);
return c;
}
ENDCG
}
}
}
Warning: this is untested code. Let me know if you run into trouble.
Step was a smart call there, Karp505, and that shader was very close! Unfortunately my solution required a hard set on alpha, which means actual alpha testing. I ended up with a shader which uses clip() which I received help with from the SomethingAwful goons.
Here’s the final shader code:
Shader "Custom/JordanAlphaTest" {
Properties {
_Color ("Main Color", Color) = (.5, .5, .5, .5)
_MainTex ("Diffuse (RGB) Alpha (A)", 2D) = "white" {}
_Cutoff ("Base Alpha cutoff", Range (0,1)) = .5
}
SubShader {
Tags { "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" }
Blend SrcAlpha OneMinusSrcAlpha
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
fixed _Cutoff;
fixed4 _Color;
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert (appdata_base v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = v.texcoord.xy;
return o;
}
float4 frag(v2f i) : COLOR
{
float4 c = tex2D(_MainTex, i.uv);
clip(c.a - _Cutoff);
c.rgb = _Color.rgb;
c.a = _Color.a;
return c;
}
ENDCG
}
}
}
The _MainTex input is a rendertexture from a camera that only views our 2D fluids, and in order to get this to not render in strange ways, I had to include some code on the quad carrying this shader to give it a 2D sorting layer and order in that layer (see Using non Sprites with the new sorting layers - Unity Engine - Unity Discussions). The tags also turned out to be very important.
The last problem I have is getting that quad to resize correctly to follow the main camera around, and adjust tiling settings on the texture to account for any squashing or stretching incurred by having a power-of-two render texture.
Edit: For anyone interested in using gradient balls for fluid particles with an alpha cutoff, a blur effect on the camera viewing them dramatically improves the effect.