写在前面
刚刚接触学习Shader,希望以后都能够通过博客记录自己的学习过程,本篇也是我开始学习Shader的第一篇。本篇以实现马赛克效果来进行练手熟练一些Shader的基本内容。
目标
- 实现2D图片的马赛克效果
- 通过阀值控制模糊程度
思路
- n*n范围内的像素取同一颜色
预览
实现
Shader "Temp/MSK"
{
Properties
{
_MainTex("MainTex", 2D) = "white" {}
_Value("Value", Range(1,50) ) = 25
}
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
sampler2D _MainTex;
float _Value;
struct a2f
{
float4 pos : POSITION ;
float2 uv : TEXCOORD0 ;
} ;
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0 ;
} ;
v2f vert(a2f v)
{
v2f f;
f.pos = UnityObjectToClipPos(v.pos);
f.uv = v.uv;
return f;
}
float4 frag(v2f i):SV_Target
{
half v = 1/_Value;
half x = floor(i.uv.x/v)*v; //i.uv.x;
half y = floor(i.uv.y/v)*v; //i.uv.y;
half2 uv = float2(x, y);
float3 res = tex2D(_MainTex, uv).rgb;
return float4 (res, 1);
}
ENDCG
}
}
Fallback "Diffuse"
}
以上代码便是其全部实现,接下来我来详细解释一下。
首先代码的第一行表明这个Shader的名字,然后Properties中声明了一些材质的属性。此外为了能够在CG代码中访问到它们我们还需要在CG代码中提前定义一个新的变量,而在CGPROGRAM和ENDCG中包裹着的就是我们的CG代码片段了,其中#pragma vertex vert和#pragma fragment frag 两句指明了我们的顶点函数是vert片元函数是frag,并且我们还定义了两个结构体a2f和v2f,冒号后面的内容则表示这个值从哪里来或者要到哪里去。
struct a2f
{
float4 pos : POSITION ;
float2 uv : TEXCOORD0 ;
} ;
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0 ;
} ;
比如上面的a2f结构体中POSITION表明当作为输入时pos属性将会来自于模型空间的顶点坐标而TEXCOORD0表明使用第一对纹理对。而在v2f中我们使用了SV_POSITION语义,它表明我们使用或输出的是裁剪空间的定点坐标。
在顶点函数中我们通过UnityObjectToClipPos内置方法将输入的模型空间顶点转换为了裁剪空间顶点坐标并进行了输出。而在片元函数中我们则根据数据进行了一些计算,其核心原理为通过tex2D函数使用uv取值,在在此之前我们对uv进行了一下小小的修改,使其只为1/_Value的整数倍(uv的取值范围为0-1表明图片的横纵坐标)最后返回结果就得到了我们的马赛克效果。