Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
初心者向けシェーダ講習会 第2回
Search
Pocol
April 01, 2020
Programming
0
250
初心者向けシェーダ講習会 第2回
社内向けにやる予定だったシェーダ講習会の資料です。
ガチ勢お断り。
Pocol
April 01, 2020
Tweet
Share
More Decks by Pocol
See All by Pocol
Hardware-Raytracingを用いたフォトンマッピングの実装について
projectasura
0
380
ReSTIRについて
projectasura
4
1.7k
初心者向けシェーダ講習会 第1回
projectasura
0
430
中級グラフィックス入門~色彩工学編~
projectasura
21
11k
中級グラフィックス入門 ~シャドウマッピング総まとめ~
projectasura
4
2.7k
Other Decks in Programming
See All in Programming
たのしいparse.y
ydah
3
120
「Chatwork」Android版アプリを 支える単体テストの現在
okuzawats
0
180
なまけものオバケたち -PHP 8.4 に入った新機能の紹介-
tanakahisateru
1
120
快速入門可觀測性
blueswen
0
340
PHPで作るWebSocketサーバー ~リアクティブなアプリケーションを知るために~ / WebSocket Server in PHP - To know reactive applications
seike460
PRO
2
110
htmxって知っていますか?次世代のHTML
hiro_ghap1
0
330
Monixと常駐プログラムの勘どころ / Scalaわいわい勉強会 #4
stoneream
0
270
DevFest Tokyo 2025 - Flutter のアプリアーキテクチャ現在地点
wasabeef
5
900
rails stats で紐解く ANDPAD のイマを支える技術たち
andpad
1
290
103 Early Hints
sugi_0000
1
230
Stackless и stackful? Корутины и асинхронность в Go
lamodatech
0
700
HTTP compression in PHP and Symfony apps
dunglas
2
1.7k
Featured
See All Featured
Docker and Python
trallard
42
3.1k
RailsConf 2023
tenderlove
29
940
The Invisible Side of Design
smashingmag
298
50k
Designing for Performance
lara
604
68k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
38
1.9k
The World Runs on Bad Software
bkeepers
PRO
65
11k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
365
25k
Designing Experiences People Love
moore
138
23k
Git: the NoSQL Database
bkeepers
PRO
427
64k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
111
49k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
29
2k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
226
22k
Transcript
初心者向け シェーダ講習会 第2回目 Presented By Pocol
目的 • アーティスト・プログラマー向けにシェーダの基礎を解説します。 • シェーダを触れるテクニカルアーティストや シェーダを触れるプログラマーの増員・増強が目的です。 • 最終目標として, Substance Painterでカスタムシェーダを作れることを目指します。
• 今回のシェーダ講習では GLSL を使用します。
本日のおしながき • 宿題解説 • ブラー処理 • シェーダの書き方
前回の宿題 ・次の動画のように画面端が波のようにうねるUVアニメーションを作ってください。
実装例 void mainImage( out vec4 fragColor, in vec2 fragCoord )
{ // Normalized pixel coordinates (from 0 to 1) vec2 uv = fragCoord/iResolution.xy; float scaleX = 0.05f; // 揺らし幅. float scaleY = 30.0f; // 縦方向の縮み. float speed = 10.0f; // 揺らしスピード. uv.x += sin(uv.y * scaleY + iTime * speed) * scaleX; vec3 col = texture(iChannel0, uv).rgb; // Output to screen fragColor = vec4(col,1.0); }
今日は… • 前回はテクスチャ座標を「ずらす」ということを学習しました。 • 前回はテクスチャ画像から1回だけデータを拾って出力しました。 (テクスチャを1回だけサンプリング) • では… 1回ではなく複数回サンプリングしたら, どのようなことができるようになるでしょうか?
ブラー処理
ブラー処理 • テクスチャを複数回サンプリングすることによってブラーが実装できます。
実装してみましょう • テクスチャを2回サンプリングします。 • 片方は +X方向にちょっとだけずらす。 • もう一方は –X方向にちょっとだけずらす。 •
2つの結果を足して合成する。 void mainImage( out vec4 fragColor, in vec2 fragCoord ) { // Normalized pixel coordinates (from 0 to 1) vec2 uv = fragCoord/iResolution.xy; vec3 col0 = texture(iChannel0, uv + 0.1).rgb; vec3 col1 = texture(iChannel0, uv - 0.1).rgb; vec3 col = col0 + col1; // Output to screen fragColor = vec4(col,1.0); }
結果 明るすぎる!! 単純な加算ではだめなようです。 では… どうすればよいでしょうか?
平均をとる • 足し合わせた結果の平均をとってみましょう。 void mainImage( out vec4 fragColor, in vec2
fragCoord ) { // Normalized pixel coordinates (from 0 to 1) vec2 uv = fragCoord/iResolution.xy; vec3 col0 = texture(iChannel0, uv + 0.1).rgb; vec3 col1 = texture(iChannel0, uv - 0.1).rgb; vec3 col = (col0 + col1) / 2.0; // Output to screen fragColor = vec4(col, 1.0); }
結果
ループ文を使えるようになろう! • テクスチャをいっぱいサンプリングしたいときに, さすがに1行ずつ書くのは疲れます。 • 同じような処理を複数回実行したい場合には ループ文を使って繰り返し処理を実行することができます。 for(int i =
開始番号; i < 終了番号; i += 増やす数) { 繰り返し処理; }
放射ブラー • ループ文を使って放射ブラーを実装してみましょう。 ブラー中心 対象ピクセル 向きを作ってブラー 向きは矢印の先端の方の座標から矢印の尻尾の座標を引き算することで作れます。
実装コード const int STEP = 32; void mainImage( out vec4
fragColor, in vec2 fragCoord ) { // Normalized pixel coordinates (from 0 to 1) vec2 uv = fragCoord/iResolution.xy; vec2 center = vec2(0.5, 0.5); vec2 offset = normalize(uv - center) * (1.0 / iResolution.y); vec3 col = vec3(0); float div = 1.0 / float(STEP); for(int i=0; i<STEP; ++i) { col += texture(iChannel0, uv).rgb * div; uv += offset; } // Output to screen fragColor = vec4(col,1.0); }
実装コード const int STEP = 32; void mainImage( out vec4
fragColor, in vec2 fragCoord ) { // Normalized pixel coordinates (from 0 to 1) vec2 uv = fragCoord/iResolution.xy; vec2 center = vec2(0.5, 0.5); vec2 offset = normalize(uv - center) * (1.0 / iResolution.y); vec3 col = vec3(0); float div = 1.0 / float(STEP); for(int i=0; i<STEP; ++i) { col += texture(iChannel0, uv).rgb * div; uv += offset; } // Output to screen fragColor = vec4(col,1.0); } ブラー中心
実装コード const int STEP = 32; void mainImage( out vec4
fragColor, in vec2 fragCoord ) { // Normalized pixel coordinates (from 0 to 1) vec2 uv = fragCoord/iResolution.xy; vec2 center = vec2(0.5, 0.5); vec2 offset = normalize(uv - center) * (1.0 / iResolution.y); vec3 col = vec3(0); float div = 1.0 / float(STEP); for(int i=0; i<STEP; ++i) { col += texture(iChannel0, uv).rgb * div; uv += offset; } // Output to screen fragColor = vec4(col,1.0); } ブラー中心 1ピクセルのブラー移動量
実装コード const int STEP = 32; void mainImage( out vec4
fragColor, in vec2 fragCoord ) { // Normalized pixel coordinates (from 0 to 1) vec2 uv = fragCoord/iResolution.xy; vec2 center = vec2(0.5, 0.5); vec2 offset = normalize(uv - center) * (1.0 / iResolution.y); vec3 col = vec3(0); float div = 1.0 / float(STEP); for(int i=0; i<STEP; ++i) { col += texture(iChannel0, uv).rgb * div; uv += offset; } // Output to screen fragColor = vec4(col,1.0); } ブラー中心 1ピクセルのブラー移動量 合計が1になるような1回あたりの重み
実装コード const int STEP = 32; void mainImage( out vec4
fragColor, in vec2 fragCoord ) { // Normalized pixel coordinates (from 0 to 1) vec2 uv = fragCoord/iResolution.xy; vec2 center = vec2(0.5, 0.5); vec2 offset = normalize(uv - center) * (1.0 / iResolution.y); vec3 col = vec3(0); float div = 1.0 / float(STEP); for(int i=0; i<STEP; ++i) { col += texture(iChannel0, uv).rgb * div; uv += offset; } // Output to screen fragColor = vec4(col,1.0); } ブラー中心 1ピクセルのブラー移動量 合計が1になるような1回あたりの重み 決められた回数だけ繰り返す
結果
シェーダの書き方 ※ここから先は,文法説明になります。 プログラマーの方は36枚目の「Substance Painterのシェーダ」まで読み飛ばしてもらって構いません。
いよいよ! • 次回からSubstance Painterでカスタムシェーダを動かします。 そのため,Substance Painter向けにシェーダの書き方を説明しておきます。 • ここでは,「雰囲気的に分かる」ことを優先とするため, 厳密な定義は取り扱いません。 そのため,全然定義違う可能性もあるので注意してください。
• 厳密な定義を知りたい方は,言語仕様書(※)を参照してください。 ※例えば OpenGL Shading Language Specification 4.6など https://www.khronos.org/registry/OpenGL/specs/gl/GLSLangSpec.4.60.pdf
シェーダ言語について • シェーダを記述するためには, シェーダ言語を用いてプログラミングを行います。 • シェーダ言語は大きく分けて, HLSL (High Level Shader
Language) GLSL (Open GL Shader Language) MSL (Metal Shader Language) の3種類が存在します。 • ShaderToyやSubstance Painterでは GLSL が採用されているので, ここではGLSLの文法について簡単に紹介します。
プログラミングを始める その前に! • 次回以降,シェーダプログラムを作っていきますが, プログラムを書く際は すべて半角! で,全角の使用は基本的に禁止です。 もちろん空白も全角は絶対にダメ! 全角や日本語文字が許されるのは「コメント」と呼ばれる プログラムとして処理されない部分のみです。
大事なことなのでもう一度言います。 すべて半角で書きましょう。
計算結果をとっておきたい。 • 計算した数値などを保存したり,取り出したりする箱のような役割を持 つものが必要となります。 • 何が入っているかなどを示すためにラベルや種類も必要そうです。 いるもの いらないもの
変数(1) • 「変数」は計算結果を格納する箱のような役割を持ちます。 • 変数には「名前」とデータの種類を判別するための「データ型」が必要です。 • GLSL内で変数を使用するためには,「データ型」と「名前」を予め決めておく 必要があり,これを「変数の宣言」と呼びます。 • 具体的には次のような書式になります。
データ型 変数名; データ型 変数名 = 値; (例) float scale; float speed = 1.0;
変数(2) • 変数の「名前」には使える文字と使えない文字が存在します。 • ルール • 先頭文字には a-z, A-z,アンダースコア(_) しか使えない。
• 2文字目以降は上記に加えて 数字 が使える。 OKな例: takesann_okanekudasai _ShigotoHerashite4 TEMPRA velocity1_0 hoge_____ _1_2_3_4_5_6
データ型(1) • データ型には以下のようなものがあります。 1要素 bool int uint float double N要素
bvec2 / bvec3 / bvec4 ivec2 / ivec3 / ivec4 uvec2 / uvec3 / uvec4 vec2 / vec3 / vec4 dvec2 / devc3 / dvec4
データ型(2) N要素 dmat2 / dmat3 / dmat4 dmat2x2 / dmat2x3
/ dmat2x4 dmat3x2 / dmat3x3 / dmat3x4 dmat4x2 / dmat4x3 / dmat4x4 M要素 mat2 / mat3 / mat4 mat2x2 / mat2x3 / mat2x4 mat3x2 / mat3x3 / mat3x4 mat4x2 / mat4x3 / mat4x4
演算子(1) • 計算するためには,「演算子」と呼ばれる記号を使用します。 • 算術演算子 • + 足し算を行います。 • -
引き算を行います。 • * 掛け算を行います。 • / 割り算を行います。 5 + 2; 5 – 2; 5 * 2; 5 / 2;
演算子(2) • 代入演算子 • = 右側の値を左側に代入します。 • += 右側の値で加算してから左側に代入します。 •
-= 右側の値で減算してから左側に代入します。 • *= 右側の値で乗算してから左側に代入します。 • /= 右側の値で除算してから左側に代入します。 • ++ 値を1増やします。 • -- 値を1減らします。 float x; x = 2; x += 2; x -= 2; x *= 2; x /= 2; x++; x--;
演算子(3) • 比較演算子 • == 左側と右側の値が同じであれば true, そうでなければ false .
• != 左側と右側の値が異なれば true, そうでなければ false . • > 左側の値が大きければ true, そうでなければ false . • < 左側の値が小さければ true, そうでなければ false . • >= 左側の値が右側以上であれば true, そうでなければ false . • <= 左側の値が右側以下であれば true, そうでなければ false . int x = 2; bool y; y = (x == 2); y = (x != 2); y = (x > 3); y = (x < 3); y = (x >= 2); y = (x <= 3);
演算子(4) • 論理演算子 • && 左側と右側が両方とも true なら true, そうでなければ
false. (AND演算) • || 左側あるいは右側が true なら true, そうでなければ false. (OR演算) • ! 逆の bool 値を返します. (NOT演算). bool x = true; bool y = false; bool z; z = (x && y); z = (x || y); z = !x; z = !y;
演算子(4) • 条件演算子 • w = (x) ? y :
z; • 条件 x が true の場合に y を代入。 false の場合は z を代入します。 (例) int x = 10 / 3; int y = (x > 3) ? 5 : 6; float x = 10.0 / 3.0; float y = (x > 3.0) ? 5.0 : 6.0;
条件分岐 • 条件に応じて処理を変えたいという場面に出くわすかもしれません。 このようなときには,if 文 や else 文を使用します。 if (
条件 ) { やりたい処理; } (例) int x = 1 + 3; bool y = false; if ( x >= 2 ) { y = true; } if ( 条件 ) { やりたい処理A; } else { やりたい処理B; } if ( 条件1 ) { やりたい処理A; } else if ( 条件2 ) { やりたい処理B; } else { やりたい処理C; }
繰り返し処理 • 1から10000までの値を足した結果を求めたいような場合が出てきたらどうすればよい でしょうか? • 地道に1行ずつ書いていくのは心が折れそうです。 こうした場合にはループ文を使うと良いです。 while ( 条件
) { 繰り返しやりたい処理; } for ( カウンター; 条件; カウンターの増減 ) { 繰り返しやりたい処理; } (例) int counter = 0; int sum = 0; while (counter < 100) { sum += 2; counter++; } (例) int sum = 0; for( int i = 0; i < 100; i += 2) { sum += 2; }
配列 • いっぱいデータを持ちたい!…みたいなことがたま~にあります。 • 同じデータ型を複数持ちたい場合には配列を利用すると良いです。 データ型 変数名[配列数]; (例) vec2 texture_offset[4]
= { vec2(-1, 0), vec2( 1, 0), vec2( 0, -1), vec2( 0, 1) };
関数 • ほぼ同じ処理で,入力データのみを差し替えれば,まとめられるのに… …といった場面に出くわすかもしれません。 • 計算プロセスは同一で,入力データや出力データが異なる場合は 計算プロセスを「関数」として纏めておくことが可能です。 (例) average関数としてまとめる。 float
avarage(float inputs, int count) { float sum = 0.0f; for(int i=0; i<count; ++i) { sum += inputs[i]; } sum /= (float)count; return sum; }
Substance Painterのシェーダ
プログラムの大枠 すごい処理 入力データ 出力データ 大抵は,入力と出力があります。
描画フロー 頂点シェーダ ピクセルシェーダ 頂点バッファ 加工済み頂点 ピクセルカラー Substance Painterでは ユーザーカスタマイズできない ユーザーカスタマイズ可能
シェーダの処理フロー はじまり すごい計算 結果を出力 おしまい ここを自由に書く 書式が決まってる
シェーダ処理の はじまり と おわり • Substance Painterの場合は shade() という関数内をユーザーが自由に カスタマイズできる仕様になっています。
void shade(V2F inputs) { } はじまり おしまい すごい計算
シェーダ処理の はじまり と おわり • Substance Painterの場合は shade() という関数内をユーザーが自由に カスタマイズできる仕様になっています。
void shade(V2F inputs) { } はじまり おしまい すごい計算 入力データ 加工済み頂点データ
入力データについて • Substance Painterのピクセルシェーダ(フラグメントシェーダ)の 入力データは次のように定められています。 struct V2F { vec3 normal;
// 補間済み法線ベクトル vec3 tangent; // 補間済み接線ベクトル vec3 bitangent; // 補間済み従接線ベクトル vec3 position; // 補間済み位置座標 vec4 color[1]; // 補間済み頂点カラー (color0) vec2 tex_coord; // 補間済みテクスチャ座標 (uv0) SparseCoord sparse_coord; // textureSparse() サンプリング関数によって使用される補間済み離散テクスチャ座標 vec2 multi_tex_coord[8]; // 補間済みテクスチャ座標 (uv0-uv7) }; struct SparseCoord { vec2 tex_coord; vec2 dfdx; vec2 dfdy; float lod; uint material_lod_mask; };
組み込み関数 • 計算をする上での便利な機能が「組み込み関数」として提供されています。 https://qiita.com/edo_m18/items/71f6064f3355be7e4f45 • 詳細について知りたい方は, GLSLの仕様書(The OpenGL Shading Language)を参照してください。
https://www.khronos.org/registry/OpenGL/specs/gl/GLSLangSpec.4.60.html
データの出力方法 • Substance Painterではデータの出力方法が決められています。 具体的には以下の関数を用いて出力を行います。 // fragment opacity. default value:
1.0 void alphaOutput(float); // diffuse lighting contribution. default value: vec3(0.0) void diffuseShadingOutput(vec3); // specular lighting contribution. default value: vec3(0.0) void specularShadingOutput(vec3); // color emitted by the fragment. default value: vec3(0.0) void emissiveColorOutput(vec3); // fragment color. default value: vec3(1.0) void albedoOutput(vec3); // subsurface scattering properties, see lib-sss.glsl for details. default value: vec4(0.0) void sssCoefficientsOutput(vec4);
ピクセルカラーの出力 • ピクセルカラーを計算する最も基本的な式は次のようになります。 emissiveColor + albedo * diffuseShading + specularShading
void diffuseShadingOutput(vec3); void specularShadingOutput(vec3); void albedoOutput(vec3); void emissiveColorOutput(vec3);
本日はここまで! 次回は… カスタムシェーダを作って,Substance Painter上で描画してみます。
興味が出てきた人は… (1) • シェーダについて興味が出てきた人は The Book of Shaders https://thebookofshaders.com/00/?lan=jp …などをみると良いかもしません。
• プログラマーであれば, LEARN OpenGL https://learnopengl.com/ …などがライティング勉強におすすめです。
興味が出てきた人は… (2) ・アーティスト向けであれば, 「モンスターハンター:ワールド」アーティストによるシェーダ作成のノウハウ https://cedil.cesa.or.jp/cedil_sessions/view/1892 の講演資料 CEDiL_1892_2.zip などを参照するのがお勧めです。