对网站内容建设的建议,福州网站建设外包,彩页模板,四川省住房和城乡建设厅网站打不开功能点 实例化SkinnedMesh 修改NodeMaterial着色器 节点材质系统 shader 语言 使用uniform和attribute 中合其他几篇博客中的内容 代码仓库 克隆后需要放到three源码同级别目录下 运行 three源码部分不在git仓库中(太大了) 使用vscode的live-server启动后访问 http://127.0.0.… 功能点 实例化SkinnedMesh 修改NodeMaterial着色器 节点材质系统 shader 语言 使用uniform和attribute 中合其他几篇博客中的内容 代码仓库 克隆后需要放到three源码同级别目录下 运行 three源码部分不在git仓库中(太大了) 使用vscode的live-server启动后访问 http://127.0.0.1:5501/demo/webgpu_skinning_instancing/webgpu_skinning_instancing.html html部分
!--* Author: hongbin* Date: 2024-07-30 07:48:37* LastEditors: hongbin* LastEditTime: 2024-08-12 22:03:13* Description:
--
!DOCTYPE html
html langenheadtitlethree.js webgpu - skinning instancing/titlemeta charsetutf-8 /metanameviewportcontentwidthdevice-width, user-scalableno, minimum-scale1.0, maximum-scale1.0 /linktypetext/cssrelstylesheethref../../three.js/examples/main.css //headbodydiv idinfoahrefhttps://threejs.orgtarget_blankrelnoopenerthree.js/awebgpu - skinning instancing/divscript typeimportmap{imports: {three: ./three.webgpu.js,three/tsl: ./three.webgpu.js,three/addons/: ../../three.js/examples/jsm/}}/scriptscript typemoduleimport * as THREE from three;import {pass,mix,range,color,oscSine,timerLocal,texture,TextureNode,normalLocal,min,max,abs,uniform,floor,float,nodeObject,instanceIndex,buffer,varyingProperty,int,instancedBufferAttribute,instancedDynamicBufferAttribute,vec3,reference,} from three/tsl;import { GUI } from three/addons/libs/lil-gui.module.min.js;import { GLTFLoader } from three/addons/loaders/GLTFLoader.js;import { OrbitControls } from three/addons/controls/OrbitControls.js;import { RectAreaLightHelper } from three/addons/helpers/RectAreaLightHelper.js;import { RectAreaLightTexturesLib } from three/addons/lights/RectAreaLightTexturesLib.js;import TWEEN from three/addons/libs/tween.module.js;let camera, scene, renderer, controls;let postProcessing, gui;let clock, delta;let dir 1;let mixer, skinningInstanceMatrices;let mixer2, skinningInstanceMatrices2;let mixer3, skinningInstanceMatrices3;const tt uniform(0, float);const ttI uniform(0, float);class SkinningInstanceMatrices {constructor(mesh, count 2, delay 5,useTween false) {this.mesh mesh;this.count count;this.delay 0.016 * delay;this.time 0;this.useTween useTween;// const params {opacity:1};// new TWEEN.Tween(params)// .to({ opacity: 0 }, 1000)// .onUpdate(function () {// console.log(new Date().getSeconds(),params.opacity)// })// .start();}setStatus(status 1) {this.isFinished !status;// this.mesh.setOmitOneOpacity this.mesh.setOmitOneOpacity(status);const params { opacity: 1 - status };new TWEEN.Tween(params).to({ opacity: status }, 16 * 10).onUpdate(() {this.mesh.setOmitOneOpacity this.mesh.setOmitOneOpacity(params.opacity);}).start();}start() {this.setStatus(1);}isFinished false;/* 动画播放完 隐藏其他实例 */finished() {this.setStatus(0);}prevAction(delta) {this.time delta;if (this.time this.delay !this.isFinished) {for (let i this.count; i 1; i--) {this.syncOnes(i, i - 1);// if (this.useTween) {// const params { opacity: 1 };// new TWEEN.Tween(params)// .to({ opacity: 0 }, this.delay * 1000)// .onUpdate(() {// this.mesh.setOpacity this.mesh.setOpacity(i, params.opacity);// })// .start();// }}// this.syncOnes(3, 2);// this.syncOnes(2, 1);// this.syncOnes(1);this.time 0;// const params {opacity:1};// new TWEEN.Tween(params)// .to({ opacity: 0 }, this.delay * 1000)// .onUpdate(function () {// // console.log(params.opacity)// window.setInstanceOpacity window.setInstanceOpacity(1,params.opacity)// window.setInstanceOpacity window.setInstanceOpacity(2,params.opacity)// })// .start();}}syncOnes(index, copyIndex 0) {const length this.mesh.skeleton.bones.length;const copyIndexLeft 16 * copyIndex * length;// const end 16 * (index 1) * length;const left 16 * index * length;// for (let index left, i 0; index end; index, i) {// this.mesh.skeleton.boneMatrices[index] this.mesh.skeleton.boneMatrices[i copyIndexLeft];// }//使用内置方法 避免遍历 TypedArray.copyWithin(target start end) https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/copyWithin// this.mesh.skeleton.boneMatrices.copyWithin(copyIndexLeft, 0, length * 16);this.mesh.skeleton.boneMatrices.copyWithin(left, copyIndexLeft, copyIndexLeft length * 16);}syncIndex(index) {requestAnimationFrame(() {this.syncIndex(index);});this.syncOnes(index);// const { count } this.mesh;// const length this.mesh.skeleton.bones.length;// const end 16 * (index 1) * length;// const left 16 * index * length;// for (let index left, i 0; index end; index, i) {// this.mesh.skeleton.boneMatrices[index] this.mesh.skeleton.boneMatrices[i];// }}}init();window.offsetCount 1040;function init() {gui new GUI();window.stopAnimation false;gui.add(window, stopAnimation);THREE.RectAreaLightNode.setLTC(RectAreaLightTexturesLib.init());camera new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 40);// camera.position.set( 1, 2, 3 );camera.position.set(0, 0, 0);scene new THREE.Scene();// scene.add(new THREE.AxesHelper(1));camera.lookAt(0, 1, 0);clock new THREE.Clock();// lights{const centerLight new THREE.PointLight(0xff9900, 2, 100);centerLight.position.y 4.5;centerLight.power 400;// scene.add(centerLight);const cameraLight new THREE.PointLight(0xffffff, 1, 100);cameraLight.power 400;cameraLight.position.set(0, 2, 3);// camera.add(cameraLight);// scene.add(camera);// scene.add(cameraLight);const rectLight1 new THREE.RectAreaLight(0xffffff, 10, 100, 0.5);rectLight1.position.set(0, 2, 0);rectLight1.lookAt(0, -1, 0);scene.add(rectLight1);{const rectLight1 new THREE.RectAreaLight(0xffffff, 1, 10, 0.5);rectLight1.position.set(0, 2.1, 0);rectLight1.lookAt(0, -1, 0);scene.add(rectLight1);}{const rectLight1 new THREE.RectAreaLight(0xffffff, 10, 10, 0.1);rectLight1.position.set(0, 0, 2);rectLight1.lookAt(0, 0, 0);scene.add(rectLight1);}scene.add(new RectAreaLightHelper(rectLight1));}const thickness 10;const geometry new THREE.BoxGeometry(100, 2, thickness);geometry.translate(0, 0, -thickness / 2);geometry.rotateX(-Math.PI / 2);const floorMesh new THREE.Mesh(geometry,new THREE.MeshStandardMaterial({color: 0x000000,roughness: 1,metalness: 0.6,}));scene.add(floorMesh);// {// const floorMesh new THREE.Mesh(// new THREE.BoxGeometry(0.1, 0.1, 0.1),// new THREE.MeshStandardMaterial({// color: 0xff0000,// roughness: 0,// metalness: 0,// })// );// floorMesh.position.set(0, 1, 1);// scene.add(floorMesh);// }{const geometry new THREE.BoxGeometry(100, 2.4, 2);const map new THREE.TextureLoader().load(../../three.js/examples/textures/carbon/Carbon.png);const normalMap new THREE.TextureLoader().load(../../three.js/examples/textures/carbon/Carbon_Normal.png);map.colorSpace THREE.SRGBColorSpace;normalMap.wrapS map.wrapS THREE.RepeatWrapping;normalMap.wrapT map.wrapT THREE.RepeatWrapping;map.repeat.set(60, 1);normalMap.repeat.copy(map.repeat);const box new THREE.Mesh(geometry,new THREE.MeshStandardMaterial({color: 0xffffff,// color: 0x666666,roughness: 0.3,metalness: 0.7,map,normalMap,}));box.position.z -2;box.position.y 1.2;scene.add(box);}const loader new GLTFLoader();loader.load(../../three.js/examples/models/gltf/Michelle.glb, function (gltf) {const danceGUI gui.addFolder(跳舞);const object gltf.scene;mixer new THREE.AnimationMixer(object);const action mixer.clipAction(gltf.animations[0]);action.play();const instanceCount 10;const dummy new THREE.Object3D();object.traverse(child {if (child.isMesh) {skinningInstanceMatrices new SkinningInstanceMatrices(child);const oscNode abs(oscSine(timerLocal(0.1)));// const oscNode abs(oscSine(timerLocal(1)));// const oscNode abs(timerLocal(1).sin());const indexNode floor(oscNode.div(0.3333334));// const oscNode oscSine(timerLocal(0.1));const randomColors range(new THREE.Color(0x000000), new THREE.Color(0xffffff));const randomMetalness range(0, 1);const prevMap child.material.map;// 设置 false 不计入 视椎体可见范围计算 避免被隐藏 椎体计算 一般使用object.boundingSpherechild.frustumCulled false;// 显示卡通边框child.CartoonBorder true;child.cartoonBorder uniform(0.2);child.cartoonBorderColor uniform(new THREE.Color(#ffffff));child.CartoonBorderLight uniform(vec3(0, 0, 1), vec3);// child.CartoonBorderLight uniform(camera.position,vec3);danceGUI.add(child.cartoonBorder, value, 0, 1).name(cartoonBorder);danceGUI.add(skinningInstanceMatrices, delay, 0, 0.2).name(speed);danceGUI.addColor({ color: # child.cartoonBorderColor.value.getHexString() }, color).onChange(c {const newColor new THREE.Color(c);child.cartoonBorderColor.value.copy(newColor);});child.material new THREE.MeshStandardNodeMaterial({transparent: true,side: 0,});// child.material.onBeforeCompile (shader) {// console.log(onBeforeCompile:, shader);// };// roughnessNode是变化的 roughness是固定的// child.material.roughnessNode oscNode;// child.material.metalnessNode 0.5 || mix(0.0, randomMetalness, oscNode);// child.material.colorNode mix(texture(prevMap), randomColors, oscNode);child.material.colorNode texture(prevMap);child.isInstancedMesh true;child.instanceMatrix new THREE.InstancedBufferAttribute(new Float32Array(instanceCount * 16),16);// 透明度const floatOpacity new Float32Array(instanceCount);const buffer new THREE.InstancedBufferAttribute(floatOpacity, 1);console.log(buffer);// instancedDynamicBufferAttribute 每次写入buffer// instancedBufferAttribute 手动更新 needsUpdate truechild.instanceOpacity instancedBufferAttribute(buffer);window.setInstanceOpacity (index, opacity) {floatOpacity[index] opacity;buffer.needsUpdate true;};// child.instanceOpacity new THREE.InstancedBufferAttribute(// new Float32Array(instanceCount),// 1// );// child.instanceOpacity.setUsage(THREE.DynamicDrawUsage);// 索引 attributeconst floatIndex new Float32Array(instanceCount);const indexBuffer new THREE.InstancedBufferAttribute(floatIndex, 1);child.instanceIndex instancedBufferAttribute(indexBuffer);// 索引// child.instanceIndex new THREE.InstancedBufferAttribute(// new Float32Array(instanceCount),// 1// );// 提供uniform// 选中的实例索引// child.selectInstanceIndex ttI;// child.selectInstanceIndex indexNode;child.selectInstanceIndex uniform(-1, float);// 选中的实例索引的透明度child.selectInstanceIndexOpacity tt;// child.selectInstanceIndexOpacity uniform(0.5, float);// child.selectInstanceIndexOpacity abs(oscSine(timerLocal(0.33334)));// child.selectInstanceIndexOpacity oscNodechild.count instanceCount;//重新设置 实例矩阵长度 为原长度的instanceCount倍child.skeleton.setInstanceCount(instanceCount);// gui.add(// {// f: () {// mixer.update(delta);// console.log(child.skeleton.boneMatrices);// },// },// f// ).name(mixer.update());for (let i 0; i instanceCount; i) {// dummy.position.y Math.floor(i / 5) * -200;dummy.position.x 70;// dummy.position.x i * 70;dummy.position.z i * -0.1;dummy.updateMatrix();dummy.matrix.toArray(child.instanceMatrix.array, i * 16);floatIndex[i] i;floatOpacity[i] 1;}const setIndexFadeOut i {// child.selectInstanceIndex.value i;// dir -1;// tt.value 1;};// gui.add({ f: () skinningInstanceMatrices.syncOnes(1) }, f).name(1 ONES);// gui.add({ f: () skinningInstanceMatrices.syncOnes(2) }, f).name(2 ONES);// gui.add({ f: () skinningInstanceMatrices.syncOnes(3) }, f).name(3 ONES);// gui.add({ f: () skinningInstanceMatrices.syncOnes(4) }, f).name(4 ONES);// gui.add({ f: () skinningInstanceMatrices.syncOnes(5) }, f).name(5 ONES);// gui.add({ f: () skinningInstanceMatrices.syncOnes(6) }, f).name(6 ONES);// gui.add({ f: () skinningInstanceMatrices.syncOnes(7) }, f).name(7 ONES);// gui.add({ f: () skinningInstanceMatrices.syncOnes(8) }, f).name(8 ONES);// gui.add({ f: () skinningInstanceMatrices.syncOnes(9) }, f).name(9 ONES);// gui.add({ f: () skinningInstanceMatrices.syncIndex(1) }, f).name(1 ASYNC);// gui.add({ f: () skinningInstanceMatrices.syncIndex(2) }, f).name(2 ASYNC);// gui.add({ f: () skinningInstanceMatrices.syncIndex(3) }, f).name(3 ASYNC);// gui.add({ f: () skinningInstanceMatrices.syncIndex(4) }, f).name(4 ASYNC);// gui.add({ f: () skinningInstanceMatrices.syncIndex(5) }, f).name(5 ASYNC);// gui.add({ f: () skinningInstanceMatrices.syncIndex(6) }, f).name(6 ASYNC);// gui.add({ f: () skinningInstanceMatrices.syncIndex(7) }, f).name(7 ASYNC);// gui.add({ f: () skinningInstanceMatrices.syncIndex(8) }, f).name(8 ASYNC);// gui.add({ f: () skinningInstanceMatrices.syncIndex(9) }, f).name(9 ASYNC);}});scene.add(object);});loader.load(./avoid.glb, function (gltf) {const object gltf.scene;const avoidGUI gui.addFolder(躲避);mixer2 new THREE.AnimationMixer(object);const action mixer2.clipAction(gltf.animations[0]);// 播放一次// action.loop THREE.LoopOnce;// 播完保持当前姿势action.clampWhenFinished true;action.play();// mixer2.addEventListener(finished, c {// skinningInstanceMatrices2 skinningInstanceMatrices2.finished();// });const instanceCount 10;const dummy new THREE.Object3D();object.traverse(child {if (child.isMesh) {skinningInstanceMatrices2 new SkinningInstanceMatrices(child);skinningInstanceMatrices2.start();const oscNode abs(oscSine(timerLocal(0.1)));const indexNode floor(oscNode.div(0.3333334));const randomColors range(new THREE.Color(0x000000), new THREE.Color(0xffffff));const randomMetalness range(0, 1);const prevMap child.material.map;// 设置 false 不计入 视椎体可见范围计算 避免被隐藏 椎体计算 一般使用object.boundingSpherechild.frustumCulled false;// 显示卡通边框child.CartoonBorder true;child.cartoonBorder uniform(0.2);child.cartoonBorderColor uniform(new THREE.Color(#ffffff));child.CartoonBorderLight uniform(vec3(0, 0, 1), vec3);avoidGUI.add(child.cartoonBorder, value, 0, 1).name(cartoonBorder);avoidGUI.add(skinningInstanceMatrices2, delay, 0, 0.2).name(speed);avoidGUI.addColor({ color: # child.cartoonBorderColor.value.getHexString() }, color).onChange(c {const newColor new THREE.Color(c);child.cartoonBorderColor.value.copy(newColor);});// 重新播放avoidGUI.add({f: () {action.reset();skinningInstanceMatrices2.start();},},f).name(Reset Action);child.material new THREE.MeshStandardNodeMaterial({transparent: true,side: 0,});child.material.colorNode texture(prevMap);// child.material.colorNode mix(texture(prevMap), randomColors, oscNode);child.isInstancedMesh true;child.instanceMatrix new THREE.InstancedBufferAttribute(new Float32Array(instanceCount * 16),16);// 透明度const floatOpacity new Float32Array(instanceCount);const buffer new THREE.InstancedBufferAttribute(floatOpacity, 1);child.instanceOpacity instancedBufferAttribute(buffer);child.hideOther child.setOmitOneOpacity (op 0) {floatOpacity.fill(op);floatOpacity[0] 1;buffer.needsUpdate true;};child.showOther () {floatOpacity.fill(1);buffer.needsUpdate true;};const floatIndex new Float32Array(instanceCount);const indexBuffer new THREE.InstancedBufferAttribute(floatIndex, 1);child.instanceIndex instancedBufferAttribute(indexBuffer);child.selectInstanceIndex uniform(-1, float);// 选中的实例索引的透明度child.selectInstanceIndexOpacity tt;child.count instanceCount;//重新设置 实例矩阵长度 为原长度的instanceCount倍child.skeleton.setInstanceCount(instanceCount);for (let i 0; i instanceCount; i) {// dummy.position.y Math.floor(i / 5) * -200;dummy.position.x -70;// dummy.position.x i * 70;// dummy.position.y i * -2;dummy.updateMatrix();dummy.matrix.toArray(child.instanceMatrix.array, i * 16);floatIndex[i] i;floatOpacity[i] 1;}}});scene.add(object);});loader.load(./AirEvasion.glb, function (gltf) {const object gltf.scene;const AirEvasionGUI gui.addFolder(空中躲避);mixer3 new THREE.AnimationMixer(object);const action mixer3.clipAction(gltf.animations[0]);// 播放一次action.loop THREE.LoopOnce;// 播完保持当前姿势action.clampWhenFinished true;action.play();mixer3.addEventListener(finished, c {skinningInstanceMatrices3 skinningInstanceMatrices3.finished();});const instanceCount 10;const dummy new THREE.Object3D();object.traverse(child {if (child.isMesh) {skinningInstanceMatrices3 new SkinningInstanceMatrices(child, 2, 29, true);skinningInstanceMatrices3.start();const oscNode abs(oscSine(timerLocal(0.1)));const indexNode floor(oscNode.div(0.3333334));const randomColors range(new THREE.Color(0x000000), new THREE.Color(0xffffff));const randomMetalness range(0, 1);const prevMap child.material.map;// 设置 false 不计入 视椎体可见范围计算 避免被隐藏 椎体计算 一般使用object.boundingSpherechild.frustumCulled false;// 显示卡通边框child.CartoonBorder true;child.cartoonBorder uniform(0.2);child.cartoonBorderColor uniform(new THREE.Color(#ffffff));child.CartoonBorderLight uniform(vec3(0, 0, 1), vec3);AirEvasionGUI.add(child.cartoonBorder, value, 0, 1).name(cartoonBorder);AirEvasionGUI.add(skinningInstanceMatrices3, delay, 0, 0.5).name(speed);AirEvasionGUI.addColor({ color: # child.cartoonBorderColor.value.getHexString() },color).onChange(c {const newColor new THREE.Color(c);child.cartoonBorderColor.value.copy(newColor);});// 重新播放AirEvasionGUI.add({f: () {action.reset();skinningInstanceMatrices3.start();},},f).name(Reset Action);child.material new THREE.MeshStandardNodeMaterial({transparent: true,side: 0,});child.material.colorNode texture(prevMap);// child.material.colorNode mix(texture(prevMap), randomColors, oscNode);child.isInstancedMesh true;child.instanceMatrix new THREE.InstancedBufferAttribute(new Float32Array(instanceCount * 16),16);// 透明度const floatOpacity new Float32Array(instanceCount);const buffer new THREE.InstancedBufferAttribute(floatOpacity, 1);child.instanceOpacity instancedBufferAttribute(buffer);child.hideOther child.setOmitOneOpacity (op 0) {floatOpacity.fill(op);floatOpacity[0] 1;buffer.needsUpdate true;};child.setOpacity (index, op 0) {floatOpacity[index] op;buffer.needsUpdate true;};child.showOther () {floatOpacity.fill(1);buffer.needsUpdate true;};const floatIndex new Float32Array(instanceCount);const indexBuffer new THREE.InstancedBufferAttribute(floatIndex, 1);child.instanceIndex instancedBufferAttribute(indexBuffer);child.selectInstanceIndex uniform(-1, float);// 选中的实例索引的透明度child.selectInstanceIndexOpacity tt;child.count instanceCount;//重新设置 实例矩阵长度 为原长度的instanceCount倍child.skeleton.setInstanceCount(instanceCount);for (let i 0; i instanceCount; i) {// dummy.position.y Math.floor(i / 5) * -200;dummy.position.x -140;// dummy.position.x i * 70;// dummy.position.y i * -2;dummy.position.y 60;dummy.updateMatrix();dummy.matrix.toArray(child.instanceMatrix.array, i * 16);floatIndex[i] i;floatOpacity[i] 1;}}});scene.add(object);});// rendererrenderer new THREE.WebGPURenderer({ antialias: true });renderer.setPixelRatio(2);renderer.setSize(window.innerWidth, window.innerHeight);renderer.setAnimationLoop(animate);document.body.appendChild(renderer.domElement);controls new OrbitControls(camera, renderer.domElement);controls.target.set(0, 1, 0);controls.object.position.set(0, 1, 4);// post processingconst scenePass pass(scene, camera);const scenePassColor scenePass.getTextureNode();const scenePassDepth scenePass.getLinearDepthNode().remapClamp(0.15, 0.3);const scenePassColorBlurred scenePassColor.gaussianBlur();scenePassColorBlurred.directionNode scenePassDepth;// postProcessing new THREE.PostProcessing(renderer);// postProcessing.outputNode scenePassColorBlurred;// eventswindow.addEventListener(resize, onWindowResize);}function onWindowResize() {camera.aspect window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth, window.innerHeight);}function animate() {delta clock.getDelta();if (skinningInstanceMatrices) {TWEEN.update();}if (!window.stopAnimation) {skinningInstanceMatrices skinningInstanceMatrices.prevAction(delta);if (mixer) mixer.update(delta);skinningInstanceMatrices2 skinningInstanceMatrices2.prevAction(delta);if (mixer2) mixer2.update(delta);skinningInstanceMatrices3 skinningInstanceMatrices3.prevAction(delta);if (mixer3) mixer3.update(delta);}// tt.value tt.value 0.01 * dir;// if (tt.value 1 || tt.value 0) {// dir * -1;// }// ttI.value Math.floor(tt.value / 0.333334);// postProcessing.render();renderer.render(scene, camera);}/script/body
/html