「今日まなび」は最低1日1h、ゲーム作るために学びたいことなんでもいいから学んでいくコーナー。
完全自分用でまとめることは考えなくてOK.1記事1h。(1hでどんだけ学べるかのスピード感もみていきたいので)
1hのうちに次回学ぶことも決定しておくこと
環境:unity2021.3.40f
今日の学び
ステンシルバッファとはピクセル数分、値を保持することができる領域。
たとえば、ステンシルバッファの値が1の場合だけ描画する、といったことでマスク処理なんかに使われたりする。
これを使うには、
・ステンシルバッファに値を書き込む
・ステンシルバッファの値を参照してやりたい処理をやる
の2段階踏むことになる。
今回はQuad以外(↓の絵だと赤い四角)の部分をモノクロにするものを作ってみる。

ステンシルバッファに値を書き込む
Shader "Test/test20240902write" { Properties { _MainTex ("Texture", 2D) ="white" {} _Tint("Tint",Color) = (1,1,1,1) } SubShader { Tags {"Queue"="Transparent""IgnoreProjector"="True""RenderType"="Transparent"} LOD 100 Pass { Stencil { Ref 1 Comp Always Pass Replace } Tags { "LightMode"="ForwardBase" } Blend SrcAlpha OneMinusSrcAlpha CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma target 2.0 #include "UnityCG.cginc" sampler2D _MainTex; float4 _MainTex_ST; fixed4 _Tint; struct appdata_t { float4 vertex : POSITION; float2 texcoord : TEXCOORD0; UNITY_VERTEX_INPUT_INSTANCE_ID }; struct v2f { float4 vertex : SV_POSITION; float2 texcoord : TEXCOORD0; float4 pos : TEXCOORD1; UNITY_FOG_COORDS(1) UNITY_VERTEX_OUTPUT_STEREO }; v2f vert (appdata_t v) { v2f o; UNITY_SETUP_INSTANCE_ID(v); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); o.vertex = UnityObjectToClipPos(v.vertex); o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex); return o; } fixed4 frag (v2f_img i) : COLOR { fixed4 col = tex2D(_MainTex, i.uv); return col * _Tint; } ENDCG } } }
今回の学び部分は以下。
これはステンシルバッファに1を書き込むだけの定義である。
ステンシルバッファはSubShader単位でもかけるし、Pass単位でもかけるみたい。
Stencil
{
Ref 1 //対象の値は1
Comp Always //条件を定義できる。Alwaysは判定なしにTrue
Pass Replace //条件がTrueの場合に行う処理。 Replace=書き込む ということを表す
}ステンシルバッファの値を参照してやりたい処理をやる
値を参照する側は、このようなコードになる。
これは1つ目のPassでステンシルバッファが0ならモノクロ化、2つ目のPassで1ならそのままの色を描画するという処理になる。
Shader "Test/20240831"
{
Properties {
_MainTex("MainTex", 2D) = ""{}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
Stencil
{
Ref 0
Comp Equal
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
fixed4 frag(v2f_img i) : COLOR {
fixed4 c = tex2D(_MainTex, i.uv);
float gray = c.r * 0.3 + c.g * 0.6 + c.b * 0.1;
c.rgb = fixed3(gray, gray, gray);
return c;
}
ENDCG
}
Pass
{
Stencil
{
Ref 1
Comp Equal
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
fixed4 frag(v2f_img i) : COLOR {
fixed4 c = tex2D(_MainTex, i.uv);
return c;
}
ENDCG
}
}
}
ポストプロセス用のShaderでステンシルバッファの値がうまく参照できない?
さて、上述したモノクロ化のshaderはポストプロセスとしてカメラに写っているものに対して行おうとした処理なのだが、
これまで通り以下のコードでポストプロセスをかけた際に、ステンシルバッファの値を意図したように参照できていなかった。
publicvoid OnRenderImage(RenderTexture source, RenderTexture dest) { Graphics.Blit(source, dest, mat); }
このあたりは正直現状よくわからないが、CommandBufferという仕組みをすることでポストプロセスでもステンシルバッファを使うことができる。
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Rendering; [ExecuteInEditMode] [RequireComponent(typeof(Camera))] publicclassTest20240902 : MonoBehaviour { /// <summary>/// コマンドバッファ名/// </summary>privateconststring CommandBufferName ="StencilImageEffect"; /// <summary>/// イメージエフェクトで使用するマテリアル/// </summary> [SerializeField] private Material material; /// <summary>/// イメージエフェクト用コマンドバッファ/// </summary>private CommandBuffer commandBuffer; privatevoid OnEnable() { if (material ==null) return; if (commandBuffer !=null) return; var cam = GetComponent<Camera>(); var cbs = cam.GetCommandBuffers(CameraEvent.BeforeImageEffects); foreach (var cb in cbs) { // 多重登録を回避するため、名前でチェックif (cb.name == CommandBufferName) return; } commandBuffer =new CommandBuffer(); commandBuffer.name = CommandBufferName; // Blitでmaterialを適用してイメージエフェクトをかける commandBuffer.Blit( BuiltinRenderTextureType.CameraTarget, BuiltinRenderTextureType.CameraTarget, material); // カメラにコマンドバッファを登録 cam.AddCommandBuffer(CameraEvent.BeforeImageEffects, commandBuffer); } privatevoid OnDisable() { if (commandBuffer ==null) return; var cam = GetComponent<Camera>(); cam.RemoveCommandBuffer(CameraEvent.BeforeImageEffects, commandBuffer); commandBuffer =null; } }
TODO (調べたいこと、やりたいこととか疑問)
・CommandBufferってなに?
・Quad内の不透明部分のピクセルだけステンシルバッファに書き込むことはできないか
・Unityで深度バッファの値をどうやって取得できる?
・法線マップを扱う
・Unityのシャドウマップってなんだろう
・影を制御したい、動かしたり形を変えたり、色をかえたり
・StandardShaderでできることを把握しておきたい
・FrameDebuggerの使い方まなびたい
・shaderで図形描いたりするのによく使う関数と使い方まとめ
終わったやつ
・自作shaderを適応したQuadでも影を落としたい
・ポストプロセスで例えばモノクロを適応したときに、一部分(例えば特定のキャラの描画だけとか)には適応したくない場合がある。
これまでカメラをわけて対応してきたが1つのカメラでもそういった制御ができる方法はないか