Browse Source

Editor: Added WebGPURenderer support. (#32842)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
mrdoob 1 month ago
parent
commit
582ff97d1c

+ 1 - 0
editor/index.html

@@ -16,6 +16,7 @@
 			{
 				"imports": {
 					"three": "../build/three.module.js",
+					"three/webgpu": "../build/three.webgpu.js",
 					"three/addons/": "../examples/jsm/",
 
 					"three/examples/": "../examples/",

+ 1 - 0
editor/js/Config.js

@@ -15,6 +15,7 @@ function Config() {
 		'project/editable': false,
 		'project/vr': false,
 
+		'project/renderer/type': 'WebGLRenderer',
 		'project/renderer/antialias': true,
 		'project/renderer/shadows': true,
 		'project/renderer/shadowType': 1, // PCF

+ 1 - 0
editor/js/Editor.js

@@ -723,6 +723,7 @@ Editor.prototype = {
 
 			metadata: {},
 			project: {
+				renderer: this.config.getKey( 'project/renderer/type' ),
 				shadows: this.config.getKey( 'project/renderer/shadows' ),
 				shadowType: this.config.getKey( 'project/renderer/shadowType' ),
 				toneMapping: this.config.getKey( 'project/renderer/toneMapping' ),

+ 1 - 1
editor/js/Menubar.Render.js

@@ -411,7 +411,7 @@ class RenderVideoDialog {
 		renderButton.onClick( async () => {
 
 			const player = new APP.Player();
-			player.load( editor.toJSON() );
+			await player.load( editor.toJSON() );
 			player.setPixelRatio( 1 );
 			player.setSize( videoWidth.getValue(), videoHeight.getValue() );
 

+ 2 - 2
editor/js/Player.js

@@ -27,11 +27,11 @@ function Player( editor ) {
 
 	} );
 
-	signals.startPlayer.add( function () {
+	signals.startPlayer.add( async function () {
 
 		container.setDisplay( '' );
 
-		player.load( editor.toJSON() );
+		await player.load( editor.toJSON() );
 		player.setSize( container.dom.clientWidth, container.dom.clientHeight );
 		player.play();
 

+ 31 - 2
editor/js/Sidebar.Project.Renderer.js

@@ -1,4 +1,5 @@
 import * as THREE from 'three';
+import { WebGPURenderer } from 'three/webgpu';
 
 import { UINumber, UIPanel, UIRow, UISelect, UIText } from './libs/ui.js';
 import { UIBoolean } from './libs/ui.three.js';
@@ -14,6 +15,20 @@ function SidebarProjectRenderer( editor ) {
 	const container = new UIPanel();
 	container.setBorderTop( '0px' );
 
+	// Renderer
+
+	const rendererRow = new UIRow();
+	container.add( rendererRow );
+
+	rendererRow.add( new UIText( strings.getKey( 'sidebar/project/renderer' ) ).setClass( 'Label' ) );
+
+	const rendererTypeSelect = new UISelect().setOptions( {
+		'WebGLRenderer': 'WebGL',
+		'WebGPURenderer': 'WebGPU'
+	} ).setWidth( '150px' ).onChange( createRenderer );
+	rendererTypeSelect.setValue( config.getKey( 'project/renderer/type' ) );
+	rendererRow.add( rendererTypeSelect );
+
 	// Antialias
 
 	const antialiasRow = new UIRow();
@@ -89,9 +104,22 @@ function SidebarProjectRenderer( editor ) {
 
 	//
 
-	function createRenderer() {
+	async function createRenderer() {
+
+		const rendererType = rendererTypeSelect.getValue();
+		const antialias = antialiasBoolean.getValue();
+
+		if ( rendererType === 'WebGPURenderer' ) {
+
+			currentRenderer = new WebGPURenderer( { antialias: antialias, logarithmicDepthBuffer: true } );
+			await currentRenderer.init();
+
+		} else {
+
+			currentRenderer = new THREE.WebGLRenderer( { antialias: antialias, logarithmicDepthBuffer: true } );
+
+		}
 
-		currentRenderer = new THREE.WebGLRenderer( { antialias: antialiasBoolean.getValue(), logarithmicDepthBuffer: true } );
 		currentRenderer.shadowMap.enabled = shadowsBoolean.getValue();
 		currentRenderer.shadowMap.type = parseFloat( shadowTypeSelect.getValue() );
 		currentRenderer.toneMapping = parseFloat( toneMappingSelect.getValue() );
@@ -127,6 +155,7 @@ function SidebarProjectRenderer( editor ) {
 	signals.rendererUpdated.add( function () {
 
 		config.setKey(
+			'project/renderer/type', rendererTypeSelect.getValue(),
 			'project/renderer/antialias', antialiasBoolean.getValue(),
 			'project/renderer/shadows', shadowsBoolean.getValue(),
 			'project/renderer/shadowType', parseFloat( shadowTypeSelect.getValue() ),

+ 6 - 0
editor/js/Strings.js

@@ -351,6 +351,7 @@ function Strings( config ) {
 			'sidebar/script/remove': 'حذف',
 
 			'sidebar/project': 'پروژه ها',
+			'sidebar/project/renderer': 'رندرر',
 			'sidebar/project/antialias': 'آنتی الآیس',
 			'sidebar/project/shadows': 'سایه ها',
 			'sidebar/project/toneMapping': 'تون مپینگ',
@@ -766,6 +767,7 @@ function Strings( config ) {
 			'sidebar/script/remove': 'Remove',
 
 			'sidebar/project': 'Project',
+			'sidebar/project/renderer': 'Renderer',
 			'sidebar/project/antialias': 'Antialias',
 			'sidebar/project/shadows': 'Shadows',
 			'sidebar/project/toneMapping': 'Tonemapping',
@@ -1182,6 +1184,7 @@ function Strings( config ) {
 			'sidebar/script/remove': 'Supprimer',
 
 			'sidebar/project': 'Projet',
+			'sidebar/project/renderer': 'Moteur',
 			'sidebar/project/antialias': 'Anticrénelage',
 			'sidebar/project/shadows': 'Ombres',
 			'sidebar/project/toneMapping': 'Mappage des nuances',
@@ -1598,6 +1601,7 @@ function Strings( config ) {
 			'sidebar/script/remove': '删除',
 
 			'sidebar/project': '项目',
+			'sidebar/project/renderer': '渲染器',
 			'sidebar/project/antialias': '抗锯齿',
 			'sidebar/project/shadows': '阴影',
 			'sidebar/project/toneMapping': '色调映射',
@@ -2014,6 +2018,7 @@ function Strings( config ) {
 			'sidebar/script/remove': '削除',
 
 			'sidebar/project': 'プロジェクト',
+			'sidebar/project/renderer': 'レンダラー',
 			'sidebar/project/antialias': 'アンチエイリアス',
 			'sidebar/project/shadows': 'シャドウ',
 			'sidebar/project/toneMapping': 'トーンマッピング',
@@ -2429,6 +2434,7 @@ function Strings( config ) {
 			'sidebar/script/remove': '삭제',
 
 			'sidebar/project': '프로젝트',
+			'sidebar/project/renderer': '렌더러',
 			'sidebar/project/antialias': '안티앨리어싱',
 			'sidebar/project/shadows': '그림자',
 			'sidebar/project/toneMapping': '톤 매핑',

+ 40 - 13
editor/js/Viewport.js

@@ -1,4 +1,5 @@
 import * as THREE from 'three';
+import { PMREMGenerator } from 'three/webgpu';
 
 import { TransformControls } from 'three/addons/controls/TransformControls.js';
 
@@ -291,7 +292,7 @@ function Viewport( editor ) {
 	signals.editorCleared.add( function () {
 
 		controls.center.set( 0, 0, 0 );
-		pathtracer.reset();
+		if ( pathtracer ) pathtracer.reset();
 
 		initPT();
 
@@ -342,8 +343,18 @@ function Viewport( editor ) {
 		if ( renderer !== null ) {
 
 			renderer.setAnimationLoop( null );
+
+			try {
+
+				pmremGenerator.dispose();
+
+			} catch ( e ) {
+
+				console.warn( 'PMREMGenerator dispose error:', e );
+
+			}
+
 			renderer.dispose();
-			pmremGenerator.dispose();
 
 			container.dom.removeChild( renderer.domElement );
 
@@ -377,13 +388,25 @@ function Viewport( editor ) {
 		renderer.setPixelRatio( window.devicePixelRatio );
 		renderer.setSize( container.dom.offsetWidth, container.dom.offsetHeight );
 
-		pmremGenerator = new THREE.PMREMGenerator( renderer );
-		pmremGenerator.compileEquirectangularShader();
+		if ( renderer.isWebGLRenderer ) {
+
+			pmremGenerator = new THREE.PMREMGenerator( renderer );
+			pmremGenerator.compileEquirectangularShader();
+
+			pathtracer = new ViewportPathtracer( renderer );
 
-		pathtracer = new ViewportPathtracer( renderer );
+		} else {
+
+			pmremGenerator = new PMREMGenerator( renderer );
+
+			pathtracer = null;
+
+		}
 
 		container.dom.appendChild( renderer.domElement );
 
+		signals.sceneEnvironmentChanged.dispatch( editor.environmentType );
+
 		render();
 
 	} );
@@ -403,7 +426,7 @@ function Viewport( editor ) {
 
 	signals.cameraChanged.add( function () {
 
-		pathtracer.reset();
+		if ( pathtracer ) pathtracer.reset();
 
 		render();
 
@@ -682,7 +705,7 @@ function Viewport( editor ) {
 		switch ( viewportShading ) {
 
 			case 'realistic':
-				pathtracer.init( scene, editor.viewportCamera );
+				if ( pathtracer ) pathtracer.init( scene, editor.viewportCamera );
 				break;
 
 			case 'solid':
@@ -709,8 +732,10 @@ function Viewport( editor ) {
 
 		updateAspectRatio();
 
+		if ( renderer === null ) return;
+
 		renderer.setSize( container.dom.offsetWidth, container.dom.offsetHeight );
-		pathtracer.setSize( container.dom.offsetWidth, container.dom.offsetHeight );
+		if ( pathtracer ) pathtracer.setSize( container.dom.offsetWidth, container.dom.offsetHeight );
 
 		render();
 
@@ -831,7 +856,7 @@ function Viewport( editor ) {
 
 	function initPT() {
 
-		if ( editor.viewportShading === 'realistic' ) {
+		if ( pathtracer && editor.viewportShading === 'realistic' ) {
 
 			pathtracer.init( scene, editor.viewportCamera );
 
@@ -841,7 +866,7 @@ function Viewport( editor ) {
 
 	function updatePTBackground() {
 
-		if ( editor.viewportShading === 'realistic' ) {
+		if ( pathtracer && editor.viewportShading === 'realistic' ) {
 
 			pathtracer.setBackground( scene.background, scene.backgroundBlurriness );
 
@@ -851,7 +876,7 @@ function Viewport( editor ) {
 
 	function updatePTEnvironment() {
 
-		if ( editor.viewportShading === 'realistic' ) {
+		if ( pathtracer && editor.viewportShading === 'realistic' ) {
 
 			pathtracer.setEnvironment( scene.environment );
 
@@ -861,7 +886,7 @@ function Viewport( editor ) {
 
 	function updatePTMaterials() {
 
-		if ( editor.viewportShading === 'realistic' ) {
+		if ( pathtracer && editor.viewportShading === 'realistic' ) {
 
 			pathtracer.updateMaterials();
 
@@ -871,7 +896,7 @@ function Viewport( editor ) {
 
 	function updatePT() {
 
-		if ( editor.viewportShading === 'realistic' ) {
+		if ( pathtracer && editor.viewportShading === 'realistic' ) {
 
 			pathtracer.update();
 			editor.signals.pathTracerUpdated.dispatch( pathtracer.getSamples() );
@@ -887,6 +912,8 @@ function Viewport( editor ) {
 
 	function render() {
 
+		if ( renderer === null ) return;
+
 		startTime = performance.now();
 
 		renderer.setViewport( 0, 0, container.dom.offsetWidth, container.dom.offsetHeight );

+ 37 - 7
editor/js/libs/app.js

@@ -2,8 +2,7 @@ const APP = {
 
 	Player: function () {
 
-		const renderer = new THREE.WebGLRenderer( { antialias: true, logarithmicDepthBuffer: true } );
-		renderer.setPixelRatio( window.devicePixelRatio ); // TODO: Use player.setPixelRatio()
+		let renderer;
 
 		const loader = new THREE.ObjectLoader();
 		let camera, scene;
@@ -11,23 +10,46 @@ const APP = {
 		let events = {};
 
 		const dom = document.createElement( 'div' );
-		dom.appendChild( renderer.domElement );
 
 		this.dom = dom;
-		this.canvas = renderer.domElement;
 
 		this.width = 500;
 		this.height = 500;
 
-		this.load = function ( json ) {
+		this.load = async function ( json ) {
 
 			const project = json.project;
 
+			// Create renderer based on project settings
+
+			if ( renderer !== undefined ) {
+
+				renderer.dispose();
+				dom.removeChild( renderer.domElement );
+
+			}
+
+			if ( project.renderer === 'WebGPURenderer' ) {
+
+				const { WebGPURenderer } = await import( 'three/webgpu' );
+				renderer = new WebGPURenderer( { antialias: true, logarithmicDepthBuffer: true } );
+				await renderer.init();
+
+			} else {
+
+				renderer = new THREE.WebGLRenderer( { antialias: true, logarithmicDepthBuffer: true } );
+
+			}
+
+			renderer.setPixelRatio( window.devicePixelRatio );
+
 			if ( project.shadows !== undefined ) renderer.shadowMap.enabled = project.shadows;
 			if ( project.shadowType !== undefined ) renderer.shadowMap.type = project.shadowType;
 			if ( project.toneMapping !== undefined ) renderer.toneMapping = project.toneMapping;
 			if ( project.toneMappingExposure !== undefined ) renderer.toneMappingExposure = project.toneMappingExposure;
 
+			dom.appendChild( renderer.domElement );
+
 			this.setScene( loader.parse( json.scene ) );
 			this.setCamera( loader.parse( json.camera ) );
 
@@ -129,7 +151,11 @@ const APP = {
 
 			}
 
-			renderer.setSize( width, height );
+			if ( renderer ) {
+
+				renderer.setSize( width, height );
+
+			}
 
 		};
 
@@ -205,7 +231,11 @@ const APP = {
 
 		this.dispose = function () {
 
-			renderer.dispose();
+			if ( renderer ) {
+
+				renderer.dispose();
+
+			}
 
 			camera = undefined;
 			scene = undefined;

+ 4 - 3
editor/js/libs/app/index.html

@@ -21,7 +21,8 @@
 		<script type="importmap">
 			{
 				"imports": {
-					"three": "./js/three.module.js"
+					"three": "./js/three.module.js",
+					"three/webgpu": "./js/three.webgpu.js"
 				}
 			}
 		</script>
@@ -33,10 +34,10 @@
 			window.THREE = THREE; // Used by APP Scripts.
 
 			var loader = new THREE.FileLoader();
-			loader.load( 'app.json', function ( text ) {
+			loader.load( 'app.json', async function ( text ) {
 
 				var player = new APP.Player();
-				player.load( JSON.parse( text ) );
+				await player.load( JSON.parse( text ) );
 				player.setSize( window.innerWidth, window.innerHeight );
 				player.play();
 

粤ICP备19079148号