【UnityShader】Bloomシェーダー #106
前回の成果
3Dモデルを液体のようにするシェーダーについて学んだ。
今回やること
以下のサイト様を参考に、Bloomシェーダーを実装します。
事前準備
Scene上にSpriteRendererを配置します。
そして、MainCameraに今回使用するScriptをアタッチして事前準備完了となります。
ソースコード
Script
using UnityEngine; [ExecuteInEditMode] public class BloomTexture : MonoBehaviour { [SerializeField] private Shader _shader; // 全体の強度 [SerializeField, Range(0, 1f)] public float _strength = 0.3f; // ブラーの強度 [SerializeField, Range(1, 64)] public int _blur = 20; // 明るさのしきい値 [SerializeField, Range(0, 1f)] public float _threshold = 0.3f; // RenderTextureサイズの分母 [SerializeField, Range(1,12)] public int _ratio = 1; [SerializeField, Range(1f, 10f)] private float _offset = 1f; private Material _material; private float[] _weights = new float[10]; void OnRenderImage(RenderTexture src, RenderTexture dest) { if (_material == null) { _material = new Material(_shader); _material.hideFlags = HideFlags.DontSave; } _OnRenderImage(src, dest); } private void _OnRenderImage (RenderTexture src, RenderTexture dest) { int renderTextureX = src.width / _ratio; int renderTextureY = src.height / _ratio; RenderTexture tmp = CreateRenderTexture(renderTextureX, renderTextureY); RenderTexture tmp2 = CreateRenderTexture(renderTextureX, renderTextureY); // Bloom _material.SetFloat ("_Strength", _strength); _material.SetFloat ("_Threshold", _threshold); _material.SetFloat ("_Blur", _blur); _material.SetTexture ("_Tmp", tmp); Graphics.Blit (src, tmp, _material, 0); // ガウシアンブラー UpdateWeights(); _material.SetFloatArray("_Weights", _weights); float x = _offset / tmp2.width; float y = _offset / tmp2.height; _material.SetVector("_Offset", new Vector4(x, 0, 0, 0)); Graphics.Blit(src, tmp2, _material, 1); _material.SetVector("_Offset", new Vector4(0, y, 0, 0)); Graphics.Blit(tmp2, dest, _material, 1); RenderTexture.ReleaseTemporary(tmp); RenderTexture.ReleaseTemporary(tmp2); } /// <summary> /// RenderTextureの生成 /// </summary> /// <param name="width"></param> /// <param name="height"></param> /// <returns></returns> private RenderTexture CreateRenderTexture(int width, int height) { RenderTexture renderTexture = RenderTexture.GetTemporary(width, height, 0, RenderTextureFormat.ARGB32); renderTexture.filterMode = FilterMode.Bilinear; return renderTexture; } /// <summary> /// 重みの計算 /// </summary> private void UpdateWeights() { float total = 0; float d = _blur * _blur * 0.01f; for (int i = 0; i < _weights.Length; i++) { float x = 1.0f + i * 2f; float w = Mathf.Exp(-0.5f * (x * x) / d); _weights[i] = w; // xとyがあるので2倍 if (i > 0) { w *= 2.0f; } total += w; } // 正規化 for (int i = 0; i < _weights.Length; i++) { _weights[i] /= total; } } }
シェーダー
Shader "Unlit/BloomTexture" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Cull Off ZWrite Off ZTest Always Pass { CGPROGRAM #pragma vertex vert_img #pragma fragment fragBright ENDCG } Pass { CGPROGRAM #pragma vertex vert_img #pragma fragment fragGauss ENDCG } CGINCLUDE #include "UnityCG.cginc" sampler2D _MainTex; sampler2D _Tmp; float _Strength; float _Blur; float _Threshold; half4 _Offset; static const int samplingCount = 10; half _Weights[samplingCount]; fixed4 fragBright (v2f_img i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv); // ピクセルの明るさ float bright = (col.r + col.g + col.b) / 3; // しきい値によって明るさを決める float tmp = step(_Threshold, bright); return col * tmp * _Strength; } fixed4 fragGauss (v2f_img i) : SV_Target { fixed4 col = 0; // メモリサイズを大きくする代わりに高速にする [unroll] // 左右へのサンプリング for (int j = samplingCount - 1; j > 0; j--) { col += tex2D(_Tmp, i.uv - (_Offset.xy * j)) * _Weights[j]; } [unroll] // 上下へのサンプリング for (int j = 0; j < samplingCount; j++) { col += tex2D(_Tmp, i.uv + (_Offset.xy * j)) * _Weights[j]; } return col + tex2D(_MainTex, i.uv); } ENDCG } }
Bloom
Bloomとは
ブルームとは、光源から光があふれ出るようなエフェクトのことを指します。ゲームでは光源は数えきれないほど存在します。太陽の光、反射するミラーやガラス、スポットライトやサーチライトといったオブジェクトの光だけでなく、シューティングゲームの弾幕や、スキルエフェクトなども光源として扱われます。
とのことです。
このエフェクトを使用することで、オブジェクトにライティングを付与させてリッチな表現にすることができます。
Bloom適応前
Bloom - Unity マニュアル:より引用
Bloom適応後
Bloom - Unity マニュアル:より引用
BloomはPostProcessVolumeコンポーネントから追加することもできます。
光らせる箇所の取得
フラグメントシェーダーでテクセルの色を取得し、スクリプトで渡したしきい値とstep()します。
そうすることで、しきい値以上の明るさを箇所を2Pass目に渡して処理することができます。
// ピクセルの明るさ float bright = (col.r + col.g + col.b) / 3; // しきい値によって明るさを決める float tmp = step(_Threshold, bright);
光らせる箇所
しきい値が0.8の時の2Pass目にわたす箇所になります。
2Pass目で元画像に合成するため、黒くしておくことで計算を楽にできるようにしています。
ブラー処理
1Pass目で取得した箇所にブラー処理をかけて、元の画像に加算合成します。
fixed4 col = 0; // メモリサイズを大きくする代わりに高速にする [unroll] // 左右へのサンプリング for (int j = samplingCount - 1; j > 0; j--) { col += tex2D(_Tmp, i.uv - (_Offset.xy * j)) * _Weights[j]; } [unroll] // 上下へのサンプリング for (int j = 0; j < samplingCount; j++) { col += tex2D(_Tmp, i.uv + (_Offset.xy * j)) * _Weights[j]; } return col + tex2D(_MainTex, i.uv);
今回は以前勉強した、ガウシアンブラーを使用しています。
このブラーを使用することで、より軽量な計算でブラー処理を行うことが出来ます。
詳しくは以下を参考にしてください。
ガウシアンブラーの横方向サンプリング結果
ガウシアンブラーの縦方向サンプリング結果
スクリプト側
備忘録程度に記載します。
OnRenderImage
カメラのレンダリングが完了した時に呼ばれる関数になります。
第一引数には、入力画像が、第二引数には出力画像が渡されます。
ExecuteInEditMode
UnityをPlayモードにせず、Editモードのままで実行することが出来ます。
ただし、実行できるのは以下の3つのみになります。
- Update
- OnGUI
- OnRenderObject
Playモード
PlayモードはUnityの右三角が押されている状態のことを指します。
結果
SpriteRendererにBloomエフェクトがかかれば成功です。
また、普通のオブジェクトに対しても使用することができます。
今回は以上となります。
ここまでご視聴ありがとうございました。
この作品はユニティちゃんライセンス条項の元に提供されています