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.
Related
I'm trying to archieve a chilindrical effect like this on Unity3D:
But every solution is using material based shader, sadly I must have a Post Process effect or an Image Effect, for these reasons:
One map out of 30 needs to use this effect and there are many materials that are shared between them...
Every solution is vertex based. I've done some experiments but I have models with different polygon count, this means that the effect would create visual artifacts (but this can by fixed by editing the 3d models eventually).
I'm at an advanced stage of development.
Do you think it's possible to create a simple effect (even a fake one) that moves the pixels downwards/upwards based on the distance to the camera? (I assume I need to use the depth map)
I've tried very hard but I had no success, the effect doesn't do anything or just won't compile :(
This is the best I could come up with, the grayscale in the frag method is only to check if the shader is working, but once I define the Vert function the grayscale disappears and the shader does nothing.
Shader "Hidden/Custom/WorldCurvature"
{
HLSLINCLUDE
#include "Packages/com.unity.postprocessing/PostProcessing/Shaders/StdLib.hlsl"
TEXTURE2D_SAMPLER2D(_MainTex, sampler_MainTex);
float _Bend;
struct Attributes
{
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
};
struct Varyings
{
float4 vertex : SV_POSITION;
float2 texcoord : TEXCOORD0;
};
float4 Frag(Varyings i) : SV_Target
{
float4 color = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord);
float luminance = dot(color.rgb, float3(0.2126729, 0.7151522, 0.0721750));
color.rgb = lerp(color.rgb, luminance.xxx, _Bend.xxx);
return color;
}
Varyings Vert(Attributes v)
{
Varyings o;
float4 vv = mul(unity_ObjectToWorld, v.vertex );
vv.xyz -= _WorldSpaceCameraPos.xyz;
vv = float4( 0.0f, (vv.x * vv.x) * - _Bend, 0.0f, 0.0f );
v.vertex += mul(unity_WorldToCamera, vv);
o.vertex = mul(unity_WorldToCamera, vv);
o.texcoord = v.texcoord;
return o;
}
ENDHLSL
SubShader
{
Cull Off ZWrite Off ZTest Always
Pass
{
HLSLPROGRAM
#pragma vertex Vert
#pragma fragment Frag
ENDHLSL
}
}
}
I've done another experiment but I think it would only work in a 2D environment, here the image stops once I activate the image effect:
Shader "Hidden/Custom/CylinderImageEffect" {
Properties {
_MainTex ("Texture", 2D) = "white" {}
}
SubShader {
Cull Off ZWrite Off ZTest Always
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f {
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert( appdata_img v )
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord.xy;
return o;
}
sampler2D _MainTex;
fixed4 frag (v2f i) : SV_Target {
i.uv.x = 1 - acos(i.uv.x * 2 - 1) / 3.14159265;
return tex2D(_MainTex, i.uv);
}
ENDCG
}
}
}
Im pretty new to scripting shaders and i was trying to make a unlit shader work so it generates an outline around the object and lets me interpolate between 2 diferent textures, but I get 3 errors:
·Shader error in 'Unlit/Both': Duplicate system value semantic definition: output semantic 'POSITION' and output semantic 'SV_POSITION' at line 38
·Shader error in 'Unlit/Both': Duplicate system value semantic definition: input semantic 'SV_POSITION' and input semantic 'POSITION' at line 39
·Shader error in 'Unlit/Both': Duplicate system value semantic definition: output semantic 'SV_POSITION' and output semantic 'POSITION' at line 39
I guess there is something wrong with the position values but i have no idea about what to do so if anyone knows how to fix it i would be so thankful
{
Properties
{
_Color("Main Color", Color) = (0.5,0.5,0.5,1)
_MainTex ("Texture", 2D) = "white" {}
_SecondaryTex ("2nd Texture", 2D) = "white" {}
_LerpValue("Transition float", Range(0,1))= 0.5
_OutlineColor("Outline color", color) = (0,0,0,1)
_OutlineWidth("Outline width", Range(1.0,5.0)) = 1.01
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
float4 pos : POSITION;
float3 normal : NORMAL;
};
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _SecondaryTex;
float4 _SecondaryTex_ST;
float4 _LerpValue;
float _OutlineWidth;
float4 _OutlineColor;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
v.vertex.xyz *= _OutlineWidth;
o.pos = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed4 col = lerp(tex2D(_MainTex, i.uv), tex2D(_SecondaryTex, i.uv),_LerpValue);
return col;
}
ENDCG
}
}
SubShader
{
Tags{"Queue" = "Transparent"}
Pass//Render the Outline
{
ZWrite Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
half4 frag(v2f i) : COLOR
{
return _OutlineColor;
}
ENDCG
}
Pass//Normal render
{
ZWrite On
Material
{
Diffuse[_Color]
Ambient[_Color]
}
Lighting On
SetTexture[_MainTex]
{
ConstantColor[_Color]
}
SetTexture[_MainTex]
{
Combine previous * primary DOUBLE
}
}
}
}
When you want to pass data from vertex shader to pixel shader you need to use TEXCOORD as semantic, in this case, you should change the 'POSITION' semantic with 'TEXCOORD2'(in v2f struct).
Semantics will specify what data you are passing(actually how to
interpret data). The first stage in the pipeline is IA(Input
Assembler) when you pass data from IA to vertex shader you use
semantic to know which property you are using, for example, in your 'appdata' struct, vertex is POSITION so when the data(like geometry,3d objects) is passed to the IA stage you need to know which data you are using in the GPU so by using semantic you can easily find the properties of each object(each object must have position and it's better to have normal, tangent, color,....)
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
I had the following problem. I need to hide part of the game object while viewing through another invisible game object. In theory, this should work as follows (as shown in the pictures below).
part of the AR-hat hides behind the head
but when viewed through the AR-hat, this part should not be transparent
But in practice, when using the following stencil shader for AR-head:
Shader "Custom/Stencil/Mask OneZLess"
{
SubShader
{
Tags { "RenderType"="Opaque" "Queue"="Geometry-1" }
ColorMask 0
ZWrite off
Stencil
{
Ref 1
Comp always
Pass replace
}
Pass
{
Cull Back
ZTest Less
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct appdata
{
float4 vertex : POSITION;
};
struct v2f
{
float4 pos : SV_POSITION;
};
v2f vert(appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
return o;
}
half4 frag(v2f i) : COLOR
{
return half4(1,1,0,1);
}
ENDCG
}
}
}
And the next shader for the AR-hat:
Shader "Custom/Stencil/Diffuse NotEqualOne"
{
Properties
{
_Color ("Main Color", Color) = (1,1,1,1)
_MainTex ("Base (RGB)", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" "Queue"="Geometry" }
LOD 200
Stencil
{
Ref 1
Comp notequal
Pass keep
}
CGPROGRAM
#pragma surface surf Lambert
sampler2D _MainTex;
fixed4 _Color;
struct Input
{
float2 uv_MainTex;
};
void surf (Input IN, inout SurfaceOutput o)
{
fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
}
Fallback "VertexLit"
}
The scene is as follows (as in the picture):
The hat is always transparent when it crosses with the head
Maybe someone will help me (who has experience in working with shaders), how to make the AR-hat become transparent only when viewed through the AR-head. And if we look at the AR-head through the AR-hat, then nothing should happen.
I apologize for the confusing statement of the question, thank you in advance for your help.
http://wiki.unity3d.com/index.php/DepthMask
I think this is the way you mentioned finding. Putting it here for anyone searching in future like me.
Just make sure not to use the Geometry values which yields 2000 and use the provided values in the article(3000).
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.