Open3

C-WebGL: シェーダのbuilt-inを何とかする

okuokuokuoku

WebGL1のシェーダ言語はOpenGL ES2のシェーダ言語である ESSL 1に多少の制限を加えたものになっている。これがWebGL2や最近のGPU事情から比べると絶妙に古く、いくつかのBuilt-inで特別扱いが必要になる。

標準が規定するBuilt-in

https://www.khronos.org/files/opengles_shading_language.pdf

"7 Built-in Variables" 節に一覧がある。

頂点シェーダ

いずれもシェーダ出力。

highp   vec4  gl_Position;    // should be written to
mediump float gl_PointSize;   // may be written to

gl_Position は必須の出力で、シェーダが演算した頂点位置を表わす。 gl_PointSize はいわゆるポイントスプライト用のサイズとなる。

フラグメントシェーダ

フラグメントシェーダは、ラスタライズが生成するデータを表わす入力built-inと、シェーダの結果を格納するための出力built-inの両方を持っている。

// ★ 入力
mediump vec4  gl_FragCoord;
mediump vec2  gl_PointCoord;
        bool  gl_FrontFacing;
// ★ 出力
mediump vec4  gl_FragColor;
mediump vec4  gl_FragData[gl_MaxDrawBuffers];

Uniform

... 使った事無かったから知らなかった。。

//
// Depth range in window coordinates
//
struct gl_DepthRangeParameters {
    highp float near;        // n
    highp float far;         // f
    highp float diff;        // f - n
};
uniform gl_DepthRangeParameters gl_DepthRange;
okuokuokuoku

WebGL2で消えたbuilt-in

WebGL2では、フラグメントシェーダの出力built-inが2つとも消えている(ので、 WebGL2への移行時に gl_FragColor を消して自前の変数にするようにアドバイス される)。これは(built-in以外に対する)出力をimplicitにカラー出力として扱うようになったのでbuilt-inとしての名前が不要になったことによる。ただし、それだと MRT に対応できないので、ESSL3の location を使った方法を規格に入れている:

https://www.khronos.org/registry/OpenGL/specs/es/3.0/es_spec_3.0.pdf

GLES3仕様の、"3.9.2.3 Shader Outputs" には、

If there is only a single output variable, it does not need to be explicitly bound to a fragment color within the shader text, in which case it is implicitly bound to fragment color zero. If there is more than one output variable, all output variables must be explicitly bound to fragment colors within the shader text.

とあり、 "all output variables must be explicitly bound to 〜" は、GLSLソース内で layout を使って location を振っておくのと、 glDrawBuffers でlocationとフレームバッファを関連付けておくことを指す。

フラグメントシェーダの出力built-inが消滅したわけではなく、WebGL1の EXT_frag_depth 拡張がコア機能になったので、新規のbuilt-inとして gl_FragDepth が増えている。

okuokuokuoku

Vulkan(のSPIR-V)とWebGL1のbuilt-inの対応

Vulkanのbuilt-inはSPIR-Vのbuilt-inで表現され、仕様書に纏めて一覧されている。

例えば、 gl_PositionPosition に対応する。そして gl_FragColor に相当するbuilt-inは存在せず、 gl_FragDepth に相当するものは存在するなど、基本的にWebGL2のシチュエーションに近い状況になっている。

gl_FragColor はOpenGLとVulkanで動作が違うことになっている。 ... もっとも、この違いは(WebGL1では)そもそもピクセルシェーダが出力できるcolorは1つだけなので関係ない。

WebGL2の追加ぶんも拾うと以下のようになる:

// ★ WebGL2の追加ぶん (頂点シェーダ入力)
in highp int gl_VertexID;
in highp int gl_InstanceID;
// ★ WebGL2の追加ぶん (フラグメントシェーダ出力)
out highp   float   gl_FragDepth;
WebGL Vulkan
gl_Position Position
gl_PointSize PointSize
gl_VertexID VertexIndex (たぶん)
gl_InstanceID InstanceIndex (たぶん)
gl_FragCood FragCoord
gl_PointCood PointCoord
gl_FrontFacing FrontFacing
gl_FragDepth FragDepth
gl_FragColor (なくなった、glslangは適切なlocationを自動で打つ)
gl_FragData (なくなった、 gl_FragColor 同様 )

FIXME: WEBGL_multi_draw とか OVR_multiview2 をどうすんのか考える必要がある。

これらにはlocationを振らず、そのままにしておく対応が必要になる。