فهرست منبع

TSL: Introduce `billboarding()` (#29011)

* ShaderNode: Fix defined() primitive input

* TSL: Introduce billboarding()

* rev
sunag 1 سال پیش
والد
کامیت
1ac4c6d5e2

+ 2 - 27
examples/webgpu_compute_particles_rain.html

@@ -24,7 +24,7 @@
 		<script type="module">
 
 			import * as THREE from 'three';
-			import { tslFn, texture, uv, uint, positionWorld, modelWorldMatrix, cameraViewMatrix, timerLocal, timerDelta, cameraProjectionMatrix, vec2, instanceIndex, positionGeometry, storage, If } from 'three/tsl';
+			import { tslFn, texture, uv, uint, positionWorld, billboarding, timerLocal, timerDelta, vec2, instanceIndex, positionGeometry, storage, If } from 'three/tsl';
 
 			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
 
@@ -187,34 +187,9 @@
 
 				// rain
 
-				const billboarding = tslFn( () => {
-
-					const particlePosition = positionBuffer.toAttribute();
-
-					const worldMatrix = modelWorldMatrix.toVar();
-					worldMatrix[ 3 ][ 0 ] = particlePosition.x;
-					worldMatrix[ 3 ][ 1 ] = particlePosition.y;
-					worldMatrix[ 3 ][ 2 ] = particlePosition.z;
-
-					const modelViewMatrix = cameraViewMatrix.mul( worldMatrix );
-					modelViewMatrix[ 0 ][ 0 ] = 1;
-					modelViewMatrix[ 0 ][ 1 ] = 0;
-					modelViewMatrix[ 0 ][ 2 ] = 0;
-
-					//modelViewMatrix[ 0 ][ 0 ] = modelWorldMatrix[ 0 ].length();
-					//modelViewMatrix[ 1 ][ 1 ] = modelWorldMatrix[ 1 ].length();
-
-					modelViewMatrix[ 2 ][ 0 ] = 0;
-					modelViewMatrix[ 2 ][ 1 ] = 0;
-					modelViewMatrix[ 2 ][ 2 ] = 1;
-
-					return cameraProjectionMatrix.mul( modelViewMatrix ).mul( positionGeometry );
-
-				} );
-
 				const rainMaterial = new THREE.MeshBasicNodeMaterial();
 				rainMaterial.colorNode = uv().distance( vec2( .5, 0 ) ).oneMinus().mul( 3 ).exp().mul( .1 );
-				rainMaterial.vertexNode = billboarding();
+				rainMaterial.vertexNode = billboarding( { position: positionBuffer.toAttribute() } );
 				rainMaterial.opacity = .2;
 				rainMaterial.side = THREE.DoubleSide;
 				rainMaterial.forceSinglePass = true;

+ 11 - 7
examples/webgpu_tsl_vfx_flames.html

@@ -27,7 +27,7 @@
 		<script type="module">
 
 			import * as THREE from 'three';
-			import { PI2, spherizeUV, sin, step, texture, timerLocal, tslFn, uv, vec2, vec3, vec4, mix } from 'three/tsl';
+			import { PI2, spherizeUV, sin, step, texture, timerLocal, tslFn, uv, vec2, vec3, vec4, mix, billboarding } from 'three/tsl';
 
 			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
 
@@ -92,7 +92,7 @@
 
 				// flame 1 material
 
-				const flame1Material = new THREE.MeshBasicNodeMaterial( { transparent: true, side: THREE.DoubleSide } );
+				const flame1Material = new THREE.SpriteNodeMaterial( { transparent: true, side: THREE.DoubleSide } );
 
 				flame1Material.colorNode = tslFn( () => {
 
@@ -130,7 +130,7 @@
 
 				// flame 2 material
 
-				const flame2Material = new THREE.MeshBasicNodeMaterial( { transparent: true, side: THREE.DoubleSide } );
+				const flame2Material = new THREE.SpriteNodeMaterial( { transparent: true, side: THREE.DoubleSide } );
 
 				flame2Material.colorNode = tslFn( () => {
 
@@ -175,17 +175,21 @@
 			
 				} )();
 
-				// geometry
+				// billboarding - follow the camera rotation only horizontally
 
-				const geometry = new THREE.PlaneGeometry( 1, 1, 64, 64 );
+				flame1Material.vertexNode = billboarding();
+				flame2Material.vertexNode = billboarding();
 
 				// meshes
 
-				const flame1 = new THREE.Mesh( geometry, flame1Material );
+				const flame1 = new THREE.Sprite( flame1Material );
+				flame1.center.set( 0.5, 0 );
+				flame1.scale.x = 0.5; // optional
 				flame1.position.x = - 0.5;
 				scene.add( flame1 );
 
-				const flame2 = new THREE.Mesh( geometry, flame2Material );
+				const flame2 = new THREE.Sprite( flame2Material );
+				flame2.center.set( 0.5, 0 );
 				flame2.position.x = 0.5;
 				scene.add( flame2 );
 

+ 1 - 0
src/nodes/Nodes.js

@@ -63,6 +63,7 @@ export { default as OscNode, oscSine, oscSquare, oscTriangle, oscSawtooth } from
 export { default as PackingNode, directionToColor, colorToDirection } from './utils/PackingNode.js';
 export { default as RemapNode, remap, remapClamp } from './utils/RemapNode.js';
 export * from './utils/UVUtils.js';
+export * from './utils/SpriteUtils.js';
 export { default as RotateNode, rotate } from './utils/RotateNode.js';
 export { default as SetNode } from './utils/SetNode.js';
 export { default as SplitNode } from './utils/SplitNode.js';

+ 1 - 1
src/nodes/shadernode/ShaderNode.js

@@ -472,7 +472,7 @@ const ConvertType = function ( type, cacheMap = null ) {
 
 // exports
 
-export const defined = ( value ) => value && value.value;
+export const defined = ( v ) => typeof v === 'object' && v !== null ? v.value : v; // TODO: remove boolean conversion and defined function
 
 // utils
 

+ 47 - 0
src/nodes/utils/SpriteUtils.js

@@ -0,0 +1,47 @@
+import { modelWorldMatrix } from '../accessors/ModelNode.js';
+import { cameraViewMatrix, cameraProjectionMatrix } from '../accessors/CameraNode.js';
+import { positionLocal } from '../accessors/PositionNode.js';
+import { tslFn, defined } from '../shadernode/ShaderNode.js';
+
+export const billboarding = tslFn( ( { position = null, horizontal = true, vertical = false } ) => {
+
+	let worldMatrix;
+
+	if ( position !== null ) {
+
+		worldMatrix = modelWorldMatrix.toVar();
+		worldMatrix[ 3 ][ 0 ] = position.x;
+		worldMatrix[ 3 ][ 1 ] = position.y;
+		worldMatrix[ 3 ][ 2 ] = position.z;
+
+	} else {
+
+		worldMatrix = modelWorldMatrix;
+
+	}
+
+	const modelViewMatrix = cameraViewMatrix.mul( worldMatrix );
+
+	if ( defined( horizontal ) ) {
+
+		modelViewMatrix[ 0 ][ 0 ] = modelWorldMatrix[ 0 ].length();
+		modelViewMatrix[ 0 ][ 1 ] = 0;
+		modelViewMatrix[ 0 ][ 2 ] = 0;
+
+	}
+
+	if ( defined( vertical ) ) {
+
+		modelViewMatrix[ 1 ][ 0 ] = 0;
+		modelViewMatrix[ 1 ][ 1 ] = modelWorldMatrix[ 1 ].length();
+		modelViewMatrix[ 1 ][ 2 ] = 0;
+
+	}
+
+	modelViewMatrix[ 2 ][ 0 ] = 0;
+	modelViewMatrix[ 2 ][ 1 ] = 0;
+	modelViewMatrix[ 2 ][ 2 ] = 1;
+
+	return cameraProjectionMatrix.mul( modelViewMatrix ).mul( positionLocal );
+
+} );

粤ICP备19079148号