调整屏幕的亮度,饱和度和对比度 【Unity Shader入门精要12.2】

调整屏幕的亮度,饱和度和对比度 【Unity Shader入门精要12.2】

 

 

 

 

//12.1
//一个用于检查的基类
//检查当前平台是否支持渲染纹理和屏幕特效,是否支持Unity Shader



using System.Collections.Generic;
using UnityEngine;

//在编辑模式执行
[ExecuteInEditMode]
//需要绑定组件(类型(相机))
[RequireComponent(typeof(Camera))]

public class PostEffectsBase : MonoBehaviour {



   

   
    //新建检查资源函数 开始时调用
    protected void CheckResources()
    {
        //新建布尔类型 调用检查支持函数
        bool isSupported = CheckSupport();
        //如果 检查支持函数结果是假
        if (isSupported == false)
        {
            //调用没有支持函数
            NotSupported();
        }
    }

    //新建检查支持函数 调用CheckResources检查此平台上的支持
    protected bool CheckSupport()
    {
        //if (访问系统和硬件信息.支持图像效果 为假  或  访问系统和硬件信息.支持渲染纹理 为假)
        if (SystemInfo.supportsImageEffects == false || SystemInfo.supportsRenderTextures == false)
        {
            
                //输出 balabala
                Debug.LogWarning("This Platform does not support image effects or render textures.");
            //返回 假
            return false;
        }
        //返回真
        return true;
    }
   
   

    //新建 没有支持 函数,当平台不支持此效果时调用
    protected void NotSupported()
    {
        //enabled  启用的行为被更新,禁用的行为没有被更新
        enabled = false;
    }

    //开始函数
    protected void Start()
    {
        //调用 检查资源函数
        CheckResources();
    }
   

    //当需要创建此效果所使用的材质时调用
    // 材质 检查着色器和创建材质 ,接受两个参数(渲染器,材质)
    protected Material CheckShaderAndCreateMaterial(Shader shader,Material material)
    {//检查 渲染器 可用性
        //如果 着色器为空
        if (shader ==null)
        {
            //返回空
            return null;
        }

        //如果 着色器有纹理,有材质,有渲染器
        if (shader .isSupported && material && material.shader == shader)
        {
            //返回 
            return material;
        }
        if (!shader.isSupported)
        {
            return null;
        }

        else
        {
            //材质赋值
            material = new Material(shader);
            //不在场景中保存
            material.hideFlags = HideFlags.DontSave;

            //检查赋值成功就返回材质
            if (material)
                return material;
            else
                return null;
        }

    }
}
//12.2 
//放在摄像机上,调用基类检查是否可用,
//有shader的接口,并对应调整亮度,饱和度,对比度的值,建立新的材质,显示到屏幕上


using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//继承12.1节中创建的基类PostEffectsBase
public class BrightnessSaturationAndContrast : PostEffectsBase
{
    //声明该效果需要的shader,并据此创建相应的材质
    public Shader briSatConShader;
    private Material briSatConMaterial;

    public Material material {
        get
        {
            //briSatConMaterial是指定的shader , CheckShaderAndCreateMaterial得到对应的材质
            briSatConMaterial = CheckShaderAndCreateMaterial(briSatConShader, briSatConMaterial);
            return briSatConMaterial;
        }
    }

    //在脚本中提供调节亮度,饱和度,对比度的参数
    [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;


    //定义OnRenderImage函数来进行真正的特效处理
    void OnRenderImage(RenderTexture src, RenderTexture dest)
    {
        //每当OnRenderImage函数被调用的时候都检查材质是否可用
        if (material !=null)
        {
            //如果可用就把参数传递
            material.SetFloat("_Brightness", brightness);
            material.SetFloat("_Saturation", saturation );
            material.SetFloat("_Contrast", contrast );

            //再调用Graphics.Blit进行处理
            Graphics.Blit(src, dest, material);
        }
        else
        {
            //否则直接把图像显示到屏幕上,不做任何处理
            Graphics.Blit(src, dest);
        }
        
    }

   
}
//12.2 调整屏幕的亮度,饱和度和对比度

Shader "Unlit/Chapter12-BrightnessSaturationAndContrast"
{
	Properties
	{
		//主纹理
		_MainTex ("Texture", 2D) = "white" {}
		//亮度
	    _Brightness("Brightness",Float)=1
		//饱和度
		_Saturation("Saturation",Float)=1
		//对比度
		_Contrast("Contrat",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;
				float2 uv : TEXCOORD0;
			};

			//顶点着色器
			/*使用了Unity内置的appdata_img结构体作为顶点着色器的输入
			它只包含了图像处理时必须的顶点坐标和纹理坐标等变量*/
			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);

			//Apply brightness 调整亮度
			//颜色值=源颜色*亮度系数
			fixed3 finalColor = renderTex.rgb*_Brightness;

			//饱和度的计算
			//计算对应的luminance(亮度值),通过对每一个颜色分量乘以一个特定的系数再相加得到的
			fixed luminance = 0.2125*renderTex.r + 0.7154*renderTex.g + 0.0721*renderTex.b;
			//使用该亮度值创建了一个饱和度为0的颜色值。
			fixed luminanceColor = fixed3(luminance, luminance, luminance);
			//使用_Saturation属性在  饱和度为0的颜色 和上一步得到的颜色 之间进行插值
			finalColor = lerp(luminanceColor, finalColor, _Saturation);

			//Apply contrast 对比度计算
			//创建一个对比度为零的颜色值(各分量均为0.5)
			fixed3 avgColor = fixed3(0.5, 0.5, 0.5);
			//使用_Contrast与 对比度为0的颜色值, 和 上一步得到的颜色之间进行插值
			finalColor = lerp(avgColor, finalColor, _Contrast);

			//返回最后的颜色值和a通道
			return fixed4(finalColor, renderTex.a);
				
			}
			ENDCG
		}
	}
}

一个基本的屏幕后处理脚本的系统,调整屏幕亮度,对比,饱和度的

三部分,一个C#用于检查,一个C#用于连接摄像头和shader并传递参数和调用检查的基类,一个Shader用于实现渲染

p245有关于OnRenderImage函数的相关信息,还有 Graphics.Blit函数的使用

OnRenderImage:

  • MonoBehavior.OnRenderImage(RenderTexture src ,RenderTexture dest);
  • 当脚本声明此函数后,Unity会把当前的图像存储在第一个参数对应的源渲染纹理中,通过函数一系列操作后再把目标渲染纹理,即第二个参数对应的渲染纹理显示到屏幕上。

Graphics.Blit:

  • Public Static void Blit(Texture scr,RenderTexture dest);
  • Public Static void Blit(Texture scr,RenderTexture dest,Material mat,int pass=-1);
  • Public Static void Blit(Texture scr,RenderTexture dest,int pass=-1);
  • scr对应源纹理,scr纹理将会被传递给shader中命名为_MainTex的纹理属性。dest 目标渲染纹理。mat 我们使用的材质。pass 默认为-1,表示将会依次调用Shader内的所有Pass

通常过程:首先在摄像机中添加一个用于屏幕后处理的脚本,在这个脚本中实现OnRenderImage函数来获取当前屏幕的渲染纹理。然后调用Graphics.Blit函数使用特定的UnityShader来对当前的图像进行处理,并把返回的渲染纹理显示到屏幕上。对于一些复杂的屏幕特效,可能需要多次调用Graphics.Blit函数来对上一步的输出结果进行下一步处理