Unity3D Sprite ... but single sided? - unity3d

Unity's excellent Sprites (Unity's excellent new sprites), among other worthy advantages, are in fact double-sided.
In a 2D or 3D use case, you can flip the little bastards around and still see them from behind - they are rendered from both sides.
I also love the unlit shader used on them (ie, Sprite-Default, not Sprite-Diffuse).
However, I have need for an old-fashioned single-sided Sprite.
Fortunately, you can freely download the source of the excellent shader used by Unity ... confusingly, you can't just open it in the Editor, but see here and thence here.
After considerable sustained effort involving inexpensive whisky, I made two (2) modifications to the shader.
I changed the name in the first line of the file to "DefaultSingle" rather than "Default"
I commented away the line Cull Off
(I then made a new Material, called it "Sprite Single Sided", set the shader to this new one, and on Sprites I replace the material slot with that new material.)
in fact, have I screwed-up anything in the shader by making this modification? Can one gaily comment out "Cull off" without causing further problems? Is there some gotchya in changing that usual material used by Sprite (you can't "see" it so I don't know). More broadly, is there a more correct way to achieve single-sided Sprite?
// file "Sprites-DefaultSingle.shader"
Shader "Sprites/DefaultSingle"
{
Properties
{
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
_Color ("Tint", Color) = (1,1,1,1)
[MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
}
SubShader
{
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
"PreviewType"="Plane"
"CanUseSpriteAtlas"="True"
}
/// Cull Off
Lighting Off
ZWrite Off
Blend One OneMinusSrcAlpha
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _ PIXELSNAP_ON
#pragma shader_feature ETC1_EXTERNAL_ALPHA
#include "UnityCG.cginc"
struct appdata_t
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 texcoord : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
};
fixed4 _Color;
v2f vert(appdata_t IN)
{
v2f OUT;
OUT.vertex = mul(UNITY_MATRIX_MVP, IN.vertex);
OUT.texcoord = IN.texcoord;
OUT.color = IN.color * _Color;
#ifdef PIXELSNAP_ON
OUT.vertex = UnityPixelSnap (OUT.vertex);
#endif
return OUT;
}
sampler2D _MainTex;
sampler2D _AlphaTex;
fixed4 SampleSpriteTexture (float2 uv)
{
fixed4 color = tex2D (_MainTex, uv);
#if ETC1_EXTERNAL_ALPHA
// get the color from an external texture ..
color.a = tex2D (_AlphaTex, uv).r;
#endif //ETC1_EXTERNAL_ALPHA
return color;
}
fixed4 frag(v2f IN) : SV_Target
{
fixed4 c = SampleSpriteTexture (IN.texcoord) * IN.color;
c.rgb *= c.a;
return c;
}
ENDCG
}
}
}

That won't cause any problems and is the correct way of doing things. If you want to be really explicit about it you could change the line to Cull Back instead of commenting it out, but back-face culling is defined as the default behaviour so just removing it is fine.

As far as I'm aware, neither of these changes have any effects other than the that which you intended. Also, this is the most correct way to achieve this- it's the only thing Cull is for.

Related

How to use Unity3d shader to mask another two-pass shader?

I am developing a game where I want to visualize user path - showing also where track overlaps (with some darker color than normal "not overlapped" track). Long time ago I have found this answer where it was advised to use stencil to create two-pass shader. That day it was very helpful.
But lately I have met an additional requirement: I want to expose the path (part of mesh) which is inside some irregular area. For the rest of the path there are two options based on user choice in the runtime:
should be marked with any other different color (e.g. darker),
should be not visible at all.
I have found that answer as a suggestion to use stencil in another shader which will be put to some mesh to create a "mask". I made some adjustments and I ended up with those shaders:
Mask shader:
Shader "Custom/AreaValidator"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
}
SubShader
{
Tags { "RenderType"="Opaque" "Queue"="Geometry-1" }
ColorMask 0
ZWrite off
Stencil{
Ref 3
Comp always
Pass replace
}
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
struct Input
{
float2 uv_MainTex;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutputStandard o)
{
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
And path shader:
Shader "Unlit/Path"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
Stencil {
Ref 0
Comp Equal
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
};
struct v2f
{
float4 vertex : SV_POSITION;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = fixed4(0.545, 0.780, 0.341, 0.8);
return col;
}
ENDCG
}
Pass
{
Stencil {
Ref 3
Comp Equal
Pass IncrSat
Fail IncrSat
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
};
struct v2f
{
float4 vertex : SV_POSITION;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = fixed4(0.710, 0.859, 0.580, 1);
return col;
}
ENDCG
}
Pass
{
Stencil {
Ref 4
Comp Less
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
};
struct v2f
{
float4 vertex : SV_POSITION;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = fixed4(0.545, 0.780, 0.341, 0.8);
return col;
}
ENDCG
}
}
}
As you can see in mask shader I have:
Stencil{
Ref 3
Comp always
Pass replace
}
but in path shader I have 3 passes: for "outside validated area", "inside validated area - without overlaps", "inside validated area - overlapping".
1.
Stencil {
Ref 0
Comp Equal
}
2.
Stencil {
Ref 3
Comp Equal
Pass IncrSat
Fail IncrSat
}
3.
Stencil {
Ref 4
Comp Less
}
I do not fully understand how shaders are rendered - I am showing the solution which was the closest to my goal. In general it "works" in some simple cases:
here - all tracks visible (outside valid area color is darker like
during overlapping),
here - when I remove from shader code full section responsible for first pass the track outside area is removed - that is good,
but there are 3 major problems which I cannot solve.
First problem - "glitching/flickering" when 2 different meshes are overlapping:
Another problem- because stencil 2) is incrementing buffer in both pass/fail situations when user goes around few times then even outside "valid" area track will be rendered.
Third problem is that I do not know how switch between "visible outside track" and "not visible outside track" rendering options during runtime.
Summing up:
I would like to have a mesh:
which is rendered differently when it overlaps,
can be render only inside certain "masking" area,
user is able to show/hide mesh outside masking area dynamically, visible mesh outside has other color,
but I do not know how to achieve that without glitches and some weird artifacts.

Alpha channel on my unity shader is not working

So I am very new to shader programing (basically just started today) and I got this code from a tutorial on Youtube which works great. It just found the pixel on the edge of a texture, and if so, replace it with a plain color. I want to be able to set the transparency of the color i'm returning.
But it does not seems to be working
Shader "Custom/OutlineShader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Color("Color", Color) = (1, 1, 1, 1)
_AlphaOffset("Transparency", Range(0,1)) = 1
}
SubShader
{
Tags{ "Queue"="Transparent" "RenderType"="Opaque"}
Cull Off ZWrite Off ZTest Always
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag Lambert alpha
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTexelSize;
float _AlphaOffset;
v2f vert (appdata v)
{
v2f OUT;
OUT.vertex = UnityObjectToClipPos(v.vertex);
OUT.uv = v.uv;
return OUT;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
col.rgb *= col.a;
fixed4 outlineColor = _Color;
// This is where I want the shader to be transparent or not based on the _AlphaOffset Value
outlineColor.a *= ceil(col.a) * _AlphaOffset;
// This a code just to check is the current pixel is on the edge of the texture
fixed upAlpha = tex2D(_MainTex, i.uv + fixed2(0, _MainTexelSize.y)).a;
fixed downAlpha = tex2D(_MainTex, i.uv - fixed2(0, _MainTexelSize.y)).a;
fixed leftAlpha = tex2D(_MainTex, i.uv - fixed2(_MainTexelSize.x, 0)).a;
fixed rightAlpha = tex2D(_MainTex, i.uv + fixed2(_MainTexelSize.x, 0)).a;
// If it's on the edge, return the color (+ alpha) else, just return the same pixel
return lerp(outlineColor, col, ceil(upAlpha * downAlpha * leftAlpha * rightAlpha));
}
ENDCG
}
}
}
I would like this line
outlineColor.a *= ceil(col.a) * _AlphaOffset; to set the alpha of the pixel I'm returning.
Thanks !
There are primarily two things which are wrong here - firstly, you have set your RenderType to "Opaque", which expectedly makes it render as non-transparent. This should instead be set to "Transparent". Second, you need to specify a blend mode to determine how the color from this object blends with what's already rendered to the buffer. From the Unity manual on blending:
Blend SrcFactor DstFactor: Configure and enable blending. The
generated color is multiplied by the SrcFactor. The color already on
screen is multiplied by DstFactor and the two are added together.
For regular alpha blending, add this statement inside your subshader:
Blend SrcAlpha OneMinusSrcAlpha
For additive blending, which results in a glow-like effect, use this:
Blend One One

Unity transparent shadow receiving plane for AR app to receive only shadows and not light reflections

I'm working on a transparent shadow receiving shader for Augmented Reality app in Unity, that is put into a plane which will receive shadows from point light or spotlight. It works fine, but the plane receives light also as you can see in the image.
Output Screenshots
I'm using unity 5.6.3p4. Please tell me what could be the problem in this script or what should I change in this code to receive only shadows and not light reflections on the plane?
The Shader Script is below:
Shader "SP/InvisibleShadowCasterForPLight" {
Properties {
_MainTex("Base (RGB)", 2D) = "white" {}
}
SubShader {
Pass {
Blend One One
Tags { "LightMode" = "ForwardAdd" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#pragma multi_compile_fwdadd_fullshadows
#include "AutoLight.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
struct v2f {
float4 pos : SV_POSITION;
LIGHTING_COORDS(0,1)
float2 uv : TEXCOORD2;
};
v2f vert(appdata_base v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX (v.texcoord, _MainTex);
TRANSFER_VERTEX_TO_FRAGMENT(o);
return o;
}
fixed4 frag(v2f i) : COLOR
{
float attenuation = LIGHT_ATTENUATION(i);
return tex2D (_MainTex, i.uv) * attenuation;
}
ENDCG
}
}
Fallback "VertexLit"
}
Replace:
LIGHTING_COORDS(0,1)
with:
UNITY_SHADOW_COORDS(0)
Then replace:
TRANSFER_VERTEX_TO_FRAGMENT(0)
with:
TRANSFER_SHADOW(o);
Finally, in your fragment function, use this:
half shadow = SHADOW_ATTENUATION(i);
return tex2D (_MainTex, i.uv) * shadow;
That should do it!
Oh, and you may or may not also want to change blendmode to multiplicative... If the above doesn't work, try changing:
Blend One One
with:
Blend DstColor Zero

Why does my shaders stop rendering at a certain angle?

I'm trying to make a nice looking forest to a new game im working on.
But theres a problem with the rendering of my shaders. I have it currently setup to render close by trees with an 2d-sprite renderer. And the other trees that arent close enough will get render using the shader instead. But the problem is with the trees that are using the shader. Cause when i look at them and move my camera up they just stop rendering.. why?
Here's a gif of what i mean.
https://gyazo.com/64acbf5cadd9a89b0ba2cd5f123605ce
And here's my shader script.
Shader "Custom/Tree_Billboard"
{
Properties
{
[PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
_Color("Tint", Color) = (1,1,1,1)
//_Time ("Time", Float) = 0
[MaterialToggle] PixelSnap("Pixel snap", Float) = 0
}
SubShader
{
Tags
{
"Queue" = "Transparent"
"DisableBatching" = "True"
"SortingLayer" = "Resources_Sprites"
"IgnoreProjector" = "True"
"RenderType" = "Transparent"
"PreviewType" = "Plane"
"CanUseSpriteAtlas" = "True"
}
Cull Off
Lighting Off
ZWrite Off
Blend One OneMinusSrcAlpha
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#pragma multi_compile _ PIXELSNAP_ON
#pragma multi_compile _ ETC1_EXTERNAL_ALPHA
#include "UnityCG.cginc"
// uniform Float _Time;
struct appdata_t
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
UNITY_VERTEX_OUTPUT_STEREO
};
fixed4 _Color;
v2f vert(appdata_t IN)
{
v2f OUT;
UNITY_SETUP_INSTANCE_ID(IN);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
// OUT.vertex = UnityObjectToClipPos(IN.vertex);
OUT.texcoord = IN.texcoord;
OUT.color = IN.color * _Color;
// #ifdef PIXELSNAP_ON
OUT.vertex = mul(UNITY_MATRIX_P,
mul(UNITY_MATRIX_MV, float4(0.0, 4.1, 0.0, 1.0))
- float4(IN.vertex.x, -IN.vertex.y, 0.0, 0.0)
* float4(6.0, 8.0, 1.0, 1.0));
// OUT.vertex = UnityPixelSnap (OUT.vertex);
// #endif
return OUT;
}
sampler2D _MainTex;
sampler2D _AlphaTex;
fixed4 SampleSpriteTexture(float2 uv)
{
fixed4 color = tex2D(_MainTex, uv);
#if ETC1_EXTERNAL_ALPHA
// get the color from an external texture (usecase: Alpha support for ETC1 on android)
color.a = tex2D(_AlphaTex, uv).r;
#endif //ETC1_EXTERNAL_ALPHA
return color;
}
fixed4 frag(v2f IN) : SV_Target
{
fixed4 c = SampleSpriteTexture(IN.texcoord) * IN.color;
c.rgb *= c.a;
return c;
}
ENDCG
}
}
}
Here's what "jvo3dc" said on the Unity Forum's
"The objects get frustum culled. The culling is done on the bounding box of the object.
You're adjusting the vertices in the shader so they fall outside the original bounding box. This means the culling process can sometimes assume they are invisible, while they actually are visible.
The solution is to match the bounding box with the actual maximum displacement in the vertex shader."
So i went and searched a bit on Google and found a solution. I created a new quad-gameObject. Added a new scripts and added this to the Start() method and that seems to have fixed it.
transform.GetChild(0).GetComponent<MeshFilter>().sharedMesh.bounds = new Bounds(new Vector3(0, 0, 0), new Vector3(1, 1,1) * 10.0f);
(Here's the Unity forum post)
This is clearly not a shader problem but a problem with your clip plane. Check your camera settings first.

Why is this Shader producing a parse error when given as string to the Material constructor?

I wrote the following Shader:
Shader "Custom/SimpleShader" {
Properties {
_MainTex("Base (RGB)", 2D) = "white" { }
}
SubShader {
Pass {
ZTest Always
Cull Off
ZWrite Off
/* LINE 16*/CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float2 uvb : TEXCOORD1;
};
uniform sampler2D _MainTex;
uniform sampler2D _Texture0;
uniform fixed _Alpha0;
v2f vert(appdata_img v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = MultiplyUV(UNITY_MATRIX_TEXTURE0, v.texcoord);
o.uvb = MultiplyUV(UNITY_MATRIX_TEXTURE0, v.texcoord);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
col.rgb += (tex2D(_Texture0, i.uvb).rgb * _Alpha0);
return col;
}
ENDCG
}
}
Fallback off
}
Unity tells me that there is a "Syntax Error" in line 16 (see mark). This happens, when I put this text into a string and give this string to the Material(string contents) constructor. However, when I put the text into a .shader file and import it in the normal way, the shader is accepted and does not produce an error.
This documentation concerning the Material(string contents) constructor (or rather its JavaScript counterpart) says that:
[...] Creating materials this way supports only simple shaders (fixed function ones). If you need a surface shader, or vertex/pixel shaders, you'll need to create shader asset in the editor and use that.
Thus, the answer is that fragment- and/or vertex-shaders are not supported this way.