using
System;
using
DG.Tweening;
using
UnityEngine;
using
UnityEngine.UI;
[ExecuteAlways]
/// <summary>
/// RenderTextureをシェーダーに流し込んで画像を発光させる処理
/// </summary>
public
class
Glow3PassController
:
MonoBehaviour
{
[Serializable]
/// <summary>
/// シェーダーに渡すパラメータはこちらで管理
/// </summary>
private
class
Parameters
{
[SerializeField,
Range(0,
10)]
private
float
luminance
=
5;
public
float
Luminance
=>
luminance;
[SerializeField,
Range(0,
10)]
private
int
blurCount
=
5;
public
int
BlurCount
=>
blurCount;
[SerializeField,
Range(0,
10)]
private
float
blurDistance
=
3;
public
float
BlurDistance
=>
blurDistance;
[SerializeField,
Range(0,
10)]
private
float
blurPower
=
5;
public
float
BlurPower
=>
blurPower;
// 変更があったときだけ更新するためのキャッシュ
private
float
_lastLuminance;
private
int
_lastBlurCount;
private
float
_lastBlurPower;
private
float
_lastBlurDistance;
/// <summary>
/// インスペクターのパラメータに変化があった場合trueを返す
/// </summary>
public
bool
CahngedParameter
=>
!Mathf.Approximately(luminance,
_lastLuminance)
||
blurCount
!=
_lastBlurCount
||
!Mathf.Approximately(blurPower,
_lastBlurPower)
||
!Mathf.Approximately(blurDistance,
_lastBlurDistance);
/// <summary>
/// キャッシュに最新のパラメーター値を反映させる
/// </summary>
public
void
UpdateParameter()
{
_lastLuminance
=
luminance;
_lastBlurCount
=
blurCount;
_lastBlurPower
=
blurPower;
_lastBlurDistance
=
blurDistance;
}
}
[SerializeField]
private
Image
targetImage;
[SerializeField]
private
Shader
targetShader;
[SerializeField]
private
Parameters
parameters;
private
Texture2D
sourceTexture;
private
Material
createdMaterial;
private
RenderTexture
tmpRenderTexture1;
private
RenderTexture
tmpRenderTexture2;
/// <summary>
/// 初期化処理の必要性がある場合trueを返す
/// </summary>
private
bool
NeedsInitialization
=>
sourceTexture
==
null
||
sourceTexture
!=
targetImage.sprite.texture
||
createdMaterial
==
null
||
targetImage.material
!=
createdMaterial
||
tmpRenderTexture1
==
null
||
tmpRenderTexture2
==
null;
/// <summary>
/// 初期化処理
/// </summary>
private
void
Init()
{
Release();
sourceTexture
=
targetImage.sprite.texture;
createdMaterial
=
new
(targetShader);
createdMaterial.hideFlags
=
HideFlags.DontSave;
targetImage.material
=
createdMaterial;
//処理負荷軽減のため、大元の画像サイズから1/2のサイズでtmpRenderTexture1, 2を作成
//tmpRenderTexture1, 2は両方ともぼかし処理のための画像なので、サイズが小さくても見た目に違和感は出ない
tmpRenderTexture1
=
new
RenderTexture(sourceTexture.width
/
2,
sourceTexture.height
/2,
0);
tmpRenderTexture2
=
new
RenderTexture(sourceTexture.width
/
2,
sourceTexture.height
/
2,
0);
//事前にBlitで更新される予定のtmpRenderTexture2を、_CustomTexの名前でシェーダー側と紐づけておく
//後はCanvasRender内の処理で自動的にPass0を実行させて、その_CustomTexを画像に反映させる
targetImage.material.SetTexture("_CustomTex",
tmpRenderTexture2);
//初期化後は問答無用でUpdateTextureを呼ぶ
UpdateTexture();
}
/// <summary>
/// 最初にUnityが1回だけ呼ぶ処理
/// </summary>
private
void
Start()
{
if
(NeedsInitialization)
{
Init();
}
}
/// <summary>
/// Unityから毎フレーム呼ばれる処理
/// </summary>
private
void
Update()
{
if
(targetImage
==
null
||
targetImage.sprite
==
null
||
targetShader
==
null)
{
return;
}
if
(NeedsInitialization)
{
Init();
}
else
if(parameters.CahngedParameter)
{
UpdateTexture();
}
}
/// <summary>
/// 対象のシェーダーのPass1, 2を実行させる
/// </summary>
private
void
UpdateTexture()
{
parameters.UpdateParameter();
//輝度をセット
createdMaterial.SetFloat("_Luminance",
parameters.Luminance);
//Blur設定をセット
createdMaterial.SetInt("_BlurCount",
parameters.BlurCount);
createdMaterial.SetFloat("_BlurPower",
parameters.BlurPower);
createdMaterial.SetFloat("_BlurDistance",
parameters.BlurDistance);
//第1引数の画像を元に、Blitでシェーダー内のPass1を実行させ、結果を第2引数に書き込む
//Blitの仕様上、この書き込み処理は、第3引数のMaterial内の_MainTexを通して行われる
Graphics.Blit(sourceTexture,
tmpRenderTexture1,
createdMaterial,
1);
//Pass1の結果を元にPass2を実行
Graphics.Blit(tmpRenderTexture1,
tmpRenderTexture2,
createdMaterial,
2);
}
/// <summary>
/// 作成したRenderTextureやMaterialのメモリを解放する
/// </summary>
private
void
Release()
{
if
(tmpRenderTexture1
!=
null)
{
tmpRenderTexture1.Release();
tmpRenderTexture2.Release();
}
if
(createdMaterial
!=
null)
{
DestroyImmediate(createdMaterial);
}
}
/// <summary>
/// このGameObjectを非表示にした時の処理
/// </summary>
private
void
OnDisable()
{
Release();
}
}