- Home /
Using Discard In A Shader Based On A Position Set From Script
Hi,
I have a screen space canvas with 2 images (grey background and small red square). I am trying to create a shader that will discard any part of the background that is beneath the red square. I have tried several combinations of comparing different values in the shader, but the best I have been able to achieve is the first thing I tried, where I sent in the red square's world position(transform.position.y) and would discard the background if the value from mul(unity_ObjectToWorld, IN.vertex).xyz was less than it. The result is that it works perfectly in the scene view, but not in the game view, which I don't understand.
I've tried comparing so many different things that it's all kind of a jumble in my head and I assume that I am simply not understanding how the different spaces work and I am comparing the wrong values.
Any guidance would be much appreciated.
This is how it looks now, where it works just fine in scene view, but it is offset in game view:
I've left some unused values in the vert function so you can see some things I've tried. Here's the shader(default-ui from Unity that I have added on to):
Shader "UI/DiscardBeneath"
{
Properties
{
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
_Color ("Tint", Color) = (1,1,1,1)
_TargetYPos("TargetYPos", float) = 0
_StencilComp ("Stencil Comparison", Float) = 8
_Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0
_StencilWriteMask ("Stencil Write Mask", Float) = 255
_StencilReadMask ("Stencil Read Mask", Float) = 255
_ColorMask ("Color Mask", Float) = 15
[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
}
SubShader
{
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
"PreviewType"="Plane"
"CanUseSpriteAtlas"="True"
}
Stencil
{
Ref [_Stencil]
Comp [_StencilComp]
Pass [_StencilOp]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}
Cull Off
Lighting Off
ZWrite Off
ZTest [unity_GUIZTestMode]
Blend SrcAlpha OneMinusSrcAlpha
ColorMask [_ColorMask]
Pass
{
Name "Default"
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#include "UnityCG.cginc"
#include "UnityUI.cginc"
#pragma multi_compile __ UNITY_UI_ALPHACLIP
struct appdata_t
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 texcoord : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
half2 texcoord : TEXCOORD0;
float4 worldPosition : TEXCOORD1;
float3 realWorldPosition : TEXCOORD2;
float4 screenPos : TEXCOORD3;
float2 screenUV : TEXCOORD4;
};
fixed4 _Color;
float _TargetYPos;
fixed4 _TextureSampleAdd;
float4 _ClipRect;
v2f vert(appdata_t IN)
{
v2f OUT;
OUT.worldPosition = IN.vertex;
OUT.realWorldPosition = mul(unity_ObjectToWorld, IN.vertex).xyz;
OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);
OUT.screenPos = ComputeScreenPos(OUT.vertex);
OUT.screenUV = OUT.screenPos.xy / OUT.screenPos.w;
OUT.texcoord = IN.texcoord;
#ifdef UNITY_HALF_TEXEL_OFFSET
OUT.vertex.xy += (_ScreenParams.zw-1.0) * float2(-1,1) * OUT.vertex.w;
#endif
OUT.color = IN.color * _Color;
return OUT;
}
sampler2D _MainTex;
fixed4 frag(v2f IN) : SV_Target
{
half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
#ifdef UNITY_UI_ALPHACLIP
clip (color.a - 0.001);
#endif
if(IN.realWorldPosition.y < _TargetYPos)
{
discard;
}
return color;
}
ENDCG
}
}
}
Here is the C# script, which is on the background image, for passing the y pos to the shader:
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
[ExecuteInEditMode]
public class DiscardControl : MonoBehaviour
{
[SerializeField]
private GameObject _target = null;
[SerializeField]
private Canvas _canvas = null;
private Material _material = null;
private int _YPositionPropertyID;
void Start()
{
_YPositionPropertyID = Shader.PropertyToID("_TargetYPos");
_material = GetComponent<Image>().material;
}
void Update()
{
_material.SetFloat(_YPositionPropertyID, _target.transform.position.y);
//Vector3 worldToScreen = Camera.main.WorldToScreenPoint(_target.transform.position);
//_material.SetFloat(_YPositionPropertyID, worldToScreen.y);
//Vector2 screenSpacePos = RectTransformUtility.PixelAdjustPoint(_target.GetComponent<RectTransform>().anchoredPosition, _target.transform, _canvas);
//_material.SetFloat(_YPositionPropertyID, (screenSpacePos.y));
//_material.SetFloat(_YPositionPropertyID, _target.GetComponent<RectTransform>().anchoredPosition.y);
//Debug.Log(worldToScreen);
}
}
Your answer
![](https://koobas.hobune.stream/wayback/20220612160135im_/https://answers.unity.com/themes/thub/images/avi.jpg)
Follow this Question
Related Questions
How to convert Screen Units to World Units 4 Answers
World and Screen space Multiplayer[Touch Screens] 1 Answer
Recreating basic function in C# 1 Answer
World Space material Stretching when object is rotated. 0 Answers
Worldspace Conversion 1 Answer