SlideShare a Scribd company logo
シェーダを書けるプログラマになろう
ユニティ・テクノロジーズ・ジャパン
安原 祐二
Part I
シェーダを理解しよう
理解とは?
理解とは?
境界の把握
それができないことは何か?
できる
できる
できる
できる
できる
できる
〜〇〇を理解したい〜
それができないことは何か?
できる
できる
できる
できる
できる
できる
できる
できる
できる
できる
できる
できる
できない
できない
できない
できない
できない
できない
できない
できない
できない
できない
〜〇〇を理解したい〜
【Unity道場 2月】シェーダを書けるプログラマになろう
こんなことにも使えるよ!
・・・という話はまたこんど
道具の特徴を抑えて
持ち手
接合部
刃
紙が切れる理由を知ろう
Part I
シェーダを理解しよう
8ステップで描画する
ステップ8
ステップ7
ステップ6
ステップ5 
ステップ3 
ステップ4 
ステップ1
ステップ2
3Dモデルを準備
Transformの値を4x4行列に変換
点を打つ
描画点に打つべき色を確定
描画点をデプスバッファと比較
描画点を確定
頂点ごとに描画位置を算出
表裏を調べ、裏なら描画しない
シェーダとは
GPU搭載の基本シェーダはこのふたつ
頂点シェーダ
フラグメントシェーダ
描画とは
モニタ上の画素に色付きの点を打つこと
ステップ1
ステップ1 3Dモデルを準備
ステップ2
ステップ2 Transformの値を4x4行列に変換
モデル行列という
Rotation
&
Scale
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1固定
Position
ステップ3
ステップ3 頂点ごとに描画位置を算出
モデル行列
カメラ情報
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
ステップ4
ステップ4 表裏を調べ、裏なら描画しない
3点そろったら
ステップ5
ステップ5 描画点を確定
ピクセルの中心が三角形の内側にあるかどうか
ステップ6
ステップ6 描画点を深度バッファと比較
描画済みの点が手前にあるなら描画しない
深度バッファ
※GPUごとに実装が異なります
ステップ7
ステップ7 描画点に打つべき色を確定
テクスチャ、ライティング、シャドウ、フォグなどなど考慮
ステップ8
ステップ8 点を打つ
ブレンド関数を指定可 深度バッファも更新
ステップ8
ステップ7
ステップ6
ステップ5 
ステップ3 
ステップ4 
ステップ1
ステップ2
3Dモデルを準備
Transformの値を4x4行列に変換
点を打つ
描画点に打つべき色を確定
描画点をデプスバッファと比較
描画点を確定
頂点ごとに描画位置を算出
表裏を調べ、裏なら描画しない
3Dモデルを準備
Transformの値を4x4行列に変換
点を打つ
描画点に打つべき色を確定
描画点をデプスバッファと比較
描画点を確定
頂点ごとに描画位置を算出
表裏を調べ、裏なら描画しない
CPUGPU
ステップ8
ステップ7
ステップ6
ステップ5 
ステップ3 
ステップ4 
ステップ1
ステップ2
3Dモデルを準備
Transformの値を4x4行列に変換
点を打つ
描画点に打つべき色を確定
描画点をデプスバッファと比較
描画点を確定
頂点ごとに描画位置を算出
表裏を調べ、裏なら描画しない
CPUGPU
頂点シェーダ
フラグメント
シェーダ
ステップ8
ステップ7
ステップ6
ステップ5 
ステップ3 
ステップ4 
ステップ1
ステップ2
シェーダで
対応できない例
線を引きたい
ステップ8
ステップ7
ステップ6
ステップ5 
ステップ3 
ステップ4 
ステップ1
ステップ2
3Dモデルを準備
Transformの値を4x4行列に変換
点を打つ
描画点に打つべき色を確定
描画点をデプスバッファと比較
描画点を確定
頂点ごとに描画位置を算出
表裏を調べ、裏なら描画しない
CPUGPU
頂点シェーダ
フラグメント
シェーダ
ステップ1で対応が必要
3Dモデル(Mesh)の構造
・頂点列
・三角形列
頂点座標
法線
UV
頂点座標
法線
UV
頂点座標
法線
UV
頂点座標
法線
UV
0
1
2
0 1 2 3
2
1
3
4
5
6
6
5
7
5
0
7
7
0
2
1
4
3
3
4
6
線を引くには線分用のデータが必要
シェーダで
対応できない例
半透明描画をしたい
窓ガラス
ステップ8
ステップ7
ステップ6
ステップ5 
ステップ3 
ステップ4 
ステップ1
ステップ2
3Dモデルを準備
Transformの値を4x4行列に変換
点を打つ
描画点に打つべき色を確定
描画点をデプスバッファと比較
描画点を確定
頂点ごとに描画位置を算出
表裏を調べ、裏なら描画しない
CPUGPU
頂点シェーダ
フラグメント
シェーダ
ステップ8で対応が必要
+
=
2
半透明に必要な計算例
+
=
2
計算にはすでに打ってある色が必要
フラグメント
シェーダの出力 打つべき色
半透明はBlend で設定可能
ステップ8に対する設定
Shader "Custom/minimum" {
SubShader {
Tags { "RenderType"="Transparent" }
Blend SrcAlpha OneMinusSrcAlpha
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
…
プログラマブルではない 演算は書けない
ピクセルごとに変えられない
余談
半透明&深度バッファ問題
不透明を半透明の後に描画する困難
透明部の深度バッファを更新
不透明を半透明の後に描画する困難
透明部の深度バッファを
更新しない
現代における対策:半透明は全ての不透明のあとに描画
不透明を半透明の後に描画する困難
透明部の深度バッファを更新
改めて
シェーダコードを観察
最小のシェーダ
Shader "Custom/minimum" {
SubShader {
Tags { "RenderType"="Opaque" }
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata {
float4 vertex : POSITION;
};
struct v2f {
float4 vertex : SV_POSITION;
};
v2f vert(appdata v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag(v2f i) : SV_Target {
return fixed4(1,0,0,1);
}
ENDCG
}
}
}
最小のシェーダ
ほぼ一般的な
シェーダコード
Shader "Custom/minimum" {
SubShader {
Tags { "RenderType"="Opaque" }
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata {
float4 vertex : POSITION;
};
struct v2f {
float4 vertex : SV_POSITION;
};
v2f vert(appdata v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag(v2f i) : SV_Target {
return fixed4(1,0,0,1);
}
ENDCG
}
}
}
頂点シェーダ
フラグメント
シェーダ
最小のシェーダ
Unityが用意している
便利な記述
Shader "Custom/minimum" {
SubShader {
Tags { "RenderType"="Opaque" }
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata {
float4 vertex : POSITION;
};
struct v2f {
float4 vertex : SV_POSITION;
};
v2f vert(appdata v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag(v2f i) : SV_Target {
return fixed4(1,0,0,1);
}
ENDCG
}
}
}
様々な設定が可能
超重要ポイントふたつ
セマンティクス
補間
最小のシェーダ
Shader "Custom/minimum" {
SubShader {
Tags { "RenderType"="Opaque" }
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata {
float4 vertex : POSITION;
};
struct v2f {
float4 vertex : SV_POSITION;
};
v2f vert(appdata v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag(v2f i) : SV_Target {
return fixed4(1,0,0,1);
}
ENDCG
}
}
}
頂点シェーダ
フラグメント
シェーダ
セマンティクス付き構造体
セマンティクス
struct appdata {
float4 vertex : POSITION;
};
struct v2f {
float4 vertex : SV_POSITION;
};
変数の内容の意味
GPUに扱い方を伝える
補間
ここが28で
ここが96なら
このへんは71.3
途中の値を算出する
GPUががんばる
テクスチャマッピングの例
v2f vert(appdata v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
fixed4 frag(v2f i) : SV_Target {
return tex2D(_MainTex, i.uv);
}
テクスチャマッピングのシェーダ
uvを渡す
テクセルを拾う
struct appdata {
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f {
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
};
uvはテクスチャ座標
uvはテクスチャ座標
v2f vert(appdata v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
fixed4 frag(v2f i) : SV_Target {
return tex2D(_MainTex, i.uv);
}
uvを渡す
テクセルを拾う
v2f
v2f
v2f
頂点シェーダの出力
v2f
v2f
v2f
フラグメントシェーダ
の入力
頂点シェーダの出力
補間
v2f
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 i) : SV_Target {
return tex2D(_MainTex, i.uv);
}
v2f vert(appdata v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
fixed4 frag(v2f i) : SV_Target {
return tex2D(_MainTex, i.uv);
}補間されたUV座標で
テクスチャから色を得る
struct appdata {
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f {
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
};
ところで
ってなんなの?
サーフェスシェーダ(surface shader)
v2f vert(appdata v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
頂点シェーダ
フラグメントシェーダ
fixed4 frag(v2f i) : SV_Target {
return tex2D(_MainTex, i.uv);
}
v2f vert(appdata v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
たいてい
同じことを書く 省略しても
生成してくれる
頂点シェーダ
フラグメントシェーダ
fixed4 frag(v2f i) : SV_Target {
return tex2D(_MainTex, i.uv);
}
v2f vert(appdata v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
たいてい
同じことを書く
たいてい
フクザツになる
省略しても
生成してくれる
楽な記述で
生成してくれる
頂点シェーダ
フラグメントシェーダ
fixed4 frag(v2f i) : SV_Target {
return tex2D(_MainTex, i.uv);
}
サーフェスシェーダ(surface shader)は
v2f vert(appdata v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
たいてい
同じことを書く
たいてい
フクザツになる
省略しても
生成してくれる
楽な記述で
生成してくれる
頂点シェーダ
フラグメントシェーダ
Unityからのプレゼント
fixed4 frag(v2f i) : SV_Target {
return tex2D(_MainTex, i.uv);
}
3Dモデルを準備
Transformの値を4x4行列に変換
点を打つ
描画点に打つべき色を確定
描画点をデプスバッファと比較
描画点を確定
頂点ごとに描画位置を算出
表裏を調べ、裏なら描画しない
CPUGPU
頂点シェーダ
フラグメント
シェーダ
設定変更可
設定変更可
設定変更可ステップ8
ステップ7
ステップ6
ステップ5 
ステップ3 
ステップ4 
ステップ1
ステップ2
休憩
Part II
GPUの神秘
fixed4 frag(v2f i) : SV_Target {
return tex2D(_MainTex, i.uv);
}
tex2Dでテクセルをゲット
tex2DはGPUに直結
ここにシェーダの神秘がある
テクスチャは縮小に課題あり
一般論
品質の低下
ちらつき
ORIGINAL NEAREST
KAISER BOX
BICUBIC
HAMMING
ORIGINAL NEAREST
高度な縮小は時間がかかる
UVが大きく動くと
テクセルを飛ばしてしまう
tex2D(_MainTex, i.uv)
高度な縮小はシェーダでは使えない
無限平面を作ろう
【Unity道場 2月】シェーダを書けるプログラマになろう
カメラの視界に必ず入るように平面を作る
頂点シェーダで計算
作戦
1.0
1.0の四角形を準備する
1.0
vertexシェーダで
頂点を拡大・移動
カメラの前方ベクトル
カメラの座標
カメラの前方ベクトル
シェーダまめ知識
float3 forward = -UNITY_MATRIX_V._m20_m21_m22;
ビュー行列
uvにワールド座標を入れちゃう
【Unity道場 2月】シェーダを書けるプログラマになろう
(0, 1)
(0, 0)
(1, 1)
(1, 0)
テクスチャ座標
画像のサイズに関わらず
テクスチャ座標は [0, 1]
(0, 2)
(0, 0)
(2, 2)
(2, 0)
テクスチャ座標が[0, 1]の範囲外の場合
2.0
? ?
?
(0, 1)
(0, 0)
(1, 1)
(1, 0)
(0, 2)
(0, 0)
(2, 2)
(2, 0)
2.0
? ?
?
tex2D(1.5, 1.5)
は何を返すのか?
(0, 2)
(0, 0)
(2, 2)
(2, 0)
2.0
? ?
?
tex2D(1.5, 1.5)
は何を返すのか?
tex2D(0.5, 0.5)
の値を返す
(0, 1)
(0, 0)
(1, 1)
(1, 0)
(0, 2)
(0, 0)
(2, 2)
(2, 0)
テクスチャ座標が[0, 1]の範囲外の場合
2.0
Repeat
Clamp
テクスチャのインポート設定で
tex2Dの動作を変えられる
[0.0, 1.0]範囲外のuvの扱い
uvにワールド座標(x, z)を入れる
(-728, 1041)
(235, 1021)
(-549, 103)
(346, 124)
【Unity道場 2月】シェーダを書けるプログラマになろう
キレイに縮小されている不思議
ミップマップ(mipmap)の効果
テクスチャインポート時に
高度な縮小を作成
mipmap生成
時間がかかっても良い
縮小済みのテクスチャが使用される
ミップマップなしミップマップあり
ミップマップなしミップマップあり
mipmap生成を
指定するだけ
シェーダはtex2Dを
呼んでいるだけ
それでも
mipmapレベルが選ばれる・・・
tex2Dはどうやって
mipmapレベルを
選択しているのか?
mipmap生成を
指定するだけ
シェーダはtex2Dを
呼んでいるだけ
疑問
それでも
mipmapレベルが選ばれる・・・
それはさておき
繰り返しが気になる
単位範囲ごとに
uv座標をずらしてみる
uvをずらす
fixed4 frag(v2f i) : SV_Target
{
float4 off = hash4fast(floor(i.uv));
off.zw = off.zw >= float2(0.5, 0.5) ?
float2(1, 1) : float2(-1, -1);
float2 fuv = frac(i.uv);
float2 uv = fuv * off.zw + off.xy;
fixed4 col = tex2D(_MainTex, uv);
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
コード例
※hash4fastは別に定義
不連続が気になるかどうかは
テクスチャ次第
(意外と大丈夫だった)
やや不連続が気になる
テクスチャ
チラつきが発生する
いよいよシェーダの神秘へ
チラつきの原因をわかりやすいテクスチャで比較
チラつきの原因をわかりやすいテクスチャで比較
間違ったmipmap 正しいmipmap
ここで衝撃の事実
フラグメントシェーダは必ず4ピクセル同時に実行している
実行単位の4ピクセル
ここで衝撃の事実
フラグメントシェーダは必ず4ピクセル同時に実行している
fixed4 frag(v2f i) : SV_Target
{
float4 off = hash4fast(floor(i.uv));
off.zw = ((step(0.5, off.zw)) - 0.5) * 2;
float2 fuv = frac(i.uv);
float2 uv = fuv * off.zw + off.xy;
fixed4 col = tex2D(_MainTex, uv);
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
本当に同時に実行される
fixed4 frag(v2f i) : SV_Target
{
float4 off = hash4fast(floor(i.uv));
off.zw = ((step(0.5, off.zw)) - 0.5) * 2;
float2 fuv = frac(i.uv);
float2 uv = fuv * off.zw + off.xy;
fixed4 col = tex2D(_MainTex, uv);
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
fixed4 frag(v2f i) : SV_Target
{
float4 off = hash4fast(floor(i.uv));
off.zw = ((step(0.5, off.zw)) - 0.5) * 2;
float2 fuv = frac(i.uv);
float2 uv = fuv * off.zw + off.xy;
fixed4 col = tex2D(_MainTex, uv);
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
fixed4 frag(v2f i) : SV_Target
{
float4 off = hash4fast(floor(i.uv));
off.zw = ((step(0.5, off.zw)) - 0.5) * 2;
float2 fuv = frac(i.uv);
float2 uv = fuv * off.zw + off.xy;
fixed4 col = tex2D(_MainTex, uv);
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
隣の3つも
同じ場所を実行
fixed4 frag(v2f i) : SV_Target
{
float4 off = hash4fast(floor(i.uv));
off.zw = off.zw >= float2(0.5, 0.5) ?
float2(1, 1) : float2(-1, -1);
float2 fuv = frac(i.uv);
float2 uv = fuv * off.zw + off.xy;
fixed4 col = tex2D(_MainTex, uv);
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
fixed4 frag(v2f i) : SV_Target
{
float4 off = hash4fast(floor(i.uv));
off.zw = off.zw >= float2(0.5, 0.5) ?
float2(1, 1) : float2(-1, -1);
float2 fuv = frac(i.uv);
float2 uv = fuv * off.zw + off.xy;
fixed4 col = tex2D(_MainTex, uv);
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
tex2Dは
隣の3つの情報も使う!
tex2D(_MainTex, uv)
GPUは異なる4つのuv値を入手している
uv uv
uvuv
tex2D(_MainTex, uv)
GPUは異なる4つのuv値を入手している
uv uv
uvuv
隣接ピクセルのuv値との差分を取れる
uv値の飛び具合がわかる!
tex2Dはどうやって
mipmapレベルを
選択しているのか?
mipmap生成を
指定するだけ・・・
シェーダはtex2Dを
呼んでいるだけ・・・
疑問
それでも
mipmapレベルが選ばれる・・・
再掲
tex2D(_MainTex, uv)
uv uv
uvuv
GPUは異なる4つのuv値を入手している
隣で実行中の値を使って適切なmipmapを選ぶ
チラつきが発生する
uvを操作したので
適切なmipmapが
選ばれていない問題
対処:適切なuvを指定
fixed4 frag(v2f i) : SV_Target
{
float4 off = hash4fast(floor(i.uv));
off.zw = off.zw >= float2(0.5, 0.5) ?
float2(1, 1) : float2(-1, -1);
float2 fuv = frac(i.uv);
float2 uv = fuv * off.zw + off.xy;
float2 dx = ddx(i.uv) * off.zw;
float2 dy = ddy(i.uv) * off.zw;
fixed4 col = tex2Dgrad(_MainTex, uv, dx, dy);
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ddx, ddy で適切な傾きを得て tex2Dgradで指定
チラつきが消滅
完成!
ddx, ddy の神秘
fixed4 frag(v2f i) : SV_Target
{
float4 off = hash4fast(floor(i.uv));
off.zw = off.zw >= float2(0.5, 0.5) ?
float2(1, 1) : float2(-1, -1);
float2 fuv = frac(i.uv);
float2 uv = fuv * off.zw + off.xy;
float2 dx = ddx(i.uv) * off.zw;
float2 dy = ddy(i.uv) * off.zw;
fixed4 col = tex2Dgrad(_MainTex, uv, dx, dy);
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ddx, ddy の神秘
fixed4 frag(v2f i) : SV_Target
{
float4 off = hash4fast(floor(i.uv));
off.zw = off.zw >= float2(0.5, 0.5) ?
float2(1, 1) : float2(-1, -1);
float2 fuv = frac(i.uv);
float2 uv = fuv * off.zw + off.xy;
float2 dx = ddx(i.uv) * off.zw;
float2 dy = ddy(i.uv) * off.zw;
fixed4 col = tex2Dgrad(_MainTex, uv, dx, dy);
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ddx, ddy の神秘
fixed4 frag(v2f i) : SV_Target
{
float4 off = hash4fast(floor(i.uv));
off.zw = off.zw >= float2(0.5, 0.5) ?
float2(1, 1) : float2(-1, -1);
float2 fuv = frac(i.uv);
float2 uv = fuv * off.zw + off.xy;
float2 dx = ddx(i.uv) * off.zw;
float2 dy = ddy(i.uv) * off.zw;
fixed4 col = tex2Dgrad(_MainTex, uv, dx, dy);
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
横の隣との差
ddx, ddy の神秘
fixed4 frag(v2f i) : SV_Target
{
float4 off = hash4fast(floor(i.uv));
off.zw = off.zw >= float2(0.5, 0.5) ?
float2(1, 1) : float2(-1, -1);
float2 fuv = frac(i.uv);
float2 uv = fuv * off.zw + off.xy;
float2 dx = ddx(i.uv) * off.zw;
float2 dy = ddy(i.uv) * off.zw;
fixed4 col = tex2Dgrad(_MainTex, uv, dx, dy);
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
縦の隣との差
fixed4 frag(v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
return col;
}
fixed4 frag(v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
return fwidth(col);
}
fwidth(v)=abs(ddx(v)) + abs(ddy(v))
fixed4 frag(v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
return lerp(col, fixed4(0,0,0,1), fwidth(col.a));
}
fwidth(v)=abs(ddx(v)) + abs(ddy(v))
【Unity道場 2月】シェーダを書けるプログラマになろう
https://blogs.unity3d.com/jp/2019/02/14/procedural-stochastic-texturing-in-unity/
fixed4 frag(v2f i) : SV_Target
{
float4 off = hash4fast(floor(i.uv));
off.zw = ((step(0.5, off.zw)) - 0.5) * 2;
float2 fuv = frac(i.uv);
float2 uv = fuv * off.zw + off.xy;
fixed4 col = tex2D(_MainTex, uv);
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
同時に実行されることに想いを馳せる
fixed4 frag(v2f i) : SV_Target
{
float4 off = hash4fast(floor(i.uv));
off.zw = ((step(0.5, off.zw)) - 0.5) * 2;
float2 fuv = frac(i.uv);
float2 uv = fuv * off.zw + off.xy;
fixed4 col = tex2D(_MainTex, uv);
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
fixed4 frag(v2f i) : SV_Target
{
float4 off = hash4fast(floor(i.uv));
off.zw = ((step(0.5, off.zw)) - 0.5) * 2;
float2 fuv = frac(i.uv);
float2 uv = fuv * off.zw + off.xy;
fixed4 col = tex2D(_MainTex, uv);
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
fixed4 frag(v2f i) : SV_Target
{
float4 off = hash4fast(floor(i.uv));
off.zw = ((step(0.5, off.zw)) - 0.5) * 2;
float2 fuv = frac(i.uv);
float2 uv = fuv * off.zw + off.xy;
fixed4 col = tex2D(_MainTex, uv);
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
GPUを扱うときの心構え
シェーダはbranch(ようするにif文)が苦手
fixed4 frag(v2f i) : SV_Target
{
float4 col;
if (i.uv.x < 0.5)
col = tex2D(_MainTex, i.uv);
else
col = tex2D(_MainTex, -i.uv);
return col;
}
同時実行することを思えば残酷な記述
休憩
カメラの前方ベクトル
float3 forward = -UNITY_MATRIX_V._m20_m21_m22;
? ? ? ?
? ? ? ?
? ? ? ?
? ? ? ?
M =
(
a b
c d)
I =
(
1 0
0 1)
=
(
a b
c d)
−1
=
1
ad − bc (
d −b
−c a )M−1
行列単位行列 行列 の逆行列M−1
M MI
おしまい
詳しく解説したブログ記事
https://qiita.com/yuji_yasuhara/private/158bab01cfff3c39214b
[Unity]無限平面を描画する過程でGPUの理解を深める
https://qiita.com/yuji_yasuhara/private/8d63455d1d277af4c270
[Unity] CGに使用される行列についての考察
ボツスライド集
プログラマブルシェーダで出来ないこと
• アルファブレンディング
• 点・線の描画
• 頂点の生成(ジオメトリシェーダ、テセレーション)
• 描画順の指定
• FrustomCulling抑制
• テクスチャサンプリング方式の変更
• トポロジ(三角形の構成)変更
深度バッファで比較するということは
奥の物体は
手前の物体に
塗りつぶされる
手前のものを
先に描画したい
コックピットは最初に描画して効率UP
描画順はRender Queue で指定
※GPUごとに特性が異なります
・頂点列
・三角形列
頂点座標
法線
UV
頂点座標
法線
UV
頂点座標
法線
UV
頂点座標
法線
UV
0
1
2
0 1 2 3
2
1
3
4
5
6
6
5
7
5
0
7
7
0
2
1
4
3
3
4
6
ところで
この立方体の頂点数はいくつ?
struct appdata {
float4 vertex : POSITION;
float4 normal: NORMAL;
float4 uv: TEXCOORD;
};
struct v2f {
float4 vertex : SV_POSITION;
};
v2f vert(appdata v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
appdata に頂点情報
法線が異なれば頂点も別に必要
・頂点列
・三角形列
頂点座標
法線
UV
頂点座標
法線
UV
頂点座標
法線
UV
頂点座標
法線
UV
0
1
2
0 1 2 3
2
1
3
4
5
6
6
5
7
5
0
7
7
0
2
1
4
3
3
4
6
ハードエッジは法線が分かれるので24頂点
ただの補間ではない
奥行が考慮された補間単なる二次元補間
頂点シェーダのよくあるミス
モデル行列の各部の意味
Rotation
&
Scale
1 0 0
0 1 0
0 0 1
0 0 0 1固定
Position
a b c
a
b
c
v2f vert(appdata v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
inline float4 UnityObjectToClipPos(in float3 pos)
{
return mul(UNITY_MATRIX_VP, mul(unity_ObjectToWorld, float4(pos, 1.0)));
}
UnityObjectToClipPosの中身:
これを自分で書いても良いが、この1.0を0.0にしたらアウト
1 0 0 a
0 1 0 b
0 0 1 c
0 0 0 1
px
py
pz
1
=
px + a
py + b
pz + c
1
1 0 0 a
0 1 0 b
0 0 1 c
0 0 0 1
px
py
pz
0
=
px
py
pz
0
掛けるベクトルのw要素による結果の違い
Positionが加算されない
Positionが加算される
頂点ベクトルはw=1で計算
法線ベクトルはw=0で計算
フラグメントシェーダのよくあるミス
法線を渡すときは注意
v2f vert(appdata v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.normal = UnityObjectToWorldNormal(v.normal);
return o;
}
normalizeが必要
fixed4 frag(v2f i) : SV_Target {
float3 n = normalize(i.normal);
…
線形補間なので
短くなってしまう
point sampling bilinear trilinear
Kaiser Box
ミップマップ作成アリゴリズム比較
おしまい

More Related Content

PDF
UE4における大規模背景制作事例 最適化ワークフロー編
PDF
【CEDEC2017】Unityを使ったNintendo Switch™向けのタイトル開発・移植テクニック!!
PPTX
なぜなにリアルタイムレンダリング
PDF
猫でも分かるUE4のポストプロセスを使った演出・絵作り
PDF
60fpsアクションを実現する秘訣を伝授 解析編
PPTX
UE4のためのより良いゲーム設計を理解しよう!
PDF
UE4 Volumetric Fogで 空間を演出する!
PDF
そう、UE4ならね。あなたのモバイルゲームをより快適にする沢山の冴えたやり方について Part 2 <Texture Streaming, メモリプロ...
UE4における大規模背景制作事例 最適化ワークフロー編
【CEDEC2017】Unityを使ったNintendo Switch™向けのタイトル開発・移植テクニック!!
なぜなにリアルタイムレンダリング
猫でも分かるUE4のポストプロセスを使った演出・絵作り
60fpsアクションを実現する秘訣を伝授 解析編
UE4のためのより良いゲーム設計を理解しよう!
UE4 Volumetric Fogで 空間を演出する!
そう、UE4ならね。あなたのモバイルゲームをより快適にする沢山の冴えたやり方について Part 2 <Texture Streaming, メモリプロ...

What's hot (20)

PPTX
UE4におけるLoadingとGCのProfilingと最適化手法
PDF
UE4におけるエフェクトの基本戦略事例 後半
PPTX
猫でも分かる UE4のAnimation Blueprintの運用について
PDF
UE4プログラマー勉強会 in 大阪 -エンジンの内部挙動について
PPTX
大規模タイトルにおけるエフェクトマテリアル運用 (SQEX大阪: 林武尊様) #UE4DD
PDF
徹底解説!UE4を使ったモバイルゲーム開発におけるコンテンツアップデートの極意!
PDF
CEDEC2016: Unreal Engine 4 のレンダリングフロー総おさらい
PDF
UE4におけるエフェクトの基本戦略事例 前半
PDF
UE4 LODs for Optimization -Beginner-
PDF
MagicOnion入門
PDF
UE4ディープラーニングってやつでなんとかして!環境構築編(Python3+TensorFlow)
PDF
なぜなにFProperty - 対応方法と改善点 -
PDF
UE4でマルチプレイヤーゲームを作ろう
PPTX
Lightmassの仕組み ~Precomputed Light Volume編~ (Epic Games Japan: 篠山範明)
PDF
非同期ロード画面 Asynchronous Loading Screen
PPTX
UE4のライティング解体新書~効果的なNPRのためにライティングの仕組みを理解しよう~
PPTX
大規模CSゲームにおけるライトマス運用
PPTX
UE4のスレッドの流れと Input Latency改善の仕組み
PDF
UE4のモバイル開発におけるコンテンツアップデートの話 - Chunk IDとの激闘編 -
PDF
UE4における大規模背景制作事例(コリジョン編)
UE4におけるLoadingとGCのProfilingと最適化手法
UE4におけるエフェクトの基本戦略事例 後半
猫でも分かる UE4のAnimation Blueprintの運用について
UE4プログラマー勉強会 in 大阪 -エンジンの内部挙動について
大規模タイトルにおけるエフェクトマテリアル運用 (SQEX大阪: 林武尊様) #UE4DD
徹底解説!UE4を使ったモバイルゲーム開発におけるコンテンツアップデートの極意!
CEDEC2016: Unreal Engine 4 のレンダリングフロー総おさらい
UE4におけるエフェクトの基本戦略事例 前半
UE4 LODs for Optimization -Beginner-
MagicOnion入門
UE4ディープラーニングってやつでなんとかして!環境構築編(Python3+TensorFlow)
なぜなにFProperty - 対応方法と改善点 -
UE4でマルチプレイヤーゲームを作ろう
Lightmassの仕組み ~Precomputed Light Volume編~ (Epic Games Japan: 篠山範明)
非同期ロード画面 Asynchronous Loading Screen
UE4のライティング解体新書~効果的なNPRのためにライティングの仕組みを理解しよう~
大規模CSゲームにおけるライトマス運用
UE4のスレッドの流れと Input Latency改善の仕組み
UE4のモバイル開発におけるコンテンツアップデートの話 - Chunk IDとの激闘編 -
UE4における大規模背景制作事例(コリジョン編)
Ad

Similar to 【Unity道場 2月】シェーダを書けるプログラマになろう (20)

PPTX
Shadow gunのサンプルから学べるモバイル最適化
PDF
【Unite Tokyo 2018】カスタムシェーダーでモバイルでも最先端グラフィックスな格闘ゲームを!
PDF
㉑CSSでアニメーション!その2
PDF
簡単!OpenGL ES 2.0フラグメントシェーダー
KEY
シェーダーしよっ☆ Let's play shaders!
PPT
Rpscala2011 0601
PDF
ARコンテンツ作成勉強会:C#ではじめようOpenCV(カラートラッキング編)
PDF
【Unity道場スペシャル 2017京都】乱数完全マスター 京都編
PDF
【Unity道場スペシャル 2017札幌】乱数完全マスター
PPT
Gurobi python
ODP
D言語会議#1
PDF
【Unite Tokyo 2019】〈七つの大罪〉をゲームで!高品質グラフィックを具現化するための技法と開発最適化のご紹介
PPTX
Node.jsでつくるNode.js ミニインタープリター&コンパイラー
PPT
Or seminar2011final
PDF
DeNA TechCon2016 360VR Live Streaming
PDF
海外ゲーム技術勉強会#1 OGRE3D
PDF
さらに一歩踏み込んだCSS3の使い方!コツとポイントを理解して 楽しくサイトを彩る方法
PDF
LLVM最適化のこつ
PPTX
Inside of excel 方眼紙撲滅委員会 #pyfes
PDF
Unity道場京都スペシャル トゥーンシェーディングとノンフォトリアリスティック風絵づくり入門_
Shadow gunのサンプルから学べるモバイル最適化
【Unite Tokyo 2018】カスタムシェーダーでモバイルでも最先端グラフィックスな格闘ゲームを!
㉑CSSでアニメーション!その2
簡単!OpenGL ES 2.0フラグメントシェーダー
シェーダーしよっ☆ Let's play shaders!
Rpscala2011 0601
ARコンテンツ作成勉強会:C#ではじめようOpenCV(カラートラッキング編)
【Unity道場スペシャル 2017京都】乱数完全マスター 京都編
【Unity道場スペシャル 2017札幌】乱数完全マスター
Gurobi python
D言語会議#1
【Unite Tokyo 2019】〈七つの大罪〉をゲームで!高品質グラフィックを具現化するための技法と開発最適化のご紹介
Node.jsでつくるNode.js ミニインタープリター&コンパイラー
Or seminar2011final
DeNA TechCon2016 360VR Live Streaming
海外ゲーム技術勉強会#1 OGRE3D
さらに一歩踏み込んだCSS3の使い方!コツとポイントを理解して 楽しくサイトを彩る方法
LLVM最適化のこつ
Inside of excel 方眼紙撲滅委員会 #pyfes
Unity道場京都スペシャル トゥーンシェーディングとノンフォトリアリスティック風絵づくり入門_
Ad

More from Unity Technologies Japan K.K. (20)

PDF
建築革命、更に更に進化!便利さ向上【Unity Reflect ver 3.0 】
PDF
UnityのクラッシュをBacktraceでデバッグしよう!
PDF
Unityで始めるバーチャルプロダクション
PDF
ビジュアルスクリプティング (旧:Bolt) で始めるUnity入門3日目 ゲームをカスタマイズしよう
PDF
ビジュアルスクリプティングで始めるUnity入門2日目 ゴールとスコアの仕組み - Unityステーション
PDF
ビジュアルスクリプティングで始めるUnity入門1日目 プレイヤーを動かそう
PDF
PlasticSCMの活用テクニックをハンズオンで一緒に学ぼう!
PDF
点群を使いこなせ! 可視化なんて当たり前、xRと点群を組み合わせたUnityの世界 【Interact , Stipple】
PDF
Unity教える先生方注目!ティーチャートレーニングデイを体験しよう
PDF
「原神」におけるコンソールプラットフォーム開発
PDF
FANTASIANの明日使えない特殊テクニック教えます
PDF
インディーゲーム開発の現状と未来 2021
PDF
建築革命、更に進化!デジタルツイン基盤の真打ち登場【概要編 Unity Reflect ver 2.1 】
PDF
Burstを使ってSHA-256のハッシュ計算を高速に行う話
PDF
Cinemachineで見下ろし視点のカメラを作る
PDF
徹底解説 Unity Reflect【開発編 ver2.0】
PDF
徹底解説 Unity Reflect【概要編 ver2.0】
PDF
Unityティーチャートレーニングデイ -認定プログラマー編-
PDF
Unityティーチャートレーニングデイ -認定3Dアーティスト編-
PDF
Unityティーチャートレーニングデイ -認定アソシエイト編-
建築革命、更に更に進化!便利さ向上【Unity Reflect ver 3.0 】
UnityのクラッシュをBacktraceでデバッグしよう!
Unityで始めるバーチャルプロダクション
ビジュアルスクリプティング (旧:Bolt) で始めるUnity入門3日目 ゲームをカスタマイズしよう
ビジュアルスクリプティングで始めるUnity入門2日目 ゴールとスコアの仕組み - Unityステーション
ビジュアルスクリプティングで始めるUnity入門1日目 プレイヤーを動かそう
PlasticSCMの活用テクニックをハンズオンで一緒に学ぼう!
点群を使いこなせ! 可視化なんて当たり前、xRと点群を組み合わせたUnityの世界 【Interact , Stipple】
Unity教える先生方注目!ティーチャートレーニングデイを体験しよう
「原神」におけるコンソールプラットフォーム開発
FANTASIANの明日使えない特殊テクニック教えます
インディーゲーム開発の現状と未来 2021
建築革命、更に進化!デジタルツイン基盤の真打ち登場【概要編 Unity Reflect ver 2.1 】
Burstを使ってSHA-256のハッシュ計算を高速に行う話
Cinemachineで見下ろし視点のカメラを作る
徹底解説 Unity Reflect【開発編 ver2.0】
徹底解説 Unity Reflect【概要編 ver2.0】
Unityティーチャートレーニングデイ -認定プログラマー編-
Unityティーチャートレーニングデイ -認定3Dアーティスト編-
Unityティーチャートレーニングデイ -認定アソシエイト編-

【Unity道場 2月】シェーダを書けるプログラマになろう