Introduction to Shaders for VRChatter
剥_Haku
自己紹介
注意事項
事前準備
資料
ver.2
https://drive.google.com/file/d/1-RYbgU6h_ZohoFoaZtefhYKzyXxKuECS/view?usp=drive_link
旧バージョン
https://drive.google.com/file/d/1kfDmvmRBIZiuzPbvmrFK2mN1Nzq7PAr_/view?usp=sharing
今回使用するunitypackageです(アクセスできなければ TwitterのDMまで。)
シェーダーとは?
描画の内部プロセス(超重要)
シェーダを書けるプログラマになろう #1 シェーダを理解しよう - Unity道場2019 2月
ノードベースでシェーダを書くなら
実際にコードを作成する
実際にコードを見てみる(写経タイム)
Shader "Custom/VFShader_Sec1"
{
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
float4 vert (float4 vertex: POSITION):SV_POSITION
{
return UnityObjectToClipPos(vertex);
}
float4 frag () : SV_Target
{
return float4(1,1,1,1);
}
ENDCG
}
}
}
シェーダーを適用する
マテリアルを作成
マテリアルにドラッグアンドドロップ
シェーダーを適用する
マテリアルをCubeにドラッグアンドドロップ
SceneのヒエラルキーでCubeを作成
実際にコードを見てみる
Shader "Custom/VFShader_Sec1"
{
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
float4 vert (float4 vertex: POSITION):SV_POSITION
{
return UnityObjectToClipPos(vertex);
}
float4 frag () : SV_Target
{
return float4(1,1,1,1);
}
ENDCG
}
}
}
頂点シェーダ
フラグメントシェーダ
おさらい:セマンティクス
Shader "Custom/VFShader_Sec1"
{
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
float4 vert (float4 vertex: POSITION):SV_POSITION
{
return UnityObjectToClipPos(vertex);
}
float4 frag () : SV_Target
{
return float4(1,1,1,1);
}
ENDCG
}
}
}
返り値のセマンティクス情報を指定している
GPU側に頂点座標であると認識させる
GPU側に色情報であると認識させる
おさらい
Shader "Custom/VFShader_Sec1"
{
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
float4 vert (float4 vertex: POSITION):SV_POSITION
{
return UnityObjectToClipPos(vertex);
}
float4 frag () : SV_Target
{
return float4(1,1,1,1);
}
ENDCG
}
}
}
良い感じに画面上の位置
まで頂点を持っていく関数
RGBAの順で[0,1]の範囲で表現ができる
Shader "Custom/VFShader_Sec1"
{
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
float4 vert (float4 vertex: POSITION):SV_POSITION
{
return UnityObjectToClipPos(vertex);
}
float4 frag () : SV_Target
{
return float4(1,1,1,1);
}
ENDCG
}
}
}
🤔
#pragmaについて
「コンパイラが参照する情報を渡す文章」
(コンパイラ:プログラムをPCなどのマシンがわかるように変換してくれるもの)
#pragmaについて
#pragma vertex vert
#pragma fragment frag
コンパイラに「vertexシェーダとしてvertと定義した関数を使用する」と伝える
コンパイラに「fragmentシェーダとしてfragと定義した関数を使用する」と伝える
これらが無ければ、
コンパイラは自作した頂点シェーダやフラグメントシェーダを認識できない
#pragmaについて
#pragma vertex vert
#pragma fragment frag
コンパイラに「vertexシェーダとしてvertと定義した関数を使用する」と伝える
コンパイラに「fragmentシェーダとしてfragと定義した関数を使用する」と伝える
これらが無ければ、
コンパイラは自作した頂点シェーダやフラグメントシェーダを認識できない
float4 vert (float4 vertex: POSITION):SV_POSITION
{
return UnityObjectToClipPos(vertex);
}
float4 frag () : SV_Target
{
return float4(1,1,1,1);
}
Shader "Custom/VFShader_Sec1"
{
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
float4 vert (float4 vertex: POSITION):SV_POSITION
{
return UnityObjectToClipPos(vertex);
}
float4 frag () : SV_Target
{
return float4(1,1,1,1);
}
ENDCG
}
}
}
🤔
🤔
CGPROGRAM-ENDCG
CGPROGRAM
[ここにシェーダープログラムのソースコードを書く]
ENDCG
Shader "Custom/VFShader_Sec1"
{
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
float4 vert (float4 vertex: POSITION):SV_POSITION
{
return UnityObjectToClipPos(vertex);
}
float4 frag () : SV_Target
{
return float4(1,1,1,1);
}
ENDCG
}
}
}
🤔
🤔
Shader{}
Shader "Tutorial/Custom/VFShader_Sec1"
{
SubShader
{
Pass
{
...
}
}
}
まとめ
Shader "Custom/VFShader_Sec1"
{
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
float4 vert (float4 vertex: POSITION):SV_POSITION
{
return UnityObjectToClipPos(vertex);
}
float4 frag () : SV_Target
{
return float4(1,1,1,1);
}
ENDCG
}
}
}
頂点シェーダ
フラグメントシェーダ
シェーダーの紐づけ
HLSLのコードブロック宣言及びビルトイン関数のインクルード
HLSLでは記述できない内容の設定(ステップ4とステップ6の設定など)
シェーダーの宣言
float4 について(超重要)
float4 nums = float4(1, 2, 3, 4);
//宣言方法
float4 nums = float4(1,2,3,4);
// 1番目要素にアクセス
float first = nums.x;//1
// 2番目の要素にアクセス
float second = nums.y;//2
// 3番目の要素にアクセス
float third = nums.z;//3
// 4番目の要素にアクセス
float fourth = nums.w;//4
//のようにx,y,z,wでアクセスできる
//他にもr,g,b,aでアクセスできる
// 赤、緑、青、アルファとしてのfloat4の解釈 xyzwとの差はない
//例
float firstColor = nums.r;//1
float secondColor= nums.g;//2
float thirdColor = nums.b;//3
float fourthColor = nums.a;//4
//(x,y,z,w),(r,g,b,a)を連続して使うこともできる
float2 xy = nums.xy; // (1,2)
float2 yx = nums.yx; // (2,1)
float3 rgb = nums.rgb; // (1,2,3)
float3 bgr = nums.bgr; // (3,2,1)
float4 xyzw = nums; // (1,2,3,4)
float4 xyzw2 = nums.xyzw; // (1,2,3,4)
float4 について(精度)
https://docs.unity3d.com/jp/460/Manual/SL-ShaderPerformance.html
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
よくあるfragment shaderのコード
演習:float4(想定5分)
演習:Shader (休憩含めて15分)
モデル行列
1
0
0
1
0
1
0
1
0
0
1
1
0
0
0
1
モデル行列
ビュー行列
プロジェクション行列
MVP行列 (≒ UnityObjectToClipPos(vertex))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Properties
写経タイム(3分)
Shader "Custom/VFShader_Sec2"
{
Properties
{
_Color("Color", Color) = (1,1,1,1)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
float4 _Color;
#pragma vertex vert
#pragma fragment frag
float4 vert (float4 vertex: POSITION):SV_POSITION
{
return UnityObjectToClipPos(vertex);
}
float4 frag () : SV_Target
{
return _Color;
return float4(1,1,1,1);
}
ENDCG
}
}
}
Properties
https://docs.unity3d.com/ja/2019.4/Manual/SL-Properties.html
https://docs.unity3d.com/jp/460/Manual/SL-Properties.html
Shader "Unlit/NewUnlitShader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}
Shader "Custom/VFShader_Sec2"
{
Properties
{
_Color("Color", Color) = (1,1,1,1)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
float4 _Color;
#pragma vertex vert
#pragma fragment frag
float4 vert (float4 vertex: POSITION):SV_POSITION
{
return UnityObjectToClipPos(vertex);
}
float4 frag () : SV_Target
{
return _Color;
}
ENDCG
}
}
}
Properties
Shader "Custom/VFShader_Sec2"
{
Properties
{
_Color("Color", Color) = (1,1,1,1)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
float4 _Color;
#pragma vertex vert
#pragma fragment frag
float4 vert (float4 vertex: POSITION):SV_POSITION
{
return UnityObjectToClipPos(vertex);
}
float4 frag () : SV_Target
{
return _Color;
}
ENDCG
}
}
}
Shader "Unlit/NewUnlitShader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}
Texture, Struct
写経タイム
Shader "Custom/VFShader_Sec3"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
_Color("Color", Color) = (1,1,1,1)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
float4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert (appdata i)
{
v2f o;
o.vertex = UnityObjectToClipPos(i.vertex);
o.uv = TRANSFORM_TEX(i.uv, _MainTex);
return o;
}
float4 frag (v2f i) : SV_Target
{
float4 col = tex2D(_MainTex, i.uv);
return col * _Color;
}
ENDCG
}
}
}
Properties
テクスチャ名_STに格納される
Shader "Custom/VFShader_Sec3"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
_Color("Color", Color) = (1,1,1,1)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
float4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
...
ENDCG
}
}
}
Shader "Unlit/NewUnlitShader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}
Shader "Custom/VFShader_Sec3"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
_Color("Color", Color) = (1,1,1,1)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
float4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert (appdata i)
{
v2f o;
o.vertex = UnityObjectToClipPos(i.vertex);
o.uv = TRANSFORM_TEX(i.uv, _MainTex);
return o;
}
float4 frag (v2f i) : SV_Target
{
float4 col = tex2D(_MainTex, i.uv);
return col * _Color;
}
ENDCG
}
}
}
struct(構造体)
構造体:色々な型の変数をまとめて,�一つのまとまりにしたもの
Shader "Unlit/NewUnlitShader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
...
float4 vertex : SV_POSITION;
};
Appdata
頂点シェーダーに入力される�頂点情報
頂点座標、uv座標、法線など...
v2f
頂点シェーダーから�フラグメントシェーダに�送る情報
(変換された)頂点座標、uv座標...
struct v2f
{
float2 uv : TEXCOORD0;
...
float4 vertex : SV_POSITION;
};
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
UV
sampler2D, tex2Dについて
ざっくりとした説明
sampler2D _MainTex;
float4 col = tex2D(_MainTex, i.uv);
テクスチャ名_ST を適用する
画像を小さくしたり(タイリングしたり)、オフセットをしたりできる。
透明なオブジェクト
Alpha値を変えても透明にならない...?
Shader "Custom/VFShader_Sec3"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
_Color("Color", Color) = (1,1,1,1)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
float4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
...
ENDCG
}
}
}
透明なオブジェクトの描画の設定にする必要がある!
RenderTypeを透明にする以外に色々設定項目がある
写経タイム(3分)
Shader "Custom/VFShader_Sec4"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
_Color("Color", Color) = (1,1,1,1)
}
SubShader
{
// Tags { "RenderType"="Opaque" }
Tags { "RenderType"="Transparent" "Queue"="Transparent" }
Blend SrcAlpha OneMinusSrcAlpha
ZWrite Off
ZTest LEqual
LOD 100
Pass
{
...
}
}
}
先程のシェーダーに一部追加する
ZTest
1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 |
1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 |
1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 |
1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 |
1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 |
1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 |
1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 |
Z値(深度バッファ) : 各ピクセルに書き込まれている数値(深度値)
1.0 : カメラから一番遠い値 ※
0.0 : カメラから一番近い値 ※
1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 |
1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 |
1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 |
1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 |
1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 |
1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 |
1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 |
1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 |
1.0 | 1.0 | 1.0 | 0.6 | 0.6 | 1.0 | 1.0 |
1.0 | 1.0 | 0.6 | 0.6 | 0.6 | 1.0 | 1.0 |
1.0 | 0.6 | 0.6 | 0.6 | 0.6 | 1.0 | 1.0 |
1.0 | 1.0 | 0.6 | 0.6 | 0.6 | 1.0 | 1.0 |
1.0 | 1.0 | 1.0 | 0.6 | 1.0 | 1.0 | 1.0 |
1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 |
描画されたオブジェクトより手前�(数値が前回の値より小さい)
なら描画
1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 |
1.0 | 1.0 | 1.0 | 0.6 | 0.6 | 1.0 | 1.0 |
1.0 | 1.0 | 0.6 | 0.6 | 0.6 | 1.0 | 1.0 |
1.0 | 0.6 | 0.6 | 0.6 | 0.6 | 1.0 | 1.0 |
1.0 | 1.0 | 0.6 | 0.6 | 0.6 | 1.0 | 1.0 |
1.0 | 1.0 | 1.0 | 0.6 | 1.0 | 1.0 | 1.0 |
1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 |
1.0 | 1.0 | 0.8 | 0.8 | 1.0 | 1.0 | 1.0 |
1.0 | 1.0 | 0.8 | 0.6 | 0.6 | 0.8 | 1.0 |
1.0 | 1.0 | 0.6 | 0.6 | 0.6 | 0.8 | 1.0 |
1.0 | 0.6 | 0.6 | 0.6 | 0.6 | 1.0 | 1.0 |
1.0 | 1.0 | 0.6 | 0.6 | 0.6 | 1.0 | 1.0 |
1.0 | 1.0 | 1.0 | 0.6 | 1.0 | 1.0 | 1.0 |
1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 |
描画されたオブジェクトより手前�(数値が前回の値より小さい)
なら描画
ZTest : 描画する条件
ZWrite
ZTest通過時、Z値を更新するかどうか
1.0 | 1.0 | 0.8 | 0.8 | 1.0 | 1.0 | 1.0 |
1.0 | 1.0 | 0.8 | 0.6 | 0.6 | 0.8 | 1.0 |
1.0 | 1.0 | 0.6 | 0.6 | 0.6 | 0.8 | 1.0 |
1.0 | 0.6 | 0.6 | 0.6 | 0.6 | 1.0 | 1.0 |
1.0 | 1.0 | 0.6 | 0.6 | 0.6 | 1.0 | 1.0 |
1.0 | 1.0 | 1.0 | 0.6 | 1.0 | 1.0 | 1.0 |
1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 |
ZTest
Render Queue
RenderQueue
レンダリングする順番
青緑,赤の描画順
RenderQueueで決定
Shader "Custom/VFShader_Sec4"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
_Color("Color", Color) = (1,1,1,1)
}
SubShader
{
Tags { "RenderType"="Transparent" "Queue"="Transparent" }
Blend SrcAlpha OneMinusSrcAlpha
ZWrite Off
ZTest LEqual
LOD 100
Pass
{
...
}
}
}
Tags { "RenderType"="Transparent" "Queue"="Transparent" }
1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 |
1.0 | 1.0 | 1.0 | 0.6 | 0.6 | 1.0 | 1.0 |
1.0 | 1.0 | 0.6 | 0.6 | 0.6 | 1.0 | 1.0 |
1.0 | 0.6 | 0.6 | 0.6 | 0.6 | 1.0 | 1.0 |
1.0 | 1.0 | 0.6 | 0.6 | 0.6 | 1.0 | 1.0 |
1.0 | 1.0 | 1.0 | 0.6 | 1.0 | 1.0 | 1.0 |
1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 |
1.0 | 1.0 | 0.8 | 0.8 | 1.0 | 1.0 | 1.0 |
1.0 | 1.0 | 0.8 | 0.6 | 0.6 | 0.8 | 1.0 |
1.0 | 1.0 | 0.6 | 0.6 | 0.6 | 0.8 | 1.0 |
1.0 | 0.6 | 0.6 | 0.6 | 0.6 | 1.0 | 1.0 |
1.0 | 1.0 | 0.6 | 0.6 | 0.6 | 1.0 | 1.0 |
1.0 | 1.0 | 1.0 | 0.6 | 1.0 | 1.0 | 1.0 |
1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 |
Render Queue
一般的に使用される値
Tags { "RenderType"="Transparent" "Queue"="Transparent" }
曖昧です
RenderTypeとRenderQueueは一致してなくてもOKですが一致してた方がいいです(深度によるソート方向が違う)
RenderQueueを正しく設定しないと...?
Shader "Custom/VFShader_Sec4"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
_Color("Color", Color) = (1,1,1,1)
}
SubShader
{
Tags { "RenderType"="Transparent" "Queue"="Transparent" }
Blend SrcAlpha OneMinusSrcAlpha
ZWrite Off
ZTest LEqual
LOD 100
Pass
{
...
}
}
}
RenderQueue(ざっくりな理解)
Shader "Custom/VFShader_Sec4"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
_Color("Color", Color) = (1,1,1,1)
}
SubShader
{
Tags { "RenderType"="Transparent" "Queue"="Transparent" }
Blend SrcAlpha OneMinusSrcAlpha
ZWrite Off
ZTest LEqual
LOD 100
Pass
{
...
}
}
}
例 | RenderQueue |
不透明なオブジェクト | 小さくする |
透明なオブジェクト | 大きくする |
ZTest,RenderQueueはいつ行われる?
ZTest
RenderQueue
Shader "Custom/VFShader_Sec4"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
_Color("Color", Color) = (1,1,1,1)
}
SubShader
{
Tags { "RenderType"="Opaque" }
Tags { "RenderType"="Transparent" "Queue"="Transparent" }
Blend SrcAlpha OneMinusSrcAlpha
ZWrite Off
ZTest LEqual
LOD 100
Pass
{
...
}
}
}
コード内容
Shader "Custom/VFShader_Sec4"
{
...
SubShader
{
Tags { "RenderType"="Transparent" "Queue"="Transparent" }
Blend SrcAlpha OneMinusSrcAlpha
ZWrite Off
ZTest LEqual
LOD 100
Pass
{
...
}
}
}
レンダーキューをTransparentに設定
元々書かれていた色との混ぜ方の設定
Z値の書き込み設定
Z値のテスト方法の設定(書かなくてもデフォルトでLEqualにはなるが練習の為)
単一な色ではなくそれっぽい見た目にしたい
後半のものは大抵 Surface Shaderがやってくれる!
https://docs.unity3d.com/ja/2019.4/Manual/SL-SurfaceShaderExamples.html
ここまででShaderLabの基礎の基礎はおわり!
演習 UVスクロール
UVスクロール?
テクスチャをアニメーションさせるアレ
1_UV_Scroll/Sceneと
1_UV_Scroll/1_Init.shader
を開いてください
Shader "Unlit/UV_Scroll/First"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}
Always Refreshをオン
写経タイム
Shader "Unlit/UV_Scroll/Final"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Scroll("Scroll", Vector) = (0, 0, 0, 0)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
(略)
float4 _Scroll;
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv+ _Scroll.xy * _Time.y );
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}
float4 _Time はグローバル変数
Displace Shader
Displace Shader
今までの知識とちょっと知識を足して楽しいシェーダーをつくってみる
Displace Shader
2_Curtain/Sceneと、
2_Curtain/1.shader
を開いてください
Shader "Custom/DisplaceCurtaion/First"
{
Properties
{
}
SubShader
{
(略)
Pass
{
CGPROGRAM
(略)
v2f vert (appdata v)
{
v2f o;
//スクリーンまで頂点の移動
//ここを自由に変更してみてください
v.vertex = v.vertex + float4(0, 0, 0, 0);
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
//ここからライティング計算(説明省略)
o.normal = UnityObjectToWorldNormal(v.normal);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
float4 frag (v2f i) : SV_Target
{
float3 directCol;
directCol.rgb =
max(0, dot(normalize(i.normal), _WorldSpaceLightPos0.xyz))*_LightColor0.rgb;
directCol.rgb += ShadeSH9(float4(i.normal, 1));
float4 col = float4(1,1,1,1);
col.rgb *= directCol.rgb;
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}
Shader "Custom/DisplaceCurtaion/First"
{
Properties
{
}
SubShader
{
(略)
Pass
{
CGPROGRAM
(略)
v2f vert (appdata v)
{
v2f o;
//スクリーンまで頂点の移動
//ここを自由に変更してみてください
v.vertex = v.vertex + float4(0, 0, 0, 0);
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
//ここからライティング計算(説明省略)
o.normal = UnityObjectToWorldNormal(v.normal);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
float4 frag (v2f i) : SV_Target
{
(略)
}
ENDCG
}
}
}
MVP行列適用
v.vertexの値を変えてみてください
Shader "Custom/DisplaceCurtaion/Second"
{
Properties
{
}
SubShader
{
(略)
Pass
{
CGPROGRAM
(略)
v2f vert (appdata v)
{
v2f o;
//スクリーンまで頂点の移動
//ここを自由に変更してみてください
v.vertex = v.vertex + float4(0, 0, 0, 0);
v.vertex = v.vertex + float4(0,0,sin(_Time.y),0);
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
//ここからライティング計算(説明省略)
o.normal = UnityObjectToWorldNormal(v.normal);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
float4 frag (v2f i) : SV_Target
{(略)}
ENDCG
}
}
}
MVP行列適用
v.vertexにsin波を入れてみてください
Shader "Custom/DisplaceCurtaion/Third"
{
SubShader
{
(略)
Pass
{
CGPROGRAM
float4 displacement(float4 vertex)
{
float4 displace = float4(0,0,0,0);
displace.z= sin(_Time.y)*vertex.x*2;
return displace;
}
v2f vert (appdata v)
{
v2f o;
//スクリーンまで頂点の移動
//ここを自由に変更してみてください
v.vertex = v.vertex + float4(0,0,sin(_Time.y),0);
v.vertex = v.vertex + displacement(v.vertex);
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
//ここからライティング計算(説明省略)
o.normal = UnityObjectToWorldNormal(v.normal);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
float4 frag (v2f i) : SV_Target
{(略)}
ENDCG
}
}
}
displacement関数を作成
カーテンの横情報に応じて振幅の大きさを変更
赤色の行は削除
カーテンの作成
パーリンノイズの関数
//ランダム関数
float rand(float2 n)
{
return frac(sin(dot(n, float2(12.9898, 78.233)))*43758.5453);
}
//パーリンノイズ
float pnoise(float p)
{
float2 ip = floor(p);
float2 u = p - ip;
u = u*u*(3.0-2.0*u);
float2 res = lerp(
lerp(rand(ip), rand(ip + float2(1, 0)), u.x),
lerp(rand(ip + float2(0, 1)), rand(ip + float2(1, 1)), u.x),
u.y);
return res;
}
float4 displacement(float4 vertex)
{
float4 displace = float4(0,0,0,0);
float noiseFactor = vertex.x+_Time.y;
displace.z= sin(_Time.y)*vertex.x*2;
displace.z = pnoise( noiseFactor);
return displace;
}
コピペしてください
赤色の行は削除
カーテンの作成(y軸の減衰)
上に行くほどz方向へのDisplaceを減らす
float4 displacement(float4 vertex)
{
float4 displace = float4(0,0,0,0);
float easeFactor = exp(-1) - exp ( -1* (vertex.y)/2);
float noiseFactor = vertex.x+_Time.y;
displace.z = pnoise( noiseFactor)* easeFactor;
return displace;
}
SkyBox Shader�(およびVRChat対応)
Skyboxとは?
背景のコレ。
写経
Shader "Custom/Skybox/HDRCUBESkybox/Second"
{
Properties
{
_CubeTex ("Texture", CUBE) = "" {}
}
SubShader
{
Tags { "RenderType"="Opaque" "Queue"="Geometry" "IgnoreProjector"="True" }
…
Cull Off
Pass
{
CGPROGRAM
...
samplerCUBE _CubeTex;
struct appdata
{...};
struct v2f
{
float4 vertex : SV_POSITION;
float4 worldPos : TEXCOORD2;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
return o;
}
float4 frag (v2f i) : SV_Target
{
float3 viewDir = normalize(i.worldPos.xyz - _WorldSpaceCameraPos);
float3 col = texCUBE(_CubeTex, viewDir);
return float4(col, 1.0);
}
ENDCG
}
}
}
3_Skybox/Sceneと
3_Skybox/1.shader
を開いてください
TexCUBE
視線ベクトルを元にスカイマップなどのテクスチャを�適用してくれる
内部では球座標平面から逆変換されている(と思う)
acos , atan2 など...
写経(VR対応)
Shader "Custom/Skybox/HDRCUBESkybox/Final"
{
...
SubShader
{
...
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_instancing
#include "UnityCG.cginc"
samplerCUBE _CubeTex;
struct appdata
{
...
UNITY_VERTEX_INPUT_INSTANCE_ID//VR対応
};
struct v2f
{
...
UNITY_VERTEX_OUTPUT_STEREO//VR対応
};
v2f vert (appdata v)
{
v2f o;
//VR対応
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.vertex = UnityObjectToClipPos(v.vertex);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
return o;
}
float4 frag (v2f i) : SV_Target
{
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
...
}
ENDCG
}
}
}
VR対応
https://tips.hecomi.com/entry/2018/09/24/232125�https://docs.unity3d.com/ja/2019.4/Manual/SinglePassInstancing.html
Stencil
Stencil
Unity Documentation
ステンシルバッファは、一般的にピクセルマスクごとにピクセルの保存や廃棄を行うために使用されます。
00 | 00 | 00 | 00 | 00 | 00 | 00 |
00 | 00 | 00 | 00 | 00 | 00 | 00 |
00 | 00 | 00 | 00 | 00 | 00 | 00 |
00 | 00 | 00 | 00 | 00 | 00 | 00 |
00 | 00 | 00 | 00 | 00 | 00 | 00 |
00 | 00 | 00 | 00 | 00 | 00 | 00 |
00 | 00 | 00 | 00 | 00 | 00 | 00 |
Stencil値 : 各ピクセルに書き込まれる数値
0~255の整数で指定可能
デフォルトは0
00 | 00 | 00 | 00 | 00 | 00 | 00 |
00 | 00 | 00 | 00 | 00 | 00 | 00 |
00 | 00 | 00 | 00 | 00 | 00 | 00 |
00 | 00 | 00 | 00 | 00 | 00 | 00 |
00 | 00 | 00 | 00 | 00 | 00 | 00 |
00 | 00 | 00 | 00 | 00 | 00 | 00 |
00 | 00 | 00 | 00 | 00 | 00 | 00 |
00 | 00 | 00 | 00 | 00 | 00 | 00 |
00 | 00 | 00 | 01 | 01 | 00 | 00 |
00 | 00 | 01 | 01 | 01 | 00 | 00 |
00 | 01 | 01 | 01 | 01 | 00 | 00 |
00 | 00 | 01 | 01 | 01 | 00 | 00 |
00 | 00 | 00 | 01 | 00 | 00 | 00 |
00 | 00 | 00 | 00 | 00 | 00 | 00 |
Comp : Stencil値がどの値であっても、描画し、
Pass : Compが通った時、値を1にする
青緑色の三角面
ステンシル設定例
00 | 00 | 00 | 00 | 00 | 00 | 00 |
00 | 00 | 00 | 01 | 01 | 00 | 00 |
00 | 00 | 01 | 01 | 01 | 00 | 00 |
00 | 01 | 01 | 01 | 01 | 00 | 00 |
00 | 00 | 01 | 01 | 01 | 00 | 00 |
00 | 00 | 00 | 01 | 00 | 00 | 00 |
00 | 00 | 00 | 00 | 00 | 00 | 00 |
00 | 00 | 00 | 00 | 00 | 00 | 00 |
00 | 00 | 00 | 02 | 02 | 00 | 00 |
00 | 00 | 02 | 02 | 02 | 00 | 00 |
00 | 01 | 02 | 02 | 01 | 00 | 00 |
00 | 00 | 01 | 01 | 01 | 00 | 00 |
00 | 00 | 00 | 01 | 00 | 00 | 00 |
00 | 00 | 00 | 00 | 00 | 00 | 00 |
Comp : Stencil値が1の時、描画し、
Pass : Compが通った時、値を1つ増やす
赤色の三角面
ステンシル設定例
Depthとの対応付け
| Depth | Stencil | 備考 |
比較関数 | ZTest | Comp | |
値 | Z値 | Ref | 好きな値を設定可能 |
値の範囲 | 0.0f~1.0f | 0~255(整数) | |
成功時の処理 | ZWrite | Pass | ZWriteより豊富な処理※ |
Stencilはこの他にも沢山シンタックスがある
両方のテストにパスした場合、描画される
Stencilの使用方法
Stencil
{
Ref <ステンシルの値>
Comp <比較方法>
Pass <ステンシル成功時の処理>
}
Stencil
{
Ref 1
Comp Always
Pass Replace
}
利用例
記述方法
Stencil 演習
演習4.2を推奨します
ステンシル演習4.1が�複雑だった為、やさしい演習を追加しました�4.1は発展課題として使用してください
演習4.1
穴を空けるシェーダーを作る
方針
地面を描画
ステンシル値を�1にする
凹んだオブジェクトを
ステンシル値が1の時に
描画する
レンダーキュー | 小 | 中 | 大 |
Z値 | 小 | 小 | 大 |
Stencil Producer
Stencil Consumer
方針
凹んだオブジェクトを
ステンシル値が1の時に
描画する
以降は凹んだオブジェクトを元に
ZTestされる(赤色)
レンダーキュー | 大 | 最後 |
Z値 | 大 | 小~中 |
4_Stencil/Sceneと
4_Stencil/1_Producer.shader
を開いてください
ステンシル値を�1にする
Stencil Producer
ステンシル値を�1にする
Stencil Producer
Shader "Custom/Stencil/Producer/Final"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="AlphaTest" "IgnoreProjector"="True" "Queue"="AlphaTest" }
Blend SrcAlpha OneMinusSrcAlpha
LOD 100
ZWrite On
ZTest LEqual
Stencil
{
Ref 1
Comp Always
Pass Replace
}
Pass
{
CGPROGRAM
(略)
float4 frag (v2f i) : SV_Target
{
// sample the texture
float4 col = tex2D(_MainTex, i.uv);
float4 col = float4(0,0,0,0);
return col;
}
ENDCG
}
}
}
4_Stencil/Sceneと
4_Stencil/1_Producer.shader
を開いてください
Shader "Custom/Stencil/Consumer/Final"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="AlphaTest" "IgnoreProjector"="True"
"Queue"="AlphaTest+1"}
Blend SrcAlpha OneMinusSrcAlpha
LOD 100
Cull Front
ZTest GEqual
ZWrite On
Stencil
{
Ref 1
Comp Equal
Pass IncrSat
}
Pass
{
CGPROGRAM
(略)
ENDCG
}
}
}
凹んだオブジェクトを
ステンシル値が1の時に
描画する
Stencil Consumer
4_Stencil/1_Consumer.shader
を開いてください
完成
追加説明がメモ欄にあります
演習4.2 (解説)
4_Stencil_v2を開いてください
(2024/11/04更新)
以前のステンシル演習が�複雑だった為、やさしい演習を追加しました
4_Stencil_v2/1_Stencil.shader
を開いてください
方針
地面を描画
ステンシル値を�変更する
ステンシル値を元に�描画分岐
レンダーキュー | 小 | 中 | 大 |
Z値 | 小 | 大(ZWrite Offにする) | 中 |
Stencil値の変更
Stencil値の使用
写経
Shader "Unlit/1_Stencil"
{
Properties
{
[Int] _StencilRef ("Stencil Reference", Float) = 1
[Enum(UnityEngine.Rendering.CompareFunction)] _StencilComp("Stencil Comparison", Float) = 8
[Enum(UnityEngine.Rendering.StencilOp)] _StencilPass ("Stencil Pass", Float) = 0
[MainTexture] _MainTex ("Texture", 2D) = "white" {}
[MainColor] _Color ("Color", Color) = (1,1,1,1)
[Enum(UnityEngine.Rendering.CompareFunction)] _ZTest("ZTest", Float) = 4
[KeywordEnum(Off, On)] _ZWrite("ZWrite", Float) = 1
}
SubShader
{
Tags { "RenderType"="Transparent" "Queue"="AlphaTest" }
Blend SrcAlpha OneMinusSrcAlpha
LOD 100
ZTest [_ZTest]
ZWrite [_ZWrite]
Stencil
{
Ref [_StencilRef]
Comp [_StencilComp]
Pass [_StencilPass]
}
//略
}
}
Inspector側から変更可能な設定
Maskオブジェクトの値設定
ステンシル値を1にするマテリアル
ステンシル値を2にするマテリアル
描画オブジェクトの値設定
赤窓から見えるオブジェクト
青窓から見えるオブジェクト
通常、見えるオブジェクト
描画順
ステンシルを極める
この動画がわかりやすいです
レイマーチング
5_RayMarching/Sceneを開いてください
レイマーチング
レイマーチングとは
符号付き距離関数(SDF:Signed Distance Function)とは
ここからはアルゴリズム的な話になります
レイマーチング:手法
以上を繰り返す
(一般的な)レイマーチングで必要な要素
以上を繰り返す
距離関数の例
原点中心の球:
原点中心の立方体:
float SDFCircle(float3 rayPos, float radius)
{
return length(rayPos) - radius;
}
float SDFCube(float3 rayPos,float width)
{
return length(max(abs(rayPos) - width,0.0));
}
5_RayMarching/1_Init.shader
を開いてください
float3 cameraPos = i.cameraPos;
float3 object_space_viewDir = normalize(i.objSpaceVertex - cameraPos);
カメラの位置と視線の方向が�fragmentシェーダーにて事前に記入されてます。
Properties
{
[Int]_Iteration("マーチング回数", Range(1,100)) = 30
[HDR]_Color("色", Color) = (1,1,1,1)
}
マーチング制限をInspectorから�変更します
写経
float circleSDF(float3 rayPos, float radius, float3 center)
{
return (length(rayPos-center)-radius);
}
float4 RayMarching(float3 CameraPos, float3 viewDir,int iter )
{
float3 start_point = CameraPos;
float3 current_pos = start_point;
int j=0;
for (j =0;j<iter;j++)
{
float d = circleSDF(current_pos,0.5,float3(0,0,0));
if (d < 0.001)
{
break;
}
current_pos += d*viewDir;
}
float col;
col = float(j)/iter;
return float4(col.xxx,1);
}
fixed4 frag (v2f i) : SV_Target
{
float3 cameraPos = i.cameraPos;
float3 object_space_viewDir = normalize(i.objSpaceVertex - cameraPos);
fixed4 col = RayMarching(cameraPos, object_space_viewDir ,_Iteration);
return col*_Color;
}
float4 RayMarching(float3 CameraPos, float3 viewDir,int iter )
{
float3 start_point = CameraPos;
float3 current_pos = start_point;
int j=0;
for (j =0;j<iter;j++)
{
float d = SDF(current_pos,0.5,float3(0,0,0));
if (d < 0.001)
{
break;
}
current_pos += d*viewDir;
}
float col;
col = float(j)/iter;
return float4(col.xxx,1);
}
レイを進める
最短距離を計測
限りなく近いなら終了
スタート地点の設定
回数に応じて描画する色を変更
float circleSDF(float3 rayPos, float radius, float3 center)
{
return (length(rayPos-center)-radius);
}
原点から半径0.5の表面からの距離
端ほどレイマーチの回数が多い
写経
// 直方体のSDF
float rectangleSDF(float3 rayPos, float3 size)
{
return length(max(abs(rayPos) - size,0.0));
}
// SDFの計算
float SDF(float3 rayPos)
{
return rectangleSDF(rayPos, float3(0.1,0.1,0.5));
}
fixed4 RayMarching(float3 CameraPos, float3 viewDir,int iter )
{
float3 start_point = CameraPos;
float3 current_pos = start_point;
int j=0;
for (j =0;j<iter;j++)
{
float d = SDF(current_pos);
if (d < 0.001)
{
break;
}
current_pos += d*viewDir;
}
(略)
}
距離関数の合成
SDFを合成することは容易
今回は直方体が3方向に刺さったモデルを作成する=>和集合
// 3つの直方体の合成
float crossSDF(float3 rayPos, float a,float b)
{
float3 rect1 = rectangleSDF(rayPos, float3(a,a,b));
float3 rect2 = rectangleSDF(rayPos, float3(a,b,a));
float3 rect3 = rectangleSDF(rayPos, float3(b,a,a));
return min(min(rect1,rect2),rect3);
}
// 直方体のSDF
float rectangleSDF(float3 rayPos, float3 size)
{
return length(max(abs(rayPos) - size,0.0));
}
穴を開けたいなら差集合 max(-a,b)
重なった場所が欲しいなら積集合 max(a,b)
SDFの編集(写経)
float rectangleSDF(float3 rayPos, float3 size)
{
return length(max(abs(rayPos) - size,0.0));
}
float crossSDF(float3 rayPos, float a,float b)
{
float rect1 = rectangleSDF(rayPos, float3(a,a,b));
float rect2 = rectangleSDF(rayPos, float3(a,b,a));
float rect3 = rectangleSDF(rayPos, float3(b,a,a));
return min(min(rect1,rect2),rect3);
}
float SDF(float3 rayPos)
{
return crossSDF(rayPos,0.1,0.5);
}
レイマーチング
(反復関数系)
IFS(Iterated Function System), 反復関数系
その他の例:万華鏡
距離関数をIFSで折りたたむ
float3 AngleFold(float3 rayPos,int iter)
{
float phase = atan2(rayPos.x,rayPos.z);
float r = length(rayPos.xz);
const float PI = 3.14159265359;
for (int i = 0; i < iter; i++)
{
phase = abs(phase)-PI/(pow(2,1+i));
}
rayPos.x = r*cos(phase);
rayPos.z = r*sin(phase);
return rayPos;
}
float3 TransformFold(float3 rayPos)
{
const float3 shift_value = float3(3,2,-1);
const float3 scale_value = float3(1,1.0/2.0,1.0/7.0);
return rayPos*scale_value + shift_value;
}
float3 RotateXY(float3 rayPos, float angle)
{
float2x2 rot = float2x2(cos(angle),-sin(angle),sin(angle),cos(angle));
float2 xy = mul(rot,rayPos.xy);
rayPos.xy = xy;
return rayPos;
}
float3 CubeFold(float3 rayPos, float width)
{
return abs(rayPos% width) - width/2.0;
}
float3 MixFold(float3 rayPos,float _RepeatWidth)
{
const float PI = 3.14159265359;
return RotateXY(AngleFold( (CubeFold(TransformFold(rayPos),_RepeatWidth)),4),PI/6);
}
演習
SDFを以下に変更してください
float3 CubeFold(float3 rayPos, float width)
{
return abs( rayPos %width) - width/2.0;
}
float SDF(float3 rayPos)
{
rayPos = CubeFold(rayPos, 2.5);
return crossSDF(rayPos,0.1,0.5);
}
テクスチャを貼る
距離関数の勾配を取ることで、法線情報(面の向き)を入手できる
Triplanar Mappingで画像を貼れる
気が向いたらやってみてください
https://qiita.com/edo_m18/items/c8995fe91778895c875e
レイマーチングを極めたい場合
おすすめサイト
よくあるミス
Batching
同じマテリアルのオブジェクトを複数設置した場合、�今の状態だとある条件で結果が強制的にワールド座標基準になる(Mainカメラからの図)
対策1: タグに"DisableBatching"="True"を追加する
対策2:GPUInstancingに対応する(可能ならこちらが適切)
次の資料を作るときにちゃんと説明します。
よくあるミス
float a = 1/2;
代入される値は0
float a = 1.0/2.0;
.0を付けないと整数型で演算されてしまう
代入される値は0.5
float a = 1.0/2;
よくあるミス
頂点シェーダーからフラグメントシェーダーへの補間は
線形補間
頂点シェーダーでnormalizeしてもフラグメントシェーダーではnormalizeされていない可能性がある
例:頂点a,bにそれぞれ(1,0),(0,1)といったデータがあった場合、�頂点a,bの中心点の補間は(0.5,0.5)になる
よくあるミス
#define pi 3.14159265359
const float pi2 = 3.14159265359;
v2f vert (appdata v)
{
(略)
}
fixed4 frag (v2f i) : SV_Target
{�(略)
}
vert,frag関数の内部で pi , pi2を使用した場合、
#define のpiは正常に使用できるが、
const float のpi2は内部の値が0になる※
float でも同様
コンパイルエラーは発生しない為注意
#define num 987.654
const float num2 = 123.345;
fixed4 frag (v2f i) : SV_Target
{
return float4(num2.xx,num,1);
}
-- Hardware tier variant: Tier 1
-- Fragment shader for "d3d11":
Constant Buffer "$Globals" (48 bytes) on slot 0 {
Float num2 at 32
}
Shader Disassembly:
//
// Generated by Microsoft (R) D3D Shader Disassembler
//
//
// Input signature:
//
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------
// SV_POSITION 0 xyzw 0 POS float
//
//
// Output signature:
//
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------
// SV_Target 0 xyzw 0 TARGET float xyzw
//
ps_4_0
dcl_constantbuffer CB0[3], immediateIndexed
dcl_output o0.xyzw
0: mov o0.xy, cb0[2].xxxx
1: mov o0.zw, l(0,0,987.653992,1.000000)
2: ret
// Approximately 0 instruction slots used
アセンブリのコード
o0 : 色の出力値
CPU
~Shader
転送
#define num 987.654
static const float num2 = 123.345;
fixed4 frag (v2f i) : SV_Target
{
return float4(num2.xx,num,1);
}
-- Hardware tier variant: Tier 1
-- Fragment shader for "d3d11":
Shader Disassembly:
//
// Generated by Microsoft (R) D3D Shader Disassembler
//
//
// Input signature:
//
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------
// SV_POSITION 0 xyzw 0 POS float
//
//
// Output signature:
//
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------
// SV_Target 0 xyzw 0 TARGET float xyzw
//
ps_4_0
dcl_output o0.xyzw
0: mov o0.xyzw, l(123.345001,123.345001,987.653992,1.000000)
1: ret
// Approximately 0 instruction slots used
アセンブリのコード
static const float, static floatなら大丈夫
補足
アセンブリコードは
「Compiled code」から閲覧可能
演習
右下の「よくあるミス」の変数を使用して、fragment shaderにて色を描画し、�アセンブリコードを開き、値が0になっていることを確認してください
おすすめのスライド資料
おすすめのスライド資料
おすすめスライド資料
おすすめ資料
おわり
ボツ資料
今回説明していない内容(ぱっと思いつく分)
2回目があるならサーフェイスシェーダーを紹介すると思います
(省略可)CGPROGRAM-ENDCGについて
SubShader{},Pass{},Tag{}に関して
「Pass ブロックはゲームオブジェクトのジオメトリを 1回レンダリングします。」
Z値(ZTest,ZWrite)(重要)
FarClip面
NearClip面
画面(両方LEqual)
ZWrite ZTest (重要)
FarClip面
NearClip面
画面(両方LEqual)
RenderQueue , ZTest 演習問題
(まにあいませんでした)
Shader "Custom/VFShader_Sec4"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
_Color("Color", Color) = (1,1,1,1)
}
SubShader
{
Tags { "RenderType"="Opaque" }
Tags { "RenderType"="Transparent" "Queue"="Transparent" }
Blend SrcAlpha OneMinusSrcAlpha
ZWrite Off
ZTest LEqual
LOD 100
Pass
{
...
}
}
}
フロー(まとめ)