UnityShaderLab读书笔记 第10章(Image Effect)


第10章 Image Effect

  • Shader除了可以去渲染物体,还可以修改已经渲染完成的图像,常用的有GrabPass和Post-Processing

GrabPass

  • 属于Pass,运行时抓取物体所在屏幕位置的渲染图像,传给接下来的Pass进行图像处理
  • 定义形式
    – GrabPass{},通过_GrabTexture访问抓取的图像, 但是这种会对每个物体进行一次图像抓取,性能不好
    – GrabPass{“TextureName”},只会执行一次抓取,所有物体共用一张图像
Shader "Hidden/GrabPass"
{
    Properties
    {
        _GrayScale("_GrayScale", Range(0, 1)) = 0
    }
    SubShader
    {
        
        Tags{
            "Queue" = "Transparent"
        }
        GrabPass{"_ScreenTex"}

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct v2f
            {
                float4 pos:SV_POSITION;
                float4 grabPos:TEXCOORD0;
            };

            v2f vert (float4 vertex:POSITION)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(vertex);
                // 计算抓取图像的纹理坐标
                o.grabPos = ComputeGrabScreenPos(o.pos);
                return o;
            }

            float _GrayScale;
            sampler2D _ScreenTex;

            fixed4 frag (v2f i) : SV_Target
            {
                // 采样抓取图像
                half4 src = tex2Dproj(_ScreenTex, i.grabPos);
                // Luminance公式 luminance = 0.2125 * Red + 0.7154 * Green + 0.0721 * Blue
                half grayscale = Luminance(src.rgb);
                half4 dst = half4(grayscale,grayscale,grayscale,grayscale);
                return lerp(src, dst, _GrayScale);
            }
            ENDCG
        }
    }
}

Post-Processing

  • 可以理解为,你拍完图片是不是用P图软件修一下图,调个色
  • 实现逻辑: 将摄像机渲染出来的RenderImage以RenderTexture关联给Shader,处理完成后再传回给脚本
  • 2个存储区
    – 摄像机渲染完图像的图像以RenderTexture形式存储在source存储区
    – Shader处理完的图像以RenderTexture形式存储在destination存储区
  • 摄像机渲染当前帧图像存储在source,显卡调用Shader处理完图片,利用Graphics.Blit转移到destination,覆盖上以帧的图像,摄像机允许添加多个后处理,上一个后处理结果保存在RenderTexture传入下一个后处理的source,执行顺序就是摄像机挂载的后处理组件的顺序
  • 注意:多个后处理组件也就意味着多个RenderTexture,存在性能开销,所以可以考虑,将多个后处理合并到一个后处理Shader中处理
  • 下面可以获得对亮度、饱和度、对比度的后处理效果
Shader "Unlit/BrightnessSaturationContrast"
// 亮度饱和度对比度
{
    Properties
    {
        // _MainTex接受摄像机渲染的RenderTexture 
        _MainTex ("Texture", 2D) = "white" {}
        _Brightness("Brightness", float) = 1
        _Saturation("Saturation", float) = 1
        _Contrast("Contrast", float) = 1
        
    }
    SubShader
    {
        Cull Off
        ZTest Always
        ZWrite Off

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

            // 这个是Unity vert_img的源码
            // struct appdata_img
            // {
            //     float4 vertex : POSITION;
            //     half2 uv : TEXCOORD0;
            //     UNITY_VERTEX_INPUT_INSTANCE_ID
            // };

            // struct v2f_img
            // {
            //     float4 pos : SV_POSITION;
            //     half2 uv:TEXCOORD0;
            //     UNITY_VERTEX_INPUT_INSTANCE_ID
            //     UNITY_VERTEX_OUTPUT_STEREO
            // };

            sampler2D _MainTex;
            half _Brightness;
            half _Saturation;
            half _Contrast;

            // v2f_img vert_img (appdata_img v)
            // {
            //     v2f_img o;
            //     UNITY_INITIALIZE_OUTPUT(v2f_img, o);
            //     UNITY_SETUP_INSTANCE_ID(v);
            //     UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
            //     o.pos = UnityObjectToClipPos(v.vertex);
            //     o.uv = v.uv;
            //     return o;
            // }

            half4 frag (v2f_img i) : SV_Target
            {
                half4 renderTex = tex2D(_MainTex, i.uv);
                // 亮度
                half3 finalColor = renderTex.rgb * _Brightness;
                // 饱和度
                // 转变为灰度图
                half luminance = Luminance(finalColor);
                finalColor = lerp(luminance, finalColor, _Saturation);
                // 对比度
                half3 grayColor = half3(0.5,0.5,0.5);
                finalColor = lerp(grayColor, finalColor, _Contrast);

                return half4(finalColor, 1);
            }
            ENDCG
        }
    }
}

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

[RequireComponent(typeof(Camera))]
[ExecuteInEditMode]
public class BrightnessSaturationContrast : MonoBehaviour
{
    public Shader postProcessingShader;
    public float brightness = 1;
    public float saturation = 1;
    public float contrast = 1;

    public Material _postProcessingMat;
    private void OnEnable() {
        _postProcessingMat = new Material(postProcessingShader);
        Debug.Log("Start");   
    }

    private void Update() {
        brightness = Mathf.Clamp(brightness, 0, 2);
        saturation = Mathf.Clamp(saturation, 0, 2);
        contrast = Mathf.Clamp(contrast, 0, 2);
    }

    private void OnRenderImage(RenderTexture src, RenderTexture dest) {
        if(postProcessingShader){
            _postProcessingMat.SetFloat("_Brightness", brightness);
            _postProcessingMat.SetFloat("_Saturation", saturation);
            _postProcessingMat.SetFloat("_Contrast", contrast);
            Graphics.Blit(src, dest, _postProcessingMat);
        }else{
            Graphics.Blit(src, dest);
        }
    }
}

PostProcessing插件

  • 使用的HLSL语言,不需要出现在Shader下拉框下,可以将Shader定义在Hidden下
  • 最终效果和上面的一样
Shader "Hidden/BSC_HLSL"
{
    HLSLINCLUDE

        #include "Packages/com.unity.postprocessing/PostProcessing/Shaders/StdLib.hlsl"

        // 属性声明
        TEXTURE2D_SAMPLER2D(_MainTex, sampler_MainTex);
        half _Brightness;
        half _Saturation;
        half _Contrast;

        float4 Frag(VaryingsDefault i) : SV_Target
        {
            // 采样RenderTexture
            float4 color = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord);

            // 亮度
            color.rgb *= _Brightness;

            // 饱和度
            float luminance = dot(color.rgb, float3(0.2126729, 0.7151522, 0.0721750));
            color.rgb = lerp(luminance, color.rgb, _Saturation);

            // 对比度
            half3 grayColor = half3(0.5, 0.5, 0.5);
            color.rgb = lerp(grayColor, color.rgb, _Contrast);

            return color;
        }

    ENDHLSL

    SubShader
    {
        Cull Off ZWrite Off ZTest Always

        Pass
        {
            HLSLPROGRAM

                #pragma vertex VertDefault
                #pragma fragment Frag

            ENDHLSL
        }
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering.PostProcessing;

[System.Serializable]
[PostProcess(typeof(BSCRenderer), PostProcessEvent.AfterStack, "Custom/BSC")]
public class BSC : PostProcessEffectSettings{
    //开放属性
    [Range(0f, 2f), Tooltip("Brightness effect intensity.")]
    public FloatParameter Brightness = new FloatParameter { value = 1f };

    [Range(0f, 2f), Tooltip("Saturation effect intensity.")]
    public FloatParameter Saturation = new FloatParameter { value = 1f };

    [Range(0f, 2f), Tooltip("Contrast effect intensity.")]
    public FloatParameter Contrast = new FloatParameter { value = 1f };
}

// 这个类作用就是将Settings的数据传递给Shader
public class BSCRenderer:PostProcessEffectRenderer<BSC>
{
    public override void Render(PostProcessRenderContext context)
    {
        // 首先找到Shader 注意BSC-HLSL这样是找不到Shader的
        PropertySheet sheet = context.propertySheets.Get(Shader.Find("Hidden/BSC_HLSL"));
        // 然后将Settings的数据传入Shader
        sheet.properties.SetFloat("_Brightness", settings.Brightness);
        sheet.properties.SetFloat("_Saturation", settings.Saturation);
        sheet.properties.SetFloat("_Contrast", settings.Contrast);
        // 最后将src的RenderTexture经过Shader处理保存到dst
        context.command.BlitFullscreenTriangle(context.source, context.destination, sheet, 0);
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值