前言
在制作游戲時(shí),可以遇到要對(duì)字體添加描邊的需求,unity 的UGUI自帶的OutLine組件,描邊效果不好,寬度過(guò)大會(huì)出現(xiàn)穿幫,頂點(diǎn)數(shù)量也會(huì)增加,性能不好,如果對(duì)于有幾百字,頂點(diǎn)數(shù)量會(huì)很多,而且無(wú)法擴(kuò)展功能
可以看出Outline創(chuàng)建了4個(gè)方向的文字
Unity5.2以前的版本要求,每一個(gè)Canvas下至多只能有2^16-1=65535個(gè)頂點(diǎn)(使用2個(gè)字節(jié)(16位)存儲(chǔ)頂點(diǎn)索引),超過(guò)就會(huì)報(bào)錯(cuò)
以上的種種原因,讓我們不得不自己編寫文字圖片的描邊shader
在網(wǎng)上找了一圈資料后,發(fā)現(xiàn)一篇不錯(cuò)的文章,這個(gè)應(yīng)該是自己實(shí)現(xiàn)文本shader最經(jīng)典的文章
但是好像都沒有對(duì)uv偏移進(jìn)行說(shuō)明(最難的地方)
效果展示
Shader實(shí)現(xiàn)基礎(chǔ)描邊
基本思路,將uv在片元著色器進(jìn)行偏移,比較a通道,偏移后的圖片.a-原來(lái)的圖片.a
這里只是用ase可視化演示一下,實(shí)際用shader代碼實(shí)現(xiàn)
沿著8或12個(gè)方向進(jìn)行偏移,偏移的方向越多,描邊寬度大時(shí)不容易穿幫
在基礎(chǔ)描邊這里暫時(shí)聲明其它變量
shader屬性這里不聲明其它的屬性,在C#腳本中不使用material.SetColor(“_描邊顏色”, _描邊顏色);
因?yàn)檫@會(huì)導(dǎo)致材質(zhì)不同,無(wú)法讓unity動(dòng)態(tài)批合并,會(huì)增加drawcall
Properties
{
//這個(gè)特性可以和Unity組件中的屬性產(chǎn)生關(guān)聯(lián)
//表示該屬性是與每個(gè)渲染器相關(guān)的數(shù)據(jù)
//在Shader中使用此特性聲明的屬性會(huì)在每個(gè)渲染器實(shí)例中共享
[PerRendererData]_MainTex ("Texture", 2D) = "white" {}
_OutLineColor("OutLineColor",Color)=(1,0,0,0)
_OutLineWidth("OutLineWidth",Float)=1
}
Tags設(shè)置透明渲染隊(duì)列和透明渲染模式,開啟透明Blend
SubShader
{
Tags
{
"Queue"="Transparent" "IgnoreProjector"="true" "RenderType"="Transparent"
}
Lighting Off
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
頂點(diǎn)著色器,基礎(chǔ)普通的頂點(diǎn)著色器
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
片元著色器
在12個(gè)方向進(jìn)行采樣,OffsetX取cos值,OffsetY取sin值作為權(quán)重
fixed SampleTex(v2f i,fixed ii,fixed color_a)
{
const fixed OffsetX[12] = {1, 0.866, 0.5, 0, -0.5, -0.866, -1, -0.866, -0.5, 0, 0.5, 0.866};
const fixed OffsetY[12] = {0, 0.5, 0.866, 1, 0.866, 0.5, 0, -0.5, -0.866, -1, -0.866, -0.5};
fixed2 offset_uv = i.uv + fixed2(OffsetX[ii], OffsetY[ii])* _MainTex_TexelSize.xy * _OutLineWidth;
fixed sample_a = tex2D(_MainTex, offset_uv).a;
fixed a = sample_a;//-color.a;不減去color.a,因?yàn)樵谖淖诌吘塧=0-1的部分,會(huì)為0
//比如向上偏移,下面邊緣的a為0-1,減去1,負(fù)數(shù),saturate后為0,這個(gè)值應(yīng)該保留,否則邊緣顏色由原來(lái)的片元決定,沒有混合過(guò)渡
return a;
}
fixed4 frag(v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
fixed sum_a = 0;//疊加各個(gè)方向采樣a的結(jié)果
sum_a += SampleTex(i, 0, col.a);
sum_a += SampleTex(i, 1, col.a);
sum_a += SampleTex(i, 2, col.a);
sum_a += SampleTex(i, 3, col.a);
sum_a += SampleTex(i, 4, col.a);
sum_a += SampleTex(i, 5, col.a);
sum_a += SampleTex(i, 6, col.a);
sum_a += SampleTex(i, 7, col.a);
sum_a += SampleTex(i, 8, col.a);
sum_a += SampleTex(i, 9, col.a);
sum_a += SampleTex(i, 10, col.a);
sum_a += SampleTex(i, 11, col.a);
sum_a=saturate(sum_a);
fixed4 outLineColor=fixed4(_OutLineColor.rgb,sum_a);
fixed4 finalCol=lerp(outLineColor,col,col.a);//沒有文字的地方a為0,由描邊決定,文字邊界a為0-1由文字顏色和描邊共同決定
return finalCol;
}
問(wèn)題:
- 可以發(fā)現(xiàn)文字邊緣有其它的文本,只是因?yàn)槲谋緦?duì)于的紋理被打包到一個(gè)大的圖集中,uv偏移后,會(huì)采樣到臨近的文本像素
- 觀察N的左側(cè)可以看出,描邊的區(qū)域有一部分超出范圍為裁剪了
解決方案:
- 要在C#里面得到偏移之前原來(lái)的uv范圍,根據(jù)uv范圍,將不在范圍的片元a設(shè)置為即可
- 要對(duì)頂點(diǎn)進(jìn)行擴(kuò)展,讓三角形范圍變大,同時(shí)要等比例擴(kuò)展uv,只擴(kuò)展頂點(diǎn),僅僅只是圖片被放大了,還是會(huì)被裁剪,
,同時(shí)擴(kuò)展頂點(diǎn)和uv,這樣就不會(huì)被裁剪了
實(shí)現(xiàn)通用的shader
C#部分
在C#腳本要進(jìn)行頂點(diǎn)的擴(kuò)展,進(jìn)行uv的擴(kuò)展
使用C#腳本傳遞描邊顏色和文本顏色
大致看一下BaseMeshEffect
- 在Canvas中要開啟uv1和uv2
protected override void Start()
{
UseUVChannels();
}
private void UseUVChannels()
{
var shader = Shader.Find("DSShader/TextOutline");
base.graphic.material = new Material(shader);
AdditionalCanvasShaderChannels v1 = base.graphic.canvas.additionalShaderChannels;
var v2 = AdditionalCanvasShaderChannels.TexCoord1;
if ((v1 & v2) != v2)
{
base.graphic.canvas.additionalShaderChannels |= v2;
}
v2 = AdditionalCanvasShaderChannels.TexCoord2;
if ((v1 & v2) != v2)
{
base.graphic.canvas.additionalShaderChannels |= v2;
}
}
上面的 |=操作不明白可以學(xué)習(xí)一下|=位或操作
比如
int a = 5; // 二進(jìn)制表示: 0000 0101
int b = 3; // 二進(jìn)制表示: 0000 0011
a |= b; // 執(zhí)行位或運(yùn)算,并賦值給 a,有1則1
Console.WriteLine(a); // 輸出: 7 (二進(jìn)制表示: 0000 0111)
可以發(fā)現(xiàn)AdditionalCanvasShaderChannels枚舉定義的數(shù)字剛好是2^n為了位或操作
public enum AdditionalCanvasShaderChannels
{
None = 0,
TexCoord1 = 1,
TexCoord2 = 2,
TexCoord3 = 4,
Normal = 8,
Tangent = 16, // 0x00000010
}
[ExecuteAlways]
public abstract class BaseMeshEffect : UIBehaviour, IMeshModifier
{
[NonSerialized]
private Graphic m_Graphic;
/// <summary>
/// The graphic component that the Mesh Effect will aplly to.
/// </summary>
protected Graphic graphic
{
get
{
if (m_Graphic == null)
m_Graphic = GetComponent<Graphic>();
return m_Graphic;
}
}
已經(jīng)BaseMeshEffect 帶有[ExecuteAlways],子類會(huì)在編輯器模式運(yùn)行
我們要使用BaseMeshEffect , BaseMeshEffect 是一個(gè)抽象類,用于實(shí)現(xiàn)自定義的 Mesh 效果.用于擴(kuò)展和修改 UI 元素的網(wǎng)格Mesh數(shù)據(jù).通過(guò)繼承 BaseMeshEffect 類并實(shí)現(xiàn)其中的方法,可以對(duì) UI 元素的網(wǎng)格進(jìn)行自定義的修改和效果應(yīng)用.
2. 重載ModifyMesh,更改UIVertex數(shù)據(jù)
public class TextOutline : BaseMeshEffect
{
List<UIVertex> _uiVertices = new List<UIVertex>();
[Range(0, 6)]
public float outLineWidth = 1;
public Color EdgeColor = Color.red;
public Color TextColor = Color.red;
public override void ModifyMesh(VertexHelper vh)
{
vh.GetUIVertexStream(_uiVertices);
ModifyUIVertexs(_uiVertices);//下面的函數(shù)
vh.Clear();
vh.AddUIVertexTriangleStream(_uiVertices);
}
}
ModifyUIVertexs遍歷UIVertex,每次拿到3個(gè)頂點(diǎn)數(shù)據(jù)
偏移頂點(diǎn)pos的思路,判斷頂點(diǎn)是否大于三角形的中心點(diǎn),x或y大于則向上或向右偏移,加上描邊寬度outLineWidth
void ModifyUIVertexs(List<UIVertex> uiVertices)
{
for (int i = 0; i <= uiVertices.Count - 3; i += 3)
{
UIVertex uiVertex1 = uiVertices[i];
UIVertex uiVertex2 = uiVertices[i + 1];
UIVertex uiVertex3 = uiVertices[i + 2];
Vector3 pos1 = uiVertex1.position;
Vector3 pos2 = uiVertex2.position;
Vector3 pos3 = uiVertex3.position;
Vector2 uv1 = uiVertex1.uv0;
Vector2 uv2 = uiVertex2.uv0;
Vector2 uv3 = uiVertex3.uv0;
//得到三角形的中心點(diǎn),用于頂點(diǎn)的偏移
Vector3 pos_center = (pos1 + pos2 + pos3) / 3;
Vector2 uv_min = new Vector2(Mathf.Min(uv1.x, uv2.x, uv3.x), Mathf.Min(uv1.y, uv2.y, uv3.y));
Vector2 uv_max = new Vector2(Mathf.Max(uv1.x, uv2.x, uv3.x), Mathf.Max(uv1.y, uv2.y, uv3.y));
Vector4 uv_border = new Vector4(uv_min.x, uv_min.y, uv_max.x, uv_max.y);
//得到uv的范圍,傳遞給shader判斷
//以pos和uv都以pos2或uv2為原點(diǎn)
Vector2 pos_base1 = pos1 - pos2;
Vector2 pos_base2 = pos3 - pos2;
Vector2 uv_base1 = uv1 - uv2;
Vector2 uv_base2 = uv3 - uv2;
uiVertices[i] = ModifyPosUV(uiVertex1, pos_center, pos_base1, pos_base2, uv_base1, uv_base2, uv_border);
uiVertices[i + 1] = ModifyPosUV(uiVertex2, pos_center, pos_base1, pos_base2, uv_base1, uv_base2, uv_border);
uiVertices[i + 2] = ModifyPosUV(uiVertex3, pos_center, pos_base1, pos_base2, uv_base1, uv_base2, uv_border);
}
}
計(jì)算pos頂點(diǎn)的偏移很好理解,但是uv的計(jì)算比較復(fù)雜,要使用線性代數(shù)的知識(shí),對(duì)旋轉(zhuǎn)縮放矩陣有一定的理解
UIVertex ModifyPosUV(UIVertex uiVertex, Vector3 pos_centor,
Vector2 pos_base1, Vector2 pos_base2,
Vector2 uv_base1, Vector2 uv_base2, Vector4 uv_border)
{
//偏移pos
Vector3 pos = uiVertex.position;
float offsetX = pos.x > pos_centor.x ? outLineWidth : -outLineWidth;
float offsetY = pos.y > pos_centor.y ? outLineWidth : -outLineWidth;
pos.x += offsetX;
pos.y += offsetY;
uiVertex.position = pos;
Vector2 offset = new Vector2(offsetX, offsetY);
//uv偏移
Vector2 uv = uiVertex.uv0;
Matrix2x2 pos_m = new Matrix2x2(pos_base1.x,pos_base2.x,pos_base1.y,pos_base2.y);
pos_m=pos_m.Inverse();
Matrix2x2 uv_m = new Matrix2x2(uv_base1.x, uv_base2.x, uv_base1.y, uv_base2.y);
Vector2 uv_offset = uv_m * pos_m * offset;
uv += uv_offset;
//設(shè)置偏移后的uv,uv0.z設(shè)置為描邊寬度
uiVertex.uv0 = new Vector4(uv.x, uv.y, outLineWidth, 0);
//設(shè)置原始uv范圍
uiVertex.uv1 = uv_border;
//設(shè)置文本顏色
uiVertex.uv2 = new Vector4(EdgeColor.r,EdgeColor.g,EdgeColor.b,EdgeColor.a);
//Color=>Color32,UIVertex的color類型是Color32
Color32 color32 = (Color32)TextColor;
uiVertex.color =color32;
return uiVertex;
}
下面重點(diǎn)講解uv偏移部分
這里要自己實(shí)現(xiàn)Matrix2x2類,實(shí)現(xiàn)矩陣×矩陣,矩陣×向量,矩陣取逆
//uv偏移
Vector2 uv = uiVertex.uv0;
Matrix2x2 pos_m = new Matrix2x2(pos_base1.x,pos_base2.x,pos_base1.y,pos_base2.y);
pos_m=pos_m.Inverse();
Matrix2x2 uv_m = new Matrix2x2(uv_base1.x, uv_base2.x, uv_base1.y, uv_base2.y);
Vector2 uv_offset = uv_m * pos_m * offset;
uv += uv_offset;
對(duì)于uv映射
Matrix2×2類
public class Matrix2x2
{
private float[,] matrix = new float[2, 2];
public Matrix2x2(float a, float b, float c, float d)
{
matrix[0, 0] = a;
matrix[0, 1] = b;
matrix[1, 0] = c;
matrix[1, 1] = d;
}
public float Determinant()
{
return matrix[0, 0] * matrix[1, 1] - matrix[0, 1] * matrix[1, 0];
}
public Matrix2x2 Inverse()
{
float det = Determinant();
float invDet = 1 / det;
float a = matrix[1, 1] * invDet;
float b = -matrix[0, 1] * invDet;
float c = -matrix[1, 0] * invDet;
float d = matrix[0, 0] * invDet;
return new Matrix2x2(a, b, c, d);
}
public static Matrix2x2 operator *(Matrix2x2 m1, Matrix2x2 m2)
{
float a = m1.matrix[0, 0] * m2.matrix[0, 0] + m1.matrix[0, 1] * m2.matrix[1, 0];
float b = m1.matrix[0, 0] * m2.matrix[0, 1] + m1.matrix[0, 1] * m2.matrix[1, 1];
float c = m1.matrix[1, 0] * m2.matrix[0, 0] + m1.matrix[1, 1] * m2.matrix[1, 0];
float d = m1.matrix[1, 0] * m2.matrix[0, 1] + m1.matrix[1, 1] * m2.matrix[1, 1];
return new Matrix2x2(a, b, c, d);
}
public static Vector2 operator *(Matrix2x2 m, Vector2 v)
{
float x = m.matrix[0, 0] * v.x + m.matrix[0, 1] * v.y;
float y = m.matrix[1, 0] * v.x + m.matrix[1, 1] * v.y;
return new Vector2(x, y);
}
}
Shader部分
在Properties只說(shuō)明_MainTex ,其它參數(shù)由C#傳入
Properties
{
[PerRendererData]_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags
{
"Queue"="Transparent" "IgnoreProjector"="true" "RenderType"="Transparent"
}
Cull Off
Lighting Off
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
頂點(diǎn)和片元結(jié)構(gòu)體
struct appdata {
float4 vertex : POSITION;
float4 uv : TEXCOORD0;
float4 uv1 : TEXCOORD1;
float4 uv2 : TEXCOORD2;
float4 color:COLOR;
};
struct v2f {
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float4 border : TEXCOORD1;
float4 color:COLOR;
float width: TEXCOORD2;
float4 edgeColor: TEXCOORD3;
};
頂點(diǎn)z著色器
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.width = v.uv.z;//得到描邊寬度
o.color = v.color;//得到文本顏色
o.border = v.uv1;//得到原始uv范圍
o.edgeColor=v.uv2;//得到描邊顏色
return o;
}
采樣偏移uv對(duì)紋理采樣
fixed SampleTex(v2f i,fixed ii,fixed color_a)
{
const fixed OffsetX[12] = {1, 0.866, 0.5, 0, -0.5, -0.866, -1, -0.866, -0.5, 0, 0.5, 0.866};
const fixed OffsetY[12] = {0, 0.5, 0.866, 1, 0.866, 0.5, 0, -0.5, -0.866, -1, -0.866, -0.5};
float2 offset_uv = i.uv + float2(OffsetX[ii], OffsetY[ii]) * _MainTex_TexelSize.xy * i.width;
fixed sample_a = (tex2D(_MainTex, offset_uv)).a;
fixed a = sample_a;
a *= isInRange(i.border.xy, i.border.zw, offset_uv);
return a;
}
判斷uv是否在原始uv范圍
fixed isInRange(fixed2 uv_min,fixed2 uv_max,fixed2 uv)
{
fixed2 rs = step(uv_min, uv) * step(uv, uv_max);
return rs.x * rs.y;
}
片元著色器
fixed4 frag(v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv).a*i.color;
col.a *= isInRange(i.border.xy, i.border.zw, i.uv);
fixed sum_a = 0;
sum_a += SampleTex(i, 0, col.a);
sum_a += SampleTex(i, 1, col.a);
sum_a += SampleTex(i, 2, col.a);
sum_a += SampleTex(i, 3, col.a);
sum_a += SampleTex(i, 4, col.a);
sum_a += SampleTex(i, 5, col.a);
sum_a += SampleTex(i, 6, col.a);
sum_a += SampleTex(i, 7, col.a);
sum_a += SampleTex(i, 8, col.a);
sum_a += SampleTex(i, 9, col.a);
sum_a += SampleTex(i, 10, col.a);
sum_a += SampleTex(i, 11, col.a);
sum_a = saturate(sum_a);
fixed4 outLineColor = fixed4(i.edgeColor.rgb,sum_a);
fixed a=step(i.width,0.001);//寬度為0時(shí),a為1,顏色由原來(lái)顏色決定
fixed4 finalCol = lerp(outLineColor, col, saturate(a+col.a));
return finalCol;
}
完整的C#代碼文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-774852.html
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class TextOutline : BaseMeshEffect
{
List<UIVertex> _uiVertices = new List<UIVertex>();
[Range(0, 6)]
public float outLineWidth = 1;
public Color EdgeColor = Color.red;
public Color TextColor = Color.white;
public override void ModifyMesh(VertexHelper vh)
{
vh.GetUIVertexStream(_uiVertices);
ModifyUIVertexs(_uiVertices);
vh.Clear();
vh.AddUIVertexTriangleStream(_uiVertices);
}
protected override void Start()
{
UseUVChannels();
}
private void UseUVChannels()
{
var shader = Shader.Find("DSShader/TextOutline");
base.graphic.material = new Material(shader);
AdditionalCanvasShaderChannels v1 = base.graphic.canvas.additionalShaderChannels;
var v2 = AdditionalCanvasShaderChannels.TexCoord1;
if ((v1 & v2) != v2)
{
base.graphic.canvas.additionalShaderChannels |= v2;
}
v2 = AdditionalCanvasShaderChannels.TexCoord2;
if ((v1 & v2) != v2)
{
base.graphic.canvas.additionalShaderChannels |= v2;
}
}
void ModifyUIVertexs(List<UIVertex> uiVertices)
{
for (int i = 0; i <= uiVertices.Count - 3; i += 3)
{
UIVertex uiVertex1 = uiVertices[i];
UIVertex uiVertex2 = uiVertices[i + 1];
UIVertex uiVertex3 = uiVertices[i + 2];
Vector3 pos1 = uiVertex1.position;
Vector3 pos2 = uiVertex2.position;
Vector3 pos3 = uiVertex3.position;
Vector2 uv1 = uiVertex1.uv0;
Vector2 uv2 = uiVertex2.uv0;
Vector2 uv3 = uiVertex3.uv0;
Vector3 pos_center = (pos1 + pos2 + pos3) / 3;
Vector2 uv_min = new Vector2(Mathf.Min(uv1.x, uv2.x, uv3.x), Mathf.Min(uv1.y, uv2.y, uv3.y));
Vector2 uv_max = new Vector2(Mathf.Max(uv1.x, uv2.x, uv3.x), Mathf.Max(uv1.y, uv2.y, uv3.y));
Vector4 uv_border = new Vector4(uv_min.x, uv_min.y, uv_max.x, uv_max.y);
Vector2 pos_base1 = pos1 - pos2;
Vector2 pos_base2 = pos3 - pos2;
Vector2 uv_base1 = uv1 - uv2;
Vector2 uv_base2 = uv3 - uv2;
uiVertices[i] = ModifyPosUV(uiVertex1, pos_center, pos_base1, pos_base2, uv_base1, uv_base2, uv_border);
uiVertices[i + 1] = ModifyPosUV(uiVertex2, pos_center, pos_base1, pos_base2, uv_base1, uv_base2, uv_border);
uiVertices[i + 2] = ModifyPosUV(uiVertex3, pos_center, pos_base1, pos_base2, uv_base1, uv_base2, uv_border);
}
}
UIVertex ModifyPosUV(UIVertex uiVertex, Vector3 pos_centor,
Vector2 pos_base1, Vector2 pos_base2,
Vector2 uv_base1, Vector2 uv_base2, Vector4 uv_border)
{
//偏移pos
Vector3 pos = uiVertex.position;
float offsetX = pos.x > pos_centor.x ? outLineWidth : -outLineWidth;
float offsetY = pos.y > pos_centor.y ? outLineWidth : -outLineWidth;
pos.x += offsetX;
pos.y += offsetY;
uiVertex.position = pos;
Vector2 offset = new Vector2(offsetX, offsetY);
//uv偏移
Vector2 uv = uiVertex.uv0;
Matrix2x2 pos_m = new Matrix2x2(pos_base1.x,pos_base2.x,pos_base1.y,pos_base2.y);
pos_m=pos_m.Inverse();
Matrix2x2 uv_m = new Matrix2x2(uv_base1.x, uv_base2.x, uv_base1.y, uv_base2.y);
Vector2 uv_offset = uv_m * pos_m * offset;
uv += uv_offset;
//設(shè)置偏移后的uv,uv0.z設(shè)置為描邊寬度
uiVertex.uv0 = new Vector4(uv.x, uv.y, outLineWidth, 0);
//設(shè)置原始uv范圍
uiVertex.uv1 = uv_border;
//設(shè)置文本顏色
uiVertex.uv2 = new Vector4(EdgeColor.r,EdgeColor.g,EdgeColor.b,EdgeColor.a);
//Color=>Color32,UIVertex的color類型是Color32
Color32 color32 = (Color32)TextColor;
uiVertex.color =color32;
return uiVertex;
}
}
public class Matrix2x2
{
private float[,] matrix = new float[2, 2];
public Matrix2x2(float a, float b, float c, float d)
{
matrix[0, 0] = a;
matrix[0, 1] = b;
matrix[1, 0] = c;
matrix[1, 1] = d;
}
public float Determinant()
{
return matrix[0, 0] * matrix[1, 1] - matrix[0, 1] * matrix[1, 0];
}
public Matrix2x2 Inverse()
{
float det = Determinant();
float invDet = 1 / det;
float a = matrix[1, 1] * invDet;
float b = -matrix[0, 1] * invDet;
float c = -matrix[1, 0] * invDet;
float d = matrix[0, 0] * invDet;
return new Matrix2x2(a, b, c, d);
}
public static Matrix2x2 operator *(Matrix2x2 m1, Matrix2x2 m2)
{
float a = m1.matrix[0, 0] * m2.matrix[0, 0] + m1.matrix[0, 1] * m2.matrix[1, 0];
float b = m1.matrix[0, 0] * m2.matrix[0, 1] + m1.matrix[0, 1] * m2.matrix[1, 1];
float c = m1.matrix[1, 0] * m2.matrix[0, 0] + m1.matrix[1, 1] * m2.matrix[1, 0];
float d = m1.matrix[1, 0] * m2.matrix[0, 1] + m1.matrix[1, 1] * m2.matrix[1, 1];
return new Matrix2x2(a, b, c, d);
}
public static Vector2 operator *(Matrix2x2 m, Vector2 v)
{
float x = m.matrix[0, 0] * v.x + m.matrix[0, 1] * v.y;
float y = m.matrix[1, 0] * v.x + m.matrix[1, 1] * v.y;
return new Vector2(x, y);
}
}
完整的Shader代碼文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-774852.html
Shader "DSShader/TextOutline"
{
Properties
{
[PerRendererData]_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags
{
"Queue"="Transparent" "IgnoreProjector"="true" "RenderType"="Transparent"
}
Cull Off
Lighting Off
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata {
float4 vertex : POSITION;
float4 uv : TEXCOORD0;
float4 uv1 : TEXCOORD1;
float4 uv2 : TEXCOORD2;
float4 color:COLOR;
};
struct v2f {
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float4 border : TEXCOORD1;
float4 color:COLOR;
float width: TEXCOORD2;
float4 edgeColor: TEXCOORD3;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _MainTex_TexelSize;
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.width = v.uv.z;
o.color = v.color;
o.border = v.uv1;
o.edgeColor=v.uv2;
return o;
}
fixed isInRange(fixed2 uv_min,fixed2 uv_max,fixed2 uv)
{
fixed2 rs = step(uv_min, uv) * step(uv, uv_max);
return rs.x * rs.y;
}
fixed SampleTex(v2f i,fixed ii,fixed color_a)
{
const fixed OffsetX[12] = {1, 0.866, 0.5, 0, -0.5, -0.866, -1, -0.866, -0.5, 0, 0.5, 0.866};
const fixed OffsetY[12] = {0, 0.5, 0.866, 1, 0.866, 0.5, 0, -0.5, -0.866, -1, -0.866, -0.5};
float2 offset_uv = i.uv + float2(OffsetX[ii], OffsetY[ii]) * _MainTex_TexelSize.xy * i.width;
fixed sample_a = (tex2D(_MainTex, offset_uv)).a;
fixed a = sample_a ;
a *= isInRange(i.border.xy, i.border.zw, offset_uv);
return a;
}
fixed4 frag(v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv).a*i.color;
col.a *= isInRange(i.border.xy, i.border.zw, i.uv);
fixed sum_a = 0;
sum_a += SampleTex(i, 0, col.a);
sum_a += SampleTex(i, 1, col.a);
sum_a += SampleTex(i, 2, col.a);
sum_a += SampleTex(i, 3, col.a);
sum_a += SampleTex(i, 4, col.a);
sum_a += SampleTex(i, 5, col.a);
sum_a += SampleTex(i, 6, col.a);
sum_a += SampleTex(i, 7, col.a);
sum_a += SampleTex(i, 8, col.a);
sum_a += SampleTex(i, 9, col.a);
sum_a += SampleTex(i, 10, col.a);
sum_a += SampleTex(i, 11, col.a);
sum_a = saturate(sum_a);
fixed4 outLineColor = fixed4(i.edgeColor.rgb,sum_a);
fixed a=step(i.width,0.001);
fixed4 finalCol = lerp(outLineColor, col, saturate(a+col.a));
return finalCol;
}
ENDCG
}
}
}
到了這里,關(guān)于unity shader 實(shí)現(xiàn)通用描邊shader -文字描邊-字體描邊的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!