using
UnityEngine;
using
UnityEngine.Rendering;
using
UnityEngine.Rendering.RenderGraphModule;
using
UnityEngine.Rendering.Universal;
/// <summary>
/// Universal Render Dataに登録させるポストプロセス処理のクラス
/// </summary>
public
class
URPGrabScreenFeature
:
ScriptableRendererFeature
{
//カメラ描画工程に新たに介入するロジッククラスの宣言
URPGrabPass
m_ScriptablePass;
//Baseクラスの初期化処理を上書き、URPGrabPassのインスタンスを生成する
public
override
void
Create()
=>
m_ScriptablePass
=
new
URPGrabPass();
//Unityに対しURPGrabPass.RecordRenderGraphのロジックをカメラ描画工程に追加するように依頼
public
override
void
AddRenderPasses(ScriptableRenderer
renderer,
ref
RenderingData
renderingData)
=>
renderer.EnqueuePass(m_ScriptablePass);
/// <summary>
///具体的なロジック処理クラス
/// </summary>
private
class
URPGrabPass
:
ScriptableRenderPass
{
//シェーダー側のコードに参照させるテクスチャ名
private
readonly
string
_textureName
=
"_URPGrabTexture";
//処理の高速化のためテクスチャ名をintに変換して定義しておく、後のSetGlobalTexturePass関数内で使う
private
readonly
int
_textureId
=
Shader.PropertyToID("_URPGrabTexture");
//追加される新しい工程に必要なデータ(Context)を受け渡すのためクラス
private
class
PassData
{
public
TextureHandle
source;
//カメラに映っているオリジナルの画像データ
public
TextureHandle
destination;
//画像データのコピー先
public
int
textureId;
//シェーダーにテクスチャ名を伝えるためのID
}
/// <summary>
/// 初期化処理、このポストプロセスの実行タイミングを定義しているだけ
/// </summary>
public
URPGrabPass()
{
//uGUIの描画が全て終わってからRecordRenderGraphを実行させる事をUnityに伝える
renderPassEvent
=
RenderPassEvent.AfterRenderingTransparents;
}
/// <summary>
///カメラの画角に映っている情報をテクスチャとしてシェーダーに渡す処理
/// </summary>
public
override
void
RecordRenderGraph(RenderGraph
renderGraph,
ContextContainer
frameData)
{
//今この瞬間にカメラに書き込んでいる画面の色や深度のデータを取得
UniversalResourceData
resourceData
=
frameData.Get<UniversalResourceData>();
//今この瞬間のカメラ設定(解像度、HDR設定、FOVなど)を取得
UniversalCameraData
cameraData
=
frameData.Get<UniversalCameraData>();
//resourceDataから色情報を抜き出す、その中身が存在しない場合は処理を中断する
TextureHandle
source
=
resourceData.activeColorTexture;
if
(!source.IsValid())
return;
//cameraDataから解像度や色フォーマットの情報を取得
RenderTextureDescriptor
desc
=
cameraData.cameraTargetDescriptor;
//アンチエイリアス(ギザギザ補正)をオフにしてメモリを節約
desc.msaaSamples
=
1;
//深度情報を省いてメモリを節約
desc.depthBufferBits
=
0;
//descの画像設定と_textureNameを元に、まず空のテクスチャを作成
TextureHandle
destination
=
UniversalRenderer.CreateRenderGraphTexture(renderGraph,
desc,
_textureName,
false);
//カメラに映っている画像を作成したテクスチャにコピーする
GrabScreenCopyPass(renderGraph,
source,
destination);
//コピーした画像を全シェーダーに流し込む
SetGlobalTexturePass(renderGraph,
destination);
}
/// <summary>
/// Render Graphに「Grab Screen Copy Pass」という新しい描画工程のスケジュール予約を入れる
/// この工程ではカメラに映っている画像を指定した空テクスチャのpathへコピーを行う
/// スケジュール予約: builderに対してはPassDataクラスを通して必要な情報を受け渡す
/// </summary>
private
void
GrabScreenCopyPass(RenderGraph
renderGraph,
TextureHandle
source,
TextureHandle
destination)
{
using
(var
builder
=
renderGraph.AddRasterRenderPass("Grab
Screen
Copy
Pass",
out
PassData
passData))
{
//コピー元の画像データをpassDataに代入
passData.source
=
source;
//コピー先の画像データ(この時点では空のテクスチャ)をpassDataに代入
passData.destination
=
destination;
//読み込み対象をbuilderに登録
builder.UseTexture(passData.source,
AccessFlags.Read);
//書き込み対象をbuilderに登録
builder.SetRenderAttachment(passData.destination,
0,
AccessFlags.Write);
//予約されたスケジュールが実行された時の処理
builder.SetRenderFunc((PassData
data,
RasterGraphContext
context)
=>
{
//空テクスチャに予約時点での画像データをコピーする
// context.cmd: 命令を溜めておくバッファ
// data.source: コピー元のテクスチャ
// new Vector4(1, 1, 0, 0): テクスチャの全範囲(100%)をコピーするという設定(スケールとオフセット)
// 0: 使用するマテリアルのパス番号(今回は単純コピーなので0)
// false: 反転が必要かどうか(通常はfalse)
Blitter.BlitTexture(context.cmd,
data.source,
new
Vector4(1,
1,
0,
0),
0,
false);
});
}
}
/// <summary>
/// Render Graphに「Set Global Texture Pass」という新しい描画工程のスケジュール予約を入れる
/// この工程ではコピーしたテクスチャ画像をシェーダーに流し込む
/// スケジュール予約: builderに対してはPassDataクラスを通して必要な情報を受け渡す
/// </summary>
private
void
SetGlobalTexturePass(RenderGraph
renderGraph,
TextureHandle
destination)
{
using
(var
builder
=
renderGraph.AddRasterRenderPass("Set
Global
Texture
Pass",
out
PassData
passData))
{
//上のGrab Screen Copy Passの工程でコピーしたを画像をpassDataに代入
passData.destination
=
destination;
//その画像に紐づく画像名をintでpassDataに代入
passData.textureId
=
_textureId;
//読み込み対象をbuilderに登録
builder.UseTexture(passData.destination,
AccessFlags.Read);
//この描画工程の中でのみ「グローバル変数(全シェーダー共通の設定)」を書き換えることを許可する
builder.AllowGlobalStateModification(true);
//予約されたスケジュールが実行された時の処理
builder.SetRenderFunc((PassData
data,
RasterGraphContext
context)
=>
{
//GPUに対して「このテクスチャ(destination)を、このIDの名前で公開せよ」と命令を出す
//これにより、全シェーダーの _URPGrabTexture に中身が流し込まれる
context.cmd.SetGlobalTexture(data.textureId,
data.destination);
});
}
}
}
}