Unverified Commit aeb40de3 authored by Renaud Rohlinger's avatar Renaud Rohlinger Committed by GitHub
Browse files

WebGPURenderer: MSAA, Postprocessing and Wireframe support in the WebGL Backend (#27473)

* support material.wireframe in webgl backend

* fix reattach framebuffer, support msaa and wireframe demo

* fix depth samples === 0

* cleanup

* more cleanup

* more cleanup to the webgl pipeline

* postprocessing support

* fix msaa

* fix example afterimage

* improved state management and performances

* generate a new webgl slot for fbos

* revert puppeteer

* fix copyFrameBufferToTexture with correct unbinding and cleanup

* fix multipass pipeline in webgl backend and enable new demos

* regenerate screenshot with gaussian blur
No related merge requests found
Showing with 693 additions and 107 deletions
+693 -107
......@@ -367,7 +367,8 @@
"webgpu_tsl_editor",
"webgpu_tsl_transpiler",
"webgpu_video_panorama",
"webgpu_postprocessing_afterimage"
"webgpu_postprocessing_afterimage",
"webgpu_multisampled_renderbuffers"
],
"webaudio": [
"webaudio_orientation",
......
......@@ -21,7 +21,10 @@ class AfterImageNode extends TempNode {
this.damp = uniform( damp );
this._compRT = new RenderTarget();
this._compRT.texture.name = 'AfterImageNode.comp';
this._oldRT = new RenderTarget();
this._oldRT.texture.name = 'AfterImageNode.old';
this.updateBeforeType = NodeUpdateType.RENDER;
......
......@@ -29,7 +29,9 @@ class GaussianBlurNode extends TempNode {
this._passDirection = uniform( new Vector2() );
this._horizontalRT = new RenderTarget();
this._horizontalRT.texture.name = 'GaussianBlurNode.horizontal';
this._verticalRT = new RenderTarget();
this._verticalRT.texture.name = 'GaussianBlurNode.vertical';
this.updateBeforeType = NodeUpdateType.RENDER;
......
......@@ -71,6 +71,8 @@ class WebGLBackend extends Backend {
//
//
renderContextData.previousContext = this._currentContext;
this._currentContext = renderContext;
......@@ -79,7 +81,6 @@ class WebGLBackend extends Backend {
this.clear( renderContext.clearColor, renderContext.clearDepth, renderContext.clearStencil, renderContext );
//
if ( renderContext.viewport ) {
this.updateViewport( renderContext );
......@@ -110,11 +111,49 @@ class WebGLBackend extends Backend {
finishRender( renderContext ) {
const { gl } = this;
const renderContextData = this.get( renderContext );
const previousContext = renderContextData.previousContext;
this._currentContext = previousContext;
if ( renderContext.textures !== null && renderContext.renderTarget ) {
const renderTargetContextData = this.get( renderContext.renderTarget );
const { samples, stencilBuffer } = renderContext.renderTarget;
const fb = renderTargetContextData.framebuffer;
if ( samples > 0 ) {
const invalidationArray = [];
const depthStyle = stencilBuffer ? gl.DEPTH_STENCIL_ATTACHMENT : gl.DEPTH_ATTACHMENT;
invalidationArray.push( gl.COLOR_ATTACHMENT0 );
if ( renderTargetContextData.depthBuffer ) {
invalidationArray.push( depthStyle );
}
// TODO For loop support MRT
const msaaFrameBuffer = renderTargetContextData.msaaFrameBuffer;
gl.bindFramebuffer( gl.READ_FRAMEBUFFER, msaaFrameBuffer );
gl.bindFramebuffer( gl.DRAW_FRAMEBUFFER, fb );
gl.blitFramebuffer( 0, 0, renderContext.width, renderContext.height, 0, 0, renderContext.width, renderContext.height, gl.COLOR_BUFFER_BIT, gl.NEAREST );
gl.invalidateFramebuffer( gl.READ_FRAMEBUFFER, invalidationArray );
}
}
if ( previousContext !== null ) {
this._setFramebuffer( previousContext );
......@@ -133,6 +172,9 @@ class WebGLBackend extends Backend {
}
this._currentContext = renderContext;
const occlusionQueryCount = renderContext.occlusionQueryCount;
if ( occlusionQueryCount > 0 ) {
......@@ -151,6 +193,7 @@ class WebGLBackend extends Backend {
}
}
resolveOccludedAsync( renderContext ) {
......@@ -312,7 +355,7 @@ class WebGLBackend extends Backend {
draw( renderObject, info ) {
const { pipeline, material, context } = renderObject;
const { pipeline, material, context, isRenderObject } = renderObject;
const { programGPU, vaoGPU } = this.get( pipeline );
const { gl, state } = this;
......@@ -321,6 +364,13 @@ class WebGLBackend extends Backend {
//
if ( isRenderObject ) {
// we need to bind the framebuffer per object in multi pass pipeline
this._setFramebuffer( context );
}
const bindings = renderObject.getBindings();
for ( const binding of bindings ) {
......@@ -334,8 +384,7 @@ class WebGLBackend extends Backend {
} else if ( binding.isSampledTexture ) {
gl.activeTexture( gl.TEXTURE0 + index );
gl.bindTexture( bindingData.glTextureType, bindingData.textureGPU );
state.bindTexture( bindingData.glTextureType, bindingData.textureGPU, gl.TEXTURE0 + index );
}
......@@ -391,7 +440,20 @@ class WebGLBackend extends Backend {
else if ( object.isLineSegments ) mode = gl.LINES;
else if ( object.isLine ) mode = gl.LINE_STRIP;
else if ( object.isLineLoop ) mode = gl.LINE_LOOP;
else mode = gl.TRIANGLES;
else {
if ( material.wireframe === true ) {
state.setLineWidth( material.wireframeLinewidth * this.renderer.getPixelRatio() );
mode = gl.LINES;
} else {
mode = gl.TRIANGLES;
}
}
//
......@@ -482,6 +544,7 @@ class WebGLBackend extends Backend {
}
destroyTexture( texture ) {
this.textureUtils.destroyTexture( texture );
......@@ -794,100 +857,107 @@ class WebGLBackend extends Backend {
copyFramebufferToTexture( texture, renderContext ) {
const { gl } = this;
this.textureUtils.copyFramebufferToTexture( texture, renderContext );
const { textureGPU } = this.get( texture );
}
const width = texture.image.width;
const height = texture.image.height;
_setFramebuffer( renderContext ) {
gl.bindFramebuffer( gl.READ_FRAMEBUFFER, null );
const { gl, state } = this;
if ( texture.isDepthTexture ) {
let fb = null;
let currentFrameBuffer = null;
const fb = gl.createFramebuffer();
if ( renderContext.textures !== null ) {
gl.bindFramebuffer( gl.DRAW_FRAMEBUFFER, fb );
const renderTargetContextData = this.get( renderContext.renderTarget );
const { samples } = renderContext.renderTarget;
gl.framebufferTexture2D( gl.DRAW_FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, textureGPU, 0 );
fb = renderTargetContextData.framebuffer;
let msaaFb = renderTargetContextData.msaaFrameBuffer;
let depthRenderbuffer = renderTargetContextData.depthRenderbuffer;
gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, gl.DEPTH_BUFFER_BIT, gl.NEAREST );
gl.deleteFramebuffer( fb );
if ( fb === undefined ) {
fb = gl.createFramebuffer();
} else {
state.bindFramebuffer( gl.FRAMEBUFFER, fb );
gl.bindTexture( gl.TEXTURE_2D, textureGPU );
gl.copyTexSubImage2D( gl.TEXTURE_2D, 0, 0, 0, 0, 0, width, height );
const textures = renderContext.textures;
gl.bindTexture( gl.TEXTURE_2D, null );
for ( let i = 0; i < textures.length; i ++ ) {
}
const texture = textures[ i ];
const textureData = this.get( texture );
textureData.renderTarget = renderContext.renderTarget;
if ( texture.generateMipmaps ) this.generateMipmaps( texture );
const attachment = gl.COLOR_ATTACHMENT0 + i;
this._setFramebuffer( renderContext );
}
gl.framebufferTexture2D( gl.FRAMEBUFFER, attachment, gl.TEXTURE_2D, textureData.textureGPU, 0 );
_setFramebuffer( renderContext ) {
}
const { gl } = this;
if ( renderContext.depthTexture !== null ) {
if ( renderContext.textures !== null ) {
const textureData = this.get( renderContext.depthTexture );
const renderContextData = this.get( renderContext.renderTarget );
gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, textureData.textureGPU, 0 );
let fb = renderContextData.framebuffer;
}
if ( fb === undefined ) {
fb = gl.createFramebuffer();
renderTargetContextData.framebuffer = fb;
gl.bindFramebuffer( gl.FRAMEBUFFER, fb );
state.drawBuffers( renderContext, fb );
const textures = renderContext.textures;
}
const drawBuffers = [];
if ( samples > 0 ) {
for ( let i = 0; i < textures.length; i ++ ) {
if ( msaaFb === undefined ) {
const texture = textures[ i ];
const { textureGPU } = this.get( texture );
msaaFb = gl.createFramebuffer();
const attachment = gl.COLOR_ATTACHMENT0 + i;
state.bindFramebuffer( gl.FRAMEBUFFER, msaaFb );
gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i, gl.TEXTURE_2D, textureGPU, 0 );
// TODO For loop support MRT
const msaaRenderbuffer = gl.createRenderbuffer();
gl.bindRenderbuffer( gl.RENDERBUFFER, msaaRenderbuffer );
drawBuffers.push( attachment );
const texture = renderContext.textures[ 0 ];
const textureData = this.get( texture );
}
gl.renderbufferStorageMultisample( gl.RENDERBUFFER, samples, textureData.glInternalFormat, renderContext.width, renderContext.height );
gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, msaaRenderbuffer );
gl.drawBuffers( drawBuffers );
renderTargetContextData.msaaRenderbuffer = msaaRenderbuffer;
renderTargetContextData.msaaFrameBuffer = msaaFb;
if ( renderContext.depthTexture !== null ) {
if ( depthRenderbuffer === undefined ) {
const { textureGPU } = this.get( renderContext.depthTexture );
depthRenderbuffer = gl.createRenderbuffer();
this.textureUtils.setupRenderBufferStorage( depthRenderbuffer, renderContext );
gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, textureGPU, 0 );
renderTargetContextData.depthRenderbuffer = depthRenderbuffer;
}
}
renderContextData.framebuffer = fb;
currentFrameBuffer = renderTargetContextData.msaaFrameBuffer;
} else {
gl.bindFramebuffer( gl.FRAMEBUFFER, fb );
currentFrameBuffer = fb;
}
} else {
gl.bindFramebuffer( gl.FRAMEBUFFER, null );
}
state.bindFramebuffer( gl.FRAMEBUFFER, currentFrameBuffer );
}
}
......
......@@ -40,6 +40,14 @@ class WebGLState {
this.currentStencilZFail = null;
this.currentStencilZPass = null;
this.currentStencilMask = null;
this.currentLineWidth = null;
this.currentBoundFramebuffers = {};
this.currentDrawbuffers = new WeakMap();
this.maxTextures = this.gl.getParameter( this.gl.MAX_TEXTURE_IMAGE_UNITS );
this.currentTextureSlot = null;
this.currentBoundTextures = {};
if ( initialized === false ) {
......@@ -161,6 +169,21 @@ class WebGLState {
}
setLineWidth( width ) {
const { currentLineWidth, gl } = this;
if ( width !== currentLineWidth ) {
gl.lineWidth( width );
this.currentLineWidth = width;
}
}
setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) {
const { gl } = this;
......@@ -535,6 +558,180 @@ class WebGLState {
}
// framebuffer
bindFramebuffer( target, framebuffer ) {
const { gl, currentBoundFramebuffers } = this;
if ( currentBoundFramebuffers[ target ] !== framebuffer ) {
gl.bindFramebuffer( target, framebuffer );
currentBoundFramebuffers[ target ] = framebuffer;
// gl.DRAW_FRAMEBUFFER is equivalent to gl.FRAMEBUFFER
if ( target === gl.DRAW_FRAMEBUFFER ) {
currentBoundFramebuffers[ gl.FRAMEBUFFER ] = framebuffer;
}
if ( target === gl.FRAMEBUFFER ) {
currentBoundFramebuffers[ gl.DRAW_FRAMEBUFFER ] = framebuffer;
}
return true;
}
return false;
}
drawBuffers( renderContext, framebuffer ) {
const { gl } = this;
let drawBuffers = [];
let needsUpdate = false;
if ( renderContext.textures !== null ) {
drawBuffers = this.currentDrawbuffers.get( framebuffer );
if ( drawBuffers === undefined ) {
drawBuffers = [];
this.currentDrawbuffers.set( framebuffer, drawBuffers );
}
const textures = renderContext.textures;
if ( drawBuffers.length !== textures.length || drawBuffers[ 0 ] !== gl.COLOR_ATTACHMENT0 ) {
for ( let i = 0, il = textures.length; i < il; i ++ ) {
drawBuffers[ i ] = gl.COLOR_ATTACHMENT0 + i;
}
drawBuffers.length = textures.length;
needsUpdate = true;
}
} else {
if ( drawBuffers[ 0 ] !== gl.BACK ) {
drawBuffers[ 0 ] = gl.BACK;
needsUpdate = true;
}
}
if ( needsUpdate ) {
gl.drawBuffers( drawBuffers );
}
}
// texture
activeTexture( webglSlot ) {
const { gl, currentTextureSlot, maxTextures } = this;
if ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1;
if ( currentTextureSlot !== webglSlot ) {
gl.activeTexture( webglSlot );
this.currentTextureSlot = webglSlot;
}
}
bindTexture( webglType, webglTexture, webglSlot ) {
const { gl, currentTextureSlot, currentBoundTextures, maxTextures } = this;
if ( webglSlot === undefined ) {
if ( currentTextureSlot === null ) {
webglSlot = gl.TEXTURE0 + maxTextures - 1;
} else {
webglSlot = currentTextureSlot;
}
}
let boundTexture = currentBoundTextures[ webglSlot ];
if ( boundTexture === undefined ) {
boundTexture = { type: undefined, texture: undefined };
currentBoundTextures[ webglSlot ] = boundTexture;
}
if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) {
if ( currentTextureSlot !== webglSlot ) {
gl.activeTexture( webglSlot );
this.currentTextureSlot = webglSlot;
}
gl.bindTexture( webglType, webglTexture );
boundTexture.type = webglType;
boundTexture.texture = webglTexture;
}
}
unbindTexture() {
const { gl, currentTextureSlot, currentBoundTextures } = this;
const boundTexture = currentBoundTextures[ currentTextureSlot ];
if ( boundTexture !== undefined && boundTexture.type !== undefined ) {
gl.bindTexture( boundTexture.type, null );
boundTexture.type = undefined;
boundTexture.texture = undefined;
}
}
}
......
......@@ -225,11 +225,11 @@ class WebGLTextureUtils {
textureGPU = gl.createTexture();
gl.bindTexture( glTextureType, textureGPU );
backend.state.bindTexture( glTextureType, textureGPU );
gl.texParameteri( glTextureType, gl.TEXTURE_MIN_FILTER, gl.NEAREST );
gl.texParameteri( glTextureType, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
//gl.texImage2D( target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data );
// gl.texImage2D( glTextureType, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data );
defaultTextures[ glTextureType ] = textureGPU;
......@@ -255,7 +255,7 @@ class WebGLTextureUtils {
const textureGPU = gl.createTexture();
const glTextureType = this.getGLTextureType( texture );
gl.bindTexture( glTextureType, textureGPU );
backend.state.bindTexture( glTextureType, textureGPU );
gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );
gl.pixelStorei( gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha );
......@@ -264,8 +264,6 @@ class WebGLTextureUtils {
this.setTextureParameters( glTextureType, texture );
gl.bindTexture( glTextureType, textureGPU );
if ( texture.isDataArrayTexture ) {
gl.texStorage3D( gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, width, height, depth );
......@@ -311,7 +309,7 @@ class WebGLTextureUtils {
};
gl.bindTexture( glTextureType, textureGPU );
this.backend.state.bindTexture( glTextureType, textureGPU );
if ( texture.isCompressedTexture ) {
......@@ -408,7 +406,17 @@ class WebGLTextureUtils {
destroyTexture( texture ) {
const { gl, backend } = this;
const { textureGPU } = backend.get( texture );
const { textureGPU, renderTarget } = backend.get( texture );
// remove framebuffer reference
if ( renderTarget ) {
const renderContextData = backend.get( renderTarget );
renderContextData.framebuffer = undefined;
renderContextData.msaaFrameBuffer = undefined;
renderContextData.depthRenderbuffer = undefined;
}
gl.deleteTexture( textureGPU );
......@@ -416,6 +424,125 @@ class WebGLTextureUtils {
}
copyFramebufferToTexture( texture, renderContext ) {
const { gl } = this;
const { state } = this.backend;
const { textureGPU } = this.backend.get( texture );
const width = texture.image.width;
const height = texture.image.height;
state.bindFramebuffer( gl.READ_FRAMEBUFFER, null );
if ( texture.isDepthTexture ) {
const fb = gl.createFramebuffer();
gl.bindFramebuffer( gl.DRAW_FRAMEBUFFER, fb );
gl.framebufferTexture2D( gl.DRAW_FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, textureGPU, 0 );
gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, gl.DEPTH_BUFFER_BIT, gl.NEAREST );
gl.deleteFramebuffer( fb );
} else {
state.bindTexture( gl.TEXTURE_2D, textureGPU );
gl.copyTexSubImage2D( gl.TEXTURE_2D, 0, 0, 0, 0, 0, width, height );
state.unbindTexture();
}
if ( texture.generateMipmaps ) this.generateMipmaps( texture );
this.backend._setFramebuffer( renderContext );
}
// Setup storage for internal depth/stencil buffers and bind to correct framebuffer
setupRenderBufferStorage( renderbuffer, renderContext ) {
const { gl } = this;
const renderTarget = renderContext.renderTarget;
const { samples, depthTexture, depthBuffer, stencilBuffer, width, height } = renderTarget;
gl.bindRenderbuffer( gl.RENDERBUFFER, renderbuffer );
if ( depthBuffer && ! stencilBuffer ) {
let glInternalFormat = gl.DEPTH_COMPONENT24;
if ( samples > 0 ) {
if ( depthTexture && depthTexture.isDepthTexture ) {
if ( depthTexture.type === gl.FLOAT ) {
glInternalFormat = gl.DEPTH_COMPONENT32F;
}
}
gl.renderbufferStorageMultisample( gl.RENDERBUFFER, samples, glInternalFormat, width, height );
} else {
gl.renderbufferStorage( gl.RENDERBUFFER, glInternalFormat, width, height );
}
gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, renderbuffer );
} else if ( depthBuffer && stencilBuffer ) {
if ( samples > 0 ) {
gl.renderbufferStorageMultisample( gl.RENDERBUFFER, samples, gl.DEPTH24_STENCIL8, width, height );
} else {
gl.renderbufferStorage( gl.RENDERBUFFER, gl.DEPTH_STENCIL, width, height );
}
gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, renderbuffer );
} else {
const textures = renderContext.textures;
for ( let i = 0; i < textures.length; i ++ ) {
const texture = textures[ i ];
const { glInternalFormat } = this.get( texture );
if ( samples > 0 ) {
gl.renderbufferStorageMultisample( gl.RENDERBUFFER, samples, glInternalFormat, width, height );
} else {
gl.renderbufferStorage( gl.RENDERBUFFER, glInternalFormat, width, height );
}
}
}
gl.bindRenderbuffer( gl.RENDERBUFFER, null );
}
async copyTextureToBuffer( texture, x, y, width, height ) {
const { backend, gl } = this;
......
......@@ -15,10 +15,13 @@ import { getFormat } from '../utils/WebGPUTextureUtils.js';
import WGSLNodeParser from './WGSLNodeParser.js';
// GPUShaderStage is not defined in browsers not supporting WebGPU
const GPUShaderStage = window.GPUShaderStage;
const gpuShaderStageLib = {
'vertex': GPUShaderStage.VERTEX,
'fragment': GPUShaderStage.FRAGMENT,
'compute': GPUShaderStage.COMPUTE
'vertex': GPUShaderStage ? GPUShaderStage.VERTEX : 1,
'fragment': GPUShaderStage ? GPUShaderStage.FRAGMENT : 2,
'compute': GPUShaderStage ? GPUShaderStage.COMPUTE : 4
};
const supports = {
......
......@@ -102,7 +102,20 @@ class WebGPUPipelineUtils {
const primitiveState = this._getPrimitiveState( object, geometry, material );
const depthCompare = this._getDepthCompare( material );
const depthStencilFormat = utils.getCurrentDepthStencilFormat( renderObject.context );
const sampleCount = utils.getSampleCount( renderObject.context );
let sampleCount = utils.getSampleCount( renderObject.context );
if ( sampleCount > 1 ) {
// WebGPU only supports power-of-two sample counts and 2 is not a valid value
sampleCount = Math.pow( 2, Math.floor( Math.log2( sampleCount ) ) );
if ( sampleCount === 2 ) {
sampleCount = 4;
}
}
pipelineData.pipeline = device.createRenderPipeline( {
vertex: Object.assign( {}, vertexModule, { buffers: vertexBuffers } ),
......
......@@ -113,7 +113,21 @@ class WebGPUTextureUtils {
const dimension = this._getDimension( texture );
const format = texture.internalFormat || getFormat( texture, backend.device );
const sampleCount = options.sampleCount !== undefined ? options.sampleCount : 1;
let sampleCount = options.sampleCount !== undefined ? options.sampleCount : 1;
if ( sampleCount > 1 ) {
// WebGPU only supports power-of-two sample counts and 2 is not a valid value
sampleCount = Math.pow( 2, Math.floor( Math.log2( sampleCount ) ) );
if ( sampleCount === 2 ) {
sampleCount = 4;
}
}
const primarySampleCount = texture.isRenderTargetTexture ? 1 : sampleCount;
let usage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.COPY_SRC;
......
examples/screenshots/webgpu_multisampled_renderbuffers.jpg

154 KB

examples/screenshots/webgpu_portal.jpg

18.5 KB | W: 0px | H: 0px

examples/screenshots/webgpu_portal.jpg

18.5 KB | W: 0px | H: 0px

examples/screenshots/webgpu_portal.jpg
examples/screenshots/webgpu_portal.jpg
examples/screenshots/webgpu_portal.jpg
examples/screenshots/webgpu_portal.jpg
  • 2-up
  • Swipe
  • Onion skin
examples/screenshots/webgpu_postprocessing_afterimage.jpg

3.33 KB | W: 0px | H: 0px

examples/screenshots/webgpu_postprocessing_afterimage.jpg

16 KB | W: 0px | H: 0px

examples/screenshots/webgpu_postprocessing_afterimage.jpg
examples/screenshots/webgpu_postprocessing_afterimage.jpg
examples/screenshots/webgpu_postprocessing_afterimage.jpg
examples/screenshots/webgpu_postprocessing_afterimage.jpg
  • 2-up
  • Swipe
  • Onion skin
examples/screenshots/webgpu_skinning_instancing.jpg

42.1 KB | W: 0px | H: 0px

examples/screenshots/webgpu_skinning_instancing.jpg

41.1 KB | W: 0px | H: 0px

examples/screenshots/webgpu_skinning_instancing.jpg
examples/screenshots/webgpu_skinning_instancing.jpg
examples/screenshots/webgpu_skinning_instancing.jpg
examples/screenshots/webgpu_skinning_instancing.jpg
  • 2-up
  • Swipe
  • Onion skin
<html lang="en">
<head>
<title>three.js - WebGPU - Multisampled Renderbuffers</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link type="text/css" rel="stylesheet" href="main.css">
</head>
<body>
<div id="info">
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> WebGPU - Multisampled Renderbuffers
</div>
<script type="importmap">
{
"imports": {
"three": "../build/three.module.js",
"three/addons/": "./jsm/",
"three/nodes": "./jsm/nodes/Nodes.js"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import { texture, MeshBasicNodeMaterial, MeshPhongNodeMaterial } from 'three/nodes';
import WebGPU from 'three/addons/capabilities/WebGPU.js';
import WebGL from 'three/addons/capabilities/WebGL.js';
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
import WebGPURenderer from 'three/addons/renderers/webgpu/WebGPURenderer.js';
import QuadMesh from 'three/addons/objects/QuadMesh.js';
let camera, scene, renderer;
const mouse = new THREE.Vector2();
let quadMesh, renderTarget;
let box, box2;
const dpr = 1;
const params = {
animated: true,
samples: 4
};
const mat4 = new THREE.Matrix4();
const count = 50;
const fullRadius = 20; // Radius of the sphere
const halfRadius = 10; // Radius of the sphere
const positions = new Array( count ).fill().map( ( _, i ) => {
const radius = ( i % 2 === 0 ) ? fullRadius : halfRadius;
const phi = Math.acos( 2 * Math.random() - 1 ) - Math.PI / 2; // phi: latitude, range -π/2 to π/2
const theta = 2 * Math.PI * Math.random(); // theta: longitude, range 0 to 2π
return new THREE.Vector3(
radius * Math.cos( phi ) * Math.cos( theta ), // x
radius * Math.sin( phi ), // y
radius * Math.cos( phi ) * Math.sin( theta ) // z
);
} );
initGUI();
init();
function initGUI() {
const gui = new GUI();
gui.add( params, 'samples', 0, 4 ).step( 1 );
gui.add( params, 'animated', true );
}
function init() {
if ( WebGPU.isAvailable() === false && WebGL.isWebGL2Available() === false ) {
document.body.appendChild( WebGPU.getErrorMessage() );
throw new Error( 'No WebGPU or WebGL2 support' );
}
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.1, 50 );
camera.position.z = 3;
scene = new THREE.Scene();
scene.background = new THREE.Color( 0x111111 );
// textured mesh
const geometryBox = new THREE.BoxGeometry( 7, 7, 7, 12, 12, 12 );
const materialBox = new MeshPhongNodeMaterial();
const materialBoxInner = new MeshPhongNodeMaterial( { color: 0xff0000 } );
materialBox.wireframe = true;
//
box = new THREE.InstancedMesh( geometryBox, materialBox, count );
box2 = new THREE.InstancedMesh( geometryBox, materialBoxInner, count );
for ( let i = 0; i < count; i ++ ) {
box.setMatrixAt( i, mat4.identity().setPosition( positions[ i ] ) );
box2.setMatrixAt( i, mat4.multiplyScalar( 0.996 ).setPosition( positions[ i ] ) );
}
scene.add( box, box2 );
//
renderer = new WebGPURenderer( { antialias: true } );
renderer.setPixelRatio( dpr );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setAnimationLoop( animate );
document.body.appendChild( renderer.domElement );
renderTarget = new THREE.RenderTarget( window.innerWidth * dpr, window.innerHeight * dpr, {
samples: params.samples,
depthBuffer: true,
} );
window.addEventListener( 'mousemove', onWindowMouseMove );
window.addEventListener( 'resize', onWindowResize );
// FX
// modulate the final color based on the mouse position
const materialFX = new MeshBasicNodeMaterial();
materialFX.colorNode = texture( renderTarget.texture ).rgb;
quadMesh = new QuadMesh( materialFX );
}
function onWindowMouseMove( e ) {
mouse.x = e.offsetX / window.innerWidth;
mouse.y = e.offsetY / window.innerHeight;
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
renderTarget.setSize( window.innerWidth * dpr, window.innerHeight * dpr );
}
function animate() {
if ( params.animated ) {
box.rotation.x += 0.001;
box.rotation.y += 0.002;
box2.rotation.x += 0.001;
box2.rotation.y += 0.002;
}
renderTarget.samples = params.samples;
renderer.setRenderTarget( renderTarget );
renderer.render( scene, camera );
renderer.setRenderTarget( null );
quadMesh.render( renderer );
}
</script>
</body>
</html>
......@@ -29,8 +29,6 @@
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import WebGPU from 'three/addons/capabilities/WebGPU.js';
import WebGPURenderer from 'three/addons/renderers/webgpu/WebGPURenderer.js';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
......@@ -44,14 +42,6 @@
function init() {
if ( WebGPU.isAvailable() === false ) {
document.body.appendChild( WebGPU.getErrorMessage() );
throw new Error( 'No WebGPU support' );
}
//
sceneMain = new THREE.Scene();
......
......@@ -23,7 +23,6 @@
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
import WebGPU from 'three/addons/capabilities/WebGPU.js';
import WebGPURenderer from 'three/addons/renderers/webgpu/WebGPURenderer.js';
import PostProcessing from 'three/addons/renderers/common/PostProcessing.js';
import { pass } from 'three/nodes';
......@@ -43,14 +42,6 @@
function init() {
if ( WebGPU.isAvailable() === false ) {
document.body.appendChild( WebGPU.getErrorMessage() );
throw new Error( 'No WebGPU support' );
}
renderer = new WebGPURenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
......@@ -104,15 +95,8 @@
mesh.rotation.x += 0.0075;
mesh.rotation.y += 0.015;
if ( renderer.backend.isWebGPUBackend ) {
postProcessing.render();
} else {
renderer.render( scene, camera );
}
postProcessing.render();
}
......
......@@ -142,19 +142,17 @@
// post processing ( just for WebGPUBackend for now )
if ( renderer.backend.isWebGPUBackend ) {
const scenePass = pass( scene, camera );
const scenePassColor = scenePass.getTextureNode();
const scenePassDepth = scenePass.getDepthNode().remapClamp( .15, .3 );
const scenePass = pass( scene, camera );
const scenePassColor = scenePass.getTextureNode();
const scenePassDepth = scenePass.getDepthNode().remapClamp( .15, .3 );
const scenePassColorBlurred = scenePassColor.gaussianBlur();
scenePassColorBlurred.directionNode = scenePassDepth;
const scenePassColorBlurred = scenePassColor.gaussianBlur();
scenePassColorBlurred.directionNode = scenePassDepth;
postProcessing = new PostProcessing( renderer );
postProcessing.outputNode = scenePassColorBlurred;
postProcessing = new PostProcessing( renderer );
postProcessing.outputNode = scenePassColorBlurred;
}
// events
......@@ -177,15 +175,9 @@
if ( mixer ) mixer.update( delta );
if ( renderer.backend.isWebGPUBackend ) {
postProcessing.render();
} else {
postProcessing.render();
renderer.render( scene, camera );
}
}
......
......@@ -128,6 +128,7 @@ const exceptionList = [
'webgpu_sprites',
'webgpu_video_panorama',
'webgpu_postprocessing_afterimage',
'webgpu_multisampled_renderbuffers',
// WebGPURenderer: Unknown problem
'webgpu_backdrop_water',
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment