Shader 常用函数总结

1.向量的模长

length(v)

使用length可以实现从中心向四周发散的效果
在这里插入图片描述
下面shader略去不必要内容

v2f vert (appdata v)
{
    
    
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = v.uv;
    return o;
}

fixed4 frag (v2f i) : SV_Target
{
    
    
    // return i.uv.x;
    // return i.uv.y;
    // return length(i.uv);
    // i.uv - 0.5 相当于把原点从(0, 0)移动到了(0.5, 0.5)
    return length(i.uv - 0.5);
}

2.绝对值

abs(x)

abs可以实现一些对称效果
在这里插入图片描述

v2f vert (appdata v)
{
    
    
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = v.uv;
    return o;
}

fixed4 frag (v2f i) : SV_Target
{
    
    
    //从 0 ~ 1 转换到 -1 ~ 1 
    // return i.uv.x * 2 - 1;
    return abs(i.uv.x * 2 - 1);
}

3.裁剪

step(a, b)		//如果 a <= b,返回1,否则返回0,即 a <= b ? 1 : 0

用step绘制圆形
在这里插入图片描述

v2f vert (appdata v)
{
    
    
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = v.uv;
    return o;
}

fixed4 frag (v2f i) : SV_Target
{
    
    
    // i.uv - 0.5 相当于把原点从(0, 0)移动到了(0.5, 0.5)
    // fixed4 color = length(i.uv);
    fixed4 color = length(i.uv - 0.5);
    color = step(color, 0.5);
    return color;
}

step常用于代替if else,伪代码表示

if (uv < 0.5)
	return 1; //白色
else
	return 0; //黑色

两个step结合可以实现细线的效果
在这里插入图片描述

float4 _Offset;     //偏移值
float _LineWidth;   //线的宽度

v2f vert (appdata v)
{
    
    
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = v.uv;
    return o;
}

fixed4 frag (v2f i) : SV_Target
{
    
    
    float color1 = step(i.uv.x, _Offset.x);
    float color2 = step(i.uv.x, _Offset.x - _LineWidth);
    // return color1;
    // return color2;
    return color1 - color2;
}

添加斜率参数也可以实现斜线效果
在这里插入图片描述
改变斜率

float _Slope;   //斜率

v2f vert (appdata v)
{
    
    
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = v.uv;
    return o;
}

fixed4 frag (v2f i) : SV_Target
{
    
    
    return step(i.uv.x, _Slope * i.uv.y);
}

4.指数函数

pow(a, b)	//求a^b

pow可以改变斜率,实现曲线的效果
在这里插入图片描述

float _Power;   //指数

v2f vert (appdata v)
{
    
    
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = v.uv;
    return o;
}

fixed4 frag (v2f i) : SV_Target
{
    
    
    float k = pow(i.uv.x, _Power);
    return step(i.uv.x, k * i.uv.y);
}

5.最小值,最大值

min(a, b)
max(a, b)

min和max可以看作是不同部分取并集
在这里插入图片描述
min可以看作是对黑色部分取并集,max可以看作是对白色部分取并集
在这里插入图片描述
相乘的结果和min一样,因为只有左下角相乘结果不为0,类似可以推断出,多个颜色相乘,只会保留白色交集部分,其他全是黑色

v2f vert (appdata v)
{
    
    
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = v.uv;
    return o;
}

fixed4 frag (v2f i) : SV_Target
{
    
    
    fixed color1 = step(i.uv.x, 0.3);
    fixed color2 = step(i.uv.y, 0.3);
    // return color1;
    // return color2;
    // return min(color1, color2);
    return max(color1, color2);
}

6.取余

fmod(x,y)	//返回x%y的余数

在这里插入图片描述
f = fmod(x, 1) 的函数图像
在这里插入图片描述
f = fmod(x, 2) 的函数图像
fmod可以实现重复变化的效果
在这里插入图片描述

float _TilingX;     //x方向循环次数

v2f vert (appdata v)
{
    
    
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = v.uv;
    return o;
}

fixed4 frag (v2f i) : SV_Target
{
    
    
    //将x的范围从 0 ~ 1 转到 0 ~ _TilingX
    i.uv.x = i.uv.x * _TilingX;
    //每当x值到达一个整数,就会归0,在区间内反复从0变化到1
    return fmod(i.uv.x, 1);
}

7.截取小数

frac(x)		//返回x的小数部分

在这里插入图片描述
y = frac(x) 的函数图像,和 f = fmod(x, 1) 的函数图像一样
和fmod类似也可以实现重复变化的效果

在这里插入图片描述

float _Tiling; //循环次数

v2f vert (appdata v)
{
    
    
   v2f o;
   o.vertex = UnityObjectToClipPos(v.vertex);
   o.uv = v.uv;
   return o;
}

fixed4 frag (v2f i) : SV_Target
{
    
    
   //将x,y分量的范围从 0 ~ 1 转到 0 ~ _Tiling
   i.uv = i.uv * _Tiling;
   return frac(i.uv.x) + frac(i.uv.y);
}

8.插值,平滑,抗锯齿

(1)smoothstep

//若 x <= min,返回0,若 x >= max,返回1
//若x在两者之间,返回 3x^2 - 2x^3 ,其中x = (x0-min)/(max-min)
smoothstep(min, max, x)

在这里插入图片描述
维基百科上显示的公式
在这里插入图片描述
绘制曲线可以看出smoothstep曲线在0 ~ 1区间内的特点,中间变化快,两头变化慢
smoothstep常用于抗锯齿
在这里插入图片描述
从图中可以看出step实现的圆弧上有锯齿,而smoothstep对边缘进行了柔化处理(抗锯齿),如果smoothstep的第一个参数大于第二次参数,颜色会取反。

v2f vert (appdata v)
{
    
    
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = v.uv;
    return o;
}

fixed4 frag (v2f i) : SV_Target
{
    
    
    float color = length(i.uv);
    color = step(color, 0.5);
    // color = smoothstep(0.5, 0.6, color);
    // color = smoothstep(0.6, 0.5, color);
    return color;
}

(2)lerp

另一个常用的插值函数lerp

//计算 a + w*(b-a),在a和b之间插值,w表示权值,范围0~1,w = 0 结果 = a,w = 1 结果 = b
lerp(a, b, w)

在这里插入图片描述
lerp会将所有值都映射到区间内,而smoothstep是对区间内的值进行插值,区间外的返回0或1

v2f vert (appdata v)
{
    
    
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = v.uv;
    return o;
}

fixed4 frag (v2f i) : SV_Target
{
    
    
    float col = frac(length(i.uv - 0.5) * 5);
    // col = lerp(0.3, 0.5, col);
    col = smoothstep(0.3, 0.5, col);
    return col;
}

(3)fwidth

第三个常用的抗锯齿函数是fwidth

ddx(v) = 该像素点右边的值 - 该像素点的值
ddy(v) = 该像素点下面的值 - 该像素点的值
fwidth(v) = abs(ddx(v)) + abs(ddy(v))  //邻域像素之间的近似导数值

ddx,ddy反映了相邻像素在屏幕空间x和y方向上的变化率
在这里插入图片描述
在这里插入图片描述
用fwidth获取一个小范围,配合smoothstep函数在区间内做插值运算

v2f vert (appdata v)
{
    
    
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = v.uv;
    return o;
}

fixed4 frag (v2f i) : SV_Target
{
    
    
    fixed4 color = length(i.uv);
    // color = step(0.5, color);
    
    //w是一个很小的值,这里乘以5是为了扩大范围,当color小于0.5 - w时,返回0,
    //大于0.5 + w时,返回1,否则在0到1之间进行插值。这样我们可以在[−w, w]区间内,
    //即高光区域的边界处,得到从0到1平滑变化的值
    float w = fwidth(color) * 5;
    color = lerp(0, 1, smoothstep(0.5 - w, 0.5 + w, color));
    return color;
}

9.点乘

dot(a,b)

求向量的夹角或b向量在a向量上的投影

10.叉乘

cross(a,b)

求与这两个向量组成的平面垂直的向量

11.限制值的范围

saturate(x)		//把x限制到[0,1]之间
clamp(x, a, b)	//把x限制到[a,b]之间

12.获取变量正负号

sign(x)		//x > 0,返回1;x < 0,返回-1;x = 0,返回0

在这里插入图片描述

13.角度,弧度

degrees(x)	//将弧度值转换为角度值
radians(x)	//将角度值转换为弧度值

14.矩阵,向量

transpose(M)  //返回矩阵的转置矩阵
normalize(v)  //返回单位向量

15.反射,折射

reflect(l, n)		  //入射光线方向l和表面法向量n,计算反射向量
refract(l, n, ratio)  //光线方向l,法向量n和两种介质折射率的比值ratio,计算折射向量

计算反射或折射光线后,一般会用来在CubeMap上采样,详情参考另一篇文章:反射,折射,菲涅尔反射总结

16.取整

ceil(x)   //向上取整
round(x)  //四舍五入
floor(x)  //向下取整

在这里插入图片描述

17.三角函数

sin(x)	//正弦
cos(x)	//余弦
tan(x)	//正切

在这里插入图片描述
常使用sin函数来实现波浪的效果
在这里插入图片描述

#define PI 3.141592653

v2f vert (appdata v)
{
    
    
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = v.uv;
    return o;
}

fixed4 frag (v2f i) : SV_Target
{
    
    
    //从 0 ~ 1 转换到 0 ~ 2 * PI,即sin函数的一个周期
    float2 uv2 = i.uv * float2(2 * PI, 2 * PI);
    //将sin函数的结果从 -1 ~ 1 转换到 0 ~ 1
    float sinUV = sin(uv2) * 0.5 + 0.5;
    float slope = uv2.y - sinUV;
    slope = round(slope);
    return slope;
}

反三角函数

asin(x)		 //反正弦
acos(x)		 //反余切
atan(x)		 //反正切
atan2(y,x)	 //求y/x的反正切,其返回值为[-π, π]之间

在这里插入图片描述
在这里插入图片描述
y = atan2(y,x)的图像
在这里插入图片描述

18.时间

时间和三角函数组合形成变化的效果

在这里插入图片描述

这里用时间控制颜色在0到1之间循环

v2f vert (appdata v)
{
    
    
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = v.uv;
    return o;
}

fixed4 frag (v2f i) : SV_Target
{
    
    
    float4 color = 1;
    // _Time的4个分量的值分别是 (t/20, t, t*2, t*3),t是自该场景加载开始所经过的时间
    color.xyz = sin(_Time.z) * 0.5 + 0.5;
    return color;
}

19.随机

在这里插入图片描述
Shader Graph中有个Simple Noise节点,右键选择Open Documentation就可以看到它实现的代码,这些代码可以复制到项目里直接使用

在这里插入图片描述
使用Unity_SimpleNoise_float这个函数,传入uv和缩放值,就会能得到一个随机值,上图是修改缩放值得到的噪声图

float _Seed;

inline float unity_noise_randomValue (float2 uv)
{
    
    
    return frac(sin(dot(uv, float2(12.9898, 78.233)))*43758.5453);
}

inline float unity_noise_interpolate (float a, float b, float t)
{
    
    
    return (1.0-t)*a + (t*b);
}

inline float unity_valueNoise (float2 uv)
{
    
    
    float2 i = floor(uv);
    float2 f = frac(uv);
    f = f * f * (3.0 - 2.0 * f);

    uv = abs(frac(uv) - 0.5);
    float2 c0 = i + float2(0.0, 0.0);
    float2 c1 = i + float2(1.0, 0.0);
    float2 c2 = i + float2(0.0, 1.0);
    float2 c3 = i + float2(1.0, 1.0);
    float r0 = unity_noise_randomValue(c0);
    float r1 = unity_noise_randomValue(c1);
    float r2 = unity_noise_randomValue(c2);
    float r3 = unity_noise_randomValue(c3);

    float bottomOfGrid = unity_noise_interpolate(r0, r1, f.x);
    float topOfGrid = unity_noise_interpolate(r2, r3, f.x);
    float t = unity_noise_interpolate(bottomOfGrid, topOfGrid, f.y);
    return t;
}

void Unity_SimpleNoise_float(float2 UV, float Scale, out float Out)
{
    
    
    float t = 0.0;

    float freq = pow(2.0, float(0));
    float amp = pow(0.5, float(3-0));
    t += unity_valueNoise(float2(UV.x*Scale/freq, UV.y*Scale/freq))*amp;

    freq = pow(2.0, float(1));
    amp = pow(0.5, float(3-1));
    t += unity_valueNoise(float2(UV.x*Scale/freq, UV.y*Scale/freq))*amp;

    freq = pow(2.0, float(2));
    amp = pow(0.5, float(3-2));
    t += unity_valueNoise(float2(UV.x*Scale/freq, UV.y*Scale/freq))*amp;

    Out = t;
}

v2f vert (appdata v)
{
    
    
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = v.uv;
    return o;
}

fixed4 frag (v2f i) : SV_Target
{
    
    
    float noise;
    Unity_SimpleNoise_float(i.uv, _Seed, noise);
    return noise;
}

20.获取片元在屏幕上的像素位置

方法1:片元着色器的输入中声明VPOS或WPOS语义

在这里插入图片描述

// VPOS是HLSL中对屏幕坐标的语义,而WPOS是Cg中对屏幕坐标的语义。两者在Unity Shader中是等价的
fixed4 frag (float4 sp : WPOS) : SV_Target
{
    
    
    // sp.xy就是当前片元在屏幕空间的坐标
    // 用屏幕坐标除以屏幕分辨率_ScreenParams.xy,得到视口空间中的坐标
    return fixed4(sp.xy / _ScreenParams.xy, 0.0, 1.0);
}

方法2:使用ComputeScreenPos函数
实现效果和上图一样

struct v2f
{
    
    
    float4 pos : SV_POSITION;
    float2 uv : TEXCOORD0;
    float4 scrPos : TEXCOORD1;
};

v2f vert (appdata v)
{
    
    
    v2f o;
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv = v.uv;
    // 第一步:把ComputeScreenPos的结果保存到scrPos中
    o.scrPos = ComputeScreenPos(o.pos);
    return o;
}

fixed4 frag (v2f i) : SV_Target
{
    
    
    // 第二步:用scrPos.xy除以scrPos.w得到视口空间中的坐标
    float2 wcoord = i.scrPos.xy / i.scrPos.w;
    return fixed4(wcoord, 0.0, 1.0);
}

参考

HLSL 中可用的内部函数
Simple Noise

《Shader 入门精要》

猜你喜欢

转载自blog.csdn.net/sinat_34014668/article/details/127465475