-->




Shader "UI/Test2Pass"
{
//以下は全部インスペクター上には表示しないけど必要な処理
//ここで宣言しておかないとSceneセーブした時などに値が維持されない
Properties
{
[HideInInspector] _MainTex("-", 2D) = "white"{} //uGUI及びGraphics.Blitが標準で扱うテクスチャ
[HideInInspector] _CustomTex("-", 2D)="white"{}
[HideInInspector] _IgnoreRange("-", float) = 0
}
SubShader
{
Tags
{
"RenderPipeline" = "UniversalPipeline"
"Queue" = "Transparent"
}
Cull Off
ZWrite Off
HLSLINCLUDE
#pragma vertex vert
#pragma fragment frag
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
//uGUI Image > Source Imageにアタッチされた画像を参照できるようにする
TEXTURE2D(_MainTex);
SAMPLER(sampler_MainTex);
//最終的に画面に出力されるテクスチャを宣言、中身のデータはC#側から渡される
TEXTURE2D(_CustomTex);
SAMPLER(sampler_CustomTex);
//SRP Batcherを適用、動的に変更される数値はこの中に定義
CBUFFER_START(UnityPerMaterial)
half _IgnoreRange;
CBUFFER_END
struct Attributes
{
half2 uv : TEXCOORD0;
float4 positionOS : POSITION;
};
struct Varyings
{
half2 uv : TEXCOORD0;
float4 positionCS : SV_POSITION;
};
Varyings vert (Attributes input)
{
Varyings output;
output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
output.uv = input.uv;
return output;
}
ENDHLSL
//Pass0: 一番上のPassはuGUIのCanvasRendererから自動的に呼び出される、処理的には下のPass1より後に実行される
Pass
{
//最終出力は背景と合成させる
Blend SrcAlpha OneMinusSrcAlpha
HLSLPROGRAM
//画像x軸の真ん中あたりの描画を飛ばす
half4 frag (Varyings input) : SV_Target
{
//_MainTexではなく、C#側から書き込みを行った_CustomTexを最終的な描画対象とする
//これによりuGUIの標準の描画ロジックを回避して、Pass1の内容を引き継いで出力できる
//まずこの_CustomTexから、指定のUV座標のピクセル色を抜き出す
half4 original = SAMPLE_TEXTURE2D(_CustomTex, sampler_CustomTex, input.uv);
half isRightInUV = step(0.5 + _IgnoreRange, input.uv.x); //渡されたUV座標が画像の右側に存在するか
half isLeftInUV = step(input.uv.x, 0.5 - _IgnoreRange); //渡されたUV座標が画像の左側に存在するか
half isCenter = 1 - max(isRightInUV, isLeftInUV); //渡されたUV座標がx軸の真ん中あたりかどうか
return lerp(original, half4(0, 0, 0, 0), isCenter); //真ん中辺りだったら透明色を渡して描画させない
}
ENDHLSL
}
//Pass1: C#から明示的に呼び出す
Pass
{
//この段階では背景との合成が必要ないのでBlend Off
Blend One Zero
HLSLPROGRAM
//画像y軸の真ん中あたりの描画を飛ばす
half4 frag (Varyings input) : SV_Target
{
//SourceImageにアタッチされた画像の、指定UV座標のピクセル色を取得
half4 original = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv);
half isUpInUV = step(0.5 + _IgnoreRange, input.uv.y); //渡されたUV座標が画像の上側に存在するか
half isDownInUV = step(input.uv.y, 0.5 - _IgnoreRange); //渡されたUV座標が画像の下側に存在するか
half isCenter = 1 - max(isUpInUV, isDownInUV); //渡されたUV座標がy軸の真ん中あたりかどうか
return lerp(original, half4(0, 0, 0, 0), isCenter); //真ん中辺りだったら透明色を渡して描画させない
}
ENDHLSL
}
}
}
//Pass0: 一番上のPassはuGUIのCanvasRendererから自動的に呼び出される、処理的には下のPass1より後に実行される
Pass
{
//最終出力は背景と合成させる
Blend SrcAlpha OneMinusSrcAlpha
HLSLPROGRAM
//画像x軸の真ん中あたりの描画を飛ばす
half4 frag (Varyings input) : SV_Target
//Pass1: C#から明示的に呼び出す
Pass
{
//この段階では背景との合成が必要ないのでBlend Off
Blend One Zero
HLSLPROGRAM
//画像y軸の真ん中あたりの描画を飛ばす
half4 frag (Varyings input) : SV_Target
//_MainTexではなく、C#側から書き込みを行った_CustomTexを最終的な描画対象とする
//これによりuGUIの標準の描画ロジックを回避して、Pass1の内容を引き継いで出力できる
//まずこの_CustomTexから、指定のUV座標のピクセル色を抜き出す
half4 original = SAMPLE_TEXTURE2D(_CustomTex, sampler_CustomTex, input.uv);

using UnityEngine;
using UnityEngine.UI;
[ExecuteAlways]
/// <summary>
/// uGUIの2Passを実装するために、RenderTextureを自前で流し込むクラス
/// </summary>
using UnityEngine;
using UnityEngine.UI;
[ExecuteAlways]
/// <summary>
/// uGUIの2Passを実装するために、RenderTextureを自前で流し込むクラス
/// </summary>
public class Test2PassController : MonoBehaviour
{
[SerializeField] private Image targetImage;
[SerializeField] private Shader targetShader;
[SerializeField, Range(0f, 0.5f)] private float ignoreRange; //画像描画の無視範囲を設定する
private Texture2D sourceTexture;
private Material createdMaterial;
private RenderTexture tmpRenderTexture;
//変更があったときだけ更新するためのキャッシュ
private float _lastIgnoreRange;
/// <summary>
/// 初期化処理の必要性がある場合trueを返す
/// </summary>
private bool NeedsInitialization => sourceTexture == null || sourceTexture != targetImage.sprite.texture ||
createdMaterial == null || targetImage.material != createdMaterial ||
tmpRenderTexture == null;
/// <summary>
/// ignoreRangeのパラメータに変化があったかチェック
/// </summary>
private bool CahngedParameter => !Mathf.Approximately(ignoreRange, _lastIgnoreRange);
/// <summary>
/// コンポーネントをアタッチした時一度だけ呼ばれる
/// </summary>
private void Reset()
{
if(targetImage == null)
{
targetImage = GetComponent<Image>();
}
}
/// <summary>
/// 初期化処理
/// </summary>
private void Init()
{
Release();
sourceTexture = targetImage.sprite.texture;
createdMaterial = new (targetShader);
createdMaterial.hideFlags = HideFlags.DontSave;
targetImage.material = createdMaterial;
//Imageとアタッチされた画像と同じサイズでRenderTextureを作成
tmpRenderTexture = new RenderTexture(sourceTexture.width, sourceTexture.height, 0);
//事前にBlitで更新される予定のtmpRenderTextureを、_CustomTexの名前でシェーダー側と紐づけておく
//後はCanvasRender内の処理で自動的にPass0を実行させて、その_CustomTexを画像に反映させる
targetImage.material.SetTexture("_CustomTex", tmpRenderTexture);
//初期化後は問答無用で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(CahngedParameter)
{
UpdateTexture();
}
}
/// <summary>
/// 対象のシェーダーの2Passを実行させる
/// </summary>
private void UpdateTexture()
{
_lastIgnoreRange = ignoreRange;
//非表示領域をセット
createdMaterial.SetFloat("_IgnoreRange", ignoreRange);
//第1引数の画像を元に、Blitでシェーダー内のPass1を実行させ、結果を第2引数に書き込む
//Blitの仕様上、この書き込み処理は、第3引数のMaterial内の_MainTexを通して行われる
Graphics.Blit(sourceTexture, tmpRenderTexture, createdMaterial, 1);
}
/// <summary>
/// 作成したRenderTextureやMaterialのメモリを解放する
/// </summary>
private void Release()
{
if (tmpRenderTexture != null)
{
tmpRenderTexture.Release();
}
if (createdMaterial != null)
{
DestroyImmediate(createdMaterial);
}
}
/// <summary>
/// このGameObjectを非表示にした時の処理
/// </summary>
private void OnDisable()
{
Release();
}
}
//第1引数の画像を元に、Blitでシェーダー内のPass1を実行させ、結果を第2引数に書き込む
//Blitの仕様上、この書き込み処理は、第3引数のMaterial内の_MainTexを通して行われる
Graphics.Blit(sourceTexture, tmpRenderTexture, createdMaterial, 1);
//事前にBlitで更新される予定のtmpRenderTextureを、_CustomTexの名前でシェーダー側と紐づけておく
//後はCanvasRender内の処理で自動的にPass0を実行させて、その_CustomTexを画像に反映させる
targetImage.material.SetTexture("_CustomTex", tmpRenderTexture);


//_MainTexではなく、C#側から書き込みを行った_CustomTexを最終的な描画対象とする
//これによりuGUIの標準の描画ロジックを回避して、Pass1の内容を引き継いで出力できる
//まずこの_CustomTexから、指定のUV座標のピクセル色を抜き出す
//half4 original = SAMPLE_TEXTURE2D(_CustomTex, sampler_CustomTex, input.uv);
half4 original = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv);