@carbonenginejs/runtime-resource 0.19.1 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -133,8 +133,33 @@ class DxbcGlslEmitter {
133
133
  // into one array binding frees two texture units on shaders that
134
134
  // sit exactly on WebGL2's 16-unit limit.
135
135
  detailMapArrayRegisters: [],
136
+ // Which end of the source clip range is NEAR, and therefore which
137
+ // form the depth-range fixup takes.
138
+ //
139
+ // "reversed" (default) z_clip [w, 0] -> gl_Position.z = w - 2z
140
+ // "forward" z_clip [0, w] -> gl_Position.z = 2z - w
141
+ //
142
+ // The default is "reversed" because that is what the shaders this
143
+ // emitter translates were compiled against, and it is a property of
144
+ // the INPUT, not a preference of any one consumer. It is stated as
145
+ // an option rather than hard-coded for two reasons: the assumption
146
+ // is otherwise invisible in the output, and a future source may not
147
+ // share it. Deciding it here also means a consumer supplies a
148
+ // matching projection or gets inverted depth, which is a real
149
+ // coupling and better declared than implied.
150
+ //
151
+ // Switch to "forward" to A/B the convention against a forward-range
152
+ // projection. Expect an unbiased vertex to look identical either
153
+ // way - the fixup and the projection compose to the identity - and
154
+ // only shader-authored depth offsets to change sign. That is the
155
+ // whole observable difference, and it is what makes the wrong
156
+ // choice so hard to see.
157
+ depthRange: "reversed",
136
158
  ...options.profile
137
159
  };
160
+ if (this.profile.depthRange !== "reversed" && this.profile.depthRange !== "forward") {
161
+ throw new TypeError(`DxbcGlslEmitter: depthRange must be "reversed" or "forward", got ${JSON.stringify(this.profile.depthRange)}`);
162
+ }
138
163
  }
139
164
 
140
165
  /**
@@ -3129,6 +3154,18 @@ DxbcGlslEmitter.prototype._gather4Channel = function _gather4Channel(operand) {
3129
3154
  /**
3130
3155
  * Assembles the final GLSL text.
3131
3156
  */
3157
+ /**
3158
+ * The depth-range fixup emitted at the end of a vertex stage, by source range.
3159
+ *
3160
+ * Both entries map their range onto GL NDC [-1, 1] and both compose to the
3161
+ * identity with a projection of the matching form, so an unbiased vertex is
3162
+ * unchanged either way. See `profile.depthRange`.
3163
+ * @type {Object.<string,string>}
3164
+ */
3165
+ const DEPTH_RANGE_FIXUP = Object.freeze({
3166
+ reversed: "gl_Position.z = gl_Position.w - 2.0 * gl_Position.z;",
3167
+ forward: "gl_Position.z = 2.0 * gl_Position.z - gl_Position.w;"
3168
+ });
3132
3169
  DxbcGlslEmitter.prototype._assemble = function _assemble(state) {
3133
3170
  const lines = ["#version 300 es"];
3134
3171
  if (state.isPixel || state.isCompute) {
@@ -3139,16 +3176,27 @@ DxbcGlslEmitter.prototype._assemble = function _assemble(state) {
3139
3176
  if (helperSource) {
3140
3177
  lines.push("", helperSource);
3141
3178
  }
3142
- // Vertex stages get the standard DX->GL depth-range fixup: EVE's
3143
- // projection matrices are D3D-style (z_clip in [0, w]), so the raw
3144
- // DXBC position would land in GL NDC [0, 1] instead of [-1, 1]
3145
- // halving depth precision and breaking every pixel-stage read of
3146
- // SV_Position.z (gl_FragCoord.z) and depth-encoding technique against
3147
- // the DX-convention values the shaders were compiled for. Remapping
3148
- // z after the translated body runs makes depth output, clipping and
3149
- // gl_FragCoord.z all match D3D semantics exactly. The body is wrapped
3150
- // in a helper so early `ret` (return) statements cannot skip the
3151
- // fixup.
3179
+ // Vertex stages get the depth-range fixup that turns a compiled shader's
3180
+ // clip z into GL's. These shaders are compiled against a REVERSED depth
3181
+ // range: z_clip runs [w, 0], near at w and far at 0, and `w - 2z` maps that
3182
+ // onto GL NDC [-1, 1].
3183
+ //
3184
+ // The reversal is observable in the shipped bodies themselves. Surface
3185
+ // decals add a small positive constant to clip z to lift themselves off the
3186
+ // hull they sit on, and a lift is only toward the camera on a reversed
3187
+ // axis.
3188
+ //
3189
+ // The caller MUST supply a reversed-D3D projection. Composed with one, this
3190
+ // is exactly the identity for an unbiased vertex, so the depth buffer keeps
3191
+ // conventional GL values and legacy shaders sharing it are unaffected. What
3192
+ // changes is the direction of any offset the shader authors ITSELF: the
3193
+ // decal family adds `+1e-5` to lift itself off the hull, which is toward
3194
+ // the camera only on a reversed axis. Hand it a forward-D3D projection and
3195
+ // every such offset inverts - decals sink INTO the hull by `2e-5/w`, an
3196
+ // error that grows as the camera closes in.
3197
+ //
3198
+ // The body is wrapped in a helper so early `ret` (return) statements cannot
3199
+ // skip the fixup.
3152
3200
  const writesPosition = [...state.outputNames.values()].includes("gl_Position");
3153
3201
  if (!state.isPixel && !state.isCompute && writesPosition) {
3154
3202
  lines.push("", "void dxbc_main() {");
@@ -3156,7 +3204,7 @@ DxbcGlslEmitter.prototype._assemble = function _assemble(state) {
3156
3204
  lines.push(` ${early}`);
3157
3205
  }
3158
3206
  lines.push(...state.bodyLines, "}");
3159
- lines.push("", "void main() {", " dxbc_main();", " gl_Position.z = 2.0 * gl_Position.z - gl_Position.w;", "}");
3207
+ lines.push("", "void main() {", " dxbc_main();", ` ${DEPTH_RANGE_FIXUP[this.profile.depthRange]}`, "}");
3160
3208
  return `${lines.join("\n")}\n`;
3161
3209
  }
3162
3210
  lines.push("", "void main() {");