PostEffectを掛ける際にUIのスプライト(テクスチャでもいい)を利用したかったメモ。
あまりにもニッチな内容だけど、なかなか苦労したのでメモ残しておきます。
何が問題なのか
ちょっとググれば出るけど、PostEffectを掛けるシェーダーを書く際はまずカメラに処理用のスクリプトを書く。
んで、処理中にRenderTextureを用意して、画面に映ったものをシェーダーで処理してRenderTextureに映す。
シェーダーで描画を実行するにはOnRenderImageで値を渡してGraphics.Blitで描画するんだけど、そのとき_MainTexとして画面のレンダリングイメージが送られる。
_MainTexのUVは画面全体を0-1として送られるので、UIの領域を参照するには画面全体からどうにかして算出しなければならない。
コード
とりあえず上手く行った(っぽい)コード。
説明の必要部分だけ抜粋します。
UIのポジション変換はこちらを参考にさせて頂きました。
http://karanokan.info/2019/02/03/post-2213/
UVは左下から0-1なので、そこに上手く合わせられるようにする。
スクリプト側
public GameObject imageobj = null;
public Texture AddTexture = null;
public Material mat = null;
private RectTransform trfTarget = null;
private RectTransform rectCanvas = null;
private Vector2 ressize = new Vector2(0f,0f);
private Vector2 piv = new Vector2(0f,0f);
private Vector4 rct;
void OnValidate(){
if(imageobj != null){
AddTexture = imageobj.GetComponent<Image>().sprite.texture;
trfTarget = imageobj.GetComponent<RectTransform>();
var top = imageobj.GetComponent<RectTransform>().root;
rectCanvas = top.GetComponent<RectTransform>();
ressize = new Vector2(rectCanvas.rect.width,rectCanvas.rect.height);
piv = rectCanvas.pivot;
}
}
private void OnRenderImage(RenderTexture source, RenderTexture dest){
mat.SetTexture("_AddTex", AddTexture);
mat.SetVector("_Rect", rct);
Graphics.Blit(source, dest, mat);
}
private void Update(){
//World座標をUI座標へ変換
Vector2 spos = RectTransformUtility.WorldToScreenPoint(Camera.main, trfTarget.position);
Vector2 pos;
RectTransformUtility.ScreenPointToLocalPointInRectangle(rectCanvas, spos, Camera.main, out pos);
//左下を計算上のpivotにする
var imgsize = new Vector2(trfTarget.rect.width * trfTarget.localScale.x,trfTarget.rect.height * trfTarget.localScale.y);
var imgpiv = trfTarget.pivot;
var imgpos = new Vector2(pos.x - (imgsize.x * imgpiv.x),pos.y - (imgsize.y * imgpiv.y));
//CanvasのRectTransformから解像度とピボットを取り、左下0にする
var resetpos = new Vector2(imgpos.x + (ressize.x * piv.x),imgpos.y + (ressize.y * piv.y));
//リニア化 (minx,miny,解像度xに対する横の割合,解像度yに対する高さの割合)
rct = new Vector4(resetpos.x / ressize.x, resetpos.y / ressize.y, imgsize.x / ressize.x, imgsize.y / ressize.y);
}
シェーダー側
範囲が分かりやすいようにスプライト部分を黒で塗りつぶしてます。
取得したUIのRect情報でいかにしてUVをずらすか。
Shader "UIPostEffect"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_AddTex ("AddTexture", 2D) = "white" {}
}
SubShader
{
Cull Off ZWrite Off ZTest Always
Pass
{
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
sampler2D _AddTex;
float4 _Rect;
fixed4 frag (v2f_img i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
float2 uv = float2((i.uv.x-_Rect.x)/_Rect.z, (i.uv.y-_Rect.y)/_Rect.w);
fixed4 addcol = tex2D(_AddTex, uv);
return lerp(col,fixed4(0,0,0,1),addcol.a);
}
ENDCG
}
}
Fallback Off
}
正直上手く説明できない
数学苦手なんでツッコミあったらよろしくお願いします。