一、Shader实现部分
Shader "Shader/PixelEffect"
{
//------------------------------------【属性值】------------------------------------
Properties
{
//主纹理
_MainTex("Texture", 2D) = "white" {}
//封装的变量值
_Params("PixelNumPerRow (X) Ratio (Y)", Vector) = (80, 1, 1, 1.5)
}
//------------------------------------【唯一的子着色器】------------------------------------
SubShader
{
//关闭剔除操作
Cull Off
//关闭深度写入模式
ZWrite Off
//设置深度测试模式:渲染所有像素.等同于关闭透明度测试(AlphaTest Off)
ZTest Always
//--------------------------------唯一的通道-------------------------------
Pass
{
//===========开启CG着色器语言编写模块===========
CGPROGRAM
//编译指令:告知编译器顶点和片段着色函数的名称
#pragma vertex vert
#pragma fragment frag
//包含头文件
#include "UnityCG.cginc"
//顶点着色器输入结构
struct vertexInput
{
float4 vertex : POSITION;//顶点位置
float2 uv : TEXCOORD0;//一级纹理坐标
};
//顶点着色器输出结构
struct vertexOutput
{
float4 vertex : SV_POSITION;//像素位置
float2 uv : TEXCOORD0;//一级纹理坐标
};
//--------------------------------【顶点着色函数】-----------------------------
// 输入:顶点输入结构体
// 输出:顶点输出结构体
//---------------------------------------------------------------------------------
//顶点着色函数
vertexOutput vert(vertexInput v)
{
//【1】实例化一个输入结构体
vertexOutput o;
//【2】填充此输出结构
//输出的顶点位置(像素位置)为模型视图投影矩阵乘以顶点位置,也就是将三维空间中的坐标投影到了二维窗口
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
//输入的UV纹理坐标为顶点输出的坐标
o.uv = v.uv;
//【3】返回此输出结构对象
return o;
}
//变量的声明
sampler2D _MainTex;
half4 _Params;
//进行像素化操作的自定义函数PixelateOperation
half4 PixelateOperation(sampler2D tex, half2 uv, half scale, half ratio)
{
//【1】计算每个像素块的尺寸
half PixelSize = 1.0 / scale;
//【2】取整计算每个像素块的坐标值,ceil函数,对输入参数向上取整
half coordX=PixelSize * ceil(uv.x / PixelSize);
half coordY = (ratio * PixelSize)* ceil(uv.y / PixelSize / ratio);
//【3】组合坐标值
half2 coord = half2(coordX,coordY);
//【4】返回坐标值
return half4(tex2D(tex, coord).xyzw);
}
//--------------------------------【片段着色函数】-----------------------------
// 输入:顶点输出结构体
// 输出:float4型的像素颜色值
//---------------------------------------------------------------------------------
fixed4 frag(vertexOutput Input) : COLOR
{
//使用自定义的PixelateOperation函数,计算每个像素经过取整后的颜色值
return PixelateOperation(_MainTex, Input.uv, _Params.x, _Params.y);
}
//===========结束CG着色器语言编写模块===========
ENDCG
}
}
}
屏幕像素化特效主要用一个自定义函数来实现,实现代码如下:
//进行像素化操作的自定义函数PixelateOperation
half4 PixelateOperation(sampler2D tex, half2 uv, half scale, half ratio)
{
//【1】计算每个像素块的尺寸
half PixelSize = 1.0 / scale;
//【2】取整计算每个像素块的坐标值,ceil函数,对输入参数向上取整
half coordX=PixelSize * ceil(uv.x / PixelSize);
half coordY=( ratio * PixelSize ) * ceil(uv.y / PixelSize / ratio);
//【3】组合坐标值
half2 coord = half2(coordX,coordY);
//【4】返回坐标值
return half4(tex2D(tex, coord).xyzw);
}
ps:此自定义函数中用到了CG标准函数库中的一个库函数——ceil。ceil(x)的作用是对输入参数向上取整。例如:ceil(float(1.3)) ,返回值就为2.0。
PixelateOperation函数的首先先计算出每个像素块的尺寸,然后根据这里的向上取整函数ceil,分别表示出像素块的坐标值。X坐标值为PixelSize * ceil(uv.x / PixelSize)。而Y轴这边还引入了一个系数ratio,先在式子一开头乘以此系数,然后在ceil函数之中的分母部分除以一个ratio,以达到用此参数实现自定义像素长宽比的调整操作。
然后在片段着色器中调用此自定义的PixelateOperation函数,其返回值就作为片段函数frag的返回值即可:
fixed4 frag(vertexOutput Input) : COLOR
{
//使用自定义的PixelateOperation函数,计算每个像素经过取整后的颜色值
return PixelateOperation(_MainTex, Input.uv, _Params.x, _Params.y);
}
二、脚本实现部分
using UnityEngine;
using System.Collections;
//设置在编辑模式下也执行该脚本
[ExecuteInEditMode]
//添加选项到菜单中
[AddComponentMenu("Shader/PixelEffect")]
public class PixelEffect : MonoBehaviour
{
//-----------------------------变量声明部分---------------------------
#region Variables
//着色器和材质实例
public Shader CurShader;
private Material CurMaterial;
//三个可调节的自定义参数
[Range(1f, 1024f), Tooltip("屏幕每行将被均分为多少个像素块")]
public float PixelNumPerRow = 580.0f;
[Tooltip("自动计算平方像素所需的长宽比与否")]
public bool AutoCalulateRatio = true;
[Range(0f, 24f), Tooltip("此参数用于自定义长宽比")]
public float Ratio = 1.0f;
#endregion
//-------------------------材质的get&set----------------------------
#region MaterialGetAndSet
Material material
{
get
{
if(CurMaterial == null)
{
CurMaterial = new Material(CurShader);
CurMaterial.hideFlags = HideFlags.HideAndDontSave;
}
return CurMaterial;
}
}
#endregion
//-----------------------------------------【Start()函数】---------------------------------------------
// 说明:此函数仅在Update函数第一次被调用前被调用
//--------------------------------------------------------------------------------------------------------
void Start ()
{
//找到当前的Shader文件
CurShader = Shader.Find("浅墨Shader编程/Volume11/PixelEffect");
//判断当前设备是否支持屏幕特效
if(!SystemInfo.supportsImageEffects)
{
enabled = false;
return;
}
}
//-------------------------------------【OnRenderImage()函数】------------------------------------
// 说明:此函数在当完成所有渲染图片后被调用,用来渲染图片后期效果
//--------------------------------------------------------------------------------------------------------
void OnRenderImage (RenderTexture sourceTexture, RenderTexture destTexture)
{
//着色器实例不为空,就进行参数设置
if(CurShader != null)
{
float pixelNumPerRow = PixelNumPerRow;
//给Shader中的外部变量赋值
material.SetVector("_Params", new Vector2(pixelNumPerRow,
AutoCalulateRatio ? ((float)sourceTexture.width / (float)sourceTexture.height) : Ratio ));
Graphics.Blit(sourceTexture, destTexture, material);
}
//着色器实例为空,直接拷贝屏幕上的效果。此情况下是没有实现屏幕特效的
else
{
//直接拷贝源纹理到目标渲染纹理
Graphics.Blit(sourceTexture, destTexture);
}
}
//-----------------------------------------【Update()函数】----------------------------------------
// 说明:此函数在每一帧中都会被调用
//------------------------------------------------------------------------------------------------------
void Update()
{
//若程序在运行,进行赋值
if (Application.isPlaying)
{
#if UNITY_EDITOR
if (Application.isPlaying != true)
{
CurShader = Shader.Find("浅墨Shader编程/Volume11/PixelEffect");
}
#endif
}
}
//-----------------------------------------【OnDisable()函数】---------------------------------------
// 说明:当对象变为不可用或非激活状态时此函数便被调用
//--------------------------------------------------------------------------------------------------------
void OnDisable ()
{
if(CurMaterial)
{
//立即销毁材质实例
DestroyImmediate(CurMaterial);
}
}
}
根据C#脚本中参数的设定,可以有每行每列的像素个数PixelNumPerRow参数、是否自动计算正方形像素所需的长宽比与否AutoCalulateRatio参数、自定义长宽比的Ratio参数可以调节。
ps:若AutoCalulateRatio参数被勾选,Shader将自动计算正方形像素所需的长宽比,这样第三个参数Ratio也就失效了。反正,若AutoCalulateRatio参数没有被勾选,就可以用Ratio参数自己定制像素的长宽比。