1、向摄像机添加后处理脚本,这个脚本指向一个材质,材质所携带的shader就是后处理shader
(很好,你猜对了。后处理脚本就是给摄像机的)(带上材质是为了材质能够携带贴图,shader本身不带)
2、shader编写
每次后处理之前都要检查一系列条件是否被满足,例如当前平台是否支持使用渲染纹理和屏幕特效,是否支持当前的shader等(就像是subshader一样,要考虑平台兼容性的)
进行检查的各种函数已经编写好,如下所示(实际中将下列代码导入Assets中)
在进行后处理脚本的编写时,可行性检查部分调用该基类即可
(这个脚本暂时没有看太懂,以后多仔细看,多记笔记)
start 函数用来提前做一些检查,当一些效果需要更多的检查和配置的时候,可以重载(同名函数)start、CheckSupport、CheckResources函数等
using UnityEngine;
using System.Collections;
[ExecuteInEditMode] //编辑器状态下也可以执行脚本来查看效果
[RequireComponent (typeof(Camera))] //需要相机组件
//继承自基类 MonoBehaviour
public class PostEffectsBase : MonoBehaviour {
//函数
// CheckResources函数用来检查各种资源和条件是否满足(为了提前检查,放在Start函数中)
protected void CheckResources() {
bool isSupported = CheckSupport(); //这个函数调用在下面
if (isSupported == false) {
NotSupported(); //也在下面
}
}
//检查是否支持屏幕特效(image effects)和渲染纹理
protected bool CheckSupport() {
if (SystemInfo.supportsImageEffects == false || SystemInfo.supportsRenderTextures == false)
//上面这两个语句显然就是真正作用部分
{
Debug.LogWarning("This platform does not support image effects or render textures.");
return false;
}
return true;
}
// 平台不支持时执行,但是这是什么意思?不执行此脚本吗?
protected void NotSupported() {
enabled = false;
}
// Called when start,main entrance
protected void Start() {
CheckResources();
}
//后处理不能光一个shade,r一定要有一个材质
//最终效果为:没shader和shader不支持的,返回null
//shader支持的,有材质的返回材质,没有的创建材质返回材质
protected Material CheckShader_CreateMat(Shader shader, Material material) //接收参数 需要使用的shader,附带的材质
{
if (shader == null) //先检查有没有shader
{
return null;
}
if (shader.isSupported && material && material.shader == shader) //若支持该shader&&材质存在&&材质附带的shader为参数中的shader(就是摄像机面板里的shader)(有时候材质附带的shader带错了)
return material;
if (!shader.isSupported) {
return null;
}
else //即 shader支持,材质不存在/材质携带错误
//虽然看不懂这里的语句,但是我知道这里的作用:创建临时材质并返回携带shader的材质
{
material = new Material(shader); //不懂
material.hideFlags = HideFlags.DontSave; //不懂 DontSave很明显就是临时
if (material)
return material;
else
return null;
}
}
}
Unity 提供的后处理接口为 OnRenderImage函数,其声明如下:
MonoBehaviour.OnRenderImage(RenderTexture src, RenderTextre dest)
//参数1表示源渲染纹理source, 参数2表示目标渲染纹理destination
//也就是这个函数接受src纹理,进行后处理后,输出为dest纹理
在 OnRenderImage函数 中我们会使用 Graphics.Blit函数 来完成其对src纹理的处理,其声明为如下之一
注意这里直接创建了两张纹理,并不需要另行创建
注意该函数声明即执行
public static void Blit(RenderTexture src, RenderTextre dest); //缺失材质,会直接传递,不会执行pass
public static void Blit(RenderTexture src, RenderTextre dest, Material mat, int pass = -1); //pass值默认-1吧?
public static void Blit(RenderTexture src, Material mat, int pass = -1);
//参数Material mat 为材质,并不是所有的后处理都需要材质
//src会被传递给_MainTex,也就是shader中的MainTex就是渲染纹理
//int pass = -1 表示遍历后处理shader的所有Pass,若为其他索引值,则仅执行一个Pass(应该是调试用的)
当后处理比较复杂多样时,可能需要调用多次Graphics.Blit
需要注意后处理的shader并不需要appdata数据结构
但顶点着色器还是需要数据传入,并且后处理并不需要处理顶点,所以后处理的shader顶点着色器大部分都如下:
v2f vert(appdata_img v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
return o;
}
如果需要进行卷积,那还需要进一步:
v2f vert (appdata_img v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
half2 uv = v.texcoord;
o.uv[0] = uv + _MainTex_TexelSize.xy * half2(-1,-1);
o.uv[1] = uv + _MainTex_TexelSize.xy * half2( 0,-1);
o.uv[2] = uv + _MainTex_TexelSize.xy * half2( 1,-1);
o.uv[3] = uv + _MainTex_TexelSize.xy * half2(-1, 0);
o.uv[4] = uv + _MainTex_TexelSize.xy * half2( 0, 0);
o.uv[5] = uv + _MainTex_TexelSize.xy * half2( 1, 0);
o.uv[6] = uv + _MainTex_TexelSize.xy * half2(-1, 1);
o.uv[7] = uv + _MainTex_TexelSize.xy * half2( 0, 1);
o.uv[8] = uv + _MainTex_TexelSize.xy * half2( 1, 1);
return o;
}
using UnityEngine;
using System.Collections;
//这里冒号前的名称应与脚本名保持一致
//继承了上述检测脚本
public class B_S_C : PostEffectsBase {
//shader和Mat的声明
public Shader B_S_C_Shader;
private Material B_S_C_Mat;
public Material material {
get {
//这里用到的函数在 PostEffectsBase 里,进行可行性检验
B_S_C_Mat = CheckShader_CreateMat(B_S_C_Shader, B_S_C_Mat);
return B_S_C_Mat;
}
}
//这里的部分会显示到脚本面板中
[Range(0.0f, 3.0f)]
public float brightness = 1.0f;
[Range(0.0f, 3.0f)]
public float saturation = 1.0f;
[Range(0.0f, 3.0f)]
public float contrast = 1.0f;
//还记得这个函数是干嘛的吗?
void OnRenderImage(RenderTexture src, RenderTexture dest) {
if (material != null) {
//虽然这里是Mat的函数,但是其会直接作用到shader中的变量,与mat面板上的参数无关
//所以其实在后处理shader中我们并不需要写properties,但Tex要写
material.SetFloat("_Brightness", brightness);
material.SetFloat("_Saturation", saturation);
material.SetFloat("_Contrast", contrast);
//这个记得吗?上面是改参,这一句是执行
Graphics.Blit(src, dest, material);
}
//没有材质就不进行处理了,说明前面出错了,我觉得debug一下会更好?
else {
Graphics.Blit(src, dest);
}
}
}
Shader "Unity Shaders Book/Chapter 12/Brightness Saturation And Contrast" {
Properties
{
_MainTex ("Base (RGB)", 2D) = "white" {} //MainTex不能省略
//_Brightness ("Brightness", Float) = 1
//_Saturation("Saturation", Float) = 1
//_Contrast("Contrast", Float) = 1
}
SubShader
{
Pass
{
//这些设定是为了不让渲染纹理后面的对象干扰
ZTest Always
Cull Off
ZWrite Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
half _Brightness;
half _Saturation;
half _Contrast;
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);
// 明度
fixed3 finalColor = renderTex.rgb * _Brightness;
// 饱和度,注意饱和度是怎么算的
fixed luminance = 0.2125 * renderTex.r + 0.7154 * renderTex.g + 0.0721 * renderTex.b;
fixed3 luminanceColor = fixed3(luminance, luminance, luminance);
finalColor = lerp(luminanceColor, finalColor, _Saturation);
// 对比度
fixed3 avgColor = fixed3(0.5, 0.5, 0.5);
finalColor = lerp(avgColor, finalColor, _Contrast);
return fixed4(finalColor, renderTex.a);
}
ENDCG
}
}
Fallback Off
}