From d2fc542d58f5c91fa7b585e6a3efb7ba67b295ca Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Thu, 3 Sep 2026 15:34:47 +0200 Subject: [PATCH 1/2] WebGPURenderer: Add `packed_4x8_integer_dot_product` support. (#34451) --- src/Three.TSL.js | 8 + src/nodes/Nodes.js | 1 + src/nodes/TSL.js | 1 + src/nodes/math/Packed4x8IntegerNode.js | 433 ++++++++++++++++++ .../addons/tsl/TSLPacked4x8Integer.tests.js | 96 ++++ test/unit/three.addons.unit.js | 1 + 6 files changed, 540 insertions(+) create mode 100644 src/nodes/math/Packed4x8IntegerNode.js create mode 100644 test/unit/addons/tsl/TSLPacked4x8Integer.tests.js diff --git a/src/Three.TSL.js b/src/Three.TSL.js index 8f4ce9e61e1a8e..b75d68b59ef257 100644 --- a/src/Three.TSL.js +++ b/src/Three.TSL.js @@ -191,6 +191,8 @@ export const dispersion = TSL.dispersion; export const distance = TSL.distance; export const div = TSL.div; export const dot = TSL.dot; +export const dot4I8Packed = TSL.dot4I8Packed; +export const dot4U8Packed = TSL.dot4U8Packed; export const drawIndex = TSL.drawIndex; export const dynamicBufferAttribute = TSL.dynamicBufferAttribute; export const element = TSL.element; @@ -446,6 +448,10 @@ export const outputStruct = TSL.outputStruct; export const overloadingFn = TSL.overloadingFn; export const overrideNode = TSL.overrideNode; export const overrideNodes = TSL.overrideNodes; +export const pack4xI8 = TSL.pack4xI8; +export const pack4xI8Clamp = TSL.pack4xI8Clamp; +export const pack4xU8 = TSL.pack4xU8; +export const pack4xU8Clamp = TSL.pack4xU8Clamp; export const packHalf2x16 = TSL.packHalf2x16; export const packNormalToRGB = TSL.packNormalToRGB; export const packSnorm2x16 = TSL.packSnorm2x16; @@ -626,6 +632,8 @@ export const uniformCubeTexture = TSL.uniformCubeTexture; export const uniformFlow = TSL.uniformFlow; export const uniformGroup = TSL.uniformGroup; export const uniformTexture = TSL.uniformTexture; +export const unpack4xI8 = TSL.unpack4xI8; +export const unpack4xU8 = TSL.unpack4xU8; export const unpackHalf2x16 = TSL.unpackHalf2x16; export const unpackNormal = TSL.unpackNormal; export const unpackRGBToNormal = TSL.unpackRGBToNormal; diff --git a/src/nodes/Nodes.js b/src/nodes/Nodes.js index 5a27ea0e7e69b7..c48c339f91d617 100644 --- a/src/nodes/Nodes.js +++ b/src/nodes/Nodes.js @@ -131,6 +131,7 @@ export { default as ConditionalNode } from './math/ConditionalNode.js'; export { default as MathNode } from './math/MathNode.js'; export { default as OperatorNode } from './math/OperatorNode.js'; export { default as PackFloatNode } from './math/PackFloatNode.js'; +export { default as Packed4x8IntegerNode } from './math/Packed4x8IntegerNode.js'; export { default as UnpackFloatNode } from './math/UnpackFloatNode.js'; // parsers diff --git a/src/nodes/TSL.js b/src/nodes/TSL.js index 6f305aa348eee6..45c10c743fb8b7 100644 --- a/src/nodes/TSL.js +++ b/src/nodes/TSL.js @@ -25,6 +25,7 @@ export * from './math/BitcountNode.js'; export * from './math/Hash.js'; export * from './math/MathUtils.js'; export * from './math/PackFloatNode.js'; +export * from './math/Packed4x8IntegerNode.js'; export * from './math/UnpackFloatNode.js'; export * from './math/TriNoise3D.js'; diff --git a/src/nodes/math/Packed4x8IntegerNode.js b/src/nodes/math/Packed4x8IntegerNode.js new file mode 100644 index 00000000000000..018998d69c8c2d --- /dev/null +++ b/src/nodes/math/Packed4x8IntegerNode.js @@ -0,0 +1,433 @@ +import TempNode from '../core/TempNode.js'; +import { nodeProxyIntent, Fn, uint, int, ivec4, uvec4 } from '../tsl/TSLCore.js'; +import { clamp } from './MathNode.js'; + +/** + * Represents one of the built-in functions of WGSL's `packed_4x8_integer_dot_product` + * language extension. If the extension is not available, the node falls back to an + * emulation with plain integer bit operations. + * + * @augments TempNode + */ +class Packed4x8IntegerNode extends TempNode { + + static get type() { + + return 'Packed4x8IntegerNode'; + + } + + /** + * Constructs a packed 4x8 integer function node. + * + * @param {string} method - The WGSL built-in function name. + * @param {Node} aNode - The first argument. + * @param {?Node} [bNode=null] - The optional second argument. + */ + constructor( method, aNode, bNode = null ) { + + super(); + + /** + * The WGSL built-in function name. + * + * @type {string} + */ + this.method = method; + + /** + * The first argument. + * + * @type {Node} + */ + this.aNode = aNode; + + /** + * The optional second argument. + * + * @type {?Node} + */ + this.bNode = bNode; + + /** + * This flag can be used for type testing. + * + * @type {boolean} + * @readonly + * @default true + */ + this.isPacked4x8IntegerNode = true; + + } + + getInputType() { + + const method = this.method; + + if ( method === Packed4x8IntegerNode.PACK4X_I8 || method === Packed4x8IntegerNode.PACK4X_I8_CLAMP ) { + + return 'ivec4'; + + } else if ( method === Packed4x8IntegerNode.PACK4X_U8 || method === Packed4x8IntegerNode.PACK4X_U8_CLAMP ) { + + return 'uvec4'; + + } + + return 'uint'; + + } + + generateNodeType() { + + const method = this.method; + + if ( method === Packed4x8IntegerNode.DOT4_I8_PACKED ) { + + return 'int'; + + } else if ( method === Packed4x8IntegerNode.UNPACK4X_I8 ) { + + return 'ivec4'; + + } else if ( method === Packed4x8IntegerNode.UNPACK4X_U8 ) { + + return 'uvec4'; + + } + + return 'uint'; + + } + + /** + * Returns the reusable `Fn()` definition that emulates this node's method. + * + * @private + * @returns {Function} The emulation function. + */ + _getEmulatedFn() { + + switch ( this.method ) { + + case Packed4x8IntegerNode.DOT4_U8_PACKED: return emulatedDot4U8Packed; + case Packed4x8IntegerNode.DOT4_I8_PACKED: return emulatedDot4I8Packed; + case Packed4x8IntegerNode.PACK4X_I8: return emulatedPack4xI8; + case Packed4x8IntegerNode.PACK4X_U8: return emulatedPack4xU8; + case Packed4x8IntegerNode.PACK4X_I8_CLAMP: return emulatedPack4xI8Clamp; + case Packed4x8IntegerNode.PACK4X_U8_CLAMP: return emulatedPack4xU8Clamp; + case Packed4x8IntegerNode.UNPACK4X_I8: return emulatedUnpack4xI8; + case Packed4x8IntegerNode.UNPACK4X_U8: return emulatedUnpack4xU8; + + } + + } + + setup( builder ) { + + // check for native language support + + if ( builder.renderer.backend.isWebGPUBackend === true && + typeof navigator !== 'undefined' && navigator.gpu !== undefined && + navigator.gpu.wgslLanguageFeatures !== undefined && navigator.gpu.wgslLanguageFeatures.has( 'packed_4x8_integer_dot_product' ) + ) { + + return super.setup( builder ); + + } + + // emulation + + const { aNode, bNode } = this; + const fn = this._getEmulatedFn(); + + return bNode !== null ? fn( aNode, bNode ) : fn( aNode ); + + } + + generate( builder, output ) { + + const properties = builder.getNodeProperties( this ); + + if ( properties.outputNode ) { + + return super.generate( builder, output ); + + } + + // generate native WGSL call + + const type = this.getNodeType( builder ); + const inputType = this.getInputType(); + const params = [ this.aNode.build( builder, inputType ) ]; + + if ( this.bNode !== null ) params.push( this.bNode.build( builder, inputType ) ); + + return builder.format( `${this.method}( ${params.join( ', ' )} )`, type, output ); + + } + + serialize( data ) { + + super.serialize( data ); + + data.method = this.method; + + } + + deserialize( data ) { + + super.deserialize( data ); + + this.method = data.method; + + } + + static get DOT4_U8_PACKED() { + + return 'dot4U8Packed'; + + } + + static get DOT4_I8_PACKED() { + + return 'dot4I8Packed'; + + } + + static get PACK4X_I8() { + + return 'pack4xI8'; + + } + + static get PACK4X_U8() { + + return 'pack4xU8'; + + } + + static get PACK4X_I8_CLAMP() { + + return 'pack4xI8Clamp'; + + } + + static get PACK4X_U8_CLAMP() { + + return 'pack4xU8Clamp'; + + } + + static get UNPACK4X_I8() { + + return 'unpack4xI8'; + + } + + static get UNPACK4X_U8() { + + return 'unpack4xU8'; + + } + +} + +// emulations + +const emulatedPack4xU8 = /*@__PURE__*/ Fn( ( [ v ] ) => { + + const x = v.x.bitAnd( uint( 0xff ) ); + const y = v.y.bitAnd( uint( 0xff ) ); + const z = v.z.bitAnd( uint( 0xff ) ); + const w = v.w.bitAnd( uint( 0xff ) ); + + return x.bitOr( y.shiftLeft( uint( 8 ) ) ).bitOr( z.shiftLeft( uint( 16 ) ) ).bitOr( w.shiftLeft( uint( 24 ) ) ); + +} ).setLayout( { + name: 'tsl_packed4x8_pack4xU8', + type: 'uint', + inputs: [ { name: 'v', type: 'uvec4' } ] +} ); + +const emulatedPack4xI8 = /*@__PURE__*/ Fn( ( [ v ] ) => { + + return emulatedPack4xU8( uvec4( v ) ); + +} ).setLayout( { + name: 'tsl_packed4x8_pack4xI8', + type: 'uint', + inputs: [ { name: 'v', type: 'ivec4' } ] +} ); + +const emulatedPack4xU8Clamp = /*@__PURE__*/ Fn( ( [ v ] ) => { + + return emulatedPack4xU8( clamp( v, uvec4( 0 ), uvec4( 255 ) ) ); + +} ).setLayout( { + name: 'tsl_packed4x8_pack4xU8Clamp', + type: 'uint', + inputs: [ { name: 'v', type: 'uvec4' } ] +} ); + +const emulatedPack4xI8Clamp = /*@__PURE__*/ Fn( ( [ v ] ) => { + + return emulatedPack4xI8( clamp( v, ivec4( - 128 ), ivec4( 127 ) ) ); + +} ).setLayout( { + name: 'tsl_packed4x8_pack4xI8Clamp', + type: 'uint', + inputs: [ { name: 'v', type: 'ivec4' } ] +} ); + +const emulatedUnpack4xU8 = /*@__PURE__*/ Fn( ( [ v ] ) => { + + return uvec4( + v.bitAnd( uint( 0xff ) ), + v.shiftRight( uint( 8 ) ).bitAnd( uint( 0xff ) ), + v.shiftRight( uint( 16 ) ).bitAnd( uint( 0xff ) ), + v.shiftRight( uint( 24 ) ).bitAnd( uint( 0xff ) ) + ); + +} ).setLayout( { + name: 'tsl_packed4x8_unpack4xU8', + type: 'uvec4', + inputs: [ { name: 'v', type: 'uint' } ] +} ); + +function signExtendByte( v, byteShift ) { + + return int( v.shiftLeft( uint( 24 - byteShift ) ) ).shiftRight( int( 24 ) ); + +} + +const emulatedUnpack4xI8 = /*@__PURE__*/ Fn( ( [ v ] ) => { + + return ivec4( + signExtendByte( v, 0 ), + signExtendByte( v, 8 ), + signExtendByte( v, 16 ), + signExtendByte( v, 24 ) + ); + +} ).setLayout( { + name: 'tsl_packed4x8_unpack4xI8', + type: 'ivec4', + inputs: [ { name: 'v', type: 'uint' } ] +} ); + +const emulatedDot4U8Packed = /*@__PURE__*/ Fn( ( [ a, b ] ) => { + + const ua = emulatedUnpack4xU8( a ); + const ub = emulatedUnpack4xU8( b ); + + return ua.x.mul( ub.x ).add( ua.y.mul( ub.y ) ).add( ua.z.mul( ub.z ) ).add( ua.w.mul( ub.w ) ); + +} ).setLayout( { + name: 'tsl_packed4x8_dot4U8Packed', + type: 'uint', + inputs: [ { name: 'a', type: 'uint' }, { name: 'b', type: 'uint' } ] +} ); + +const emulatedDot4I8Packed = /*@__PURE__*/ Fn( ( [ a, b ] ) => { + + const ia = emulatedUnpack4xI8( a ); + const ib = emulatedUnpack4xI8( b ); + + return ia.x.mul( ib.x ).add( ia.y.mul( ib.y ) ).add( ia.z.mul( ib.z ) ).add( ia.w.mul( ib.w ) ); + +} ).setLayout( { + name: 'tsl_packed4x8_dot4I8Packed', + type: 'int', + inputs: [ { name: 'a', type: 'uint' }, { name: 'b', type: 'uint' } ] +} ); + +// exports + +export default Packed4x8IntegerNode; + +/** + * Computes the dot product of four unsigned 8-bit integer components packed + * into each input. + * + * @tsl + * @function + * @param {Node} a - The first packed unsigned integer vector. + * @param {Node} b - The second packed unsigned integer vector. + * @returns {Node} The dot product. + */ +export const dot4U8Packed = /*@__PURE__*/ nodeProxyIntent( Packed4x8IntegerNode, Packed4x8IntegerNode.DOT4_U8_PACKED ).setParameterLength( 2 ); + +/** + * Computes the dot product of four signed 8-bit integer components packed + * into each input. + * + * @tsl + * @function + * @param {Node} a - The first packed signed integer vector. + * @param {Node} b - The second packed signed integer vector. + * @returns {Node} The dot product. + */ +export const dot4I8Packed = /*@__PURE__*/ nodeProxyIntent( Packed4x8IntegerNode, Packed4x8IntegerNode.DOT4_I8_PACKED ).setParameterLength( 2 ); + +/** + * Packs the least significant 8 bits of four signed integers into a `uint`. + * + * @tsl + * @function + * @param {Node} value - The signed integer vector to pack. + * @returns {Node} The packed value. + */ +export const pack4xI8 = /*@__PURE__*/ nodeProxyIntent( Packed4x8IntegerNode, Packed4x8IntegerNode.PACK4X_I8 ).setParameterLength( 1 ); + +/** + * Packs the least significant 8 bits of four unsigned integers into a `uint`. + * + * @tsl + * @function + * @param {Node} value - The unsigned integer vector to pack. + * @returns {Node} The packed value. + */ +export const pack4xU8 = /*@__PURE__*/ nodeProxyIntent( Packed4x8IntegerNode, Packed4x8IntegerNode.PACK4X_U8 ).setParameterLength( 1 ); + +/** + * Clamps four signed integers to the signed 8-bit range and packs them into a + * `uint`. + * + * @tsl + * @function + * @param {Node} value - The signed integer vector to clamp and pack. + * @returns {Node} The packed value. + */ +export const pack4xI8Clamp = /*@__PURE__*/ nodeProxyIntent( Packed4x8IntegerNode, Packed4x8IntegerNode.PACK4X_I8_CLAMP ).setParameterLength( 1 ); + +/** + * Clamps four unsigned integers to the unsigned 8-bit range and packs them + * into a `uint`. + * + * @tsl + * @function + * @param {Node} value - The unsigned integer vector to clamp and pack. + * @returns {Node} The packed value. + */ +export const pack4xU8Clamp = /*@__PURE__*/ nodeProxyIntent( Packed4x8IntegerNode, Packed4x8IntegerNode.PACK4X_U8_CLAMP ).setParameterLength( 1 ); + +/** + * Unpacks a `uint` into four sign-extended signed 8-bit integer components. + * + * @tsl + * @function + * @param {Node} value - The packed value. + * @returns {Node} The unpacked signed integer vector. + */ +export const unpack4xI8 = /*@__PURE__*/ nodeProxyIntent( Packed4x8IntegerNode, Packed4x8IntegerNode.UNPACK4X_I8 ).setParameterLength( 1 ); + +/** + * Unpacks a `uint` into four zero-extended unsigned 8-bit integer components. + * + * @tsl + * @function + * @param {Node} value - The packed value. + * @returns {Node} The unpacked unsigned integer vector. + */ +export const unpack4xU8 = /*@__PURE__*/ nodeProxyIntent( Packed4x8IntegerNode, Packed4x8IntegerNode.UNPACK4X_U8 ).setParameterLength( 1 ); diff --git a/test/unit/addons/tsl/TSLPacked4x8Integer.tests.js b/test/unit/addons/tsl/TSLPacked4x8Integer.tests.js new file mode 100644 index 00000000000000..5c7d53d912f8d8 --- /dev/null +++ b/test/unit/addons/tsl/TSLPacked4x8Integer.tests.js @@ -0,0 +1,96 @@ +import { + int, uint, ivec4, uvec4, + bitAnd, shiftRight, + dot4I8Packed, dot4U8Packed, + pack4xI8, pack4xI8Clamp, pack4xU8, pack4xU8Clamp, + unpack4xI8, unpack4xU8 +} from 'three/tsl'; +import { gpuTest } from './gpu-test-utils.js'; + +function byte( value, offset ) { + + return bitAnd( shiftRight( value, uint( offset ) ), uint( 0xff ) ); + +} + +export default QUnit.module( 'TSL', () => { + + QUnit.module( 'packed 4x8 integer operations', () => { + + gpuTest( 'packed integer dot products', ( { assert } ) => { + + assert.eq( + dot4U8Packed( uint( 0x01020304 ), uint( 0x02040405 ) ), + uint( 42 ), + 'dot4U8Packed matches the WGSL specification example' + ); + + const signedA = pack4xI8( ivec4( 1, - 1, 2, - 2 ) ); + const signedB = pack4xI8( ivec4( 3, 4, - 1, - 1 ) ); + + assert.eq( + dot4I8Packed( signedA, signedB ), + int( - 1 ), + 'dot4I8Packed sign-extends each packed byte' + ); + + } ); + + gpuTest( 'pack4xU8 and pack4xI8 bit layout', ( { assert } ) => { + + const packedUnsigned = pack4xU8( uvec4( 1, 2, 3, 4 ) ); + + assert.eq( byte( packedUnsigned, 0 ), uint( 1 ), 'pack4xU8 stores x in bits 0..7' ); + assert.eq( byte( packedUnsigned, 8 ), uint( 2 ), 'pack4xU8 stores y in bits 8..15' ); + assert.eq( byte( packedUnsigned, 16 ), uint( 3 ), 'pack4xU8 stores z in bits 16..23' ); + assert.eq( byte( packedUnsigned, 24 ), uint( 4 ), 'pack4xU8 stores w in bits 24..31' ); + + const packedSigned = pack4xI8( ivec4( - 1, - 2, 127, - 128 ) ); + + assert.eq( byte( packedSigned, 0 ), uint( 0xff ), 'pack4xI8 stores the two\'s-complement x byte' ); + assert.eq( byte( packedSigned, 8 ), uint( 0xfe ), 'pack4xI8 stores the two\'s-complement y byte' ); + assert.eq( byte( packedSigned, 16 ), uint( 0x7f ), 'pack4xI8 stores the positive z byte' ); + assert.eq( byte( packedSigned, 24 ), uint( 0x80 ), 'pack4xI8 stores the negative w byte' ); + + assert.eq( byte( pack4xU8( uvec4( 256, 0, 0, 0 ) ), 0 ), uint( 0 ), 'pack4xU8 truncates to the low byte' ); + assert.eq( byte( pack4xI8( ivec4( 128, 0, 0, 0 ) ), 0 ), uint( 0x80 ), 'pack4xI8 truncates to the low byte' ); + + } ); + + gpuTest( 'pack4xU8Clamp and pack4xI8Clamp ranges', ( { assert } ) => { + + const packedUnsigned = pack4xU8Clamp( uvec4( 0, 256, 1000, 255 ) ); + + assert.eq( byte( packedUnsigned, 0 ), uint( 0 ), 'pack4xU8Clamp preserves zero' ); + assert.eq( byte( packedUnsigned, 8 ), uint( 255 ), 'pack4xU8Clamp clamps 256' ); + assert.eq( byte( packedUnsigned, 16 ), uint( 255 ), 'pack4xU8Clamp clamps larger values' ); + assert.eq( byte( packedUnsigned, 24 ), uint( 255 ), 'pack4xU8Clamp preserves 255' ); + + const packedSigned = pack4xI8Clamp( ivec4( 200, - 200, 127, - 128 ) ); + + assert.eq( byte( packedSigned, 0 ), uint( 0x7f ), 'pack4xI8Clamp clamps above 127' ); + assert.eq( byte( packedSigned, 8 ), uint( 0x80 ), 'pack4xI8Clamp clamps below -128' ); + assert.eq( byte( packedSigned, 16 ), uint( 0x7f ), 'pack4xI8Clamp preserves 127' ); + assert.eq( byte( packedSigned, 24 ), uint( 0x80 ), 'pack4xI8Clamp preserves -128' ); + + } ); + + gpuTest( 'unpack4xU8 and unpack4xI8 extension', ( { assert } ) => { + + assert.eq( + unpack4xU8( uint( 0x04030201 ) ), + uvec4( 1, 2, 3, 4 ), + 'unpack4xU8 zero-extends all four bytes' + ); + + assert.eq( + unpack4xI8( uint( 0x807ffeff ) ), + ivec4( - 1, - 2, 127, - 128 ), + 'unpack4xI8 sign-extends all four bytes' + ); + + } ); + + } ); + +} ); diff --git a/test/unit/three.addons.unit.js b/test/unit/three.addons.unit.js index 4ee61b6d8b0214..b57a7b4d2ca1de 100644 --- a/test/unit/three.addons.unit.js +++ b/test/unit/three.addons.unit.js @@ -36,6 +36,7 @@ import './addons/tsl/TSLBitOps.tests.js'; import './addons/tsl/TSLNoise.tests.js'; import './addons/tsl/TSLMath.tests.js'; import './addons/tsl/TSLPacking.tests.js'; +import './addons/tsl/TSLPacked4x8Integer.tests.js'; import './addons/tsl/TSLConversion.tests.js'; import './addons/tsl/TSLVectorMatrix.tests.js'; import './addons/tsl/TSLMathExtra.tests.js'; From 994260a7a59abe466de8e59db1ecc9e30352751d Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Thu, 3 Sep 2026 18:25:27 +0200 Subject: [PATCH 2/2] PassNode: Fix pixel ratio in derived passes. (#34453) --- examples/jsm/tsl/display/SSAAPassNode.js | 4 +--- examples/jsm/tsl/display/StereoCompositePassNode.js | 4 +--- examples/jsm/tsl/display/StereoPassNode.js | 4 +--- examples/webgl_postprocessing_ssaa.html | 5 ++--- 4 files changed, 5 insertions(+), 12 deletions(-) diff --git a/examples/jsm/tsl/display/SSAAPassNode.js b/examples/jsm/tsl/display/SSAAPassNode.js index cf6cd332e39043..dda0087ab2e8c4 100644 --- a/examples/jsm/tsl/display/SSAAPassNode.js +++ b/examples/jsm/tsl/display/SSAAPassNode.js @@ -99,9 +99,7 @@ class SSAAPassNode extends PassNode { // - this._pixelRatio = renderer.getPixelRatio(); - - const size = renderer.getSize( _size ); + const size = renderer.getDrawingBufferSize( _size ); this.setSize( size.width, size.height ); this._sampleRenderTarget.setSize( this.renderTarget.width, this.renderTarget.height ); diff --git a/examples/jsm/tsl/display/StereoCompositePassNode.js b/examples/jsm/tsl/display/StereoCompositePassNode.js index a2d291c6d49cc1..e3518458f20804 100644 --- a/examples/jsm/tsl/display/StereoCompositePassNode.js +++ b/examples/jsm/tsl/display/StereoCompositePassNode.js @@ -139,11 +139,9 @@ class StereoCompositePassNode extends PassNode { // - this._pixelRatio = renderer.getPixelRatio(); - this.updateStereoCamera( renderer.coordinateSystem ); - const size = renderer.getSize( _size ); + const size = renderer.getDrawingBufferSize( _size ); this.setSize( size.width, size.height ); // left diff --git a/examples/jsm/tsl/display/StereoPassNode.js b/examples/jsm/tsl/display/StereoPassNode.js index 261c3429cbd097..5e8ce8f22c088a 100644 --- a/examples/jsm/tsl/display/StereoPassNode.js +++ b/examples/jsm/tsl/display/StereoPassNode.js @@ -61,13 +61,11 @@ class StereoPassNode extends PassNode { // - this._pixelRatio = renderer.getPixelRatio(); - stereo.cameraL.coordinateSystem = renderer.coordinateSystem; stereo.cameraR.coordinateSystem = renderer.coordinateSystem; stereo.update( camera ); - const size = renderer.getSize( _size ); + const size = renderer.getDrawingBufferSize( _size ); this.setSize( size.width, size.height ); renderer.autoClear = false; diff --git a/examples/webgl_postprocessing_ssaa.html b/examples/webgl_postprocessing_ssaa.html index 14a1906c82a2c2..4dcf9004ae53e1 100644 --- a/examples/webgl_postprocessing_ssaa.html +++ b/examples/webgl_postprocessing_ssaa.html @@ -45,7 +45,7 @@ let gui, stats; const params = { - sampleLevel: 4, + sampleLevel: 3, unbiased: true, camera: 'perspective', clearColor: 'black', @@ -174,7 +174,6 @@ // postprocessing composer = new EffectComposer( renderer ); - composer.setPixelRatio( 1 ); // ensure pixel ratio is always 1 for performance reasons ssaaRenderPassP = new SSAARenderPass( scene, cameraP ); composer.addPass( ssaaRenderPassP ); ssaaRenderPassO = new SSAARenderPass( scene, cameraO ); @@ -194,7 +193,7 @@ cameraP.aspect = aspect; cameraP.setViewOffset( width, height, params.viewOffsetX, 0, width, height ); - cameraO.updateProjectionMatrix(); + cameraP.updateProjectionMatrix(); cameraO.left = - height * aspect; cameraO.right = height * aspect;