Discard Pixels from Fragment Shader Based on Screen Pos
I'm trying to write a shader for a UI element with a canvas in world space that discards any pixels to the left of a pair of game objects, so that it looks like the text is revealed by the objects moving from right to left. The objects are also at a (variable) angle, so I've used a linear equation in the shader to determine when it is to the left or right.
I'm having trouble with the conversion to screen points in the shader. I read around a little bit and figured out that ComputeScreenPos() gives a range from 0 to 1 when normalized by .w, and then I tried to multiply this by _ScreenParams to get the pixel position that would be equivalent to Unity's Camera.WorldToScreenPoint(), but I'm still not able to get it working. Here is the code:
Shader "UI/TextShader"
{
Properties
{
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
_Color ("Tint", Color) = (1,1,1,1)
_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
_Point1 ("_Point1", Vector) = (0,0,0,0)
_Point2 ("_Point2", Vector) = (0,0,0,0)
[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
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#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 screenPos : TEXCOORD1;
float3 worldPos : TEXCOORD2;
};
fixed4 _Color;
fixed4 _TextureSampleAdd;
float4 _ClipRect;
uniform float4 _Point1;
uniform float4 _Point2;
v2f vert(appdata_t IN)
{
v2f OUT;
OUT.vertex = mul(UNITY_MATRIX_MVP, IN.vertex);
OUT.worldPos = mul(_Object2World, IN.vertex).xyz;
OUT.screenPos = ComputeScreenPos(OUT.vertex);
OUT.texcoord = IN.texcoord;
#ifdef UNITY_HALF_TEXEL_OFFSET
OUT.vertex.xy += (_ScreenParams.zw-1.0)*float2(-1,1);
#endif
OUT.color = IN.color * _Color;
return OUT;
}
sampler2D _MainTex;
fixed4 frag(v2f IN) : SV_Target
{
IN.screenPos /= IN.screenPos.w;
//Equation of the line
float x1 = _Point1.x;
float x2 = _Point2.x;
float y1 = _Point1.y;
float y2 = _Point2.y;
float m = (y2 - y1)/(x2 - x1);
float b = y1 - (m * x1);
float lineXPosition = (IN.screenPos.y * _ScreenParams.y - b)/m;
half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
color.a *= UnityGet2DClipping(IN.worldPos.xy, _ClipRect);
if(_ScreenParams.x * IN.screenPos.x > lineXPosition){
discard;
}
#ifdef UNITY_UI_ALPHACLIP
clip (color.a - 0.001);
#endif
return color;
}
ENDCG
}
}
}
And here is the code for the script that passes the points in to the shader:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class SetTextline : MonoBehaviour {
Transform pointOneTrans;
Transform pointTwoTrans;
Material textMaterial;
// Use this for initialization
void Awake () {
pointOneTrans = GameObject.Find ("Point").GetComponent<Transform> ();
pointTwoTrans = GameObject.Find ("Head").GetComponent<Transform> ();
textMaterial = GameObject.Find ("Poem").GetComponent<Text> ().material;
}
// Update is called once per frame
void Update () {
Vector2 point1 = Camera.main.WorldToScreenPoint (pointOneTrans.position);
Vector2 point2 = Camera.main.WorldToScreenPoint (pointTwoTrans.position);
textMaterial.SetVector ("_Point1", point1);
textMaterial.SetVector ("_Point2", point2);
Debug.Log (point1);
Debug.Log (point2);
}
}
Thanks in advance for any help!
Answer by alxdncn · Sep 13, 2016 at 05:53 PM
This is actually working now, it seems. I don't know what I had setup incorrectly in my scene, but it's all fine now.
Your answer
Follow this Question
Related Questions
How do you port old shaders to hlsl so they work with the new post-processing stack? 1 Answer
How to change/fix Shader Graph node connection ports 3 Answers
Material.SetFloat not working 0 Answers
Where is the best place to learn how to write shaders for Unity 5? 0 Answers
UI Missing and Shaders look broken! 0 Answers