Kaynağa Gözat

CubeTextureNode: Support `CubeRefractionMapping`. (#28823)

* CubeTextureNode: Support `CubeRefractionMapping`.

* fix cache key mapping

* Clean up.

* E2E: Add example to exception list.

---------

Co-authored-by: sunag <[email protected]>
Michael Herzog 1 yıl önce
ebeveyn
işleme
ac95b71239

+ 1 - 0
examples/files.json

@@ -344,6 +344,7 @@
 		"webgpu_loader_gltf_transmission",
 		"webgpu_loader_gltf_transmission",
 		"webgpu_loader_materialx",
 		"webgpu_loader_materialx",
 		"webgpu_materials",
 		"webgpu_materials",
+		"webgpu_materials_basic",
 		"webgpu_materials_displacementmap",
 		"webgpu_materials_displacementmap",
 		"webgpu_materials_lightmap",
 		"webgpu_materials_lightmap",
 		"webgpu_materials_matcap",
 		"webgpu_materials_matcap",

BIN
examples/screenshots/webgpu_materials_basic.jpg


+ 169 - 0
examples/webgpu_materials_basic.html

@@ -0,0 +1,169 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgpu - material - basic</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>
+		<script type="importmap">
+			{
+				"imports": {
+					"three": "../build/three.webgpu.js",
+					"three/tsl": "../build/three.webgpu.js",
+					"three/addons/": "./jsm/"
+				}
+			}
+		</script>
+
+		<script type="module">
+
+			import * as THREE from 'three';
+
+			import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
+
+			let camera, scene, renderer;
+
+			const spheres = [];
+
+			let mouseX = 0;
+			let mouseY = 0;
+
+			let windowHalfX = window.innerWidth / 2;
+			let windowHalfY = window.innerHeight / 2;
+
+			const params = {
+				color: '#ffffff',
+				mapping: THREE.CubeReflectionMapping,
+				refractionRatio: 0.98,
+				transparent: false,
+				opacity: 1
+			};
+
+			const mappings = { ReflectionMapping: THREE.CubeReflectionMapping, RefractionMapping: THREE.CubeRefractionMapping };
+
+			document.addEventListener( 'mousemove', onDocumentMouseMove );
+
+			init();
+
+			function init() {
+
+				camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.01, 100 );
+				camera.position.z = 3;
+
+				const path = './textures/cube/pisa/';
+				const format = '.png';
+				const urls = [
+					path + 'px' + format, path + 'nx' + format,
+					path + 'py' + format, path + 'ny' + format,
+					path + 'pz' + format, path + 'nz' + format
+				];
+
+				const textureCube = new THREE.CubeTextureLoader().load( urls );
+
+				scene = new THREE.Scene();
+				scene.background = textureCube;
+
+				const geometry = new THREE.SphereGeometry( 0.1, 32, 16 );
+				const material = new THREE.MeshBasicMaterial( { color: 0xffffff, envMap: textureCube } );
+
+				for ( let i = 0; i < 500; i ++ ) {
+
+					const mesh = new THREE.Mesh( geometry, material );
+
+					mesh.position.x = Math.random() * 10 - 5;
+					mesh.position.y = Math.random() * 10 - 5;
+					mesh.position.z = Math.random() * 10 - 5;
+
+					mesh.scale.x = mesh.scale.y = mesh.scale.z = Math.random() * 3 + 1;
+
+					scene.add( mesh );
+
+					spheres.push( mesh );
+
+				}
+
+				//
+
+				renderer = new THREE.WebGPURenderer();
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				renderer.setAnimationLoop( animate );
+				document.body.appendChild( renderer.domElement );
+
+				//
+
+				const gui = new GUI( { width: 300 } );
+
+				gui.addColor( params, 'color' ).onChange( ( value ) => material.color.set( value ) );
+				gui.add( params, 'mapping', mappings ).onChange( ( value ) => {
+
+					textureCube.mapping = value;
+					material.needsUpdate = true;
+			
+				} );
+				gui.add( params, 'refractionRatio' ).min( 0.0 ).max( 1.0 ).step( 0.01 ).onChange( ( value ) => material.refractionRatio = value );
+				gui.add( params, 'transparent' ).onChange( ( value ) => {
+			
+					material.transparent = value;
+					material.needsUpdate = true;
+
+				} );
+				gui.add( params, 'opacity' ).min( 0.0 ).max( 1.0 ).step( 0.01 ).onChange( ( value ) => material.opacity = value );
+				gui.open();
+
+				//
+
+				window.addEventListener( 'resize', onWindowResize );
+
+			}
+
+			function onWindowResize() {
+
+				windowHalfX = window.innerWidth / 2;
+				windowHalfY = window.innerHeight / 2;
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+
+			}
+
+			function onDocumentMouseMove( event ) {
+
+				mouseX = ( event.clientX - windowHalfX ) / 100;
+				mouseY = ( event.clientY - windowHalfY ) / 100;
+
+			}
+
+			//
+
+			function animate() {
+
+				const timer = 0.0001 * Date.now();
+
+				camera.position.x += ( mouseX - camera.position.x ) * .05;
+				camera.position.y += ( - mouseY - camera.position.y ) * .05;
+
+				camera.lookAt( scene.position );
+
+				for ( let i = 0, il = spheres.length; i < il; i ++ ) {
+
+					const sphere = spheres[ i ];
+
+					sphere.position.x = 5 * Math.cos( timer + i );
+					sphere.position.y = 5 * Math.sin( timer + i * 1.1 );
+
+				}
+
+				renderer.render( scene, camera );
+
+			}
+
+		</script>
+
+	</body>
+</html>

+ 19 - 3
src/nodes/accessors/CubeTextureNode.js

@@ -1,9 +1,9 @@
 import TextureNode from './TextureNode.js';
 import TextureNode from './TextureNode.js';
-import { reflectVector } from './ReflectVectorNode.js';
+import { reflectVector, refractVector } from './ReflectVectorNode.js';
 import { addNodeClass } from '../core/Node.js';
 import { addNodeClass } from '../core/Node.js';
 import { addNodeElement, nodeProxy, vec3 } from '../shadernode/ShaderNode.js';
 import { addNodeElement, nodeProxy, vec3 } from '../shadernode/ShaderNode.js';
 
 
-import { WebGPUCoordinateSystem } from '../../constants.js';
+import { CubeReflectionMapping, CubeRefractionMapping, WebGPUCoordinateSystem } from '../../constants.js';
 
 
 class CubeTextureNode extends TextureNode {
 class CubeTextureNode extends TextureNode {
 
 
@@ -23,7 +23,23 @@ class CubeTextureNode extends TextureNode {
 
 
 	getDefaultUV() {
 	getDefaultUV() {
 
 
-		return reflectVector;
+		const texture = this.value;
+
+		if ( texture.mapping === CubeReflectionMapping ) {
+
+			return reflectVector;
+
+		} else if ( texture.mapping === CubeRefractionMapping ) {
+
+			return refractVector;
+
+		} else {
+
+			console.error( 'THREE.CubeTextureNode: Mapping "%s" not supported.', texture.mapping );
+
+			return vec3( 0, 0, 0 );
+
+		}
 
 
 	}
 	}
 
 

+ 6 - 0
src/nodes/accessors/MaterialNode.js

@@ -319,6 +319,10 @@ class MaterialNode extends Node {
 
 
 			node = this.getFloat( scope );
 			node = this.getFloat( scope );
 
 
+		} else if ( scope === MaterialNode.REFRACTION_RATIO ) {
+
+			node = this.getFloat( scope );
+
 		} else if ( scope === MaterialNode.LIGHT_MAP ) {
 		} else if ( scope === MaterialNode.LIGHT_MAP ) {
 
 
 			node = this.getTexture( scope ).rgb.mul( this.getFloat( 'lightMapIntensity' ) );
 			node = this.getTexture( scope ).rgb.mul( this.getFloat( 'lightMapIntensity' ) );
@@ -378,6 +382,7 @@ MaterialNode.POINT_WIDTH = 'pointWidth';
 MaterialNode.DISPERSION = 'dispersion';
 MaterialNode.DISPERSION = 'dispersion';
 MaterialNode.LIGHT_MAP = 'light';
 MaterialNode.LIGHT_MAP = 'light';
 MaterialNode.AO_MAP = 'ao';
 MaterialNode.AO_MAP = 'ao';
+MaterialNode.REFRACTION_RATIO = 'refractionRatio';
 
 
 export default MaterialNode;
 export default MaterialNode;
 
 
@@ -420,6 +425,7 @@ export const materialPointWidth = nodeImmutable( MaterialNode, MaterialNode.POIN
 export const materialDispersion = nodeImmutable( MaterialNode, MaterialNode.DISPERSION );
 export const materialDispersion = nodeImmutable( MaterialNode, MaterialNode.DISPERSION );
 export const materialLightMap = nodeImmutable( MaterialNode, MaterialNode.LIGHT_MAP );
 export const materialLightMap = nodeImmutable( MaterialNode, MaterialNode.LIGHT_MAP );
 export const materialAOMap = nodeImmutable( MaterialNode, MaterialNode.AO_MAP );
 export const materialAOMap = nodeImmutable( MaterialNode, MaterialNode.AO_MAP );
+export const materialRefractionRatio = nodeImmutable( MaterialNode, MaterialNode.REFRACTION_RATIO );
 export const materialAnisotropyVector = uniform( new Vector2() ).onReference( function ( frame ) {
 export const materialAnisotropyVector = uniform( new Vector2() ).onReference( function ( frame ) {
 
 
 	return frame.material;
 	return frame.material;

+ 4 - 0
src/nodes/accessors/ReflectVectorNode.js

@@ -1,6 +1,10 @@
 import { cameraViewMatrix } from './CameraNode.js';
 import { cameraViewMatrix } from './CameraNode.js';
 import { transformedNormalView } from './NormalNode.js';
 import { transformedNormalView } from './NormalNode.js';
 import { positionViewDirection } from './PositionNode.js';
 import { positionViewDirection } from './PositionNode.js';
+import { materialRefractionRatio } from './MaterialNode.js';
 
 
 export const reflectView = /*#__PURE__*/ positionViewDirection.negate().reflect( transformedNormalView );
 export const reflectView = /*#__PURE__*/ positionViewDirection.negate().reflect( transformedNormalView );
+export const refractView = /*#__PURE__*/ positionViewDirection.negate().refract( transformedNormalView, materialRefractionRatio );
+
 export const reflectVector = /*#__PURE__*/ reflectView.transformDirection( cameraViewMatrix ).toVar( 'reflectVector' );
 export const reflectVector = /*#__PURE__*/ reflectView.transformDirection( cameraViewMatrix ).toVar( 'reflectVector' );
+export const refractVector = /*#__PURE__*/ refractView.transformDirection( cameraViewMatrix ).toVar( 'reflectVector' );

+ 22 - 4
src/renderers/common/RenderObject.js

@@ -193,18 +193,36 @@ export default class RenderObject {
 
 
 			if ( /^(is[A-Z]|_)|^(visible|version|uuid|name|opacity|userData)$/.test( property ) ) continue;
 			if ( /^(is[A-Z]|_)|^(visible|version|uuid|name|opacity|userData)$/.test( property ) ) continue;
 
 
-			let value = material[ property ];
+			const value = material[ property ];
+
+			let valueKey;
 
 
 			if ( value !== null ) {
 			if ( value !== null ) {
 
 
 				const type = typeof value;
 				const type = typeof value;
 
 
-				if ( type === 'number' ) value = value !== 0 ? '1' : '0'; // Convert to on/off, important for clearcoat, transmission, etc
-				else if ( type === 'object' ) value = '{}';
+				if ( type === 'number' ) valueKey = value !== 0 ? '1' : '0'; // Convert to on/off, important for clearcoat, transmission, etc
+				else if ( type === 'object' ) {
+
+					valueKey = '{';
+
+					if ( value.isTexture ) {
+
+						valueKey += value.mapping;
+
+					}
+
+					valueKey += '}';
+
+				}
+
+			} else {
+
+				valueKey = String( value );
 
 
 			}
 			}
 
 
-			cacheKey += /*property + ':' +*/ value + ',';
+			cacheKey += /*property + ':' +*/ valueKey + ',';
 
 
 		}
 		}
 
 

+ 1 - 0
test/e2e/puppeteer.js

@@ -103,6 +103,7 @@ const exceptionList = [
 	'webgl_tiled_forward',
 	'webgl_tiled_forward',
 	'webgl_points_dynamic',
 	'webgl_points_dynamic',
 	'webgpu_multisampled_renderbuffers',
 	'webgpu_multisampled_renderbuffers',
+	'webgl_test_wide_gamut',
 
 
 	// TODO: implement determinism for setTimeout and setInterval
 	// TODO: implement determinism for setTimeout and setInterval
 	// could it fix some examples from above?
 	// could it fix some examples from above?