Skip to content

Commit 2a9ab7d

Browse files
vincentfretinclaude
andcommitted
Fix screenshot orientation with the WebGPU backend, address review comments
- Pixels read back from WebGL are bottom-up but top-down with the WebGPU backend of WebGPURenderer, so the vertical flip is now based on the renderer coordinate system. The equirectangular projection quad renders vertically inverted so it needs the opposite flip of the perspective projection. Verified on an actual WebGPU backend (NVIDIA, Vulkan), on the WebGL 2 fallback backend and on WebGLRenderer. - Query maxTextureDimension2D from the WebGPU device limits for cubeMapSize instead of hardcoding 2048. - Rename gl to ctx in setup() since it can be a GPUCanvasContext. - Store this.cubeTextureUniform for both the ShaderMaterial uniform and the TSL cubeTexture node, both expose the texture as a value property. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent b9e0413 commit 2a9ab7d

1 file changed

Lines changed: 19 additions & 11 deletions

File tree

src/components/scene/screenshot.js

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,15 @@ export var Component = registerComponent('screenshot', {
6060
setup: function () {
6161
var el = this.el;
6262
if (this.canvas) { return; }
63-
var gl = el.renderer.getContext();
64-
if (!gl) { return; }
65-
this.cubeMapSize = gl.getParameter ? gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE) : 2048;
63+
var ctx = el.renderer.getContext();
64+
if (!ctx) { return; }
65+
if (ctx.getParameter) {
66+
this.cubeMapSize = ctx.getParameter(ctx.MAX_CUBE_MAP_TEXTURE_SIZE);
67+
} else {
68+
// ctx is a GPUCanvasContext; cube map faces are limited by maxTextureDimension2D.
69+
var device = el.renderer.backend.device;
70+
this.cubeMapSize = device ? device.limits.maxTextureDimension2D : 2048;
71+
}
6672
// WebGPURenderer does not support RawShaderMaterial, use a node material with TSL.
6773
if (THREE.TSL) {
6874
this.material = this.createNodeMaterial();
@@ -73,6 +79,7 @@ export var Component = registerComponent('screenshot', {
7379
fragmentShader: FRAGMENT_SHADER,
7480
side: THREE.DoubleSide
7581
});
82+
this.cubeTextureUniform = this.material.uniforms.map;
7683
}
7784
this.quad = new THREE.Mesh(
7885
new THREE.PlaneGeometry(1, 1),
@@ -101,8 +108,8 @@ export var Component = registerComponent('screenshot', {
101108
TSL.cos(latitude),
102109
TSL.cos(longitude).mul(TSL.sin(latitude)).negate()
103110
);
104-
this.cubeTextureNode = TSL.cubeTexture(new THREE.CubeTexture(), dir);
105-
material.colorNode = TSL.vec4(this.cubeTextureNode.rgb, 1);
111+
this.cubeTextureUniform = TSL.cubeTexture(new THREE.CubeTexture(), dir);
112+
material.colorNode = TSL.vec4(this.cubeTextureUniform.rgb, 1);
106113
return material;
107114
},
108115

@@ -185,11 +192,7 @@ export var Component = registerComponent('screenshot', {
185192
el.camera.getWorldQuaternion(cubeCamera.quaternion);
186193
// Render scene with cube camera.
187194
cubeCamera.update(el.renderer, el.object3D);
188-
if (this.cubeTextureNode) {
189-
this.cubeTextureNode.value = cubeCamera.renderTarget.texture;
190-
} else {
191-
this.quad.material.uniforms.map.value = cubeCamera.renderTarget.texture;
192-
}
195+
this.cubeTextureUniform.value = cubeCamera.renderTarget.texture;
193196
size = {width: this.data.width, height: this.data.height};
194197
// Use quad to project image taken by the cube camera.
195198
this.quad.visible = true;
@@ -278,7 +281,12 @@ export var Component = registerComponent('screenshot', {
278281

279282
copyCapture: function (pixels, size, projection) {
280283
var imageData;
281-
if (projection === 'perspective') {
284+
// Pixels read back from WebGL are bottom-up, they are top-down with the
285+
// WebGPU backend of WebGPURenderer. The equirectangular projection quad
286+
// renders vertically inverted, so it needs the opposite flip of the
287+
// perspective projection.
288+
var topDown = this.el.renderer.coordinateSystem === THREE.WebGPUCoordinateSystem;
289+
if ((projection === 'perspective') !== topDown) {
282290
pixels = this.flipPixelsVertically(pixels, size.width, size.height);
283291
}
284292
imageData = new ImageData(new Uint8ClampedArray(pixels), size.width, size.height);

0 commit comments

Comments
 (0)