|
一、思路
Bloom的效果就是再模糊效果的基础上完成的。整体的思路就是设置一个阈值,提取图中高亮区域,对其进行模糊处理,然后再将处理后的图像与原图混合。
具体的过程是:创建一个Shader文件和一个Render Feature文件,Shader文件中有三个Pass(两个一个维高斯模糊的情况下有四个pass)
第一个pass中使用luminance函数获取纹理的亮度,然后根据设置的阈值来获取高亮区域。
第二个pass中则对纹理图像进行高斯模糊计算。
第三个pass讲原始纹理图像与高斯模糊后得到的纹理图像进行混合。
在SimpleBloom.cs文件中,依次使用shader中的三个pass来处理图像。其中高斯模糊计算后的图像可以使用CommandBuffer.SetGlobalTexture()来将buffer中的纹理存储在rendertexture中。
cmd.SetGlobalTexture("_Bloom", buffer2);另外,在shader入门精要中提到,在开启抗锯齿并且有多个rendertexture的情况下,要根据平台差异来改变纹理的y轴方向
output.uv = input.texcoord;
output.uv2 = input.texcoord;
#if UNITY_UV_STARTS_AT_TOP
if(_MainTex_TexelSize.y<0.0)
output.uv2.y = 1.0 - output.uv2.y;
#endif二、代码
Bloom.shader
Shader &#34;Unlit/20_Bloom&#34;
{
Properties
{
[HideInInspector]_MainTex (&#34;Texture&#34;, 2D) = &#34;white&#34; {}
_BlurSize(&#34;Blur Size&#34;, Float) = 1.0
_Threshold(&#34;Threshold&#34;, Float) = 1.0
_Bloom(&#34;Bloom&#34;,2D) = &#34;Black&#34; {}
_Intensity(&#34;Intensity&#34;, Float) = 1.0
}
SubShader
{
Tags
{
&#34;RenderPipeline&#34;=&#34;UniversalRenderPipeline&#34;
}
Cull Off
ZWrite Off
ZTest Always
HLSLINCLUDE
#include &#34;Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl&#34;
CBUFFER_START(UnityPerMaterial)
float _BlurSize;
float4 _MainTex_TexelSize;
float _Threshold;
float _Intensity;
CBUFFER_END
TEXTURE2D(_MainTex);
SAMPLER(sampler_MainTex);
TEXTURE2D(_Bloom);
SAMPLER(sampler_Bloom);
struct Attributes
{
float4 positionOS: POSITION;
float2 texcoord: TEXCOORD0;
};
struct Varyings
{
float4 positionCS:SV_POSITION;
float2 uv:TEXCOORD0;
float2 uv2: TEXCOORD1;
};
ENDHLSL
//获取较亮部分
Pass
{
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
float luminance(float4 color)
{
return 0.2125*color.r+0.7154*color.g+0.0721*color.b;
}
Varyings vert(Attributes input)
{
Varyings output;
output.positionCS = TransformObjectToHClip(input.positionOS);
output.uv = input.texcoord;
return output;
}
float4 frag (Varyings input) : SV_Target
{
half4 tex = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv);
float bloom = clamp(luminance(tex) - _Threshold, 0.0, 1.0);
return tex*bloom;
}
ENDHLSL
}
//进行高斯模糊
Pass
{
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
Varyings vert(Attributes input)
{
Varyings output;
output.positionCS = TransformObjectToHClip(input.positionOS);
output.uv = input.texcoord;
return output;
}
float4 frag (Varyings input) : SV_Target
{
half4 tex = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv);
float2 uv = input.uv;
half4 col = float4(0,0,0,0);
col += 0.060 * SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv + float2(-1,-1)*_MainTex_TexelSize.xy*_BlurSize);
col += 0.098 * SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv + float2(0,-1)*_MainTex_TexelSize.xy*_BlurSize);
col += 0.060 * SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv + float2(1,-1)*_MainTex_TexelSize.xy*_BlurSize);
col += 0.098 * SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv + float2(-1,0)*_MainTex_TexelSize.xy*_BlurSize);
col += 0.162 * SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv);
col += 0.098 * SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv + float2(1,0)*_MainTex_TexelSize.xy*_BlurSize);
col += 0.060 * SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv + float2(1,-1)*_MainTex_TexelSize.xy*_BlurSize);
col += 0.022 * SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv + float2(1,0)*_MainTex_TexelSize.xy*_BlurSize);
col += 0.060 * SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv + float2(1,1)*_MainTex_TexelSize.xy*_BlurSize);
return col;
}
ENDHLSL
}
//混合原图像,高亮区域高斯模糊的后的结果
Pass
{
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
Varyings vert(Attributes input)
{
Varyings output;
output.positionCS = TransformObjectToHClip(input.positionOS);
output.uv = input.texcoord;
output.uv2 = input.texcoord;
#if UNITY_UV_STARTS_AT_TOP
if(_MainTex_TexelSize.y<0.0)
output.uv2.y = 1.0 - output.uv2.y;
#endif
return output;
}
float4 frag (Varyings input) : SV_Target
{
half4 tex = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv);
half4 bloom = SAMPLE_TEXTURE2D(_Bloom, sampler_Bloom, input.uv2);
return tex+bloom*_Intensity;
}
ENDHLSL
}
}
}
SimpleBloom.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
public class SimpleBloom : ScriptableRendererFeature
{
//定义配置类
[System.Serializable]
public class Setting
{
//默认在透明物体绘制完成后进行处理
public RenderPassEvent passEvent = RenderPassEvent.AfterRenderingTransparents;
//后续需要用到的材质
public Material mat;
[Range(0, 4)] public int iterations = 3;
[Range(0.2f, 3.0f)] public float blurSpread = 0.6f;
[Range(1, 8)] public int downSample = 2;
}
public Setting setting = new Setting();
//*************************************************
//********** 自定义Pass类 **************
//**************************************************
class CustomRenderPass : ScriptableRenderPass
{
//后处理用到的材质
public Material passMat = null;
private RenderTargetIdentifier passSource { get; set; }//源图像,目标图像
private RenderTargetIdentifier buffer1;
private RenderTargetIdentifier buffer2;
private RenderTargetHandle passTempleColorTex;//临时计算的图像
//标签名,用于在帧调试器中显示缓冲区的名称
private string passTag;
public int iterations;
public float blurSpread;
public int downSample;
public FilterMode passFilterMode { get; set; }
//构造函数
public CustomRenderPass(RenderPassEvent passEvent, Material material, string tag)
{
this.renderPassEvent = passEvent;
this.passMat = material;
passTag = tag;
}
//设置Pass
public void SetUp(RenderTargetIdentifier source)
{
this.passSource = source;
}
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
int bufferid1 = Shader.PropertyToID(&#34;bufferblur1&#34;);
int bufferid2 = Shader.PropertyToID(&#34;bufferblur2&#34;);
//从命令缓冲区池获取一个带标签的命令缓冲区,该标签名在帧调试器中可以见到
CommandBuffer cmd = CommandBufferPool.Get(passTag);
//获取目标相机的描述信息,创建一个结构体,里面有render texture各中信息,比如尺寸,深度图精度等等
RenderTextureDescriptor opaqueDesc = renderingData.cameraData.cameraTargetDescriptor;
int width = opaqueDesc.width / downSample;
int height = opaqueDesc.height / downSample;
//设置深度缓冲区,0表示不需要深度缓冲区
opaqueDesc.depthBufferBits = 0;
//申请临时图像
cmd.GetTemporaryRT(bufferid1, width, height, 0, FilterMode.Bilinear, RenderTextureFormat.ARGB32);
cmd.GetTemporaryRT(bufferid2, width, height, 0, FilterMode.Bilinear, RenderTextureFormat.ARGB32);
buffer1 = new RenderTargetIdentifier(bufferid1);
buffer2 = new RenderTargetIdentifier(bufferid2);
//获取index为0个pass,进行处理
//获取高亮区域
cmd.Blit(passSource, buffer1,passMat,0);
for (int i = 0; i < iterations; i++)
{
cmd.SetGlobalFloat(&#34;BlurSize&#34;, 1.0f+i*blurSpread);
//进行高斯模糊
cmd.Blit(buffer1, buffer2, passMat,1);
var tmpRT = buffer1;
buffer1 = buffer2;
buffer2 = tmpRT;
}
//生成名为Bloom的Render Texture
//passMat.SetTexture(&#34;_Bloom&#34;, bufferid1);
cmd.SetGlobalTexture(&#34;_Bloom&#34;, buffer2);
//执行pass:混合高亮区域和原图像
cmd.Blit(passSource,buffer1, passMat ,2);
cmd.Blit(buffer1, passSource);
//执行命令缓冲区
context.ExecuteCommandBuffer(cmd);
//释放命令缓存
CommandBufferPool.Release(cmd);
cmd.ReleaseTemporaryRT(bufferid1);
cmd.ReleaseTemporaryRT(bufferid2);
}
}
private CustomRenderPass pass;
//*************************************************
//********** 初始化 **************
//**************************************************
public override void Create()
{
pass = new CustomRenderPass(setting.passEvent, setting.mat, name);
pass.blurSpread = setting.blurSpread;
pass.downSample = setting.downSample;
pass.iterations = setting.iterations;
}
//*************************************************
//********** 将pass添加到渲染管线 **************
//**************************************************
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
//获取当前相机中的图像
var src = renderer.cameraColorTarget;
pass.SetUp(src);
//将该pass添加到渲染管线中
renderer.EnqueuePass(pass);
}
}
三、结果
法线图像有些模糊,并且bloom效果不太理想。 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|