《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

}
相关推荐
AllBlue9 小时前
unity嵌入安卓界面,如何显示状态
android·unity·游戏引擎
tealcwu10 小时前
【Unity技巧】实现在Play时自动保存当前场景
java·unity·游戏引擎
tealcwu10 小时前
【Unity基础】实现Scroll View跟随动态内容滚动
java·unity·游戏引擎
野奔在山外的猫11 小时前
【文档】VSCode 配置 Unity 环境流程
unity
技术小甜甜12 小时前
[Godot排错] 上传 Google Play Console 封闭测试时签名证书不匹配错误的解决方案
游戏引擎·godot·游戏开发
变身缎带20 小时前
Unity中的NetworkManager基于protobuf, Socket-TCP
tcp/ip·unity·游戏引擎
AllBlue1 天前
unity调用安卓方法
android·unity·游戏引擎
郝学胜-神的一滴1 天前
Horse3D游戏引擎研发笔记(十):在QtOpenGL环境下,视图矩阵与投影矩阵(摄像机)带你正式进入三维世界
c++·3d·unity·游戏引擎·godot·图形渲染·unreal engine
AllBlue1 天前
unity导出成安卓工程,集成到安卓显示
android·unity·游戏引擎