浏览代码

Improved HTMLTexture support. (#33400)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
mrdoob 3 周之前
父节点
当前提交
87374ef822

二进制
examples/screenshots/webgl_materials_texture_html.jpg


二进制
examples/screenshots/webgpu_materials_texture_html.jpg


+ 10 - 7
examples/webgl_materials_texture_html.html

@@ -60,7 +60,8 @@
 			{
 				"imports": {
 					"three": "../build/three.module.js",
-					"three/addons/": "./jsm/"
+					"three/addons/": "./jsm/",
+					"three-html-render/polyfill": "https://cdn.jsdelivr.net/npm/three-html-render/dist/polyfill.mjs"
 				}
 			}
 		</script>
@@ -68,10 +69,18 @@
 		<script type="module">
 
 			import * as THREE from 'three';
+			import { installHtmlInCanvasPolyfill } from 'three-html-render/polyfill';
 			import { RoundedBoxGeometry } from 'three/addons/geometries/RoundedBoxGeometry.js';
 			import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js';
 			import { InteractionManager } from 'three/addons/interaction/InteractionManager.js';
 
+			if ( ! ( 'requestPaint' in HTMLCanvasElement.prototype ) ) {
+
+				installHtmlInCanvasPolyfill();
+				info.innerHTML += '<br><a href="https://github.com/WICG/html-in-canvas" target="_blank">HTML-in-Canvas API</a> not available. Using <a href="https://github.com/repalash/three-html-render" target="_blank">polyfill</a>.';
+
+			}
+
 			let camera, scene, renderer, mesh, interactions;
 
 			init();
@@ -80,12 +89,6 @@
 
 				renderer = new THREE.WebGLRenderer( { antialias: true } );
 
-				if ( renderer.getContext().texElementImage2D === undefined ) {
-
-					info.innerHTML += '<br>This browser does not support the <a href="https://github.com/WICG/html-in-canvas" target="_blank">HTML-in-Canvas API</a>.';
-
-				}
-
 				renderer.toneMapping = THREE.NeutralToneMapping;
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth, window.innerHeight );

+ 10 - 7
examples/webgpu_materials_texture_html.html

@@ -61,7 +61,8 @@
 				"imports": {
 					"three": "../build/three.webgpu.js",
 					"three/addons/": "./jsm/",
-					"three/tsl": "../build/three.tsl.js"
+					"three/tsl": "../build/three.tsl.js",
+					"three-html-render/polyfill": "https://cdn.jsdelivr.net/npm/three-html-render/dist/polyfill.mjs"
 				}
 			}
 		</script>
@@ -69,10 +70,18 @@
 		<script type="module">
 
 			import * as THREE from 'three';
+			import { installHtmlInCanvasPolyfill } from 'three-html-render/polyfill';
 			import { RoundedBoxGeometry } from 'three/addons/geometries/RoundedBoxGeometry.js';
 			import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js';
 			import { InteractionManager } from 'three/addons/interaction/InteractionManager.js';
 
+			if ( ! ( 'requestPaint' in HTMLCanvasElement.prototype ) ) {
+
+				installHtmlInCanvasPolyfill();
+				info.innerHTML += '<br><a href="https://github.com/WICG/html-in-canvas" target="_blank">HTML-in-Canvas API</a> not available. Using <a href="https://github.com/repalash/three-html-render" target="_blank">polyfill</a>.';
+
+			}
+
 			let camera, scene, renderer, mesh, interactions;
 
 			init();
@@ -82,12 +91,6 @@
 				renderer = new THREE.WebGPURenderer( { antialias: true } );
 				await renderer.init();
 
-				if ( ! ( 'requestPaint' in renderer.domElement ) ) {
-
-					info.innerHTML += '<br>This browser does not support the <a href="https://github.com/WICG/html-in-canvas" target="_blank">HTML-in-Canvas API</a>.';
-
-				}
-
 				renderer.toneMapping = THREE.NeutralToneMapping;
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth, window.innerHeight );

+ 35 - 0
src/renderers/common/Textures.js

@@ -48,6 +48,13 @@ class Textures extends DataMap {
 		 */
 		this.info = info;
 
+		/**
+		 * A set of HTMLTextures that need paint updates.
+		 *
+		 * @type {Set<HTMLTexture>}
+		 */
+		this._htmlTextures = new Set();
+
 	}
 
 	/**
@@ -240,6 +247,32 @@ class Textures extends DataMap {
 
 				}
 
+				// Set up shared paint callback for all HTMLTextures.
+
+				if ( this._htmlTextures.size === 0 ) {
+
+					const htmlTextures = this._htmlTextures;
+
+					canvas.onpaint = ( event ) => {
+
+						const changed = event && event.changedElements;
+
+						for ( const t of htmlTextures ) {
+
+							if ( ! changed || changed.includes( t.image ) ) {
+
+								t.needsUpdate = true;
+
+							}
+
+						}
+
+					};
+
+				}
+
+				this._htmlTextures.add( texture );
+
 			}
 
 		}
@@ -581,6 +614,8 @@ class Textures extends DataMap {
 
 			}
 
+			this._htmlTextures.delete( texture );
+
 			this.delete( texture );
 
 			this.info.destroyTexture( texture );

+ 7 - 0
src/renderers/webgl-fallback/utils/WebGLTextureUtils.js

@@ -634,6 +634,13 @@ class WebGLTextureUtils {
 
 			gl.texImage2D( glTextureType, 0, glInternalFormat, glFormat, glType, options.image );
 
+		} else if ( texture.isHTMLTexture ) {
+
+			if ( typeof gl.texElementImage2D === 'function' ) {
+
+				gl.texElementImage2D( gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, options.image );
+
+			}
 
 		} else {
 

+ 1 - 1
src/renderers/webgl/WebGLTextures.js

@@ -1256,7 +1256,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
 
 			} else if ( texture.isHTMLTexture ) {
 
-				if ( 'texElement2D' in _gl ) {
+				if ( 'texElementImage2D' in _gl ) {
 
 					const canvas = _gl.canvas;
 

+ 1 - 48
src/renderers/webgpu/utils/WebGPUTextureUtils.js

@@ -95,13 +95,6 @@ class WebGPUTextureUtils {
 		 */
 		this._samplerCache = new Map();
 
-		/**
-		 * A set of HTMLTextures that need paint updates.
-		 *
-		 * @type {Set<HTMLTexture>}
-		 */
-		this._htmlTextures = new Set();
-
 	}
 
 	/**
@@ -364,8 +357,6 @@ class WebGPUTextureUtils {
 
 		if ( textureData.msaaTexture !== undefined ) textureData.msaaTexture.destroy();
 
-		this._htmlTextures.delete( texture );
-
 		backend.delete( texture );
 
 	}
@@ -583,14 +574,10 @@ class WebGPUTextureUtils {
 
 			if ( typeof device.queue.copyElementImageToTexture !== 'function' ) return;
 
-			// Set up paint callback if not already done.
+			// Skip the first frame — the element needs a paint record first.
 			if ( ! textureData.hasPaintCallback ) {
 
 				textureData.hasPaintCallback = true;
-
-				this._addHTMLTexture( texture );
-
-				// Wait for the browser to paint the element before uploading.
 				canvas.requestPaint();
 				return;
 
@@ -700,46 +687,12 @@ class WebGPUTextureUtils {
 
 	}
 
-	/**
-	 * Registers an HTMLTexture for paint updates.
-	 * Sets up a single shared `onpaint` handler on the canvas
-	 * that notifies all registered HTMLTextures.
-	 *
-	 * @private
-	 * @param {HTMLTexture} texture - The HTMLTexture to register.
-	 */
-	_addHTMLTexture( texture ) {
-
-		this._htmlTextures.add( texture );
-
-		const canvas = this.backend.renderer.domElement;
-		const htmlTextures = this._htmlTextures;
-
-		canvas.onpaint = ( event ) => {
-
-			const changed = event.changedElements;
-
-			for ( const t of htmlTextures ) {
-
-				if ( changed.includes( t.image ) ) {
-
-					t.needsUpdate = true;
-
-				}
-
-			}
-
-		};
-
-	}
-
 	/**
 	 * Frees all internal resources.
 	 */
 	dispose() {
 
 		this._samplerCache.clear();
-		this._htmlTextures.clear();
 
 	}
 

+ 4 - 0
test/e2e/puppeteer.js

@@ -17,6 +17,10 @@ const exceptionList = [
 	'webgl_materials_envmaps_hdr', 		// 1 min
 	'webgpu_water', 					// 1 min
 
+	// Requires HTML-in-Canvas API
+	'webgl_materials_texture_html',
+	'webgpu_materials_texture_html',
+
 	// Black screen
 	'webgpu_postprocessing_ao',
 	'webgpu_postprocessing_dof',

粤ICP备19079148号