首页 > 其他分享 >Unity 自定义Postprocess BSC明度饱和度对比度

Unity 自定义Postprocess BSC明度饱和度对比度

时间:2023-11-08 15:02:53浏览次数:46  
标签:自定义 render cmd texAlbedo Unity Postprocess pass public passSetting

前言

本篇将介绍如何通过添加RenderFeature实现自定义的postprocess——调整屏幕的明度、饱和度、对比度(以下统称BSC)

关于RenderFeature的基础可以看这篇https://www.cnblogs.com/chenglixue/p/17816447.html

Shader

  • Brightness : 很简单乘以render target texture即可
  • Saturation:一个经验公式,饱和度为0:half luminance = 0.2125 * texAlbedo.r + 0.7154 * texAlbedo.g + 0.0721 * texAlbedo.b
  • Contrast:基于half3(0.5, 0.5, 0.5)的颜色值(对比度为0)和render target texture进行lerp
  • 最后对他们依次进行lerp
Shader "Custom/BSC"
{
    Properties
    {
        _MainTex("Main Tex", 2D) = "white"{}
        _Brightness("Brightness", Range(0, 3)) = 1
        _Saturation("Saturation", Range(0, 3)) = 1
        _Contrast("Contrast", Range(0, 3)) = 1
    }
    
    SubShader
    {
        Tags
        {
            "RenderPipeline" = "UniversalPipeline"
        }
        
        HLSLINCLUDE
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

        CBUFFER_START(UnityPerMaterial)
        half4 _MainTex_ST;
        half _Brightness;
        half _Saturation;
        half _Contrast;
        CBUFFER_END
        
        TEXTURE2D(_MainTex);
        SAMPLER(sampler_MainTex);
        
        ENDHLSL

        Pass
        {
            ZTest Always 
            Cull Off
            ZWrite Off
            
            HLSLPROGRAM
            #pragma vertex VS
            #pragma fragment PS

            struct VSInput
            {
                float4 positionL : position;
                float2 uv : TEXCOORD0;
            };

            struct PSInput
            {
                float4 positionH : SV_POSITION;
                float2 uv : TEXCOORD0;
            };

            PSInput VS(VSInput vsInput)
            {
                PSInput vsOutput;

                vsOutput.positionH = TransformObjectToHClip(vsInput.positionL);
                vsOutput.uv = TRANSFORM_TEX(vsInput.uv, _MainTex);

                return vsOutput;
            }

            float4 PS(PSInput psInput) : SV_TARGET
            {
                float3 resultColor;

                // Brightness
                half4 texAlbedo = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, psInput.uv);
                resultColor = texAlbedo * _Brightness;

                // Saturate
                // 饱和度为0 = 对应颜色分量 * 对应特定系数
                half luminance = 0.2125 * texAlbedo.r + 0.7154 * texAlbedo.g + 0.0721 * texAlbedo.b;
                half3 luminanceColor = half3(luminance , luminance, luminance);
                resultColor = lerp(luminanceColor, resultColor, _Saturation);

                // Contrast
                // 对比度为0
                half3 avgColor = half3(0.5, 0.5, 0.5);
                resultColor = lerp(avgColor, resultColor, _Contrast);

                return float4(resultColor, texAlbedo.a);
            }
            ENDHLSL
        }
    }
    Fallback Off
}

RenderFeature

  • 创建自定义RenderFeature,继承ScriptableRendererFeature

    • 创建一个名为Setting的 class,用于存储Pass需要的数据
    • 复写Create(),用于初始化Pass
    • 复写AddRenderPasses(),用于添加Pass
    public class BSCPassFeature : ScriptableRendererFeature
    {
        // render feature 显示内容
        [System.Serializable]
        public class PassSetting
        {
            // 安插位置
            public RenderPassEvent m_passEvent = RenderPassEvent.AfterRenderingTransparents;
            // 控制分辨率
            //[Range(1, 4)] 
            //public int m_sampleWeaken = 1;
    
            // 明度控制
            [Range(0, 3)] 
            public float m_Brightness = 1;
            
            // 饱和度控制
            [Range(0, 3)]
            public float m_Saturation = 1;
            
            // 对比度控制
            [Range(0, 3)]
            public float m_Contrast = 1;
        }
        
        public PassSetting m_Setting = new PassSetting();
        BSCRenderPass m_BSCPass;
        
        /// <inheritdoc/>
        public override void Create()
        {
            m_BSCPass = new BSCRenderPass(m_Setting);
        }
    
        // Here you can inject one or multiple render passes in the renderer.
        // This method is called when setting up the renderer once per-camera.
        public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
        {
            // can queue up multiple passes after each other
            renderer.EnqueuePass(m_BSCPass);
        }
    }
    

Render Pass

  • 创建一个自定义Pass,继承于ScriptableRenderPass

    • 创建一系列数据,这些数据用于后续的实现
    • 构造函数:设置setting,Pass安插位置,以及material Properties
    • OnCameraSetup():获取camera render target和创建临时的渲染纹理,及控制depth buffer的精度
    • Execute():用于定义PASS的实现,后处理实现最重要的部分。将需要执行的pass放置于profiling scope,Blit()用于将源纹理计算后复制到目标渲染纹理,最后执行并释放命令缓冲区
    • OnCameraCleanup():执行完pass后,释放render target占用的内存
    class BSCRenderPass : ScriptableRenderPass
    {
        // profiler tag will show up in frame debugger
        private const string m_ProfilerTag = "BSC Pass";
    
        // 用于存储pass setting
        private BSCPassFeature.PassSetting m_passSetting;
    
        // Render Target Texture and Temp Render Target Texture
        private RenderTargetIdentifier m_TargetBuffer, m_TempBuffer;
        private int m_TempBufferID = Shader.PropertyToID("_TemporaryBuffer");
    
        private Material m_Material;
    
        // int 相较于 string可以获得更好的性能,因为这是预处理的
        private static readonly int m_BrightnessProperty = Shader.PropertyToID("_Brightness");
        private static readonly int m_SaturationProperty = Shader.PropertyToID("_Saturation");
        private static readonly int m_ContrastProperty = Shader.PropertyToID("_Contrast");
    
        // 用于设置material 属性
        public BSCRenderPass(BSCPassFeature.PassSetting passSetting) 
        {
            this.m_passSetting = passSetting;
    
            renderPassEvent = m_passSetting.m_passEvent;
    
            if (m_Material == null) m_Material = CoreUtils.CreateEngineMaterial("Custom/BSC");
    
            // 基于pass setting设置material Properties
            m_Material.SetFloat(m_BrightnessProperty, m_passSetting.m_Brightness);
            m_Material.SetFloat(m_SaturationProperty, m_passSetting.m_Saturation);
            m_Material.SetFloat(m_ContrastProperty, m_passSetting.m_Contrast);
        }
    
        // Gets called by the renderer before executing the pass.
        // Can be used to configure render targets and their clearing state.
        // Can be used to create temporary render target textures.
        // If this method is not overriden, the render pass will render to the active camera render target.
        public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
        {
            // camera target descriptor will be used when creating a temporary render texture
            RenderTextureDescriptor descriptor = renderingData.cameraData.cameraTargetDescriptor;
    
            // Downsample original camera target descriptor
            //descriptor.width /= m_passSetting.m_sampleWeaken;
            //descriptor.height /= m_passSetting.m_sampleWeaken;
    
            // Set the number of depth bits we need for temporary render texture
            descriptor.depthBufferBits = 0;
    
            // Enable these if pass requires access to the CameraDepthTexture or the CameraNormalsTexture.
            // ConfigureInput(ScriptableRenderPassInput.Depth);
            // ConfigureInput(ScriptableRenderPassInput.Normal);
    
            // Grab the color buffer from the renderer camera color target
            m_TargetBuffer = renderingData.cameraData.renderer.cameraColorTarget;
    
            // Create a temporary render texture using the descriptor from above
            cmd.GetTemporaryRT(m_TempBufferID, descriptor, FilterMode.Bilinear);
            m_TempBuffer = new RenderTargetIdentifier(m_TempBufferID);
    
        }
    
        // The actual execution of the pass. This is where custom rendering occurs
        public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
        {
            // Grab a command buffer. We put the actual execution of the pass inside of a profiling scope
            CommandBuffer cmd = CommandBufferPool.Get();
    
            using (new ProfilingScope(cmd, new ProfilingSampler(m_ProfilerTag)))
            {
                // Blit from the color buffer to a temporary buffer and back
                Blit(cmd, m_TargetBuffer, m_TempBuffer, m_Material, 0);
                Blit(cmd, m_TempBuffer, m_TargetBuffer);
            }
    
            // Execute the command buffer and release it
            context.ExecuteCommandBuffer(cmd);
            CommandBufferPool.Release(cmd);
        }
    
        // Called when the camera has finished rendering
        // release/cleanup any allocated resources that were created by this pass
        public override void OnCameraCleanup(CommandBuffer cmd)
        {
            if(cmd == null) throw new ArgumentNullException("cmd");
    
            // Since created a temporary render texture in OnCameraSetup, we need to release the memory here to avoid a leak
            cmd.ReleaseTemporaryRT(m_TempBufferID);
        }
    }
    

效果

image-20231108144613779

实现前

image-20231108144650630实现后
image-20231108144636312

标签:自定义,render,cmd,texAlbedo,Unity,Postprocess,pass,public,passSetting
From: https://www.cnblogs.com/chenglixue/p/17817410.html

相关文章

  • Unity项目开发中如何做资源加密
    Unity的游戏很容易被人反编译出来,然后再重新打包发布,把自己辛辛苦苦开发的游戏,抄写的一丝不挂。很多项目要求要做好资源加密,Unity中如何做好资源加密呢?本文給大家分享加密算法+资源打包整合思路:(1) 游戏资源加密如何选择加密算法;(2) Assetsbundle资源包的加密与解密;  ......
  • Unity架构师必备的开源库,让你3天搭建商用游戏框架
    现在Unity的相关技术已经都非常常熟了,如果你的技术能力与阅历够,搭建一个商用的游戏框架,你只需要3天的时间。今天给大家分享一个Unity老鸟3天能搭建一个自己的商用框架的几个必备的开源库,方便大家学习与使用,同时学习这些有前途的开源库也能让你在公司里面游刃有余。 1:搭建商用......
  • Unity HybridCLR(wolong)/huatuo系列
    Lua,ILRuntime,HybridCLR(wolong),huatuo热更对比分析 这两年,各种Unity热更新方案如雨后春笋般出来了,今天来写篇文章来对比一下Unity各大热更新方案的优缺点。目前主流的Unity热更新的方案有:  Lua系解决方案:内置一个Lua虚拟机,做好UnityEngine与C#框架的Lua导出。典型的......
  • Unity性能调优技术集锦
    性能调优,一直是游戏上线之前的很重要的一个环节,游戏帧率过低,手机发烫,低端机上跑不起来等,这些都需要来做优化,今天我们来给大家分享Unity做性能调优的常用技术手段。 性能调优的指导思想 接触过很多刚做性能调优的小伙伴,他们做性能调优最大的问题就是缺乏一个做性能优化......
  • Unity 编辑器UI 杂记
     用 rootVisualElement方法绘制按钮和用 GUILayout.Button绘制按钮混用的案例usingSystem.Collections;usingSystem.Collections.Generic;usingNUnit.Framework;usingUnityEditor;usingUnityEngine;usingUnityEngine.UIElements;publicclassMyTestPanel:......
  • element-plus 动态自定义主题颜色
    颜色的HEX格式颜色的HEX格式是#+六位数字/字母,其中六位数字/字母是一种十六进制的表达方式。这六位分别两个一组,从左到右分别表示红、绿、蓝。00表示最小,十进制是0;FF表示最大,十进制是255。通俗点讲,某个颜色的数值越大,包含这个颜色就越多。如:#000000-黑色、#FFFFFF-白色、#FF0000-......
  • WPF 使用 CommunityToolkit.Mvvm
    参考文档: IntroductiontotheMVVMToolkit-CommunityToolkitsfor.NET|MicrosoftLearn它是一个现代化,快速和模块化的MVVM库,对应用程序的结构或编译规范没有严格的限制。NuGet安装包搜索:CommunityToolkit.Mvvm导入usingCommunityToolkit.Mvvm;使用ObservableObjectpubli......
  • Unity游戏排行榜制作与性能优化
    游戏排行榜是一个很重要的功能,在弱联网的单机游戏与网络游戏中排行榜都是非常重要的,今天我们来详细的讲解游戏排行榜的制作方案,主要有4个点:(1) 游戏排行榜排序算法核心算法实现;(2) 游戏排行服务器如何制作;(3) Unity客户端如何对接与请求排行榜数据;(4) Unity如何优化......
  • @RequestBody接收Json参数 用自定义注解对Vo对象中Date类型日期格式校验
    @RequestBody接收Json参数|用自定义注解对Vo对象中Date类型日期格式校验问题描述昨天测试的同事测试接口的时候,测试出来一个Date类型校验问题。要求输入的日期格式是:yyyy-MM-ddHH:mm:ss,Vo中使用的注解如下:@DateTimeFormat(pattern=”yyyy-MM-ddHH:mm:ss”)测试同事输入下面两种......
  • 让自定义的容器,也能基于范围循环
      C++11起,引入了基于范围的for循环这一特性,有什么好处呢?它有时可以大大地简化遍历容器的操作,比如说STL的vector。std::vectorv{1,2,3};std::vector<int>::iteratorit=begin(v);for(;it!=end(v);++it)std::cout<<*it<<'\n';  这是使用了迭代器的写法,......