-->


using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
using UnityEngine.Rendering.RenderGraphModule.Util;
using UnityEngine.Rendering.Universal;
/// <summary>
/// Universal Render Dataに登録させるポストプロセス処理のクラス
/// </summary>
public class URPGrabRenderFeature : ScriptableRendererFeature
{
//介入する描画処理がGameViewのカメラかどうか。シェーダー側では基本GameViewのカメラにだけ処理を実行させる
//(後述の_URPGrabTextureはGameViewから画面をキャプチャするので、それ以外のViewで使用するとおかしくなる)
private readonly int _isGameViewId = Shader.PropertyToID("_IsGameView");
private URPGrabRenderPass _pass;
/// <summary>
/// URPGrabRenderPassのインスタンスを生成
/// </summary>
public override void Create() => _pass = new URPGrabRenderPass();
/// <summary>
/// Unityに対しURPGrabRenderPass.RecordRenderGraphのロジックをカメラ描画工程に追加するように依頼
/// </summary>
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
// この描画工程がGameViewのカメラかそれ以外かの情報だけは、この段階で全シェーダーに即時送信する
bool isGameViewCamera = renderingData.cameraData.cameraType == CameraType.Game;
Shader.SetGlobalInt(_isGameViewId, isGameViewCamera? 1 : 0);
//描画工程を追加
renderer.EnqueuePass(_pass);
}
}
/// <summary>
///具体的なロジック処理クラス
/// </summary>
public class URPGrabRenderPass : ScriptableRenderPass
{
private readonly int _textureId = Shader.PropertyToID("_URPGrabTexture");
private readonly int _texelSizeId = Shader.PropertyToID("_URPGrabTexture_TexelSize");
/// <summary>
/// 初期化処理、このポストプロセスの実行タイミングを定義しているだけ
/// </summary>
public URPGrabRenderPass()
{
//カメラ描画処理が全て終わってからURPGrabRenderPass.RecordRenderGraphを実行させる事をUnityに伝える
renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing;
}
/// <summary>
/// 追加される新しい工程に必要なデータ(Context)を受け渡すのためクラス
/// </summary>
private class PassData
{
public TextureHandle destination; //テクスチャコピー先のメモリ領域
public int textureId;
public int texelSizeId;
public Vector4 texelSize;
}
/// <summary>
///カメラの画角に映っている情報をテクスチャとしてシェーダーに渡す処理
/// </summary>
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
//今この瞬間にカメラに書き込んでいる画面の色や深度のデータを取得
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
//resourceDataからカメラが描画しているテクスチャのメモリ領域を取得
TextureHandle originalTextureHandle = resourceData.activeColorTexture;
//これから生成するテクスチャの設計図を作成
TextureDesc grabTextureDesc;
try
{
//テクスチャの設計図をコピー
grabTextureDesc = renderGraph.GetTextureDesc(originalTextureHandle);
}
catch
{
return;
}
//設計図を出力用に上書き
grabTextureDesc.name = "URPGrabTexture";
grabTextureDesc.clearBuffer = false;
grabTextureDesc.msaaSamples = MSAASamples.None;
grabTextureDesc.depthBufferBits = 0;
var texelSize = new Vector4(1.0f / grabTextureDesc.width, 1.0f / grabTextureDesc.height,
grabTextureDesc.width, grabTextureDesc.height);
//設計図から新しいメモリ領域を作成
TextureHandle grabTextureHandle = renderGraph.CreateTexture(grabTextureDesc);
//Blitで実物のテクスチャデータを丸々コピー
renderGraph.AddBlitPass(originalTextureHandle, grabTextureHandle, Vector2.one, Vector2.zero, passName: "BlitCopyToGrabTexture");
using (var builder = renderGraph.AddRasterRenderPass("SetGrabTextureToGlobalState", out PassData passData))
{
//読み込み対象をbuilderに登録
builder.UseTexture(grabTextureHandle, AccessFlags.Read);
//この描画工程の中でのみ「グローバル変数(全シェーダー共通の設定)」を書き換えることを許可する
builder.AllowGlobalStateModification(true);
//コピーしたを画像をpassDataに代入
passData.destination = grabTextureHandle;
passData.textureId = _textureId;
passData.texelSizeId = _texelSizeId;
passData.texelSize = texelSize;
//予約されたスケジュールが実行された時の処理
builder.SetRenderFunc((PassData data, RasterGraphContext context) =>
{
//GPUに対して「このテクスチャ(destination)を、このIDの名前で公開せよ」と命令を出す
//これにより、全シェーダーの _URPGrabTexture に中身が流し込まれる
context.cmd.SetGlobalTexture(data.textureId, data.destination);
context.cmd.SetGlobalVector(data.texelSizeId, data.texelSize);
});
}
}
}



Shader "URPGrab/Mosaic"
{
Properties
{
[HideInInspector]_MainTex("-",2D)="white"{}
[KeywordEnum(one, equalize)] _Pick("ColorPick", Int) = 1
_MosaicPixelSize ("MosaicPixelSize", Range(1, 50)) = 1
}
SubShader
{
Tags
{
"Queue"="Transparent"
"RenderPipeline" = "UniversalPipeline"
}
Cull Off
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
Pass
{
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _PICK_ONE _PICK_EQUALIZE
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
struct Attributes
{
half2 uv : TEXCOORD0;
float4 positionOS : POSITION; //オブジェクト空間座標(対象の大元の座標と寸法)
};
struct Varyings
{
half2 uv : TEXCOORD0;
float4 positionCS : SV_POSITION; //クリップ空間座表(カメラの四角い画角内での対象の座標)
float4 positionVP : TEXCOORD1; //ビューポート空間座標(クリップ空間座表 -w ~ +w を 0 ~ w の範囲に修正した座標)
};
//URPGrabRenderFeatureから全画面の色情報などを受け取る
int _IsGameView;
TEXTURE2D(_URPGrabTexture);
SAMPLER(sampler_URPGrabTexture);
half4 _URPGrabTexture_TexelSize;
//Propertiesに対応する変数
int _MosaicPixelSize;
//受け取ったUV座標を大雑把にして返す
half2 get_mosaic_uv(half2 uv)
{
// モザイク1ブロックのUVサイズ、ピクセルサイズから正規化された0~1のUVサイズに変換する
float2 mosaicUVSize = _MosaicPixelSize * _URPGrabTexture_TexelSize.xy;
// 実際の大雑把にする処理
float2 mosaicUV = floor(uv / mosaicUVSize) * mosaicUVSize;
// モザイクブロック中央の座標を返す
return mosaicUV + mosaicUVSize / 2;
}
//渡したUV座標からGrabした画像のcolorを抜き出す
half4 get_tex_color(half2 uv)
{
#ifdef _PICK_ONE
//指定したuv座標のcolorをそのまま返す
return SAMPLE_TEXTURE2D(_URPGrabTexture, sampler_URPGrabTexture, uv);
#elif _PICK_EQUALIZE
//指定したuv座標から、省略する予定のpixel全体を平均化したcolor値を返す
half4 col = 0;
int count = 0;
// ブロックの中心から、ブロック内を左右・上下対称にサンプリングする
float2 halfSize = (_MosaicPixelSize - 1) * 0.5;
for(int jx = 0; jx < _MosaicPixelSize; jx++)
{
for(int jy = 0; jy < _MosaicPixelSize; jy++)
{
//座標をズラしてピクセル色を取得
float2 offset = (float2(jx, jy) - halfSize) * _URPGrabTexture_TexelSize.xy;
col += SAMPLE_TEXTURE2D(_URPGrabTexture, sampler_URPGrabTexture, uv + offset);
count++;
}
}
return col /count;
#endif
}
//頂点シェーダー
Varyings vert(Attributes input)
{
Varyings output;
//uv座標を代入
output.uv = input.uv;
//オブジェクト空間座標をクリップ空間、ビューポート空間座標にそれぞれ変換
output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
output.positionVP = ComputeScreenPos(output.positionCS);
return output;
}
//ピクセルシェーダー
half4 frag (Varyings input) : SV_Target
{
//GameViewのカメラ以外に対しては半透明ピンクを返すだけ
if (_IsGameView == 0)
{
return half4(1, 0, 1, 0.2);
}
//ビューポート空間座標から奥行を省いて座標を平面化する
float2 screenUV = input.positionVP.xy / input.positionVP.w;
//もし_MosaicPixelSizeが1より大きいなら1を返し、それ以外は0を返す
half isUseMosaic = 1 - step(_MosaicPixelSize, 1);
//大雑把にしたUV座標。
half2 mosaicUV = get_mosaic_uv(screenUV);
//isUseMosaic==1ならモザイク処理を実行、そうでないならうけとった_URGGrabTextureの内容をそのまま返す
return lerp(
SAMPLE_TEXTURE2D(_URPGrabTexture, sampler_URPGrabTexture, screenUV),
get_tex_color(mosaicUV),
isUseMosaic
);
}
ENDHLSL
}
}
}










