一个简单的屏幕效果
在屏幕特效通用脚本的基础上,制作一个简单的灰度效果。
首先是使用的挂在摄像机上面的脚本:
[ExecuteInEditMode]
public class MyTestRenderImage : MonoBehaviour
{
public Shader curShader = null;
[Range(0f, 1f)]//添加此特性后可在Inspector面板中使用滑动条控制grayScaleAmount
//控制灰度值
public float grayScaleAmount = 1.0f;
private Material curMaterial = null;
private Material CurMaterial
{
get
{
if(curMaterial == null)
{
curMaterial = new Material(curShader);
curMaterial.hideFlags = HideFlags.HideAndDontSave;
}
return curMaterial;
}
}
private void Start()
{
if(!SystemInfo.supportsImageEffects)
{
enabled = false;
return;
}
if(!curShader && !curShader.isSupported)
{
enabled = false;
}
}
private void OnRenderImage(RenderTexture source, RenderTexture destination)
{
if(curShader)
{
//使用grayScaleAmount控制着色器中对应的属性数值
CurMaterial.SetFloat("_LuminosityAmount", grayScaleAmount);
Graphics.Blit(source, destination, CurMaterial);
}
else
{
Graphics.Blit(source, destination);
}
}
private void Update()
{
//检查并将grayScaleAmount的值限定在0到1之间
grayScaleAmount = Mathf.Clamp01(grayScaleAmount);
}
private void OnDisable()
{
if(curMaterial)
{
DestroyImmediate(curMaterial);
}
}
}
通过和之前基本结构的对比,可以看出此脚本只是多了对于grayScaleAmount这一灰度数值相关的代码。
通过CurMaterial.SetFloat("_LuminosityAmount", grayScaleAmount);
可以实现外部对于着色器的控制,从而直接看到屏幕效果的变化。
当然如果为了简便也可以通过继承的方式,将基本结构作为父类,改写其中相应的函数来简化代码,此处为了进行对比,所以还是直接将其直接写了出来。
下面是用到的shader代码:
Shader "RenderImage/MyTestRenderImage"
{
Properties
{
_MainTex("Base Tex", 2D) = "white"{}
_LuminosityAmount("GrayScale Amount", Range(0.0, 1)) = 1.0
}
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
fixed _LuminosityAmount;
struct v2f
{
float4 pos : SV_POSITION;
half2 uv : TEXCOORD0;
};
v2f vert(appdata_img v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed4 renderTex = tex2D(_MainTex, i.uv);
float luminosity = 0.299 * renderTex.r + 0.587 * renderTex.g + 0.114 * renderTex.b;
fixed4 finalColor = lerp(renderTex, luminosity, _LuminosityAmount);
return finalColor;
}
ENDCG
}
}
} //end shader
在此Shader中,使用了Unity内置的appdata_img结构体作为顶点着色器的输入。
此结构体位于UnityCG.cginc
中,只包含图像处理时必须的定点坐标和纹理坐标等变量。
struct appdata_img
{
float4 vertex : POSITION;
half2 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
在vert
顶点着色器中,仅进行了必要的顶点变换和uv坐标的赋值。
在frag
片元着色器中,通过lerp
差值返回最终的屏幕颜色。
其中需要注意的是以下代码:
float luminosity = 0.299 * renderTex.r + 0.587 * renderTex.g + 0.114 * renderTex.b;
此代码中的(0.299, 0.587, 0.114)
并不是随意写上去的,经过查询资料显示,这个是一个经典的色彩心理学公式。
Gray = R*0.299 + G*0.587 + B*0.114
目的是将彩色转化为灰度,是国际公认的一个公式。其中Gray是最终的灰度值,R代表红色通道、G代表绿色通道、B代表蓝色通道。
知道了这个公式,片元着色器中的代码意义也就可以很轻松的理解了。
//根据uv获取相应像素的颜色值
fixed4 renderTex = tex2D(_MainTex, i.uv);
//按照公式,将颜色值转化为对应的灰度值
float luminosity = 0.299 * renderTex.r + 0.587 * renderTex.g + 0.114 * renderTex.b;
//通过差值,返回经过_LuminosityAmount控制的最终颜色
fixed4 finalColor = lerp(renderTex, luminosity, _LuminosityAmount);
以下是此Shader的效果
grayScaleAmount = 0
grayScaleAmount = 0.25
grayScaleAmount = 0.5
grayScaleAmount = 0.75
grayScaleAmount = 1.0
当grayScaleAmount = 0
时,最终展示的是正常的彩色效果图像。
当grayScaleAmount = 1
时,最终展示的是完全的灰度效果图像。
由于此Shader是书中学习使用的,所以其中仅有达到效果所需的必要代码,但是实际项目过程中需要对Shader进行优化后才能使用。