- Home /
Shader with zwrite, shadows and alpha (special alpha)
Hi
I know the answer to "can I make a shader with shadows, zwrite and alpha", that is tricky, if not impossible.
However I do not want exactly that. What I am working on is a tile based system with textures that transition into each other using transition alphas. My idea is that normally, a tile will only have a single texture, but sometimes, it needs several, when there is a transition, so when there is a transition, I was thinking that I should be able to just duplicate the tile mesh so that there are two meshes occupying the exact same space, but they have different materials. The lowest prioritized texture/material one will be rendered before the second one by specifying the render queue, the lowest prioritized mesh should simply show the texture. The second material/texture should be be alpha-blended with anything rendered before it (e.g the lowest prioritized texture/material).
Note that this does not rely in any way on that the transparent objects are rendered in back to front order. What I am asking for essentially is a diffuse shader which just blends in another way with the previously rendered pixels. Preferably it should be doable in deferred rendering, I notice that when I add "alpha" to the "#pragma surface" line, it refuses to use the deferred rendering path.
The below shader blends correctly, but does not have any shadows and does not have ZWrite enabled.
I have tried editing the compiled shaders, enabling ZWrite and changing blending modes and similar, but nothing have worked so far. I can get some features, but if I can get, for example correct blending and ZWrite, then shadows are suddenly not working.
Shader "TerrainTransition"
{
properties
{
_MainTex ("Base Texture", 2D) = "white"{}
_SplatControl ("Splat Map", 2D) = "white"{}
_Emission ("Emission", Float) = 0.1
}
subshader
{
Tags { "RenderType" = "Opaque" }
ZTest LEqual
ZWrite On
CGPROGRAM
//#pragma surface surf Lambert vertex:vert alpha
#pragma surface surf Lambert alpha addshadow
#pragma target 3.0
#pragma exclude_renderers flash
#pragma exclude_renderers d3d11
sampler2D _MainTex;
sampler2D _SplatControl;
fixed _Emission;
struct Input
{
half2 uv_MainTex;
half2 uv2_SplatControl;
};
void surf (Input IN, inout SurfaceOutput o)
{
fixed4 t0 = tex2D ( _MainTex, IN.uv_MainTex );
fixed4 splat_control = tex2D ( _SplatControl, IN.uv2_SplatControl );
o.Albedo = t0.rgb; // + splat_control.rgb*0.1;
o.Alpha = splat_control.r;
o.Emission = t0.rgb * _Emission;
}
ENDCG
}
Fallback "Diffuse"
}
The below image shows how the current blending looks. Two planes with different materials are blended together. The grass material has a render queue set so that it is rendered after the dirt material. The dirt plane is completely opaque (but using the same shader as the grass material).
Notes:
Z sorting is not needed ( handled using render queues since I know the correct ordering at creation time )
Shadows are required, but they can completely ignore the alpha ( I don't think there is a way for them to respect it anyway, but it doesn't matter ).
ZWrite is required
A cutout shader is not sufficient because they have hard edges ( I need smooth blending )
I am not totally sure on when unity decides that a shader cannot run in deferred mode, but it would be nice if the shader could run in deferred mode.
Thanks in advance!
Aron Granberg
Answer by hausmaus · Feb 03, 2014 at 05:19 PM
Hi Aron,
First off, thanks for your great work on various plugins :)
As for the shader you're asking about, if you haven't found a solution yet, I wanted to post some information:
Deferred rendering doesn't support full alpha blending, as per http://docs.unity3d.com/Documentation/Components/RenderTech-DeferredLighting.html. You can verify this by adding "#pragma debug" to your shader and then clicking "Open Compiled Shader" in the Inspector panel with the shader selected, and you will see that the generated code only includes Forward lighting paths.
You may be able to simulate blending with a grab pass in the shader and doing blending that way, similar to the way refracted water is done.
Alternatively, since what you're describing is basically a terrain shader with a large number possible maps which can be in arbitrary order, you may be able to simply keep a pool of say 256 4-layer terrain materials on hand and configure them as needed depending on the tiles visible on the screen at that time. If the maps just tile in worldspace, this is a very easy solution, although it can run into problems with zooming out if you have a lot of variations or are otherwise unable to merge tiles with similar materials.
I have had success with the second method for a very large tile-based terrain system, but it didn't need a huge draw distance or any kind of crazy zoom out support.
Hope this helps, Adrian
Accepted because this is actually the solution I have ended up using. It does have its problems as you say, but it works pretty good.
PS: Glad to hear my plugins are appreciated!
Answer by TowerOfBricks · Apr 15, 2014 at 04:56 PM
I actually found a solution for this. Looking through the unity documentation, I find the little mentioned decal:blend option to be used with surface shaders. Applying that will give me exactly the result I want. The shader will use the shadow information from whatever object is behind it. The only drawback is that it does not write to the z-buffer, even where it is fully opaque, but that isn't really a large problem.
#pragma surface surf Lambert vertex:vert decal:blend