Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

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

7,276 views

Published on

2019/2/25に開催されたUnity道場 2月~シェーダを書けるプログラマになろう~の講演スライドです。
講師:安原 祐二 (ユニティ・テクノロジーズ・ジャパン合同会社)

Unityのイベント資料はこちらから:https://www.slideshare.net/UnityTechnologiesJapan/clipboards

Published in: Technology

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

  1. 1. シェーダを書けるプログラマになろう ユニティ・テクノロジーズ・ジャパン 安原 祐二
  2. 2. Part I シェーダを理解しよう
  3. 3. 理解とは?
  4. 4. 理解とは? 境界の把握
  5. 5. それができないことは何か? できる できる できる できる できる できる 〜〇〇を理解したい〜
  6. 6. それができないことは何か? できる できる できる できる できる できる できる できる できる できる できる できる できない できない できない できない できない できない できない できない できない できない 〜〇〇を理解したい〜
  7. 7. こんなことにも使えるよ! ・・・という話はまたこんど
  8. 8. 道具の特徴を抑えて 持ち手 接合部 刃 紙が切れる理由を知ろう
  9. 9. Part I シェーダを理解しよう
  10. 10. 8ステップで描画する
  11. 11. ステップ8 ステップ7 ステップ6 ステップ5  ステップ3  ステップ4  ステップ1 ステップ2 3Dモデルを準備 Transformの値を4x4行列に変換 点を打つ 描画点に打つべき色を確定 描画点をデプスバッファと比較 描画点を確定 頂点ごとに描画位置を算出 表裏を調べ、裏なら描画しない
  12. 12. シェーダとは GPU搭載の基本シェーダはこのふたつ 頂点シェーダ フラグメントシェーダ
  13. 13. 描画とは モニタ上の画素に色付きの点を打つこと
  14. 14. ステップ1
  15. 15. ステップ1 3Dモデルを準備
  16. 16. ステップ2
  17. 17. ステップ2 Transformの値を4x4行列に変換 モデル行列という Rotation & Scale 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1固定 Position
  18. 18. ステップ3
  19. 19. ステップ3 頂点ごとに描画位置を算出 モデル行列 カメラ情報 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1
  20. 20. ステップ4
  21. 21. ステップ4 表裏を調べ、裏なら描画しない 3点そろったら
  22. 22. ステップ5
  23. 23. ステップ5 描画点を確定 ピクセルの中心が三角形の内側にあるかどうか
  24. 24. ステップ6
  25. 25. ステップ6 描画点を深度バッファと比較 描画済みの点が手前にあるなら描画しない 深度バッファ ※GPUごとに実装が異なります
  26. 26. ステップ7
  27. 27. ステップ7 描画点に打つべき色を確定 テクスチャ、ライティング、シャドウ、フォグなどなど考慮
  28. 28. ステップ8
  29. 29. ステップ8 点を打つ ブレンド関数を指定可 深度バッファも更新
  30. 30. ステップ8 ステップ7 ステップ6 ステップ5  ステップ3  ステップ4  ステップ1 ステップ2 3Dモデルを準備 Transformの値を4x4行列に変換 点を打つ 描画点に打つべき色を確定 描画点をデプスバッファと比較 描画点を確定 頂点ごとに描画位置を算出 表裏を調べ、裏なら描画しない
  31. 31. 3Dモデルを準備 Transformの値を4x4行列に変換 点を打つ 描画点に打つべき色を確定 描画点をデプスバッファと比較 描画点を確定 頂点ごとに描画位置を算出 表裏を調べ、裏なら描画しない CPUGPU ステップ8 ステップ7 ステップ6 ステップ5  ステップ3  ステップ4  ステップ1 ステップ2
  32. 32. 3Dモデルを準備 Transformの値を4x4行列に変換 点を打つ 描画点に打つべき色を確定 描画点をデプスバッファと比較 描画点を確定 頂点ごとに描画位置を算出 表裏を調べ、裏なら描画しない CPUGPU 頂点シェーダ フラグメント シェーダ ステップ8 ステップ7 ステップ6 ステップ5  ステップ3  ステップ4  ステップ1 ステップ2
  33. 33. シェーダで 対応できない例
  34. 34. 線を引きたい
  35. 35. ステップ8 ステップ7 ステップ6 ステップ5  ステップ3  ステップ4  ステップ1 ステップ2 3Dモデルを準備 Transformの値を4x4行列に変換 点を打つ 描画点に打つべき色を確定 描画点をデプスバッファと比較 描画点を確定 頂点ごとに描画位置を算出 表裏を調べ、裏なら描画しない CPUGPU 頂点シェーダ フラグメント シェーダ ステップ1で対応が必要
  36. 36. 3Dモデル(Mesh)の構造
  37. 37. ・頂点列 ・三角形列 頂点座標 法線 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 線を引くには線分用のデータが必要
  38. 38. シェーダで 対応できない例
  39. 39. 半透明描画をしたい 窓ガラス
  40. 40. ステップ8 ステップ7 ステップ6 ステップ5  ステップ3  ステップ4  ステップ1 ステップ2 3Dモデルを準備 Transformの値を4x4行列に変換 点を打つ 描画点に打つべき色を確定 描画点をデプスバッファと比較 描画点を確定 頂点ごとに描画位置を算出 表裏を調べ、裏なら描画しない CPUGPU 頂点シェーダ フラグメント シェーダ ステップ8で対応が必要
  41. 41. + = 2 半透明に必要な計算例
  42. 42. + = 2 計算にはすでに打ってある色が必要 フラグメント シェーダの出力 打つべき色
  43. 43. 半透明はBlend で設定可能 ステップ8に対する設定 Shader "Custom/minimum" { SubShader { Tags { "RenderType"="Transparent" } Blend SrcAlpha OneMinusSrcAlpha Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" … プログラマブルではない 演算は書けない ピクセルごとに変えられない
  44. 44. 余談 半透明&深度バッファ問題
  45. 45. 不透明を半透明の後に描画する困難
  46. 46. 透明部の深度バッファを更新 不透明を半透明の後に描画する困難
  47. 47. 透明部の深度バッファを 更新しない 現代における対策:半透明は全ての不透明のあとに描画 不透明を半透明の後に描画する困難 透明部の深度バッファを更新
  48. 48. 改めて シェーダコードを観察
  49. 49. 最小のシェーダ 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 } } }
  50. 50. 最小のシェーダ ほぼ一般的な シェーダコード 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 } } } 頂点シェーダ フラグメント シェーダ
  51. 51. 最小のシェーダ 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 } } } 様々な設定が可能
  52. 52. 超重要ポイントふたつ セマンティクス 補間
  53. 53. 最小のシェーダ 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 } } } 頂点シェーダ フラグメント シェーダ セマンティクス付き構造体
  54. 54. セマンティクス struct appdata { float4 vertex : POSITION; }; struct v2f { float4 vertex : SV_POSITION; }; 変数の内容の意味 GPUに扱い方を伝える
  55. 55. 補間 ここが28で ここが96なら このへんは71.3 途中の値を算出する GPUががんばる
  56. 56. テクスチャマッピングの例
  57. 57. 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を渡す テクセルを拾う
  58. 58. 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を渡す テクセルを拾う
  59. 59. v2f v2f v2f 頂点シェーダの出力
  60. 60. v2f v2f v2f フラグメントシェーダ の入力 頂点シェーダの出力 補間 v2f
  61. 61. 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); }
  62. 62. 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; };
  63. 63. ところで ってなんなの? サーフェスシェーダ(surface shader)
  64. 64. 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); }
  65. 65. 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); }
  66. 66. 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); }
  67. 67. サーフェスシェーダ(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); }
  68. 68. 3Dモデルを準備 Transformの値を4x4行列に変換 点を打つ 描画点に打つべき色を確定 描画点をデプスバッファと比較 描画点を確定 頂点ごとに描画位置を算出 表裏を調べ、裏なら描画しない CPUGPU 頂点シェーダ フラグメント シェーダ 設定変更可 設定変更可 設定変更可ステップ8 ステップ7 ステップ6 ステップ5  ステップ3  ステップ4  ステップ1 ステップ2
  69. 69. 休憩
  70. 70. Part II GPUの神秘
  71. 71. fixed4 frag(v2f i) : SV_Target { return tex2D(_MainTex, i.uv); } tex2Dでテクセルをゲット tex2DはGPUに直結 ここにシェーダの神秘がある
  72. 72. テクスチャは縮小に課題あり 一般論
  73. 73. 品質の低下 ちらつき
  74. 74. ORIGINAL NEAREST
  75. 75. KAISER BOX BICUBIC HAMMING ORIGINAL NEAREST 高度な縮小は時間がかかる
  76. 76. UVが大きく動くと テクセルを飛ばしてしまう tex2D(_MainTex, i.uv) 高度な縮小はシェーダでは使えない
  77. 77. 無限平面を作ろう
  78. 78. カメラの視界に必ず入るように平面を作る 頂点シェーダで計算 作戦
  79. 79. 1.0 1.0の四角形を準備する
  80. 80. 1.0 vertexシェーダで 頂点を拡大・移動 カメラの前方ベクトル カメラの座標
  81. 81. カメラの前方ベクトル シェーダまめ知識 float3 forward = -UNITY_MATRIX_V._m20_m21_m22; ビュー行列
  82. 82. uvにワールド座標を入れちゃう
  83. 83. (0, 1) (0, 0) (1, 1) (1, 0) テクスチャ座標 画像のサイズに関わらず テクスチャ座標は [0, 1]
  84. 84. (0, 2) (0, 0) (2, 2) (2, 0) テクスチャ座標が[0, 1]の範囲外の場合 2.0 ? ? ? (0, 1) (0, 0) (1, 1) (1, 0)
  85. 85. (0, 2) (0, 0) (2, 2) (2, 0) 2.0 ? ? ? tex2D(1.5, 1.5) は何を返すのか?
  86. 86. (0, 2) (0, 0) (2, 2) (2, 0) 2.0 ? ? ? tex2D(1.5, 1.5) は何を返すのか? tex2D(0.5, 0.5) の値を返す
  87. 87. (0, 1) (0, 0) (1, 1) (1, 0) (0, 2) (0, 0) (2, 2) (2, 0) テクスチャ座標が[0, 1]の範囲外の場合 2.0
  88. 88. Repeat Clamp テクスチャのインポート設定で tex2Dの動作を変えられる [0.0, 1.0]範囲外のuvの扱い
  89. 89. uvにワールド座標(x, z)を入れる (-728, 1041) (235, 1021) (-549, 103) (346, 124)
  90. 90. キレイに縮小されている不思議 ミップマップ(mipmap)の効果
  91. 91. テクスチャインポート時に 高度な縮小を作成 mipmap生成 時間がかかっても良い
  92. 92. 縮小済みのテクスチャが使用される
  93. 93. ミップマップなしミップマップあり
  94. 94. ミップマップなしミップマップあり
  95. 95. mipmap生成を 指定するだけ シェーダはtex2Dを 呼んでいるだけ それでも mipmapレベルが選ばれる・・・
  96. 96. tex2Dはどうやって mipmapレベルを 選択しているのか? mipmap生成を 指定するだけ シェーダはtex2Dを 呼んでいるだけ 疑問 それでも mipmapレベルが選ばれる・・・
  97. 97. それはさておき
  98. 98. 繰り返しが気になる
  99. 99. 単位範囲ごとに uv座標をずらしてみる
  100. 100. uvをずらす
  101. 101. 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は別に定義
  102. 102. 不連続が気になるかどうかは テクスチャ次第 (意外と大丈夫だった)
  103. 103. やや不連続が気になる テクスチャ
  104. 104. チラつきが発生する いよいよシェーダの神秘へ
  105. 105. チラつきの原因をわかりやすいテクスチャで比較
  106. 106. チラつきの原因をわかりやすいテクスチャで比較 間違ったmipmap 正しいmipmap
  107. 107. ここで衝撃の事実 フラグメントシェーダは必ず4ピクセル同時に実行している
  108. 108. 実行単位の4ピクセル ここで衝撃の事実 フラグメントシェーダは必ず4ピクセル同時に実行している
  109. 109. 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; }
  110. 110. 隣の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; }
  111. 111. 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つの情報も使う!
  112. 112. tex2D(_MainTex, uv) GPUは異なる4つのuv値を入手している uv uv uvuv
  113. 113. tex2D(_MainTex, uv) GPUは異なる4つのuv値を入手している uv uv uvuv 隣接ピクセルのuv値との差分を取れる uv値の飛び具合がわかる!
  114. 114. tex2Dはどうやって mipmapレベルを 選択しているのか? mipmap生成を 指定するだけ・・・ シェーダはtex2Dを 呼んでいるだけ・・・ 疑問 それでも mipmapレベルが選ばれる・・・ 再掲
  115. 115. tex2D(_MainTex, uv) uv uv uvuv GPUは異なる4つのuv値を入手している 隣で実行中の値を使って適切なmipmapを選ぶ
  116. 116. チラつきが発生する uvを操作したので 適切なmipmapが 選ばれていない問題
  117. 117. 対処:適切な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で指定
  118. 118. チラつきが消滅 完成!
  119. 119. 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; }
  120. 120. 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; }
  121. 121. 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; } 横の隣との差
  122. 122. 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; } 縦の隣との差
  123. 123. 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))
  124. 124. 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))
  125. 125. https://blogs.unity3d.com/jp/2019/02/14/procedural-stochastic-texturing-in-unity/
  126. 126. 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を扱うときの心構え
  127. 127. シェーダは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; } 同時実行することを思えば残酷な記述
  128. 128. 休憩
  129. 129. カメラの前方ベクトル float3 forward = -UNITY_MATRIX_V._m20_m21_m22; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
  130. 130. 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
  131. 131. おしまい
  132. 132. 詳しく解説したブログ記事 https://qiita.com/yuji_yasuhara/private/158bab01cfff3c39214b [Unity]無限平面を描画する過程でGPUの理解を深める https://qiita.com/yuji_yasuhara/private/8d63455d1d277af4c270 [Unity] CGに使用される行列についての考察
  133. 133. ボツスライド集
  134. 134. プログラマブルシェーダで出来ないこと • アルファブレンディング • 点・線の描画 • 頂点の生成(ジオメトリシェーダ、テセレーション) • 描画順の指定 • FrustomCulling抑制 • テクスチャサンプリング方式の変更 • トポロジ(三角形の構成)変更
  135. 135. 深度バッファで比較するということは 奥の物体は 手前の物体に 塗りつぶされる 手前のものを 先に描画したい
  136. 136. コックピットは最初に描画して効率UP 描画順はRender Queue で指定 ※GPUごとに特性が異なります
  137. 137. ・頂点列 ・三角形列 頂点座標 法線 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 ところで この立方体の頂点数はいくつ?
  138. 138. 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 に頂点情報 法線が異なれば頂点も別に必要
  139. 139. ・頂点列 ・三角形列 頂点座標 法線 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頂点
  140. 140. ただの補間ではない 奥行が考慮された補間単なる二次元補間
  141. 141. 頂点シェーダのよくあるミス
  142. 142. モデル行列の各部の意味 Rotation & Scale 1 0 0 0 1 0 0 0 1 0 0 0 1固定 Position a b c a b c
  143. 143. 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にしたらアウト
  144. 144. 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で計算
  145. 145. フラグメントシェーダのよくあるミス
  146. 146. 法線を渡すときは注意 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); … 線形補間なので 短くなってしまう
  147. 147. point sampling bilinear trilinear
  148. 148. Kaiser Box ミップマップ作成アリゴリズム比較
  149. 149. おしまい

×