一、 效果與引言
相信很多小伙伴都會遇到做圓角矩形的需求,網(wǎng)上的shader還不明白是怎么實現(xiàn)的,甚至還有一部分是錯誤的,本文講從原理到代碼講解圓角矩形shader的實現(xiàn)
二、 原理分析
想要實現(xiàn)一個圓角矩形,常見的是抽象成一個數(shù)學(xué)模型,如下圖紫色區(qū)域,就是我們應(yīng)該保留的區(qū)域,為了更準(zhǔn)確的描述這個圖形,我們在四個角創(chuàng)建四個相等的圓形。
因為控制每個像素的顏色主要是由片元著色器負(fù)責(zé)的,所以我們也通過Fragment Shader去實現(xiàn)這個效果,可以看到在這個函數(shù)里我們只能拿到 uv和vertex,所以我們根據(jù)uv坐標(biāo)判定是否在上圖的紫色區(qū)域,如果在則返回原本的顏色,如果不在返回完全透明的顏色。
(對每個像素都會執(zhí)行一次frag函數(shù)獲取真正渲染的顏色)
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
fixed4 frag (v2f i) : SV_Target
{
//....省略
}
因為我們通過uv來判斷是否是紫色區(qū)域,所以我們需要先了解uv坐標(biāo)系
首先我們要了解UV坐標(biāo)系,這并不是什么高深的數(shù)學(xué)概念,而是一個簡單的道理。
首先,我們把水平方向定義為X軸,豎直方向為Y軸,把圖片左下角定義為(0,0),右上角定義為(1,1),其他的坐標(biāo)以此類推
讀者可能會產(chǎn)生疑惑,為什么xy兩個軸長度不同,但是坐標(biāo)卻不相同?
筆者暫時只能這樣描述:我們就如此定義一個坐標(biāo)系,x軸和y軸單位長度不同
我們需要用戶去調(diào)整四個圓形的半徑 設(shè)為Radius,因為uv坐標(biāo)系的xy單位長度不同,我們設(shè)Ratio = Height/Width,Ratio即為y軸單位長度與x軸單位長度之比。
float Radius 圓形半徑
float Ratio Y單位長度/X單位長度
接下來我們在圖上畫四個圓形并作輔助線
通過觀察,我們可以發(fā)現(xiàn)以下特征
1.只有在紅色區(qū)域才有可能被舍棄
2.三個圓形R2,R3,R4對應(yīng)紅色區(qū)域的任意一個位置,都能在R1內(nèi)找到等價位置
3. UV坐標(biāo)轉(zhuǎn)正常坐標(biāo)的公式為 f (x ,y)= (uv.x , uv.y * Ratio)
在UV坐標(biāo)內(nèi),我們無法通過x2 + y2 來計算長度,因為uv坐標(biāo)的xy單位不同,所以我們通過上述的坐標(biāo)轉(zhuǎn)換公式來轉(zhuǎn)化為相同的坐標(biāo)系
我們需要按以下步驟進(jìn)行處理
1.將白色區(qū)域坐標(biāo)在左下角找到等價uv坐標(biāo),position = abs(step(0.5,uv) - uv)
2.判斷等價uv是否在左下角可能舍棄區(qū)域,uv.x<Raduis && uv.y < Radius * Ratio
如果不在,則返回原色,如果在則進(jìn)入下一步
3. 求圓心距 distance = [f(uv.x,uv.y) - f(Radius,Radius)]的長度
如果大于半徑則返回fixed(0,0,0,0),否則返回原色
三、著色器代碼
在shader內(nèi)盡量不要使用if語句
以上來著自Hking_Auditore 大大的Shader入門書,通常我們用step和lerp等來代替if
step函數(shù)的邏輯可以等價為
step (a, x)
{
if (a>x) return 0;
else return 1;
}
接下來的代碼雖然看著多,實際上我們只寫了兩行
這就是這兩行文章來源:http://www.zghlxwxcb.cn/news/detail-441621.html
float2 p = abs(step(0.5,i.uv) - i.uv);
fixed4 col = tex2D(_MainTex, i.uv) * (step(_Radius,p.x) ||step( _Radius ,p.y*_Ratio) || step(length(float2(p.x-_Radius,p.y*_Ratio-_Radius)),_Radius));
這是完整的著色器代碼文章來源地址http://www.zghlxwxcb.cn/news/detail-441621.html
Shader "Unlit/MyShader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Radius ("Radius",float) = 0
_Ratio("Height/Width",float )= 1
}
SubShader
{
Tags { "RenderType"="Transparent" "Queue" = "Transparent"}
LOD 100
Blend SrcAlpha OneMinusSrcAlpha
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float _Radius;
float _Ratio;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
//坐標(biāo)等價到左下角
float2 p = abs(step(0.5,i.uv) - i.uv);
//三個條件同時成立則乘0,否則乘1
//1.在左下角 ,2.長度超過半徑
fixed4 col = tex2D(_MainTex, i.uv) * (step(_Radius,p.x) ||step( _Radius ,p.y*_Ratio) || step(length(float2(p.x-_Radius,p.y*_Ratio-_Radius)),_Radius));
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}
到了這里,關(guān)于Unity 圓角矩形Shader實現(xiàn)(支持長方形)(只寫兩行)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!