$30 off During Our Annual Pro Sale. View Details »

初心者向けシェーダ講習会 第2回

Pocol
April 01, 2020

初心者向けシェーダ講習会 第2回

社内向けにやる予定だったシェーダ講習会の資料です。
ガチ勢お断り。

Pocol

April 01, 2020
Tweet

More Decks by Pocol

Other Decks in Programming

Transcript

  1. 実装例 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); }
  2. 実装してみましょう • テクスチャを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); }
  3. 平均をとる • 足し合わせた結果の平均をとってみましょう。 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); }
  4. 実装コード 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); }
  5. 実装コード 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); } ブラー中心
  6. 実装コード 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ピクセルのブラー移動量
  7. 実装コード 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回あたりの重み
  8. 実装コード 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回あたりの重み 決められた回数だけ繰り返す
  9. シェーダ言語について • シェーダを記述するためには, シェーダ言語を用いてプログラミングを行います。 • シェーダ言語は大きく分けて, HLSL (High Level Shader

    Language) GLSL (Open GL Shader Language) MSL (Metal Shader Language) の3種類が存在します。 • ShaderToyやSubstance Painterでは GLSL が採用されているので, ここではGLSLの文法について簡単に紹介します。
  10. 変数(2) • 変数の「名前」には使える文字と使えない文字が存在します。 • ルール • 先頭文字には a-z, A-z,アンダースコア(_) しか使えない。

    • 2文字目以降は上記に加えて 数字 が使える。 OKな例: takesann_okanekudasai _ShigotoHerashite4 TEMPRA velocity1_0 hoge_____ _1_2_3_4_5_6
  11. データ型(1) • データ型には以下のようなものがあります。 1要素 bool int uint float double N要素

    bvec2 / bvec3 / bvec4 ivec2 / ivec3 / ivec4 uvec2 / uvec3 / uvec4 vec2 / vec3 / vec4 dvec2 / devc3 / dvec4
  12. データ型(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
  13. 演算子(1) • 計算するためには,「演算子」と呼ばれる記号を使用します。 • 算術演算子 • + 足し算を行います。 • -

    引き算を行います。 • * 掛け算を行います。 • / 割り算を行います。 5 + 2; 5 – 2; 5 * 2; 5 / 2;
  14. 演算子(2) • 代入演算子 • = 右側の値を左側に代入します。 • += 右側の値で加算してから左側に代入します。 •

    -= 右側の値で減算してから左側に代入します。 • *= 右側の値で乗算してから左側に代入します。 • /= 右側の値で除算してから左側に代入します。 • ++ 値を1増やします。 • -- 値を1減らします。 float x; x = 2; x += 2; x -= 2; x *= 2; x /= 2; x++; x--;
  15. 演算子(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);
  16. 演算子(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;
  17. 演算子(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;
  18. 条件分岐 • 条件に応じて処理を変えたいという場面に出くわすかもしれません。 このようなときには,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; }
  19. 繰り返し処理 • 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; }
  20. 入力データについて • 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; };
  21. データの出力方法 • 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);
  22. ピクセルカラーの出力 • ピクセルカラーを計算する最も基本的な式は次のようになります。 emissiveColor + albedo * diffuseShading + specularShading

    void diffuseShadingOutput(vec3); void specularShadingOutput(vec3); void albedoOutput(vec3); void emissiveColorOutput(vec3);
  23. 興味が出てきた人は… (1) • シェーダについて興味が出てきた人は The Book of Shaders https://thebookofshaders.com/00/?lan=jp …などをみると良いかもしません。

    • プログラマーであれば, LEARN OpenGL https://learnopengl.com/ …などがライティング勉強におすすめです。