From e070b99f0f0be152655572a729220caa3454de0c Mon Sep 17 00:00:00 2001 From: aardgoose Date: Mon, 23 Oct 2023 23:44:19 +0100 Subject: [PATCH] WebGPURenderer: enable shadow rendering in WebGLBackend (#27023) * make previous render state available to backend * make shadow coord adjustment conditional * add screenshot and enable test * rework test * rework as suggested --------- Co-authored-by: aardgoose --- .../jsm/nodes/lighting/AnalyticLightNode.js | 24 +++++++++++--- examples/jsm/renderers/webgl/WebGLBackend.js | 30 ++++++++++++++++-- examples/screenshots/webgpu_shadowmap.jpg | Bin 3413 -> 21526 bytes examples/webgpu_shadowmap.html | 6 ++-- test/e2e/puppeteer.js | 1 - 5 files changed, 51 insertions(+), 10 deletions(-) diff --git a/examples/jsm/nodes/lighting/AnalyticLightNode.js b/examples/jsm/nodes/lighting/AnalyticLightNode.js index a85cc17797e607..bef0f6481b9657 100644 --- a/examples/jsm/nodes/lighting/AnalyticLightNode.js +++ b/examples/jsm/nodes/lighting/AnalyticLightNode.js @@ -7,6 +7,7 @@ import { reference } from '../accessors/ReferenceNode.js'; import { texture } from '../accessors/TextureNode.js'; import { positionWorld } from '../accessors/PositionNode.js'; import { normalWorld } from '../accessors/NormalNode.js'; +import { WebGPUCoordinateSystem } from 'three'; //import { add } from '../math/OperatorNode.js'; import { Color, DepthTexture, NearestFilter, LessCompare } from 'three'; @@ -73,11 +74,24 @@ class AnalyticLightNode extends LightingNode { .and( shadowCoord.y.lessThanEqual( 1 ) ) .and( shadowCoord.z.lessThanEqual( 1 ) ); - shadowCoord = vec3( - shadowCoord.x, - shadowCoord.y.oneMinus(), // WebGPU: Flip Y - shadowCoord.z.add( bias ).mul( 2 ).sub( 1 ) // WebGPU: Convertion [ 0, 1 ] to [ - 1, 1 ] - ); + + if ( builder.renderer.coordinateSystem === WebGPUCoordinateSystem ) { + + shadowCoord = vec3( + shadowCoord.x, + shadowCoord.y.oneMinus(), // WebGPU: Flip Y + shadowCoord.z.add( bias ).mul( 2 ).sub( 1 ) // WebGPU: Convertion [ 0, 1 ] to [ - 1, 1 ] + ); + + } else { + + shadowCoord = vec3( + shadowCoord.x, + shadowCoord.y, + shadowCoord.z.add( bias ) + ); + + } const textureCompare = ( depthTexture, shadowCoord, compare ) => texture( depthTexture, shadowCoord ).compare( compare ); //const textureCompare = ( depthTexture, shadowCoord, compare ) => compare.step( texture( depthTexture, shadowCoord ) ); diff --git a/examples/jsm/renderers/webgl/WebGLBackend.js b/examples/jsm/renderers/webgl/WebGLBackend.js index bada50d7a24ae3..ec80edf0bfa191 100644 --- a/examples/jsm/renderers/webgl/WebGLBackend.js +++ b/examples/jsm/renderers/webgl/WebGLBackend.js @@ -41,6 +41,7 @@ class WebGLBackend extends Backend { this.defaultTextures = {}; this.extensions.get( 'EXT_color_buffer_float' ); + this._currentContext = null; } @@ -53,9 +54,13 @@ class WebGLBackend extends Backend { beginRender( renderContext ) { const { gl } = this; + const renderContextData = this.get( renderContext ); // + renderContextData.previousContext = this._currentContext; + this._currentContext = renderContext; + this._setFramebuffer( renderContext ); let clear = 0; @@ -103,8 +108,6 @@ class WebGLBackend extends Backend { if ( occlusionQueryCount > 0 ) { - const renderContextData = this.get( renderContext ); - // Get a reference to the array of objects with queries. The renderContextData property // can be changed by another render pass before the async reading of all previous queries complete renderContextData.currentOcclusionQueries = renderContextData.occlusionQueries; @@ -121,6 +124,29 @@ class WebGLBackend extends Backend { finishRender( renderContext ) { + const renderContextData = this.get( renderContext ); + const previousContext = renderContextData.previousContext; + + this._currentContext = previousContext; + + if ( previousContext !== null ) { + + this._setFramebuffer( previousContext ); + + if ( previousContext.viewport ) { + + this.updateViewport( previousContext ); + + } else { + + const gl = this.gl; + + gl.viewport( 0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight ); + + } + + } + const occlusionQueryCount = renderContext.occlusionQueryCount; if ( occlusionQueryCount > 0 ) { diff --git a/examples/screenshots/webgpu_shadowmap.jpg b/examples/screenshots/webgpu_shadowmap.jpg index 001fe203e0b94e476d12e5c1c76aa979a0fbc77f..6539397f372b2e23a977ccc623a089b47a938bf1 100644 GIT binary patch literal 21526 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 delta 71 pcmbQXg7K=