US3S10L8——Bloom效果

本章代码关键字

1
UNITY_UV_STARTS_AT_TOP    // 判断纹理是否会进行y轴翻转

Bloom 效果

Bloom 效果(中文也可以叫做高光溢出效果),是一种使画面中亮度较高的区域产生一种光晕或发光效果的图像处理技术
Bloom 效果 的主要目的是 模拟现实世界中强光源在相机镜头或人眼中造成的散射和反射现象
使得画面中较亮的区域“扩散”到周围的区域,造成一种朦胧的效果

image-20250105001900-zt1x9tp​​image

Bloom 效果的基本原理

三步骤概括 Unity Shader 中实现 Bloom 效果的基本原理:

  1. 提取:提取原图像中的亮度区域存储到一张新纹理中
  2. 模糊:将提取出来的纹理进行模糊处理(一般采用高斯模糊)
  3. 合成:将模糊处理后的亮度纹理和源纹理进行颜色叠加

关键知识点:多个 Pass​ 的运用,如何提取,如何模糊,如何合成

多个Pass的运用

通过高斯模糊效果的学习,我们知道可以在屏幕后处理时,单独调用某个 Pass​ 对渲染纹理进行处理,只需要利用 Unity 中的三个函数即可

  • 获取中间缓冲纹理:RenderTexture.GetTemporary()​
  • 对纹理应用着色器处理,复制到另一个纹理内:Graphics.Blit()​
  • 释放缓冲纹理:RenderTexture.ReleaseTemporary()

处理高斯模糊效果时,我们使用了 2 个 Pass​,分别用来计算水平卷积核竖直卷积
而我们在处理 Bloom 效果时将使用 4 个 Pass​,他们分别是:

  • 用于 提取亮度区域 存储到新纹理中的 1 个 Pass
  • 用于 模糊处理提取出来的纹理的高斯模糊 的 2 个Pass(可以复用高斯模糊 Shader 中写好的 Pass​)
  • 用于 与原图像进行合成 的 1 个 Pass

通过使用这 4 个 Pass​,我们就能够完成:提取、模糊、合成,这三个步骤

提取亮度区域

我们需要在 Shader 中声明一个亮度阈值变量,亮度低于该值的区域不会被提取,主要用于 “提取” Pass​ 的片元着色器函数当中,
用当前像素的灰度值 L=0.2125R+0.7154G+0.0721BL = 0.2125R + 0.7154G + 0.0721B 与 亮度阈值变量(_LuminanceThreshold​)进行计算

1
2
3
4
5
6
7
8
9
10
11
fixed luminance(fixed4 color)
{
return 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
}

fixed4 fragExtractBright(v2f i) : SV_Target
{
fixed4 c = tex2D(_MainTex, i.uv);
fixed val = clamp(luminance(c) - _LuminanceThreshold, 0.0, 1.0);
return c * val
}
  • 灰度值 – 亮度阈值变量: 是为了仅保留超过阈值的部分,可以提取出图像中亮度较高的地方。
  • Clamp 函数: 如果结果小于 0 则为 0,大于 1 则为 1。得到的 val​ 表示像素的亮度贡献
  • 颜色 * 亮度贡献: 基于亮度阈值调节颜色的亮度,若 val​ 为 0 则为黑色,越接近 1 越接近原始颜色

这样做的目的是保留高亮区域的颜色信息,同时衰减低亮区域的颜色。

image

如果 “提取” Pass​ 是 Shader 中的第一个 Pass​,
那么我们完全可以利用 RenderTexture.GetTemporary​ 和 Graphics.Blit​ 函数将源纹理提取亮度信息后存储到缓存区中

1
2
3
4
RenderTexture buffer = RenderTexture.GetTemporary(renderTextureW, renderTextureH, 0);
buffer.filterMode = FilterMode.Bilinear; // 采用双线性过滤模式来缩放,可以让缩放效果更平滑

Graphics.Blit(source, buffer, PostEffectMaterial, 0); // 提取亮度区域纹理

模糊亮度区域纹理

由于我们目前已经在高斯模糊的课程中写好了两个用于进行水平和竖直卷积运算的 Pass
因此我们可以使用以前学习 SubShader​ 语句时学习的 UsePass​ 指令

相关内容详见:US2S2L5-3——Pass-渲染通道

只需要在之前的高斯模糊 Shader 中为两个 Pass​ 取名,然后在 Bloom 的 Shader 中复用即可
复用了两个 Pass​ 后,我们只需要在Bloom效果对应的 C# 代码中对 “提取” 出来的纹理,进行高斯模糊 Pass​ 的调用即可(逻辑和高斯模糊一致)

接着,我们需要在 Bloom 效果的 Shader 中声明一个纹理属性 _Bloom​,用于存储模糊处理完毕后的纹理
在 C# 代码中完成高斯模糊处理后,只需要将缓存区内容,写入材质球中的纹理属性即可

完成这一步后,在 Shader 中就能够得到模糊处理后的亮度纹理信息了,我们便可以进行第三步 “合成”

合成模糊过的亮度区域纹理

在“合成”的 Pass​ 中我们只需要用主纹理 _MainTex​(其中使用的纹理是屏幕原图像)和纹理属性 _Bloom​(其中使用的纹理是模糊处理后的亮度纹理信息)
进行颜色叠加即可,我们对两张纹理进行采样,将获取到的颜色信息进行加法运算,
因为颜色相加带来的效果就是增加亮度,使得原本高亮的部分变得更加显眼,从而达到 Bloom 效果(高光溢出效果)

Bloom 效果具体实现

新建 Shader,取名Bloom ,删除无用代码,接下来在这个 Shader 内依次实现 Bloom 效果需要的 Pass

提取颜色区域纹理

主要目的:提取原图像中的亮度区域存储到一张新纹理中

Shader 代码:

  1. 声明属性

    • 主纹理 _MainTex
    • 亮度区域纹理 _Bloom
    • 亮度阈值 _LuminanceThreshold
    1
    2
    3
    4
    5
    6
    Properties
    {
    _MainTex("Texture", 2D) = "white"{}
    _Bloom("Bloom", 2D) = ""{}
    _LuminanceThreshold("_LuminanceThreshold", Float) = 0.5;
    }
  2. CGINCLUDE...ENDCG​ 中实现共享 CG 代码

    1. 属性映射
    2. 结构体(顶点,uv​)
    3. 灰度值(亮度值)计算函数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    CGINCLUDE
    #include "UnityCG.cginc"

    sampler2D _MainTex;
    sampler2D _Bloom;
    float _LuminanceThreshold;

    struct v2f
    {
    float2 uv : TEXCOORD0;
    float4 vertex : SV_POSITION;
    };

    // 计算颜色的亮度值(灰度值)
    fixed luminance(fixed4 color)
    {
    return 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
    }

    ENDCG
  3. 屏幕后处理标配

    • ZTest Always
    • Cull Off
    • ZWrite Off
    1
    2
    3
    4
    5
    Tags { "RenderType"="Opaque" }

    ZTest Always
    Cull Off
    ZWrite Off
  4. 提取 Pass​ 实现

    • 顶点着色器
    • 顶点转换、UV赋值
    • 片元着色器
    • 颜色采样、亮度贡献值计算、颜色*亮度贡献值
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    Pass
    {
    CGPROGRAM
    #pragma vertex vert
    #pragma fragment frag

    #include "UnityCG.cginc"

    v2f vert(appdata_base v)
    {
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = v.texcoord;
    return o;
    }

    fixed4 frag(v2f i) : SV_Target
    {
    // 采样原纹理颜色,得到亮度贡献值,返回颜色*纹理贡献值即得到亮度区域纹理
    fixed4 color = tex2D(_MainTex, i.uv);
    fixed value = clamp(luminance(color) - _LuminanceThreshold, 0, 1);
    return color * value;
    }

    ENDCG
    }

C# 代码:

  1. 创建 C# 脚本,名为 Bloom

  2. 继承屏幕后处理基类 PostEffect

    代码详见:US3S10L4——屏幕后处理基类

  3. 声明亮度阈值成员变量

  4. 重写 OnRenderImage​ 函数

  5. 设置材质球的亮度阈值

  6. 在其中利用 Graphics.Blit​、RenderTexture.GetTemporary​、RenderTexture.ReleaseTemporary​ 函数对纹理进行 Pass​ 处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using UnityEngine;

public class Bloom : PostEffect
{
[Range(0f, 4f)] public float luminanceThreshold = 0.5f; // 一般是0~1,如果以后使用HDR则可以大于1

protected override void OnRenderImage(RenderTexture source, RenderTexture destination)
{
if (PostEffectMaterial == null)
{
Graphics.Blit(source, destination);
return;
}

PostEffectMaterial.SetFloat("_LuminanceThreshold", luminanceThreshold); // 设置 Shader 属性
RenderTexture buffer = RenderTexture.GetTemporary(source.width, source.height, 0); // 获取中间缓冲纹理
Graphics.Blit(source, buffer, PostEffectMaterial, 0); // 提取亮度区域纹理
Graphics.Blit(buffer, destination); // TODO.. 测试看到提取效果
RenderTexture.ReleaseTemporary(buffer);
}
}

显示效果(亮度阈值是0.5):

QQ20250105-170117

模糊颜色区域纹理

主要目的:将提取出来的纹理进行模糊处理(一般采用高斯模糊)

Shader 代码:

  1. 添加模糊半径属性 _BlurSize​,进行属性映射(注意:需要用到纹素)

    1
    2
    3
    4
    5
    6
    7
    Properties
    {
    _MainTex("Texture", 2D) = "white"{}
    _Bloom("Bloom", 2D) = ""{} // 提取出来的亮度区域纹理
    _LuminanceThreshold("_LuminanceThreshold", Float) = 0.5 // 亮度阈值
    _BlurSpread("BlurSpread", Float) = 1 // 模糊半径
    }
    1
    2
    3
    4
    5
    6
    7
    #include "UnityCG.cginc"

    sampler2D _MainTex;
    half4 _MainTex_TexelSize;
    sampler2D _Bloom;
    float _LuminanceThreshold;
    float _BlurSize;
  2. 修改之前高斯模糊 Shader,为其中的两个 Pass​ 命名

    具体代码详见:US3S10L7——高斯模糊

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // Shader: "PostEffect/GaussianBlur"
    // 水平方向上的Pass
    Pass
    {
    Name "GAUSSIAN_BLUR_HORIZONTAL"

    CGPROGRAM
    #pragma vertex vertBlurHorizontal
    #pragma fragment fragBlur
    ENDCG
    }

    // 垂直方向上的Pass
    Pass
    {
    Name "GAUSSIAN_BLUR_VERTICAL"

    CGPROGRAM
    #pragma vertex vertBlurVertical
    #pragma fragment fragBlur
    ENDCG
    }
  3. 在 Bloom Shader 中,利用 UsePass​ 复用 高斯模糊 Shader 中两个 Pass

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    SubShader
    {
    CGINCLUDE
    //...
    ENDCG

    Tags { "RenderType"="Opaque" }

    ZTest Always
    Cull Off
    ZWrite Off

    Pass {/*...提取...*/}

    // 复用高斯模糊的两个Pass
    UsePass "PostEffect/GaussianBlur/GAUSSIAN_BLUR_HORIZONTAL"
    UsePass "PostEffect/GaussianBlur/GAUSSIAN_BLUR_VERTICAL"
    }

C# 代码:

  1. 复制高斯模糊中的 3 个属性
  2. 复制高斯模糊中 C# 代码中处理高斯模糊的逻辑(注意修改使用的 Pass​ 索引!)
  3. 将模糊处理后的纹理,存储 _Bloom​ 纹理属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
using UnityEngine;

public class Bloom : PostEffect
{
[Range(0f, 4f)] public float luminanceThreshold = 0.5f; // 一般是0~1,如果以后使用HDR则可以大于1
[Range(1, 8)] public int downSample = 1; // 降低采样程度
[Range(1, 8)] public int iterations = 1; // 迭代次数
[Range(0f, 3f)] public float blurSpread = 1f; // 模糊半径

protected override void OnRenderImage(RenderTexture source, RenderTexture destination)
{
if (PostEffectMaterial == null)
{
Graphics.Blit(source, destination);
return;
}

PostEffectMaterial.SetFloat("_LuminanceThreshold", luminanceThreshold);

int renderTextureW = source.width / downSample;
int renderTextureH = source.height / downSample;
RenderTexture buffer = RenderTexture.GetTemporary(renderTextureW, renderTextureH, 0);
buffer.filterMode = FilterMode.Bilinear; // 采用双线性过滤模式来缩放,可以让缩放效果更平滑
Graphics.Blit(source, buffer, PostEffectMaterial, 0); // 提取亮度区域纹理

// 高斯模糊迭代
for (int i = 0; i < iterations; i++)
{
// 模糊半径随着迭代次数增加而增加
PostEffectMaterial.SetFloat("_BlurSpread", 1 + i * blurSpread);

// 第一次卷积计算(水平卷积)
RenderTexture iterationBuffer = RenderTexture.GetTemporary(renderTextureW, renderTextureH, 0);
Graphics.Blit(buffer, iterationBuffer, PostEffectMaterial, 1);
RenderTexture.ReleaseTemporary(buffer); // 释放使用完毕的缓存
// 第二次卷积计算(垂直卷积),基于上次卷积的Color相乘计算
buffer = iterationBuffer; // 使用水平卷积完毕的迭代缓存
iterationBuffer = RenderTexture.GetTemporary(renderTextureW, renderTextureH, 0);
Graphics.Blit(buffer, iterationBuffer, PostEffectMaterial, 2);

RenderTexture.ReleaseTemporary(buffer); // 释放使用完毕的缓存
buffer = iterationBuffer; // 使用卷积完毕的缓存
}

PostEffectMaterial.SetTexture("_Bloom", buffer); // 将模糊后的亮度区域纹理传递到Shader内
Graphics.Blit(buffer, destination); // TODO.. 将模糊后的亮度区域显示到屏幕上测试
RenderTexture.ReleaseTemporary(buffer);
}
}

显示效果(亮度阈值0.5,降低采样程度2,迭代次数3,模糊半径3):

QQ20250105-172658

将模糊区域纹理合并到源图像上

主要目的:将模糊处理后的亮度纹理和源纹理进行颜色叠加

Shader代码:

  • 合并 Pass​ 实现

    1. 结构体

      顶点坐标,4 维的 uv​(xy​ 存主纹理,wz​ 存亮度提取纹理)

      1
      2
      3
      4
      5
      struct v2fBloom
      {
      float4 pos : SV_POSITION;
      half4 uv : TEXCOORD0; // xy主要用于对主纹理进行采样,zw主要用于亮度模糊后的纹理采样
      };
    2. 顶点着色器(判断外部传入的 RenderTexture​ 是否被翻转)

      顶点坐标转换,纹理坐标赋值

      注意:亮度纹理的 uv​ 坐标需要判断是否进行 y 轴翻转,因为使用 RenderTexture​ 写入到 Shader 的纹理变量时
      Unity 可能会对其进行 y 轴翻转(根据平台不同而不同),我们可以利用 Unity 提供的预处理宏进行判断

      1
      2
      #if UNITY_UV_STARTS_AT_TOP
      #endif

      如果这个宏被定义,说明当前平台的纹理坐标系的 y 轴原点在顶部,还可以在该宏中用纹素进行判断
      如果纹素的 y 小于 0,为负数,表示需要对 y 轴进行调整,配合起来使用就是

      1
      2
      3
      4
      5
          // 用宏判断uv坐标是否被翻转
      #if UNITY_UV_STARTS_AT_TOP
      // 如果纹素的y小于0,为负数,表示需要对Y轴进行调整
      o.uv.w = _MainTex_TexelSize.y < 0 ? 1 - o.uv.w : o.uv.w;
      #endif

      主纹理不需要我们进行额外处理,一般 Unity 会自动处理,一般只需要在使用 RenderTexture​ 时才考虑该问题

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      v2fBloom vertBloom(appdata_base v)
      {
      v2fBloom o;
      o.pos = UnityObjectToClipPos(v.vertex);
      o.uv.xy = v.texcoord; // 亮度纹理和主纹理要采样相同的地方进行颜色叠加
      o.uv.zw = v.texcoord;

      // 用宏判断uv坐标是否被翻转
      #if UNITY_UV_STARTS_AT_TOP
      // 如果纹素的y小于0,为负数,表示需要对Y轴进行调整
      o.uv.w = _MainTex_TexelSize.y < 0 ? 1 - o.uv.w : o.uv.w;
      #endif

      return o;
      }
    3. 片元着色器

      两个纹理颜色采样后相加

      1
      2
      3
      4
      5
      fixed4 fragBloom(v2fBloom i) : SV_Target
      {
      // 将两个纹理颜色叠加起来
      return tex2D(_MainTex, i.uv.xy) + tex2D(_Bloom, i.uv.zw);
      }
    4. FallBack Off

      这里不需要后备 Shader,因为如果 Shader 不支持就直接不执行了

C# 代码:

对源纹理进行合并处理

显示效果(亮度阈值0.5,降低采样程度2,迭代次数3,模糊半径3):

QQ20250105-175929

可见,Bloom 效果让使得高光部分更亮,并且呈现扩散效果。
其中,哪些亮度区域能够变得更亮是由亮度阈值决定的,而变亮的部分的亮度和扩散程度主要是由模糊程度,也就是那三个高斯模糊相关参数决定

Bloom 效果完整代码

完整 Shader 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
Shader "PostEffect/Bloom"
{
Properties
{
_MainTex("Texture", 2D) = "white"{}
_Bloom("Bloom", 2D) = ""{} // 提取出来的亮度区域纹理
_LuminanceThreshold("_LuminanceThreshold", Float) = 0.5 // 亮度阈值
_BlurSpread("BlurSpread", Float) = 1 // 模糊半径
}

SubShader
{
CGINCLUDE
#include "UnityCG.cginc"

sampler2D _MainTex;
half4 _MainTex_TexelSize;
sampler2D _Bloom;
float _LuminanceThreshold;
float _BlurSize;

struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};

// 计算颜色的亮度值(灰度值)
fixed luminance(fixed4 color)
{
return 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
}

ENDCG

Tags { "RenderType"="Opaque" }

ZTest Always
Cull Off
ZWrite Off

// 提取亮度区域纹理的Pass
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag

#include "UnityCG.cginc"

v2f vert(appdata_base v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
return o;
}

fixed4 frag(v2f i) : SV_Target
{
// 采样原纹理颜色,得到亮度贡献值,返回颜色*纹理贡献值即得到亮度区域纹理
fixed4 color = tex2D(_MainTex, i.uv);
fixed value = clamp(luminance(color) - _LuminanceThreshold, 0, 1);
return color * value;
}

ENDCG
}

// 复用高斯模糊的两个Pass
UsePass "PostEffect/GaussianBlur/GAUSSIAN_BLUR_HORIZONTAL"
UsePass "PostEffect/GaussianBlur/GAUSSIAN_BLUR_VERTICAL"

// 合并源图像和模糊后的亮度区域纹理的Pass
Pass
{
CGPROGRAM
#pragma vertex vertBloom
#pragma fragment fragBloom

#include "UnityCG.cginc"

struct v2fBloom
{
float4 pos : SV_POSITION;
half4 uv : TEXCOORD0; // xy主要用于对主纹理进行采样,zw主要用于亮度模糊后的纹理采样
};

v2fBloom vertBloom(appdata_base v)
{
v2fBloom o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv.xy = v.texcoord; // 亮度纹理和主纹理要采样相同的地方进行颜色叠加
o.uv.zw = v.texcoord;

// 用宏判断uv坐标是否被翻转
#if UNITY_UV_STARTS_AT_TOP
// 如果纹素的y小于0,为负数,表示需要对Y轴进行调整
o.uv.w = _MainTex_TexelSize.y < 0 ? 1 - o.uv.w : o.uv.w;
#endif

return o;
}

fixed4 fragBloom(v2fBloom i) : SV_Target
{
// 将两个纹理颜色叠加起来
return tex2D(_MainTex, i.uv.xy) + tex2D(_Bloom, i.uv.zw);
}

ENDCG
}
}

Fallback Off
}

完整 C# 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
using UnityEngine;

public class Bloom : PostEffect
{
[Range(0f, 4f)] public float luminanceThreshold = 0.5f; // 一般是0~1,如果以后使用HDR则可以大于1
[Range(1, 8)] public int downSample = 1; // 降低采样程度
[Range(1, 8)] public int iterations = 1; // 迭代次数
[Range(0f, 3f)] public float blurSpread = 1f; // 模糊半径

protected override void OnRenderImage(RenderTexture source, RenderTexture destination)
{
if (PostEffectMaterial == null)
{
Graphics.Blit(source, destination);
return;
}

PostEffectMaterial.SetFloat("_LuminanceThreshold", luminanceThreshold);

int renderTextureW = source.width / downSample;
int renderTextureH = source.height / downSample;
RenderTexture buffer = RenderTexture.GetTemporary(renderTextureW, renderTextureH, 0);
buffer.filterMode = FilterMode.Bilinear; // 采用双线性过滤模式来缩放,可以让缩放效果更平滑
Graphics.Blit(source, buffer, PostEffectMaterial, 0); // 提取亮度区域纹理

// 高斯模糊迭代
for (int i = 0; i < iterations; i++)
{
// 模糊半径随着迭代次数增加而增加
PostEffectMaterial.SetFloat("_BlurSpread", 1 + i * blurSpread);

// 第一次卷积计算(水平卷积)
RenderTexture iterationBuffer = RenderTexture.GetTemporary(renderTextureW, renderTextureH, 0);
Graphics.Blit(buffer, iterationBuffer, PostEffectMaterial, 1);
RenderTexture.ReleaseTemporary(buffer); // 释放使用完毕的缓存
// 第二次卷积计算(垂直卷积),基于上次卷积的Color相乘计算
buffer = iterationBuffer; // 使用水平卷积完毕的迭代缓存
iterationBuffer = RenderTexture.GetTemporary(renderTextureW, renderTextureH, 0);
Graphics.Blit(buffer, iterationBuffer, PostEffectMaterial, 2);

RenderTexture.ReleaseTemporary(buffer); // 释放使用完毕的缓存
buffer = iterationBuffer; // 使用卷积完毕的缓存
}

PostEffectMaterial.SetTexture("_Bloom", buffer); // 将模糊后的亮度区域纹理传递到Shader内
Graphics.Blit(source, destination, PostEffectMaterial, 3); // 将合并后的两个纹理赋值到目标纹理内
RenderTexture.ReleaseTemporary(buffer);
}
}