1. 程式人生 > >Unity 實現部分物體Bloom效果

Unity 實現部分物體Bloom效果

之前研究了一下怎麼讓螢幕裡部分東西顯示bloom效果,例如只是特效顯示bloom效果而角色不顯示,現在記錄一下

我這裡加了效果圖和工程下載地址,方便大家瞭解

擴充套件:可以修改替換shader,把特定效果部分做成不同顏色區域來進行不同螢幕處理

例如:

渲染成紅色作為bloom取圖區來處理bloom效果

渲染綠色作為扭曲取圖區來處理熱扭曲效果

場景裡部分實現bloom效果

這裡之前先說一下Bloom的實現原理

一開始我們設定一個泛光的亮度閾值,然後我們根據螢幕的渲染圖來進行篩選,所有小於這個閾值的畫素過濾掉(黑色畫素),其他畫素保留,這樣子就只保留了要發光的畫素,其他的都是黑色泛光效果是由衍射效果產生的,我們現實世界中看到的泛光效果,最亮的地方實際上是會向暗的地方擴散的,也就是說在亮的地方,邊界是不明顯的,所以我們就需要對泛光是部分,也就是我們上一步操作的結果圖片進行模糊操作,達到光溢位的效果,最後,我們將處理過的影象和原影象進行疊加,就得到了最終的效果。下面是bloom的實現流程圖(盜圖)

下面是部分Bloom實現原理

部分和上面全屏泛光的區別在於

全屏泛光:全屏泛光是那螢幕影象來處理泛光,然後和螢幕疊加

部分泛光:自己渲染一張特定的圖,然後用這張圖處理泛光,最後和螢幕疊加

區別在於這張特定的圖,其他的和全屏泛光一樣

如果分layer來渲染這張特定圖,則沒有遮擋效果,所以這種渲染可以放棄

這張特定圖除了要泛光之外其餘都要黑色也要走遮擋效果,所以用shader替換渲染方法來處理

 

泛光特定圖獲取

原理

獲取泛光圖要用到替換shader渲染方法

m_Camera.RenderWithShader(replaceShader, "RenderType");

這個替換shader是根據shader的標籤RenderType來進行替換

除了repalceShader加了對應的替換標籤

物體使用的shader也要加對應的標籤,所以我這邊直接拿原生的shader改一下標籤

我這邊自己加了“BloomTransparent”和“Bloom”標籤(這些標籤都是自己定義,自己知道直接寫在shader上面)

BloomTransparent:適合渲染佇列是Transparent的物體替換shader,對應物體shader改一下RenderType為“BloomTransparent”

Bloom:適合渲染佇列是Opaque的物體替換shader,對應的shader改一下RenderType為“Bloom”

我舉例一下自帶的Mobile-Particle-Add,我這裡順便改一下名字,加了"RenderType"="BloomTransparent",如有需要還要改standar shader,這些看情況而定,這裡只做個演示

// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)

// Simplified Additive Particle shader. Differences from regular Additive Particle one:
// - no Tint color
// - no Smooth particle support
// - no AlphaTest
// - no ColorMask

Shader "Bloom/Particles/Additive" {
Properties {
    _MainTex ("Particle Texture", 2D) = "white" {}
}

Category {
    Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="BloomTransparent" "PreviewType"="Plane" }
    Blend SrcAlpha One
    Cull Off Lighting Off ZWrite Off Fog{ Mode Off }

    BindChannels {
        Bind "Color", color
        Bind "Vertex", vertex
        Bind "TexCoord", texcoord
    }

    SubShader {
        Pass {
            SetTexture [_MainTex] {
                combine texture * primary
            }
        }
    }
}
}

下面是替換shader

分了三種渲染

RenderType為Opaque直接渲染成黑色,

RenderType為Bloom直接渲染原圖

RenderType為BloomTransparent按照混合模式渲染

其他直接渲染黑色

Shader "Hidden/Bloom Replace" 
{
	//替換標籤是Bloom的shader
    SubShader
	{
        Tags { "RenderType" = "Bloom" }
        Pass {
			CGPROGRAM
			#pragma vertex vert_img
			#pragma fragment frag
			#pragma fragmentoption ARB_precision_hint_fastest
			#include "UnityCG.cginc"

			uniform sampler2D _MainTex;

			half4 frag(v2f_img i) : COLOR
			{
				return tex2D(_MainTex,i.uv);
			}
			ENDCG
        } 
    }

	//替換標籤是BloomTransparent的shader
	SubShader
	{
		Tags{ "RenderType" = "BloomTransparent" }
		Blend SrcAlpha One
		Cull Off Lighting Off ZWrite Off Fog{ Mode Off }
		Pass
		{
			CGPROGRAM
			#pragma vertex vert_img
			#pragma fragment frag
			#pragma fragmentoption ARB_precision_hint_fastest
			#include "UnityCG.cginc"

			uniform sampler2D _MainTex;
			half4 frag(v2f_img i) : COLOR
			{
				return tex2D(_MainTex,i.uv);
			}
			ENDCG
		}
	}
    
	//替換標籤是Opaque的shader,這裡直接渲染為黑色
    SubShader 
	{
        Tags { "RenderType" = "Opaque" }
        Pass 
		{    
            CGPROGRAM
            #pragma vertex vert_img
            #pragma fragment frag
            #pragma fragmentoption ARB_precision_hint_fastest
            #include "UnityCG.cginc"
            half4 frag(v2f_img i) : COLOR
            {
                return half4(0,0,0,0);
            }
            ENDCG
        }
    }   
	Fallback Off
}

RenderType為BloomTransparent和Bloom渲染出原來的顏色,其他沒有標籤則渲染成黑色,這樣的泛光渲染圖就這樣出來了

下面是寫了個指令碼用來渲染泛光特定圖

using UnityEngine;
using System.Collections;

/// <summary>
/// 渲染需要廣泛那部分的圖
/// </summary>
public class RenderBloomTexture : MonoBehaviour
{
    /// <summary>
    /// 主攝像機
    /// </summary>
    public Camera m_FollowCamera;

    /// <summary>
    /// 渲染需要泛光的攝像機
    /// </summary>
    private Camera m_Camera;

    /// <summary>
    /// 替換shader
    /// </summary>
    public Shader replaceShader;

    void Start()
    {
        m_Camera = GetComponent<Camera>();
        //攝像機背景要設定為黑色
        m_Camera.enabled = false;
        m_Camera.clearFlags = CameraClearFlags.SolidColor;
        m_Camera.backgroundColor = Color.black;
        UpdateCamera();
        UpdateCameraSetting();
    }

    void LateUpdate()
    {
        UpdateCamera();
        //呼叫渲染
        m_Camera.RenderWithShader(replaceShader, "RenderType");
    }

    void UpdateCamera()
    {
        transform.position = m_FollowCamera.transform.position;
        transform.rotation = m_FollowCamera.transform.rotation;
    }

    void UpdateCameraSetting()
    {
        m_Camera.orthographic = m_FollowCamera.orthographic;
        m_Camera.orthographicSize = m_FollowCamera.orthographicSize;
        m_Camera.nearClipPlane = m_FollowCamera.nearClipPlane;
        m_Camera.farClipPlane = m_FollowCamera.farClipPlane;
        m_Camera.fieldOfView = m_FollowCamera.fieldOfView;
    }
}

使用

1.在場景的mian camera下面建立一個camera命名為“RenderCamera”,然後掛上RenderBloomTexture指令碼

2.建立一個1920*1080的rendertexture,我這邊做測試game檢視解析度和rendertexture一致都是1920*1080

3.把對應的以後shader拖上去和主攝像機拖上去,把新建的rendertexture拖到RenderCamera的TargetTexture,如下圖

.

下面是對應物體設定的shader(這裡的standard Shader改成加了標籤Bloom改名為standardBloom)

4.直接執行然後看看RenderTexture,如下圖有東西擋住就是ok的

泛光渲染圖就這樣實現出來

 

部分泛光實現

整個泛光實現我就不多說,網上多的是

先貼一下效果圖

實現BloomEffect效果shader

Shader "BloomEffect" {

	Properties
	{
		_MainTex("Base (RGB)", 2D) = "white" {}
		_BlurTex("Blur", 2D) = "white"{}
	}

		CGINCLUDE
#include "UnityCG.cginc"

	//用於閾值提取高亮部分
	struct v2f_threshold
	{
		float4 pos : SV_POSITION;
		float2 uv : TEXCOORD0;
	};

	//用於blur
	struct v2f_blur
	{
		float4 pos : SV_POSITION;
		float2 uv  : TEXCOORD0;
		float4 uv01 : TEXCOORD1;
		float4 uv23 : TEXCOORD2;
		float4 uv45 : TEXCOORD3;
	};

	//用於bloom
	struct v2f_bloom
	{
		float4 pos : SV_POSITION;
		float2 uv  : TEXCOORD0;
		float2 uv1 : TEXCOORD1;
	};

	sampler2D _MainTex;
	float4 _MainTex_TexelSize;
	sampler2D _BlurTex;
	float4 _BlurTex_TexelSize;
	float4 _offsets;
	float4 _colorThreshold;
	float4 _bloomColor;
	float _bloomFactor;

	//高亮部分提取shader
	v2f_threshold vert_threshold(appdata_img v)
	{
		v2f_threshold o;
		o.pos = UnityObjectToClipPos(v.vertex);
		o.uv = v.texcoord.xy;
		//dx中紋理從左上角為初始座標,需要反向
#if UNITY_UV_STARTS_AT_TOP
		if (_MainTex_TexelSize.y < 0)
			o.uv.y = 1 - o.uv.y;
#endif	
		return o;
	}

	fixed4 frag_threshold(v2f_threshold i) : SV_Target
	{
		fixed4 color = tex2D(_MainTex, i.uv);
		//僅當color大於設定的閾值的時候才輸出
		return saturate(color - _colorThreshold);
	}

		//高斯模糊 vert shader(上一篇文章有詳細註釋)
	v2f_blur vert_blur(appdata_img v)
	{
		v2f_blur o;
		_offsets *= _MainTex_TexelSize.xyxy;
		o.pos = UnityObjectToClipPos(v.vertex);
		o.uv = v.texcoord.xy;

		o.uv01 = v.texcoord.xyxy + _offsets.xyxy * float4(1, 1, -1, -1);
		o.uv23 = v.texcoord.xyxy + _offsets.xyxy * float4(1, 1, -1, -1) * 2.0;
		o.uv45 = v.texcoord.xyxy + _offsets.xyxy * float4(1, 1, -1, -1) * 3.0;

		return o;
	}

	//高斯模糊 pixel shader(上一篇文章有詳細註釋)
	fixed4 frag_blur(v2f_blur i) : SV_Target
	{
		fixed4 color = fixed4(0,0,0,0);
		color += 0.40 * tex2D(_MainTex, i.uv);
		color += 0.15 * tex2D(_MainTex, i.uv01.xy);
		color += 0.15 * tex2D(_MainTex, i.uv01.zw);
		color += 0.10 * tex2D(_MainTex, i.uv23.xy);
		color += 0.10 * tex2D(_MainTex, i.uv23.zw);
		color += 0.05 * tex2D(_MainTex, i.uv45.xy);
		color += 0.05 * tex2D(_MainTex, i.uv45.zw);
		return color;
	}

	//Bloom效果 vertex shader
	v2f_bloom vert_bloom(appdata_img v)
	{
		v2f_bloom o;
		//mvp矩陣變換
		o.pos = UnityObjectToClipPos(v.vertex);
		//uv座標傳遞
		o.uv.xy = v.texcoord.xy;
		o.uv1.xy = o.uv.xy;
#if UNITY_UV_STARTS_AT_TOP
		if (_MainTex_TexelSize.y < 0)
			o.uv.y = 1 - o.uv.y;
#endif	
		return o;
	}

	fixed4 frag_bloom(v2f_bloom i) : SV_Target
	{
		//取原始清晰圖片進行uv取樣
		fixed4 ori = tex2D(_MainTex, i.uv1);
		//取模糊普片進行uv取樣
		fixed4 blur = tex2D(_BlurTex, i.uv);
		//輸出= 原始影象,疊加bloom權值*bloom顏色*泛光顏色
		fixed4 final = ori + _bloomFactor * blur * _bloomColor;
		return final;
	}

	ENDCG

	SubShader
	{
		//pass 0: 提取高亮部分
		Pass
		{
			ZTest Off
			Cull Off
			ZWrite Off
			Fog{ Mode Off }

			CGPROGRAM
#pragma vertex vert_threshold
#pragma fragment frag_threshold
			ENDCG
		}

		//pass 1: 高斯模糊
		Pass
		{
			ZTest Off
			Cull Off
			ZWrite Off
			Fog{ Mode Off }

			CGPROGRAM
#pragma vertex vert_blur
#pragma fragment frag_blur
			ENDCG
		}

		//pass 2: Bloom效果
		Pass
		{

			ZTest Off
			Cull Off
			ZWrite Off
			Fog{ Mode Off }

			CGPROGRAM
#pragma vertex vert_bloom
#pragma fragment frag_bloom
			ENDCG
		}

	}
}

下面是控制Bloom效果指令碼

using System;
using UnityEngine;
using System.Collections;

public class PartBloom : MonoBehaviour
{
    //取樣率
    public int samplerScale = 1;
    //高亮部分提取閾值
    public Color colorThreshold = Color.gray;
    //Bloom泛光顏色
    public Color bloomColor = Color.white;
    //Bloom權值
    [Range(0.0f, 1.0f)]
    public float bloomFactor = 0.5f;

    /// <summary>
    /// Bloom材質球
    /// </summary>
    public Material _Material;

    /// <summary>
    /// 特定渲染圖
    /// </summary>
    public RenderTexture m_R;

    void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if (_Material)
        {
            RenderTexture temp1 = RenderTexture.GetTemporary(m_R.width, m_R.height, 0, m_R.format);
            RenderTexture temp2 = RenderTexture.GetTemporary(m_R.width, m_R.height, 0, m_R.format);

            //複製泛光圖
            Graphics.Blit(m_R, temp1);


            //根據閾值提取高亮部分,使用pass0進行高亮提取
            _Material.SetVector("_colorThreshold", colorThreshold);
            Graphics.Blit(temp1, temp2, _Material, 0);

            //高斯模糊,兩次模糊,橫向縱向,使用pass1進行高斯模糊
            _Material.SetVector("_offsets", new Vector4(0, samplerScale, 0, 0));
            Graphics.Blit(temp2, temp1, _Material, 1);
            _Material.SetVector("_offsets", new Vector4(samplerScale, 0, 0, 0));
            Graphics.Blit(temp1, temp2, _Material, 1);

            //Bloom,將模糊後的圖作為Material的Blur圖引數
            _Material.SetTexture("_BlurTex", temp2);
            _Material.SetVector("_bloomColor", bloomColor);
            _Material.SetFloat("_bloomFactor", bloomFactor);

            //使用pass2進行景深效果計算,清晰場景圖直接從source輸入到shader的_MainTex中
            Graphics.Blit(source, destination, _Material, 2);

            //釋放申請的RT
            RenderTexture.ReleaseTemporary(temp1);
            RenderTexture.ReleaseTemporary(temp2);
        }
    }
}

使用

1.先建立一個使用BloomEffect shader的材質球

2.把bloom控制指令碼掛到mian camera上面,然後把材質球和rendertexture拖上去如下圖

 

最後放出工程下載

1RenderBloomTexture是展示渲染泛光圖

2PartBloom是實現部分bloom的demo

PS:rendertexure的生成應該動態生成出來,尺寸也要根據螢幕解析度來計算,我這裡只加了幾個shader,看情況把官方的shader改一下,然後規定美術用那些shader,如果替換shader麻煩那就直接寫工具替換就行

連結:https://pan.baidu.com/s/1AGcAWkVMufggPsF2ZEzvEw 
提取碼:cvn1