ThreejsWebGPU运动残影demo

news2024/12/24 8:28:21

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

功能点
实例化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;
                    // 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.boundingSphere
                            child.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 = true
                            child.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);

                            // 索引 attribute
                            const 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 = oscNode
                            child.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.boundingSphere
                            child.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.boundingSphere
                            child.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);
                });

                // renderer

                renderer = 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 processing

                const 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;

                // events

                window.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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2048147.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HarmonyOS NEXT - Navigation组件封装BaseNavigation

demo 地址: https://github.com/iotjin/JhHarmonyDemo 代码不定时更新&#xff0c;请前往github查看最新代码 在demo中这些组件和工具类都通过module实现了&#xff0c;具体可以参考HarmonyOS NEXT - 通过 module 模块化引用公共组件和utils 官方介绍 组件导航 (Navigation)(推…

​【迅为电子】RK3568驱动指南|第十七篇 串口-第197章 串口通信协议

瑞芯微RK3568芯片是一款定位中高端的通用型SOC&#xff0c;采用22nm制程工艺&#xff0c;搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码&#xff0c;支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU&#xff0c;可用于轻量级人工…

vue3 ts 集成 supermap

文章目录 1. 依赖安装2. 模块声明3. css 全局引入4. 地图加载 1. 依赖安装 npm install supermapgis/iclient-leafletnpm install leaflet2. 模块声明 env.d.ts declare module leaflet {const L: any;export default L; }declare module supermapgis/iclient-leaflet3. css …

高数3.5 极值与最值

1. 极值 1.1. 定义 1.2. 推论 1.3. 求极值的步骤 1.3.1 方法1 1.3.1.1 例题

BCLinux8.*构建部署nmap 7.95

定义SPEC文件 首先从nmap的github上下载SPEC定义文件&#xff0c;然后进行修改&#xff0c;如下&#xff1a; # To build a static RPM, add # --define "static 1" # to the rpmbuild command line. To build without Ncat, add # --define "buildnc…

FastHTML:使用 Python 彻底改变 Web 开发

什么是 FastHTML&#xff1f;&#x1f310; FastHTML 是一个现代 Python Web 应用程序框架&#xff0c;其真正目的是让 Python 开发人员轻松进行 Web 开发。它大大减少了对 JavaScript 和 CSS 构建交互式和可扩展 Web 应用程序的依赖。FastHTML 通过使用 Python 对象来表示 HTM…

centos虚拟机IP地址频繁变化的原因及解决策略

文章目录 centos虚拟机IP地址频繁变化的原因及解决策略虚拟机IP地址变化的原因解决虚拟机IP地址变化的策略1. 使用静态IP地址Windows系统&#xff1a;Linux系统&#xff1a; 2. 配置DHCP保留3. 使用虚拟化平台的网络功能4. 检查和更新网络驱动程序5. 优化网络配置脚本6. 监控和…

BIO,NIO,AIO编程实战

写在前面 关于IO分类以及IO模型等理论知识&#xff0c;可以参考io之io分类和io模型这篇文章。本文主要来实现Java中相关IO模型实现程序。 1&#xff1a;BIO blocking io&#xff0c;是Java io中对阻塞IO模型的具体实现。 因为不管是server端还是client端&#xff0c;都需要…

JavaSE的【反射】和【动态代理】

作为JavaSE的两个个基础知识&#xff0c;【反射】和【动态代理】被广泛运用到spring、spring boot、mybatis......等等各种地方&#xff0c;等到后面的学习中反复提到这个知识点就会开始懵逼&#xff0c;而且这两个知识点其实也是紧密相连的&#xff0c;很多文章和课程里也并没…

零基础STM32单片机编程入门(三十七) MPU6050陀螺仪传感器详解及实战源码

文章目录 一.概要二.MPU6050芯片介绍1.MEMS传感器原理2.MPU6050芯片简介3.芯片引脚定义4.XYZ轴方向5.芯片内部框图6.芯片常用寄存器 三.MPU6050模块原理图及与模块接口定义使用四.STM32单片机驱动MPU6050读取加速度角速度值实验五.CubeMX工程源代码下载六.小结 一.概要 MPU605…

嵌入式面经篇六——寄存器与存储器

文章目录 前言一、寄存器与存储器1、ARM 的 31 个通用寄存器 R0~R15 中&#xff0c;程序计数器 PC 为 R15、程序链接寄存器 LR 为 R14、堆栈指针寄存器 SP 为 R13。2、寄存器掉电会丢失数据吗&#xff1f;3、NOR Flash 与 NAND Flash 的区别&#xff1f;4、SRAM、DRAM、SDRAM的…

使用Python创建省份城市地图选择器

在这篇博客中&#xff0c;我们将探讨如何使用Python创建一个简单而实用的省份城市地图选择器。这个项目不仅能帮助我们学习Python的基础知识&#xff0c;还能让我们了解如何处理JSON数据和集成网页浏览器到桌面应用程序中。 C:\pythoncode\new\geographicgooglemap.py 全部代码…

Camtasia 2024破解版安装教程+汉化补丁激活2024 破解版激活码

最近&#xff0c;我在网上冲浪的时候&#xff0c;发现了一款录屏软件——Camtasia 2024。它不仅功能丰富&#xff0c;而且操作简单&#xff0c;简直是我的录屏利器&#xff01;今天&#xff0c;我就来给大家分享一下这款软件的最新功能&#xff0c;让你们也感受一下它的魔力&am…

【LeetCode:3137. K 周期字符串需要的最少操作次数 | 哈希表 + 子串计数】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

政务网站(.gov)专用SSL/HTTPS证书

政府网站在选择SSL证书时不仅需要遵循网络安全法规以及密评整改&#xff0c;更要提升公众信任度。国产服务商提供的专业版SSL证书&#xff0c;全方位符合政务部门对SSL证书的要求 1 算法要求 政务服务网站需要落实等保制度、密评制度&#xff0c;在密码应用上可选择国密算法S…

从0到1教你搭建Android自动化python+appium环境(超详细~)

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、需要软件 1. JDK:JAVA安装后配置JDK环境 2. SDK:SDK下载后配置adb环境 3. Python:pyhton语言 4. Pycharm:python脚本编译工具 5. Appium-python-clien…

如何巧妙构建“LDAPS”服务器利用JNDI注入

前段时间看到群友问了这样一个问题&#xff1a; ldap:和rmi:关键字被拦截了&#xff0c;是否还可以进行JNDI注入。方法很简单&#xff0c;就是使用ldaps&#xff0c;但后来发现很多人并不知道怎么搭建LDAPS服务器&#xff0c;正好CoNote里有这个功能&#xff0c;写篇简单的文章…

【大模型】LLM工作原理简述

LLM&#xff0c;即large-language-model&#xff0c;大语言模型。 我们可以观察LLM大模型比如豆包在回复的时候&#xff0c;是不是一个一个字&#xff0c;行业里称之为流式输出的方式给你呈现内容的。为什么会这样呢&#xff1f;这是因为&#xff0c;大模型确实是在一个字一个…

Libero编译怪事(1)计数达不到目标值

最近在开发Libero工程&#xff0c;芯片是AGLN250V2。 其中一段计数的程序&#xff0c;声明了一个integer参数。当该参数大于某一值时&#xff0c;执行状态跳转。 编译烧写后&#xff0c;程序一直无法实现跳转。 以为是由于integer是有符号的&#xff0c;可能出现负值&#x…

IDEA中查看接口的所有实现类和具体实现类

1.IDEA中接口的所有实现类查看 1.CTRLH(hierarchy 结构) 我们选中要查看的接口 按住快捷键ctrlh 在界面右侧可以看到该接口的所有可能实现类 2.右击diagrams->show diagram 选中要查看的接口 右击选择diagrams->show diagram 即可以以图表的方式查看接口和所有实现类…