【Reading Notes】cp8-Screeen Effects with Unity Render Textures

写在前面

在本章节,将学习一下内容:
+ 建立简单的screen effects 脚本系统
+ 使在screen effects中用亮度,饱和度,对比度
+ 实现照片风格的screen effects
+ 在screen effect中使用Overlay Blend

介绍

要想更深刻地理解着色器的过程,是去创建我们自己的着色器,同样对于screen effects。通过编写screen effects,可以创造惊人的实时相片效果:Bloom(泛光),Motion Blur(运动模糊),HDR等等。现代游戏中很多都大量运用了这些手法,深度效果,泛光效果,甚至是颜色矫正。通过本章节的学习,将指导如果创建脚本系统去控制我们的screen effects。将了解Render Texture, 什么是depth buffer(深度缓冲区),如果通过脚本控制最好的渲染效果。

Setting up the screen effect script system(设置脚本系统)

创建screen effects过程的其中一步是抓取一张全屏的纹理,使用Shader去对它进行逐像素处理(GPU中),最后把它重新发送给Unity的渲染器。这允许我们对游戏的最后渲染结果执行实时的逐像素操作,赋予了我们更多的全局的艺术创作空间。想象你必须对游戏场景中的每个对象的材质做调整才能实现最后的效果。当这操作性极低,因为这回花费大量的时间去实行。通过利用screen effect,我们可以对最后的显示效果做整体的调整。

为了使Screen effect 系统运行,需要创建一个脚本去获得当前帧的渲染结果(Render Texture),通过脚本给shader传递Render Texture,就可以实现易于使用的screen effect系统了。首先,我们先创建一个十分简单的grayscale effect,调整渲染的黑白效果。

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

//1.为了让脚本在编辑器状态下也可以执行,使用属性[ExecuteInEditMode]
[ExecuteInEditMode]
public class TestRenderImageMe : MonoBehaviour {

    public Shader curShader;
    public float grayScaleAmount = 1.0f;
    private Material curMaterial;

    Material material
    {
        get
        {
            if(curMaterial == null)
            {
                curMaterial = new Material(curShader);
                curMaterial.hideFlags = HideFlags.HideAndDontSave;
            }
            return curMaterial;
        }
    }

    // Use this for initialization
    void Start () {
        if(!SystemInfo.supportsImageEffects)
        {
            enabled = false;
            return;
        }

        if(!curShader && !curShader.isSupported)
        {
            enabled = false;
        }
    }

    void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if(curShader != null)
        {
            material.SetFloat("_LuminosityAmount", grayScaleAmount);
            Graphics.Blit(source, destination, material);
        }
        else
        {
            Graphics.Blit(source, destination);
        }
    }

    private void Update()
    {
        grayScaleAmount = Mathf.Clamp(grayScaleAmount, 0.0f, 1.0f);
    }

    private void OnDisable()
    {
        if(curMaterial)
        {
            DestroyImmediate(curMaterial);
        }
    }
}
  • 为了使脚本可以在编辑状态下执行,使用了[ExecuteInEditMode]属性
  • 为脚本定义了几个变量保存当前使用的Shdaer,Material,调整的参数
  • 在start中对平台是否支持ImageEffects进行判断,使脚本在不同平台不至于报错
  • 重载OnRenderImage(),这是一个Unity内置的函数,可以抓取Unity当前的Render Texture。而Graphic.Blit()函数,像Unity渲染器传递我们的处理结果。
Shader "CookbookShaders/self/ImageEffect" {
    Properties {
        _MainTex("Base (RGB)", 2D) = "white" {}
        _LuminosityAmount("GrayScale Amount", Range(0.0, 1)) = 1.0
    }

        SubShader{
            Pass
            {
                CGPROGRAM
                #pragma vertex vert_img
                #pragma fragment frag
                #pragma fragmentoption ARB_precision_hint_fastest
                #include "UnityCG.cginc"

                uniform sampler2D _MainTex;
                fixed _LuminosityAmount;

                fixed4 frag(v2f_img i):COLOR
                {
                    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
            }
    }
    FallBack "Diffuse"
}
  • 定义了_LuminosityAmount 对灰度程度进行控制
  • frag中进行screen effect的处理步骤,对于0.299 r, 0.587 g, 0.144 b,这几个数值是是图像显示成灰度的经验值。
  • 通过CG的插值函数,利用_LuminosityAmount在源颜色和灰度之间进行插值得到最后颜色


图片来自cookbook书中截图。

Depth Texture

我们的基本Screen effect的框架已经搭建起来,下面来实现一些更有用的效果。

private void Update()
{
    Camear.main.depthTextureMode = DepthTextureMode.Depth;
    depth_power = Mathf.Clamp(depth_power, 0.0f, 5.0f);
}

修改了Update()函数,设置主摄像机的depthTextureMode模式为Depth, 这将使主摄像机渲染深度图到Shader的内置变量_CameraDepthTexture

Shader "CookbookShaders/self/SceneDepth_Effect"
{
    Properties
    {
        _MainTex ("Base (RGB)", 2D) = "white" {}
        _DepthPower("Depth Power", Range(1, 5)) = 1
    }
        SubShader
        {

            Pass
            {
                CGPROGRAM
                #pragma vertex vert_img
                #pragma fragment frag
                #pragma fragmentoption ARB_precision_hint_fastest
                #include "UnityCG.cginc"

                uniform sampler2D _MainTex;
                fixed _DepthPower;
                sampler2D _CameraDepthTexture;

                float4 frag(v2f_img i):COLOR
                {
                    float depth = UNITY_SAMPLE_DEPTH(tex2D(_CameraDepthTexture, i.uv.xy));
                    depth = Linear01Depth(depth);
                    return fixed4(depth,depth,depth,1);
                }
                ENDCG
            }
    }
}
  1. 对内置的_CameraDepthTexture进行采样,使用UNITY_SAMPLE_DEPTH取得深度值,这个宏大部分情况下只是直接去了采样之后的r值。
  2. Linear01Depth(depth)把深度值转化为01的的范围,这里有个值得注意的地方,如果摄像机设置的Far(能看见的最远距离)很大,那么在[0-100]这些范围转化到01范围基本就是0-0.1,基本就是黑色的,看不出什么渐变效果。需要对Far进行合理设置以便场景中的深度更均匀的分布在0-1区间内。

Using brightness(亮度)、saturation(饱和度)、contrast(对比度)

使用screen effect对最后的颜色进行调整,最重要的是提供更多的全局控制选项给艺术家。例如调整颜色比例的sliders。在这一小节,将对RenderTexture实现更多的颜色操作。这些包括brightness(亮度)、saturation(饱和度)、contrast(对比度)。学习如何对这些颜色调整进行编码,给了我们一个很好的基础去学习screen effect的艺术。

scripts部分,还是单纯的将控制变量传递给着色器

void OnRenderImage(RenderTexture source, RenderTexture destination)
{
    if (curShader != null)
    {
        material.SetFloat("_BrightnessAmount", brightnessAmount);
        material.SetFloat("_satAmount", saturationAmount);
        material.SetFloat("_conAmount", contrastAmount);
        Graphics.Blit(source, destination, material);
    }
    else
    {
        Graphics.Blit(source, destination);
    }
}

shader部分

Shader "CookbookShaders/self/BSC_Effect"
{
    Properties
    {
        _MainTex("Base (RGB)", 2D) = "white" {}
        _BrightnessAmount("Brightness Amount", Range(0.0, 1)) = 1.0
        _satAmount("Saturation Amount", Range(0.0, 1)) = 1.0
        _conAmount("Contrast Amount", Range(0.0, 1)) = 1.0
    }
    SubShader
    {

        Pass
        {
            CGPROGRAM
            #pragma vertex vert_img
            #pragma fragment frag
            #pragma fragmentoption ARB_precision_hint_fastest
            #include "UnityCG.cginc"

            uniform sampler2D _MainTex;
            fixed _BrightnessAmount;
            fixed _satAmount;
            fixed _conAmount;


            float3 ContrastSaturationBrightness(float3 color, float brt, float sat, float con)
            {
                float AvgLumR = 0.5;
                float AvgLumG = 0.5;
                float AvgLumB = 0.5;

                //CIE颜色系统标准灰度系数
                float3 LuminanceCoeff = float3(0.2125, 0.7154, 0.0721);

                float3 AvgLumin = float3(AvgLumR, AvgLumG, AvgLumB);
                float3 brtColor = color * brt;
                //计算像素对应的灰度值
                float intensityf = dot(brtColor, LuminanceCoeff);
                float3 intensity = float3(intensityf, intensityf, intensityf);

                //使用饱和度变量,在灰度值和亮度颜色中插值
                float3 satColor = lerp(intensity, brtColor, sat);

                //使用对比度变量,在平均无对比和上一步得出的计算结果插值
                //0.5 * (1-con) + col * con = 0.5 + (col - 0.5) * con
                //线性插值,可以看出当con > 1的时候最后的颜色值变得更亮要看col比0.5要大还是小。
                //这就使得亮的颜色更亮,暗的颜色更暗,达到调整对比度的效果。
                float3 conColor = lerp(AvgLumin, satColor, con);
                return conColor;
            }

            fixed4 frag(v2f_img i) :COLOR
            {
                fixed4 renderTex = tex2D(_MainTex, i.uv);
                renderTex.rgb = ContrastSaturationBrightness(renderTex.rgb,
                                            _BrightnessAmount,
                                        _satAmount,
                                        _conAmount);
                return renderTex;
            }
            ENDCG
        }
    }
}

亮度:0.5 饱和度:2 对比度:1.75

Using basic Photoshop-like Blend modes

screen effect 不仅仅只是能对渲染结果的颜色做简单的调整,还可以把Render Texture和别的Texture做混合。这种技术其实和Photoshop里面的图层叠加没什么区别。这是非常对于游戏艺术家来说,提供了一个游戏中模拟混合模式的环境,而不是通过Photoshop。

在这一小节,我们将实践多种混合方式MultiplyAddOverlay

Shader "CookbookShaders/self/BlendMode_Effect"
{
    Properties
    {
        _MainTex("Base (RGB)", 2D) = "white" {}
        _BlendTex("Blend Texture", 2D) = "white" {}
        _Opacity("Blend Opacity", Range(0.0, 1)) = 1.0
    }

    SubShader
    {

        Pass
        {
            CGPROGRAM
    #pragma vertex vert_img
    #pragma fragment frag
    #pragma fragmentoption ARB_precision_hint_fastest
    #include "UnityCG.cginc"

            uniform sampler2D _MainTex;
            uniform sampler2D _BlendTex;
            fixed _Opacity;
            fixed4 frag(v2f_img i) :COLOR
            {
                fixed4 renderTex = tex2D(_MainTex, i.uv);
                fixed4 blendTex = tex2D(_BlendTex, i.uv);
                //fixed4 blendedMultiply = renderTex * blendTex; //Multiply
                //fixed4 blendedMultiply = renderTex + blendTex; //Add
                fixed4 blendedMultiply = 1 - (1.0 - renderTex) * (1.0 - blendTex); //Screen Blend
                renderTex = lerp(renderTex, blendedMultiply, _Opacity);
                return blendedMultiply;
            }
            ENDCG
        }
    }
}

效果:从左往右分别是Mulity、Add、Screen Blend

Using the Overlay Bend mode

其实只是简单的当颜色分量小于0.5的时候使用Multiply Mode,大于0.5的时候使用Screen Blend。这里测试的贴图不太好,并没有得到很好的结果。

The End~~~~

你可能感兴趣的:(读书笔记/Reading,Notes,——U5.x,Shader&Effects,cookbook)