1 of 148

Introduction to Shaders for VRChatter

剥_Haku

2 of 148

自己紹介

剥_Haku

https://x.com/impureLily

「間違ってる箇所があったら教えてください」

3 of 148

注意事項

  • できるだけ平易な文章で説明します
  • 用語はできるだけあらかじめ解説します

  • この資料は「とあるサークル」の講習会で使用した資料です
    • たまに編集してます

4 of 148

事前準備

  • Unity2022 3 22f1(を想定してますがほぼ何でも動きます)
    • Built in pipeline(VRChatなどと同様)でプロジェクト作成してください

  • お好きなコードエディタ
    • VSCode
    • Rider
      • 非商用版は無料
      • Unity側でIDEの設定をするのみ
      • 個人的にはこちらがおすすめ

5 of 148

資料

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まで。)

6 of 148

シェーダーとは?

  • 元々はShaderの名の通り、�Shadeをする「モノ」= 陰影処理をするもの�
  • 実際は、GPUに描画手順を記述するもの

  • Unityではマテリアルを作ったときに選択するアレ
    • アバターを購入するときに注意事項としてよくあるやつ

7 of 148

描画の内部プロセス(超重要)

  • めっっっっちゃわかりやすいので、この動画を見てください�(時間を30分 確保します)

シェーダを書けるプログラマになろう #1 シェーダを理解しよう - Unity道場2019 2月

  • Part1のみでOKです。

8 of 148

ノードベースでシェーダを書くなら

  • ノードベースのシェーダエディタ等も存在します
    • Amplify Shader Editor,Shader Graph, Shadere Forgeなど...
    • Amplify Shader Editor(ASE)はVRChatで動作するシェーダーを作成できる。
      • 生成されたコードも参考になる
    • VR対応に改造されたShader Graph

9 of 148

実際にコードを作成する

  • Project > Create > Shader >� Unlit Shaderを選択
  • 作成されたシェーダーファイルをダブルクリック
  • コードを全削除

10 of 148

実際にコードを見てみる(写経タイム)

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

}

}

}

11 of 148

シェーダーを適用する

マテリアルを作成

マテリアルにドラッグアンドドロップ

12 of 148

シェーダーを適用する

マテリアルをCubeにドラッグアンドドロップ

SceneのヒエラルキーでCubeを作成

13 of 148

実際にコードを見てみる

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

}

}

}

頂点シェーダ

フラグメントシェーダ

14 of 148

おさらい:セマンティクス

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側に色情報であると認識させる

15 of 148

おさらい

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]の範囲で表現ができる

16 of 148

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

}

}

}

🤔

17 of 148

#pragmaについて

  • 簡単に言うと

「コンパイラが参照する情報を渡す文章」

(コンパイラ:プログラムをPCなどのマシンがわかるように変換してくれるもの)

  • C言語を触ったことがあるなら
    • #pragma once などを記述したことがあるかも�

18 of 148

#pragmaについて

#pragma vertex vert

#pragma fragment frag

コンパイラに「vertexシェーダとしてvertと定義した関数を使用する」と伝える

コンパイラに「fragmentシェーダとしてfragと定義した関数を使用する」と伝える

これらが無ければ、

コンパイラは自作した頂点シェーダやフラグメントシェーダを認識できない

19 of 148

#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);

}

20 of 148

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

}

}

}

🤔

🤔

21 of 148

CGPROGRAM-ENDCG

  • HLSLのコードを書く為のブロック
    • CGPROGRAMとENDCGの間にHLSLのコードが書ける
    • 初めてShaderlabをする場合はこの程度の理解度で大丈夫

CGPROGRAM

[ここにシェーダープログラムのソースコードを書く]

ENDCG

22 of 148

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

}

}

}

🤔

🤔

23 of 148

Shader{}

  • Inspector側からShaderを選択する時に使用する
  • 一番外側

Shader "Tutorial/Custom/VFShader_Sec1"

{

SubShader

{

Pass

{

...

}

}

}

24 of 148

まとめ

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の設定など)

シェーダーの宣言

25 of 148

float4 について(超重要)

  • float型が4つ格納されている型
    • float2 は2つ, float3は3つ
  • 宣言方法

  • 各要素のアクセス方法
    • 右に示しています�
  • これから息を吸うように使用します

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)  

26 of 148

float4 について(精度)

  • 実はfloat4以外に4つの小数を表現する型がある。
    • fixed4, half4
      • 精度が落ちるが、軽い
      • 色等で使う

  • この講義では初学者を前提としており、混乱回避の為、�できるだけfloat4を使います。�とはいいつつ、怠惰でところどころfixedになってます

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のコード

27 of 148

演習:float4(想定5分)

  • 問1:x,y,z,wに( 0.1 ,0.5 , -0.3 , 0.88 )が入った, 型がfloat4の変数を宣言しなさい

  • 問2:問1で宣言した変数から、�第一要素と第二要素の和を,型がfloatの変数を宣言しなさい�ただし、xyzwは使用してはならない。

  • 問3:問1で宣言した変数から、第一要素と第二要素の成分を持ち、�第四要素で除算した,型がfloat2の変数を宣言しなさい。�ただし、rgbaは使用してはならない。

28 of 148

演習:Shader (休憩含めて15分)

  • 頂点シェーダにあるUnityObjectToClipPos の引数のvertexに�float4(x,y,z,0); (x,y,zは好きな実数)を加算し、挙動を観察してみてください
    • 例:return UnityObjectToClipPos(vertex+float4(1.0,0.0,0.0,0.0));

  • フラグメントシェーダの出力の値(float4)を変更し、�お好みの色にしてみてください
    • 例:return float4(1,1,0,1);

29 of 148

モデル行列

  • モデル上の座標をワールド座標に変換する行列

1

0

0

1

0

1

0

1

0

0

1

1

0

0

0

1

モデル行列

30 of 148

ビュー行列

  • ワールド座標からカメラ座標への変換行列

31 of 148

プロジェクション行列

  • カメラからみた視界を表現する為の行列

32 of 148

MVP行列 ( UnityObjectToClipPos(vertex))

  • Model, View, Projection行列を掛け合わせたもの
  • ローカル座標の頂点位置からスクリーンに描かれる場所への変換を一度にできる

33 of 148

34 of 148

Properties

35 of 148

写経タイム(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

}

}

}

36 of 148

Properties

  • 先程より幾つか増えている
  • Properties
    • Inspector側から操作できる要素を設定する
    • Color以外の構文はこちら

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

}

}

}

37 of 148

Properties

  • 注意点
    • Propertiesの値を使用するには�CGPROGRAM内で宣言をする必要がある

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

}

}

}

38 of 148

Texture, Struct

39 of 148

写経タイム

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

}

}

}

40 of 148

Properties

  • 注意点
    • Propertiesの値を使用するには�CGPROGRAM内で宣言をする必要がある
      • _MainTex_STの宣言は任意
        • Tiling.xy,とOffset.xyが�x,y,z,wの順に入っている

テクスチャ名_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

}

}

}

41 of 148

struct(構造体)

構造体:色々な型の変数をまとめて,�一つのまとまりにしたもの

  • シェーダーでは基本的にGPU側に情報を渡すため、�セマンティクスを付ける

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;

};

42 of 148

Appdata

頂点シェーダーに入力される�頂点情報

頂点座標、uv座標、法線など...

v2f

頂点シェーダーから�フラグメントシェーダに�送る情報

(変換された)頂点座標、uv座標...

struct v2f

{

float2 uv : TEXCOORD0;

...

float4 vertex : SV_POSITION;

};

struct appdata

{

float4 vertex : POSITION;

float2 uv : TEXCOORD0;

};

43 of 148

UV

  • 画像テクスチャと3Dデータとの対応関係
  • 画像テクスチャの空間のこと

44 of 148

sampler2D, tex2Dについて

ざっくりとした説明

  • sampler2D : テクスチャの情報が入っている

sampler2D _MainTex;

  • tex2D : 2D画像テクスチャから色をuvに基づいて得るのに使う関数

float4 col = tex2D(_MainTex, i.uv);

45 of 148

テクスチャ名_ST を適用する

  • TRANSFORM_TEX を使うと「テクスチャ名_ST」に入ったTiling,Offset情報を使用できる

画像を小さくしたり(タイリングしたり)、オフセットをしたりできる。

46 of 148

透明なオブジェクト

47 of 148

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

}

}

}

  1. タグで不透明モードにしているから

透明なオブジェクトの描画の設定にする必要がある!

RenderTypeを透明にする以外に色々設定項目がある

48 of 148

写経タイム(3分)

  • Sec3から追加�(シェーダーを複製してね)

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

{

...

}

}

}

49 of 148

先程のシェーダーに一部追加する

  • 透明になった!

50 of 148

ZTest

51 of 148

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 : カメラから一番近い値

52 of 148

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

描画されたオブジェクトより手前�(数値が前回の値より小さい)

なら描画

53 of 148

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 : 描画する条件

54 of 148

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

55 of 148

ZTest

  • ZTestの条件例
    • LEqual
      • 「オブジェクトのZ値」<= 「現在のZ値」Less Equal
    • GEqual
      • 「オブジェクトのZ値」>= 「現在のZ値」Greater Equal
    • Always
      • 「オブジェクトのZ値」「現在のZ値」に関わらず常に描画

56 of 148

Render Queue

57 of 148

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

58 of 148

Render Queue

一般的に使用される値

Tags { "RenderType"="Transparent" "Queue"="Transparent" }

曖昧です

RenderTypeとRenderQueueは一致してなくてもOKですが一致してた方がいいです(深度によるソート方向が違う)

59 of 148

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

{

...

}

}

}

60 of 148

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

不透明なオブジェクト

小さくする

透明なオブジェクト

大きくする

61 of 148

ZTest,RenderQueueはいつ行われる?

ZTest

  • 頂点シェーダと、フラグメントシェーダの間で行われる
    • 前の動画では「ステップ6」

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

{

...

}

}

}

62 of 148

コード内容

Shader "Custom/VFShader_Sec4"

{

...

SubShader

{

Tags { "RenderType"="Transparent" "Queue"="Transparent" }

Blend SrcAlpha OneMinusSrcAlpha

ZWrite Off

ZTest LEqual

LOD 100

Pass

{

...

}

}

}

レンダーキューをTransparentに設定

元々書かれていた色との混ぜ方の設定

Z値の書き込み設定

Z値のテスト方法の設定(書かなくてもデフォルトでLEqualにはなるが練習の為)

63 of 148

単一な色ではなくそれっぽい見た目にしたい

  • この講習会では扱いませんが
    • サーフェースシェーダー
    • リムライト
    • Matcap
    • Phone反射, Lambert反射
    • Normalマップ適用
    • ライト計算

後半のものは大抵 Surface Shaderがやってくれる!

https://docs.unity3d.com/ja/2019.4/Manual/SL-SurfaceShaderExamples.html

64 of 148

ここまででShaderLabの基礎の基礎はおわり!

65 of 148

演習 UVスクロール

66 of 148

UVスクロール?

テクスチャをアニメーションさせるアレ

67 of 148

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をオン

68 of 148

写経タイム

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 はグローバル変数

69 of 148

Displace Shader

70 of 148

Displace Shader

今までの知識とちょっと知識を足して楽しいシェーダーをつくってみる

  • 頂点シェーダの演習
  • フラグメントシェーダ側の説明は割愛します

71 of 148

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

}

}

}

72 of 148

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の値を変えてみてください

73 of 148

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波を入れてみてください

74 of 148

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関数を作成

カーテンの横情報に応じて振幅の大きさを変更

赤色の行は削除

75 of 148

カーテンの作成

  • 今回「なびかせる」為に、1入力変数から「なめらかな値」を生成する�「パーリンノイズ」を利用する

76 of 148

パーリンノイズの関数

//ランダム関数

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;

}

コピペしてください

赤色の行は削除

77 of 148

カーテンの作成(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;

}

78 of 148

SkyBox Shader�(およびVRChat対応)

79 of 148

Skyboxとは?

背景のコレ。

80 of 148

写経

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

を開いてください

81 of 148

TexCUBE

視線ベクトルを元にスカイマップなどのテクスチャを�適用してくれる

内部では球座標平面から逆変換されている(と思う)

acos , atan2 など...

82 of 148

写経(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

}

}

}

83 of 148

VR対応

  • 基本的にどのシェーダーにもVRChat対応の為にこの操作がまぁまぁ必要�(2024年9月現在)
  • Single Pass Instancing というレンダリング方式を採用している影響
  • 前回描画されていた色を取得する場合等、�スクリーン座標ベースの情報を得るときに影響が顕著に出る

https://tips.hecomi.com/entry/2018/09/24/232125https://docs.unity3d.com/ja/2019.4/Manual/SinglePassInstancing.html

84 of 148

Stencil

85 of 148

Stencil

Unity Documentation

ステンシルバッファは、一般的にピクセルマスクごとにピクセルの保存や廃棄を行うために使用されます。

  • Z値(深度値)同様ピクセルごとに保存される特殊な値!

86 of 148

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

87 of 148

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にする

青緑色の三角面

ステンシル設定例

88 of 148

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つ増やす

赤色の三角面

ステンシル設定例

89 of 148

Depthとの対応付け

Depth

Stencil

備考

比較関数

ZTest

Comp

Z値

Ref

好きな値を設定可能

値の範囲

0.0f~1.0f

0~255(整数)

成功時の処理

ZWrite

Pass

ZWriteより豊富な処理

Stencilはこの他にも沢山シンタックスがある

両方のテストにパスした場合、描画される

90 of 148

Stencilの使用方法

Stencil

{

Ref <ステンシルの値>

Comp <比較方法>

Pass <ステンシル成功時の処理>

}

Stencil

{

Ref 1

Comp Always

Pass Replace

}

利用例

記述方法

91 of 148

Stencil 演習

演習4.2を推奨します

ステンシル演習4.1が�複雑だった為、やさしい演習を追加しました�4.1は発展課題として使用してください

92 of 148

演習4.1

穴を空けるシェーダーを作る

93 of 148

方針

地面を描画

ステンシル値を�1にする

凹んだオブジェクトを

ステンシル値が1の時に

描画する

レンダーキュー

Z値

Stencil Producer

Stencil Consumer

94 of 148

方針

凹んだオブジェクトを

ステンシル値が1の時に

描画する

以降は凹んだオブジェクトを元に

ZTestされる(赤色)

レンダーキュー

最後

Z値

小~中

95 of 148

4_Stencil/Sceneと

4_Stencil/1_Producer.shader

を開いてください

ステンシル値を�1にする

Stencil Producer

  • 透明にする
  • ステンシル値を1にする
  • ZTestはLEqual

96 of 148

ステンシル値を�1にする

Stencil Producer

  • 透明にする
  • ステンシル値を1にする
  • ZTestはLEqual

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

を開いてください

97 of 148

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

  • ProducerのQueueより大
  • ステンシル値が1の時描画
  • ZTestはGEqual

4_Stencil/1_Consumer.shader

を開いてください

98 of 148

完成

追加説明がメモ欄にあります

99 of 148

演習4.2 (解説)

4_Stencil_v2を開いてください

(2024/11/04更新)

以前のステンシル演習が�複雑だった為、やさしい演習を追加しました

4_Stencil_v2/1_Stencil.shader

を開いてください

100 of 148

方針

地面を描画

ステンシル値を�変更する

ステンシル値を元に�描画分岐

レンダーキュー

Z値

大(ZWrite Offにする)

Stencil値の変更

Stencil値の使用

101 of 148

写経

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側から変更可能な設定

102 of 148

Maskオブジェクトの値設定

ステンシル値を1にするマテリアル

ステンシル値を2にするマテリアル

103 of 148

描画オブジェクトの値設定

赤窓から見えるオブジェクト

青窓から見えるオブジェクト

通常、見えるオブジェクト

104 of 148

描画順

105 of 148

ステンシルを極める

この動画がわかりやすいです

106 of 148

レイマーチング

5_RayMarching/Sceneを開いてください

107 of 148

レイマーチング

レイマーチングとは

  • 符号付き距離関数(SDF)で交差判定を行う描画手法�(メッシュで描画する手法ではない)

符号付き距離関数(SDF:Signed Distance Function)とは

  • 描画したいオブジェクトとの最短距離を算出する関数

ここからはアルゴリズム的な話になります

108 of 148

109 of 148

レイマーチング:手法

  1. 現在の位置から描画したいオブジェクトの最小距離を求める
    1. これがスライドの円に対応する
  2. 円がとても小さいなら描画したいオブジェクトに衝突したと判定する�(交差判定)
    • ここで描画完了
  3. 2.ではないなら、円の大きさの分だけすすめる(矢印の大きさ)
    • その地点からの最小距離を求めたのでその分位置を進めてもオブジェクトを貫通しない
  4. 1.に戻る

以上を繰り返す

110 of 148

(一般的な)レイマーチングで必要な要素

  • オレンジの点:カメラの位置(開始地点)
  • 矢印:視点のベクトル
  • 緑色、円:距離関数(後述)
  • 青色:交差判定 (終了判定)
  • 矢印の数:マーチングの最大実行回数
  • 現在の位置から描画したいオブジェクトの最小距離を求める
    • これが前のスライドの円に対応する
  • 円がとても小さいなら描画したいオブジェクトに衝突したと判定する(交差判定)
    • ここで描画完了
  • 2.ではないなら、円の大きさの分だけすすめる(矢印の大きさ)
    • その地点からの最小距離を求めたのでその分位置を進めてもオブジェクトを貫通しない
  • 1.に戻る

以上を繰り返す

111 of 148

距離関数の例

原点中心の球:

原点中心の立方体:

float SDFCircle(float3 rayPos, float radius)

{

return length(rayPos) - radius;

}

float SDFCube(float3 rayPos,float width)

{

return length(max(abs(rayPos) - width,0.0));

}

112 of 148

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から�変更します

113 of 148

写経

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;

}

114 of 148

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の表面からの距離

115 of 148

端ほどレイマーチの回数が多い

116 of 148

写経

// 直方体の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;

}

(略)

}

117 of 148

距離関数の合成

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)

118 of 148

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);

}

119 of 148

レイマーチング

(反復関数系)

120 of 148

IFS(Iterated Function System), 反復関数系

  • 空間を折り畳むことで、�複雑な形状の表現や無数にオブジェクトを配置できる

その他の例:万華鏡

121 of 148

距離関数をIFSで折りたたむ

  • 空間を折りたたむことで無数にオブジェクトを描画する
  • オブジェクトを1つ作り、それらを展開するだけでできる

122 of 148

  • 角度

  • 移動回転スケール

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;

}

123 of 148

  • 一定の箱の大きさで折りたたむ
  • 合成

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);

}

  • これらの合成

124 of 148

演習

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);

}

125 of 148

テクスチャを貼る

距離関数の勾配を取ることで、法線情報(面の向き)を入手できる

Triplanar Mappingで画像を貼れる

気が向いたらやってみてください

https://qiita.com/edo_m18/items/c8995fe91778895c875e

126 of 148

レイマーチングを極めたい場合

https://github.com/pema99/shader-knowledge/blob/main/raymarching.md

↑レイマーチング空間や深度書き込みとかの話

おすすめサイト

127 of 148

よくあるミス

128 of 148

Batching

同じマテリアルのオブジェクトを複数設置した場合、�今の状態だとある条件で結果が強制的にワールド座標基準になる(Mainカメラからの図)

対策1: タグに"DisableBatching"="True"を追加する

対策2:GPUInstancingに対応する(可能ならこちらが適切)

次の資料を作るときにちゃんと説明します。

129 of 148

よくあるミス

float a = 1/2;

代入される値は0

float a = 1.0/2.0;

.0を付けないと整数型で演算されてしまう

代入される値は0.5

float a = 1.0/2;

130 of 148

よくあるミス

頂点シェーダーからフラグメントシェーダーへの補間は

線形補間

頂点シェーダーでnormalizeしてもフラグメントシェーダーではnormalizeされていない可能性がある

例:頂点a,bにそれぞれ(1,0),(0,1)といったデータがあった場合、�頂点a,bの中心点の補間は(0.5,0.5)になる

131 of 148

よくあるミス

#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 でも同様

コンパイルエラーは発生しない為注意

132 of 148

#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

転送

133 of 148

#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なら大丈夫

134 of 148

補足

アセンブリコードは

「Compiled code」から閲覧可能

135 of 148

演習

右下の「よくあるミス」の変数を使用して、fragment shaderにて色を描画し、�アセンブリコードを開き、値が0になっていることを確認してください

136 of 148

おすすめのスライド資料

137 of 148

おすすめのスライド資料

138 of 148

おすすめスライド資料

139 of 148

おすすめ資料

140 of 148

おわり

141 of 148

ボツ資料

142 of 148

今回説明していない内容(ぱっと思いつく分)

  • リムライト、法線に関するお話
  • マルチパス
  • Surface Shader
  • LOD
  • Mipmap
  • shader_feature_
  • ジオメトリシェーダー
  • テッセレーション(ハルシェーダー、ドメインシェーダ)
  • MVP行列の中身
  • Projector
  • Render Texture, Custom Render Texture
  • Particle, GPU Particle
  • GPU Instancing
  • VRChatで使用できる特有の変数(Mirrorとかの判定)
  • レイトレーシング、BVH
  • カスタムレンダーパイプライン
  • ComputeShader

2回目があるならサーフェイスシェーダーを紹介すると思います

143 of 148

(省略可)CGPROGRAM-ENDCGについて

  • 「一般的なシェーダーのコードよりちょっと広い?」
  • 例として: HLSL文を挙げると、HLSLにはtechniqueというエントリが存在する
  • そこでvertexShaderやfragment(pixel)Shaderの設定を行う

144 of 148

SubShader{},Pass{},Tag{}に関して

  • SubShader{}
    • 色々なプラットフォームに対応する為の構文
      • AndroidとiOSは描画に使う言語が異なる
  • Pass{}
    • Unity Documentation

Pass ブロックはゲームオブジェクトのジオメトリを 1回レンダリングします。

    • 動画のステップ3から8までのステップをする領域設定
  • この2つの領域で、レンダリングの順番(RenderQueue)や深度テストの設定ができる
    • 要は先程の動画の�ステップ4「表裏を調べ、裏なら描画しない」�ステップ6「描画点をデプスバッファと比較」�等の設定を変えられる。
  • Tag{}
    • 最初に見た動画の中盤で挙げられていた、不透明オブジェクトの対策の部分です。
    • 今のところはおまじないとして捉えてください。
  • LOD
    • 割愛します
    • ワールド制作で最適化を行うときに調べると良いかもしれません
    • 通常LODは200にすることが多いです

145 of 148

Z値(ZTest,ZWrite)(重要)

  • Z値(深度値): カメラからの遠さを示す数値
    • 遠くにあればあるほどZ値は大きくなる。�
    • FarClip面を1,NearClip面を0として、その間の補間された値で表される、
      • 0.05など
    • 実際はZ値の分布は非線形的でそしてGPUによってはFarが0でNearが1だったりする

FarClip面

NearClip面

画面(両方LEqual)

146 of 148

ZWrite ZTest (重要)

  • ZWrite
    • Z値(深度値)を書き込むかどうか
      • 書き込む: ZWrite On
      • 書き込まない: ZWrite Off
  • ZTest
    • 元々書き込まれているZ値と�描画したいオブジェクトのZ値がどういった条件の時�そのオブジェクトを描画するか
    • 例:
      • LEqual: 描画したいオブジェクト <= 元々のZ値
        • 元々のオブジェクトより手前にある場合描画する
      • Always: 常に描画 ( 便宜上は 描画したいオブジェクト < 0 )
      • Greater: 描画したいオブジェクト > 元々のZ値

FarClip面

NearClip面

画面(両方LEqual)

147 of 148

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

{

...

}

}

}

148 of 148

フロー(まとめ)

  1. Render Queueで描画順が指定される
  2. セマンティクスを通して頂点シェーダーに3Dデータが流し込まれる
  3. CBufferから行列、ライティング等のデータが流し込まれる
  4. 頂点シェーダーが実行される(頂点位置の設定)
  5. セマンティクスにデータを乗せる(次のステージに流し込む為)
  6. 3頂点集まる
  7. 裏表のチェックがされる
  8. ラスタライズされる
  9. 深度テスト等が行われる
  10. セマンティクスを通してフラグメントシェーダにデータが渡される
  11. CBufferからライティング等のデータが流し込まれる
  12. フラグメントシェーダーが実行される(色や深度の設定)
  13. 描画処理が行われる(色のブレンドやZ値書き込みなど)
  14. 描画される