功能点
实例化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 lang="en">
<head>
<title>three.js webgpu - skinning instancing</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="../../three.js/examples/main.css" />
</head>
<body>
<div id="info">
<a
href="https://threejs.org"
target="_blank"
rel="noopener"
>three.js</a
>
webgpu - skinning instancing
</div>
<script type="importmap">
{
"imports": {
"three": "./three.webgpu.js",
"three/tsl": "./three.webgpu.js",
"three/addons/": "../../three.js/examples/jsm/"
}
}
</script>
<script type="module">
import * 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;