C-WebGL: シェーダのbuilt-inを何とかする
WebGL1のシェーダ言語はOpenGL ES2のシェーダ言語である ESSL 1に多少の制限を加えたものになっている。これがWebGL2や最近のGPU事情から比べると絶妙に古く、いくつかのBuilt-inで特別扱いが必要になる。
標準が規定するBuilt-in
"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;
WebGL2で消えたbuilt-in
WebGL2では、フラグメントシェーダの出力built-inが2つとも消えている(ので、 WebGL2への移行時に gl_FragColor
を消して自前の変数にするようにアドバイス される)。これは(built-in以外に対する)出力をimplicitにカラー出力として扱うようになったのでbuilt-inとしての名前が不要になったことによる。ただし、それだと MRT に対応できないので、ESSL3の location
を使った方法を規格に入れている:
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
が増えている。
Vulkan(のSPIR-V)とWebGL1のbuilt-inの対応
Vulkanのbuilt-inはSPIR-Vのbuilt-inで表現され、仕様書に纏めて一覧されている。
- https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/chap15.html#interfaces-builtin-variables
- https://github.com/KhronosGroup/GLSL/blob/ab928c80851b96f3889f1287d4ce0003a9bb86e4/extensions/khr/GL_KHR_vulkan_glsl.txt#L586-L637 -- GLSLとの変換ルール
例えば、 gl_Position
は Position
に対応する。そして gl_FragColor
に相当するbuilt-inは存在せず、 gl_FragDepth
に相当するものは存在するなど、基本的にWebGL2のシチュエーションに近い状況になっている。
-
https://github.com/KhronosGroup/GLSL/blob/ab928c80851b96f3889f1287d4ce0003a9bb86e4/extensions/khr/GL_KHR_vulkan_glsl.txt#L537-L546 --
gl_FragColor
の特別ルール
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を振らず、そのままにしておく対応が必要になる。