How to use shader to control the rendering of each text character(letter)? Or, how to correctly pass custom data into vert/frag?
sorry if the description is difficult to undersand or not accurate. English is not my first language. I'm doing my best :)
I'm trying to implement a 3-color vertical gradient text shader.
I found a example shader, but it renders the texture's gradient colors as a whole. I want to apply the entire gradient range on each character/letter.
So I tried to pass the height and minimum Y-pos of each character into the shader via UIVertex.uv1
, using them to find out the actual vertical ratio of each pixel in a single character, which you can see in my code below.
But the result was always wrong: I can't correctly read the minimum Y-pos (and the character height as well, perhaps). The shader works fine when I force the minimum Y-pos (i.e. charMinY
) to 0
. I don't know where it went wrong, since I'm not sure how I can have a debug output for variables in vert()
. Is data changed when it passes from vert()
to frag()
? Or is it changed in vert()
in the beginning?
I'm a beginner of shader programming and don't know much about the theory yet. Plz tell me if there is anything I should know :)
HERE ARE THE CODES
C# part of setting custom data, including the size of a single character and its left-bottom corner in the texture, all in UV unit.
public class TextUVRemapper : BaseMeshEffect
{
public override void ModifyMesh(VertexHelper vh)
{
if(!IsActive())
{
return;
}
List<UIVertex> verts = new List<UIVertex>(vh.currentVertCount);
vh.GetUIVertexStream(verts);
var meshVertsCount = 6;
for (int i = 0; i < verts.Count; i += meshVertsCount)
{
var uvsize = verts[i + 2].uv0 - verts[i + 0].uv0;
var startUV = verts[i + 0].uv0;
Debug.Log("uv height: " + uvsize.y + "; Tex start Y: " + startUV.y);
for (int j = i; j < i + meshVertsCount; j++)
{
var v = verts[j];
v.uv1 = new Vector4(uvsize.x, uvsize.y, startUV.x, 0.5f);
verts[j] = v;
}
}
vh.Clear();
vh.AddUIVertexTriangleStream(verts);
}
}
Below is the shader part. I commented some so hopefully you can know what I was thinking :)
Shader "GUI/Text Vertical 3-Color Shader" { Properties { _MainTex ("Font Texture", 2D) = "white" {} _Color ("Text Color", Color) = (1,1,1,1) _ColorTop ("Top Color", Color) = (1,1,1,1) _ColorBot ("Bottom Color", Color) = (1,1,1,1) _Middle("Middle", Range(0.001, 0.999)) = 0.5 }
SubShader {
...
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata_t {
float4 vertex : POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
float4 params : TEXCOORD1; // here is the custom data
};
struct v2f {
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
// x and y: as usual.
// z: character's height.
// w: character's minimum y-pos
float4 texcoord : TEXCOORD0;
};
sampler2D _MainTex;
uniform float4 _MainTex_ST;
uniform fixed4 _Color;
uniform fixed4 _ColorTop;
uniform fixed4 _ColorBot;
float _Middle;
v2f vert (appdata_t v)
{
v2f o;
// charSize is the size of a single rendered text letter
// which is passed in via UIVertex.uv1.x & UIVertex.uv1.y
float2 charSize;
if(v.params.x == 0 && v.params.y == 0) {
charSize = float2(.999,.999);
} else {
charSize = float2(v.params.x, v.params.y);
}
//charSize = TRANSFORM_TEX(charSize, _MainTex); // <--- i'm not sure if i should do this.
if(v.params.w == 1) v.params.w = 0; // <--- this line is for testing. seems all v.params.w passed in are 1..
// charMinPos is the uv of the letter's bottom-left corner.
float2 charMinPos = float2(v.params.z, v.params.w);
//charMinPos = TRANSFORM_TEX(charMinPos, _MainTex); // <--- i'm not sure if i should do this.
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
o.color = v.color * _Color;
float2 uv;
uv = TRANSFORM_TEX(v.texcoord,_MainTex);
o.texcoord = float4(uv.x, uv.y, charSize.y, charMinPos.y); // z & w are for letter's demensional info
return o;
}
fixed4 frag (v2f i) : SV_Target
{
float fragY = i.texcoord.y;
// THESE 2 VALUES ARE SUPPOSED TO BE PASSED BY vert
float charHeight = i.texcoord.z;
float charMinY = i.texcoord.w;
half midY = charMinY + _Middle * charHeight; // the middle-color's Y pos of the letter
half t = fragY/midY;
float a = t;
float b = charHeight;
float c = charMinY;
// return float4(a,b,c,1); // <-- test value output
// below is the calculation of the gradient color, depending on frag's Y pos.
fixed4 col = lerp(_ColorTop, i.color,
fragY / midY
) * step(fragY, midY);
col += lerp(i.color, _ColorBot,
(fragY - midY) / ((1 - _Middle) * charHeight)
) * step(midY, fragY);
col.a *= tex2D(_MainTex, i.texcoord).a;
//col.a = 1; // i used this to see the render result
return col;
}
ENDCG
}
}
}
I've stuck on this for two days! I could really use some help! Thank you a ton in advance!