Open6

C-WebGL: Depthの互換問題

okuokuokuoku

Unityだと踏まないと思ってたのに。。

Depthの座標系がOpenGLとそれ以外で違う問題

OpenGL(= WebGL)では、ラスタライザは ( -1 , 1) のデプス値を出力してこれを glDepthRangef の設定値でマップして出力する。

Vulkanは、ラスタライザは ( 0 , 1) のデプス値を出力できる。このため、OpenGLでは処理できた負値のデプス値はクリップされてしまう。

この違いを吸収する必要がある。

okuokuokuoku

RenderDocで問題を確認する

描画されるはずのものが描画されなかったりといった変な描画一般には、Pixel Historyが役に立つ。

"Depth Clipped" と表示されているので、描画されない理由はクリッピングであることがわかる。 (gl_Position も実際に z が負値になっている)

okuokuokuoku

Depth Clampingで正常に動作することを確認する

DirectXでは、 (0,1) に収まらないdepthはクランプされる。この動作自体はVulkanでも(拡張で?)使えるのでこれを有効にして絵が出ることを確認する。

https://twitter.com/okuoku/status/1427234852483440642

結果、描画はちゃんと出るので、depthさえ変換すれば正常に動作すると期待できると言える。

okuokuokuoku

ANGLE先輩の意見を聞く

C-WebGLはANGLE + Vulkanの組合せでも動作する。というわけで同じプログラムをANGLEで動かしてVulkanのシェーダを観察できる。

ANGLE先輩曰く:

gl_Position = vec4(gl_Position.xy, (gl_Position.z + gl_Position.w) * 0.5, gl_Position.w);

普通だな!クリップ座標での z が区間 [-w, w] なのを [0,-w] に補正するには、

  1. w を足して z + w = [0, 2w]
  2. 2で割って (z + w) * 0.5 = [0, w]

よって、バーテックスシェーダの main の末尾に上記の式を挿入すれば良い。

okuokuokuoku

パッチ候補の作成

ANGLEそのままの

vec4(gl_Position.xy, (gl_Position.z + gl_Position.w) * 0.5, gl_Position.w);

では、パッチの長さが長くなってしまう。

/* ★ この命令列をmainに挿入 */
         %32 = OpLoad %v4float %gl_Position
         %33 = OpVectorShuffle %v2float %32 %32 0 1
         %37 = OpAccessChain %_ptr_Output_float %gl_Position %uint_2
         %38 = OpLoad %float %37
         %40 = OpAccessChain %_ptr_Output_float %gl_Position %uint_3
         %41 = OpLoad %float %40
         %42 = OpFAdd %float %38 %41
         %44 = OpFMul %float %42 %float_0_5
         %45 = OpAccessChain %_ptr_Output_float %gl_Position %uint_3
         %46 = OpLoad %float %45
         %47 = OpCompositeExtract %float %33 0
         %48 = OpCompositeExtract %float %33 1
         %49 = OpCompositeConstruct %v4float %47 %48 %44 %46
               OpStore %gl_Position %49
               OpReturn
               OpFunctionEnd

というわけで、ちょっとシンプルに z のみの書き換えとして、

gl_Position.z = (gl_Position.z + gl_Position.w) * 0.5;
/* ★ この命令列をmainに挿入 */
         %35 = OpAccessChain %_ptr_Output_float %gl_Position %uint_2
         %36 = OpLoad %float %35
         %38 = OpAccessChain %_ptr_Output_float %gl_Position %uint_3
         %39 = OpLoad %float %38
         %40 = OpFAdd %float %36 %39
         %42 = OpFMul %float %40 %float_0_5
         %43 = OpAccessChain %_ptr_Output_float %gl_Position %uint_2
               OpStore %43 %42
               OpReturn
               OpFunctionEnd

また、 実際には main 自体をリプレースする必要がある 。現状のパッチでやっているようなUBOのロードと一緒に専用のFunctionを出力してしまう方が簡単で、スッキリするはず。

追加が必要な定義

  1. float32 型(たぶん事前に存在するのでチェックしておく)
  2. Outputfloat32 ポインタ
  3. 新規に追加する main
  4. 新規に追加する main の Block用の Label
  5. 定数 2
  6. 定数 3
  7. 定数 0.5

名前も cwgl_entrypoint とかに変えちゃうか。(Vulkanではエントリポイントの名前は自由に設定できる)