I am trying to get an effect and I am not sure what technique I should be going with. I have tried depth mask and stencil effect and can't seem to fine tune it to get the desired effect. I have a single mesh that I would like to move around on a table and would like the mesh to only show up on the table. I am using the stencil effect below. However it seems to be extending past the mask shader. I found the following shader scripts on a previous forum that seemed to be the closest to the effect that I would like to achieve. If you have any suggestions that would be great. Code (CSharp): Shader "Custom/Mask" { SubShader{ Tags{ "RenderType" = "Transparent" } Stencil{ Ref 1 Comp always Pass replace } CGPROGRAM #pragma surface surf Lambert alpha struct Input { fixed3 Albedo; }; void surf(Input IN, inout SurfaceOutput o) { o.Albedo = fixed3(1, 1, 1); o.Alpha = 0; } ENDCG } FallBack "Diffuse" } Code (CSharp): Shader "Custom/StencilEffect" { Properties{ _MainTex("Base (RGB)", 2D) = "white" {} } SubShader{ Tags{ "RenderType" = "Opaque" } Stencil{ Ref 1 Comp equal Pass Keep } CGPROGRAM #pragma surface surf Lambert sampler2D _MainTex; struct Input { float2 uv_MainTex; }; void surf(Input IN, inout SurfaceOutput o) { half4 c = tex2D(_MainTex, IN.uv_MainTex); o.Albedo = c.rgb; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" }
-UPDATE- Changed the technique. I am still learning on how to achieve this look. I found this article upon searching for an answer. https://alastaira.wordpress.com/2013/09/07/super-mario-galaxy-reveal-hidden-platforms-cg-shader/ It has helped me achieve this look. However I am struggling to retain the shader properties of the previous look. Does anyone have any suggestions? Shader: Code (CSharp): // Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld' // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' Shader "Custom/Proximity" { Properties{ _MainTex("Base (RGB)", 2D) = "white" {} // Regular object texture _PlayerPosition("Player Position", vector) = (0,0,0,0) // The location of the player - will be set by script _VisibleDistance("Visibility Distance", float) = 10.0 // How close does the player have to be to make object visible _OutlineWidth("Outline Width", float) = 3.0 // Used to add an outline around visible area a la Mario Galaxy _OutlineColour("Outline Colour", color) = (1.0,1.0,0.0,1.0) // Colour of the outline _Glossiness("Smoothness", Range(0,1)) = 0.5 _Metallic("Metallic", Range(0,1)) = 0.0 } SubShader{ Tags{ "RenderType" = "Transparent" "Queue" = "Transparent" } Pass{ Blend SrcAlpha OneMinusSrcAlpha CGPROGRAM #pragma vertex vert #pragma fragment frag // Access the shaderlab properties //uniform sampler2D _MainTex; sampler2D _MainTex; uniform float4 _PlayerPosition; uniform float _VisibleDistance; uniform float _OutlineWidth; uniform fixed4 _OutlineColour; struct Input { float2 uv_MainTex; }; half _Glossiness; half _Metallic; fixed4 _Color; // Input to vertex shader struct vertexInput { float4 vertex : POSITION; float4 texcoord : TEXCOORD0; }; // Input to fragment shader struct vertexOutput { float4 pos : SV_POSITION; float4 position_in_world_space : TEXCOORD0; float4 tex : TEXCOORD1; }; // 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) // VERTEX SHADER vertexOutput vert(vertexInput input) { vertexOutput output; output.pos = UnityObjectToClipPos(input.vertex); output.position_in_world_space = mul(unity_ObjectToWorld, input.vertex); output.tex = input.texcoord; return output; } // FRAGMENT SHADER float4 frag(vertexOutput input) : COLOR { // Calculate distance to player position float dist = distance(input.position_in_world_space, _PlayerPosition); // Return appropriate colour if (dist < _VisibleDistance) { return tex2D(_MainTex, float4(input.tex)); // Visible } else if (dist < _VisibleDistance + _OutlineWidth) { return _OutlineColour; // Edge of visible range } else { discard; float4 tex = tex2D(_MainTex, float4(input.tex)); // Outside visible range tex.a = 0; return tex; } } ENDCG } // End Pass } // End Subshader FallBack "Diffuse" } // End Shader Code (CSharp): using System.Collections; using System.Collections.Generic; using UnityEngine; public class ProximityXray : MonoBehaviour { public Transform player; Renderer render; void Start() { render = gameObject.GetComponent<Renderer>(); } void Update() { render.sharedMaterial.SetVector("_PlayerPosition", player.position); } }
The thing to understand about stencils is they're a screen space thing. The stencil's mask are the pixels that object appears on screen, not in 3d space. Hence the object showing up "behind" where you expected in your first attempt. The second example is testing the world space position of each pixel as it's being rendered to determine its distance from a point, and hiding those pixels when they're too far away. It's also using a very simple vertex fragment shader to do this, with no lighting or shading. The first example is using a surface shader, which is a vertex fragment shader generator. You get to write a small portion of the actual shader within some restrictions, and Unity puts together the rest of the shader to add lighting based on the parameters you set. Everything that the second example is doing can be easily done in a surface shader. It even already has a built in worldPos Input variable you can use to get the per pixel world position. I would recommend reading through this tutorial if you haven't already: https://www.alanzucconi.com/2015/06/10/a-gentle-introduction-to-shaders-in-unity3d/ Also, your first example shader is actually doing the distance calculation incorrectly. It's comparing the distance between two float4 values. Generally you're not going to be caring about four dimensional space distances. It should be a distance between only the xyz components of the world position and player position. In a surface shader, worldPos is already only ever a float3, but you should likely also define your _PlayerPosition as a float3 instead of a float4 in the shader, or use _PlayerPosition.xyz. You should also be using alpha testing instead of alpha blending for this kind of effect.
Perfect! Thanks so much! Yeah I need some help understanding the difference in shaders. I will study up on the link.
Thanks again for your help. I found some more help on another forum from you and just wanted to post my findings just in case someone else goes down this rabbit hole. I stumbled across this guys tutorials after reading the tutorial in the link you gave me. http://halisavakis.com/my-take-on-shaders-spherical-mask-dissolve/ Here is where I ended up with the shader. You suggested I add an addshadow to the #pragma surface line which fixed the clipped surfaces causing shadows. Code (CSharp): Shader "Custom/Slice" { 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 [HDR]_Emission("Emission", Color) = (1,1,1,1) _NoiseSize("Noise size", float) = 1 } SubShader { Tags{ "RenderType" = "Opaque" } Cull off LOD 200 CGPROGRAM // Physically based Standard lighting model, and enable shadows on all light types #pragma surface surf Standard fullforwardshadows addshadow // Use shader model 3.0 target, to get nicer looking lighting #pragma target 3.0 sampler2D _MainTex; struct Input { float2 uv_MainTex; float3 worldPos; }; half _Glossiness; half _Metallic; fixed4 _Color; fixed4 _Emission; float _NoiseSize; float4 _GLOBALMaskPosition; half _GLOBALMaskRadius; half _GLOBALMaskSoftness; // 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) float random(float2 input) { return frac(sin(dot(input, float2(12.9898,78.233)))* 43758.5453123); } void surf(Input IN, inout SurfaceOutputStandard o) { fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color; half dist = distance(_GLOBALMaskPosition, IN.worldPos); half sphere = 1 - saturate((dist - _GLOBALMaskRadius) / _GLOBALMaskSoftness); clip(sphere - 0.1); float squares = step(0.5, random(floor(IN.uv_MainTex * _NoiseSize))); half emissionRing = step(sphere - 0.1, 0.1) * squares; o.Emission = _Emission * emissionRing; o.Albedo = c.rgb; o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" //Fallback "Legacy Shaders/Transparent/Cutout/VertexLit" } Then the script that would be your geometry that would act as your mask would look like this. Code (CSharp): using System.Collections; using System.Collections.Generic; using UnityEngine; [ExecuteInEditMode] public class SphereMaskController : MonoBehaviour { public float radius = 0.5f; public float softness = 0.5f; void Update() { Shader.SetGlobalVector("_GLOBALMaskPosition", transform.position); Shader.SetGlobalFloat("_GLOBALMaskRadius", radius); Shader.SetGlobalFloat("_GLOBALMaskSoftness", softness); } }
This is making the same mistake I mentioned above. The mask position is a float4 and the world position is a float3. On some platforms this will produce unwanted results, and on most others it won't compile at all. Basically, this works on Windows when compiling for Direct3D and nothing else because the Direct3D shader compiler makes some assumptions and fixes the bad code for you, where as other platforms will just tell you that's wrong. Just define _GLOBALMaskPosition as a float3 to avoid this.
Right! float3 for XYZ location. Got it thank you! Would I want to use a float3 when using SV_POSITION?
I have a problem similar to yours, can you help me? The idea is the same, I have a mesh and it should appear on a table. The difference is that the mask shader is rectangular and not circular. I'm new to Unity. I tried to reproduce your tutorial but it did not work for me, I have some doubts. I have the rectangle that will be the shader and the mockup, I added your shader to the cube and then the script too. I do not know what I do after that, the cube is transparent but does not work (cuts) with the model. Could you help me make the rectangular shader and how to properly apply to the cube so that I have the effect like yours?