《Unity Shader》12.2调整屏幕的亮度、饱和度和对比度

在本节中,我们就小试牛刀来实现一个非常简单的屏幕特效------调整屏幕的亮度、饱和度和对比度。在本节结束后,我们将得到类似图12.1中的效果。


为此,我们需要进行如下准备工作。

(1)新建一个场景。在本书资源中,该场景名为Scene_12_2。在Unity 5.2中,默认情况下场景将包含一个摄像机和一个平行光,并且使用了内置的天空盒子。在Window → Lighting → Skybox中去掉场景中的天空盒子。

(2)把本书资源中的Assets/Textures/Chapter12/Sakura0.jpg拖曳到场景中,并调整其的位置使它可以填充整个场景。注意,Sakura0.jpg的纹理类型已被设置为Sprite,因此可以直接拖曳到场景中。

手动设置 Texture Type为Sprite

(3)新建一个脚本。在本书资源中,该脚本名为BrightnessSaturationAndContrast.cs。把该脚本拖曳到摄像机上。

(4)新建一个Unity Shader。在本书资源中,该Shader名为Chapter12-BrightnessSaturationAndContrast。

我们首先来编写BrightnessSaturationAndContrast.cs脚本。打开该脚本,并进行如下修改。

(1)首先,继承12.1节中创建的基类

(2)声明该效果需要的Shader,并据此创建相应的材质:

(3)我们还在脚本中提供了调整亮度、饱和度和对比度的参数:

(4)最后,我们定义OnRenderImage函数来进行真正的特效处理:

https://github.com/candycat1992/Unity_Shaders_Book/blob/master/Assets/Scripts/Chapter12/PostEffectsBase.cs

创建脚本 PostEffectsBase

PostEffectsBase.cs

cs 复制代码
using UnityEngine;
using System.Collections;

[ExecuteInEditMode]
[RequireComponent (typeof(Camera))]
public class PostEffectsBase : MonoBehaviour {

	// Called when start
	protected void CheckResources() {
		bool isSupported = CheckSupport();
		
		if (isSupported == false) {
			NotSupported();
		}
	}

	// Called in CheckResources to check support on this platform
	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;
	}

	// Called when the platform doesn't support this effect
	protected void NotSupported() {
		enabled = false;
	}
	
	protected void Start() {
		CheckResources();
	}

	// Called when need to create the material used by this effect
	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;
		}
	}
}

BrightnessSaturationAndContrast.cs

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BrightnessSaturationAndContrast : PostEffectsBase
{
    public Shader briSatConShader; //briSatConShader是我们指定的Shader
    private Material briSatConMaterial; //briSatConMaterial是创建的材质
    public Material material
    {
        get
        {
            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;

    void OnRenderImage(RenderTexture src, RenderTexture dest)
    {
        if (material!= null)
        {
            material.SetFloat("_Brightness", brightness);
            material.SetFloat("_Saturation", saturation);
            material.SetFloat("_Contrast", contrast);
            Graphics.Blit(src, dest, material);
        }
        else
        {
            Graphics.Blit(src, dest);
        }

    }
}

下面,我们来实现Shader的部分。打开Chapter12-BrightnessSaturationAndContrast,进行如下修改。

(1)我们首先需要声明本例使用的各个属性:

(2)定义用于屏幕后处理的Pass:

(3)为了在代码中访问各个属性,我们需要在CG代码块中声明对应的变量:

(4)定义顶点着色器。屏幕特效使用的顶点着色器代码通常都比较简单,我们只需要进行必需的顶点变换,更重要的是,我们需要把正确的纹理坐标传递给片元着色器,以便对屏幕图像进行正确的采样:

(5)接着,我们实现了用于调整亮度、饱和度和对比度的片元着色器:

(6)最后,我们关闭该Unity Shader的Fallback:

完成后返回编辑器,并把Chapter12-BrightnessSaturationAndContrast拖曳到摄像机的Brightness SaturationAndContrast.cs脚本中的briSatConShader参数中。调整各个参数后,我们就可以得到类似图12.1中的效果。

Chapter12-BrightnessSaturationAndContrast.shader

cs 复制代码
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Custom/Chapter12-BrightnessSaturationAndContrast"
{
    Properties {
        _MainTex  ("Base  (RGB)",  2D)  =  "white"  {}
        _Brightness  ("Brightness",  Float)  =  1
        _Saturation("Saturation",  Float)  =  1
        _Contrast("Contrast",  Float)  =  1
        //事实上,我们可以省略Properties中的属性声明,Properties中声明的属性仅仅是为了显示在材质面板中,但对于屏幕特效来说,它们使用的材质都是临时创建的,我们也不需要在材质面板上调整参数,而是直接从脚本传递给Unity Shader。
    }

    Subshader{
        Pass  {
            ZTest  Always  Cull  Off  ZWrite  Off 
            //关闭了深度写入,是为了防止它"挡住"在其后面被渲染的物体。例如,如果当前的OnRenderImage函数在所有不透明的Pass执行完毕后立即被调用,不关闭深度写入就会影响后面透明的Pass的渲染。这些状态设置可以认为是用于屏幕后处理的Shader的"标配"。
            
            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)  { //appdata_img结构体作为顶点着色器的输入,unity内置,只包含了图像处理时必需的顶点坐标和纹理坐标等变量
                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; //亮度的调整非常简单,我们只需要把原颜色乘以亮度系数_Brightness即可
                //  Apply  saturation
                fixed luminance = 0.2125 * renderTex.r + 0.7154 * renderTex.g + 0.0721 * renderTex.b;//亮度值(luminance),这是通过对每个颜色分量乘以一个特定的系数再相加得到的
                fixed3  luminanceColor  =  fixed3(luminance,  luminance,  luminance);  //使用该亮度值创建了一个饱和度为0的颜色值
                finalColor  =  lerp(luminanceColor,  finalColor,  _Saturation); //使用_Saturation属性在其和上一步得到的颜色之间进行插值,从而得到希望的饱和度颜色
                //  Apply  contrast
                fixed3  avgColor  =  fixed3(0.5,  0.5,  0.5); //创建一个对比度为0的颜色值(各分量均为0.5)
                finalColor  =  lerp(avgColor,  finalColor,  _Contrast); //使用_Contrast属性在其和上一步得到的颜色之间进行插值

                return  fixed4(finalColor,  renderTex.a);
            }
            ENDCG
        }
    }
    Fallback  Off

}
相关推荐
ellis19701 天前
Unity插件SafeArea Helper适配异形屏详解
unity
nnsix1 天前
Unity Physics.Raycast的 QueryTriggerInteraction枚举作用
unity·游戏引擎
地狱为王1 天前
Cesium for Unity叠加行政区划线
unity·gis·cesium
小贺儿开发2 天前
Unity3D 八大菜系连连看
游戏·unity·互动·传统文化
在路上看风景2 天前
25. 屏幕像素和纹理像素不匹配
unity
ۓ明哲ڪ2 天前
Unity功能——创建新脚本时自动添加自定义头注释
unity·游戏引擎
熬夜敲代码的小N2 天前
Unity大场景卡顿“急救包”:从诊断到落地的全栈优化方案
java·unity·游戏引擎
派葛穆2 天前
Unity-realvirtual-S7通讯快速配置(未完结)
unity·游戏引擎
w-白兰地2 天前
【Addressable远端加载资源】
unity·addressable·资源加载
小张不爱写代码2 天前
[Unity 技巧] 如何自定义 Inspector 变量显示名称 (CustomLabel)
unity·游戏引擎