马黑黑 发表于 2025-5-14 12:40

three.js : 独立控制图形对象

<style>
        #btnPrev { box-shadow: 2px 2px 6px rgba(0,0,0,.5); }
        #btnPrev:hover { box-shadow: 2px 2px 12px rgba(0,0,0,.5); font-weight: bold; }
</style>

<p>以下代码,使用 cubes 数组变量保存立方体对象,每一个对象记为 { id: id, name: 'name', step: step },其中,id 指向所创建的立方体,name 指向立方体的名称,step 指向立方体运动歩幅。然后通过 three.js 射线类判断被点击的图形对象,若和 cubes 存储的 name 相一致,则驱动该图形对象的运动歩幅,从而达到每一个图形对象均可独立控制的目的。</p>
<div id="hEdiv"><pre id="hEpre">
&lt;script type="module"&gt;
        import * as THREE from 'https://esm.sh/three'; // 导入three.js模块

        // 初始化场景、相机、渲染器
        const scene = new THREE.Scene;
        const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
        camera.position.set(0, 0, 5);
        const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        let cubes = [], intersects = []; // 立方体合集、几何体合集

        // 正方体几何体
        const geometry = new THREE.BoxGeometry(1, 1, 1);
        geometry.rotateX(Math.PI / 3);
        geometry.rotateY(Math.PI / 2);

        // 创建两种标准材质 : 一种各面随机色,另一种金色
        const material1 = Array.from({length: 6}, () =&gt; new THREE.MeshStandardMaterial({ color: 0xffffff * Math.random() }));
        const material2 = new THREE.MeshStandardMaterial({ color: 0xffd700 });
        const cube1 = new THREE.Mesh(geometry, material1);
        const cube2 = new THREE.Mesh(geometry, material2);
        cube1.position.y += 1;
        cube2.position.y += -1;
        cube1.name = 'cube1';
        cube2.name = 'cube2';
        cubes.push({ id: cube1, name: 'cube1', step: 0.01 }, { id: cube2, name: 'cube2', step: 0.01 });
        scene.add(cube1, cube2);

        // 环境光
        const AmbientLight = new THREE.AmbientLight('yellow')
        scene.add(AmbientLight)

        // 自然光
        const hemiLight = new THREE.HemisphereLight(0xffffff, 0x000000, 1)
        hemiLight.position.set(0, 100, 0)
        scene.add(hemiLight)

        // 以下借助three射线类判断图形和设备指针的位置关系
        let raycaster = new THREE.Raycaster(); // 实例化射线拾取模型
        const pointer = new THREE.Vector2(); // 实例化二维向量点结构

        // 判断函数 :检测指针是否在three绘制的图形上
        const isMess = (event) =&gt; {
                pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
                pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;
                raycaster.setFromCamera(pointer, camera);
                intersects = raycaster.intersectObjects(, true); // 参数true表示为不拾取图形对象的子对象
                return intersects.length &gt; 0;
        }

        // 动画 :根据 step 变量值驱动对象运动或不运动
        const animate = () =&gt; {
                cubes.forEach(cube =&gt; cube.id.rotation.y += cube.step);
                renderer.render(scene, camera);
                requestAnimationFrame(animate);
        };

        // 页面单击事件 :点击到立方体对象控制其 step 变量的值
        document.onclick = (e) =&gt; {
                if (!isMess(e)) return;
                const name = intersects.object.name; // 被点击的几何体名称
                cubes.forEach(cube =&gt; {
                        // 立方体名称若等于几何体名称
                        if (cube.name === name) {
                                cube.step = cube.step === 0 ? 0.01 : 0;
                        }
                });
        };

        // 鼠标指针移入立方体对象时显示手形图标
        document.onmousemove = (e) =&gt; {
                document.body.style.cursor = isMess(e) ? 'pointer' : 'default';
        };

        window.onresize = () =&gt; renderer.setSize(window.innerWidth, window.innerHeight); // 适应窗口变化

        animate(); // 执行动画函数
&lt;/script&gt;

&lt;!-- 预览页面CSS --&gt;
&lt;style&gt; body { margin: 0; background: linear-gradient(#eee, #333); } &lt;/style&gt;
</pre></div>
<blockquote><button id="btnPrev" name="btnPrev" type="button" value="prev">运行代码</button></blockquote>

<script type="module">
        import hlight from 'https://638183.freep.cn/638183/web/helight/helight1.js';
        hlight.hl(hEdiv, hEpre);

        btnPrev.onclick = () => {
                const value = hEpre.textContent + '<style>body {margin: 0; }</style>';
                const previewWindow = window.open('', 'preview1', 'width=1200,height=768,top=100,left=100');
                previewWindow.document.open();
                previewWindow.document.write(value);
                setTimeout(function() { previewWindow.document.title = "预览" }, 100);
                previewWindow.document.close();
        };
</script>

梦江南 发表于 2025-5-14 15:34

谢谢老师辛苦。我怎么点不动“运行代码”{:4_190:}

杨帆 发表于 2025-5-14 18:05

分享精彩,恭喜老师又有新探索{:4_191:}

花飞飞 发表于 2025-5-14 19:31

两个立方体在旋转。。这种亚光色真的好漂亮的。。

我在研究标题:为何叫做独立控制图形对象。
操作后发现,点击任何一个都可以单独停止,再击旋转。。
也可以叫做独立控制几何体{:4_173:}

花飞飞 发表于 2025-5-14 19:40

对环境光和自然光很感兴趣,
试着换了一下,这跟PS里常用的渐变映射有点像。。。
环境光渲染的相当自然,在保持原本颜色的基础上,加上环境光,很自然的。{:4_199:}

花飞飞 发表于 2025-5-14 19:47

cubes 数组变量保存立方体对象,两个ID不同,名称不同。。步幅相同,当然也可以设为不同。。

通过three.js 射线判断点击的是谁,这个也是个新名词。。。

我们是看到成品,独立执行得相当完美~~
想想要构思设想,再到尝试,最后实现,这个过程小白就是仰望的存在。。
给白老师点一万个大赞{:4_173:}

花飞飞 发表于 2025-5-14 19:48

上帝说要有光,于是今天就看到了光。。。真是让人欣喜。。{:4_173:}

花飞飞 发表于 2025-5-14 19:49

梦江南 发表于 2025-5-14 15:34
谢谢老师辛苦。我怎么点不动“运行代码”

换个浏览器试一试,我点击正常的。

马黑黑 发表于 2025-5-14 20:08

杨帆 发表于 2025-5-14 18:05
分享精彩,恭喜老师又有新探索

{:4_191:}

马黑黑 发表于 2025-5-14 20:09

梦江南 发表于 2025-5-14 15:34
谢谢老师辛苦。我怎么点不动“运行代码”

原因可能有两个:

一,杂牌浏览器(使用规范浏览器);
二,缓存太多(清除缓存即可);

马黑黑 发表于 2025-5-14 20:10

花飞飞 发表于 2025-5-14 19:48
上帝说要有光,于是今天就看到了光。。。真是让人欣喜。。

现在用的光源还是比较简单的光源

马黑黑 发表于 2025-5-14 20:10

花飞飞 发表于 2025-5-14 19:47
cubes 数组变量保存立方体对象,两个ID不同,名称不同。。步幅相同,当然也可以设为不同。。

通过thre ...

谢一万

马黑黑 发表于 2025-5-14 20:11

花飞飞 发表于 2025-5-14 19:40
对环境光和自然光很感兴趣,
试着换了一下,这跟PS里常用的渐变映射有点像。。。
环境光渲染的相当自然, ...

光源是一门比较复杂的学问

红影 发表于 2025-5-14 21:05

还真是独立控制的,而且停止后,再运行也是独立的呢{:4_187:}

红影 发表于 2025-5-14 21:06

这个有趣,想让哪个停,就能控制哪个{:4_187:}

马黑黑 发表于 2025-5-14 21:07

红影 发表于 2025-5-14 21:06
这个有趣,想让哪个停,就能控制哪个

就是复杂得多

马黑黑 发表于 2025-5-14 21:08

红影 发表于 2025-5-14 21:05
还真是独立控制的,而且停止后,再运行也是独立的呢

每一个子对象有自己的速度变量(歩幅)

红影 发表于 2025-5-14 21:09

“检测指针是否在three绘制的图形上”
这个里面的判断方式没看懂。{:4_203:}

马黑黑 发表于 2025-5-14 21:13

红影 发表于 2025-5-14 21:09
“检测指针是否在three绘制的图形上”
这个里面的判断方式没看懂。

而且不是特别准确,这是将就的方法。原理是将相机所拍到的图形的射线计算出二维平面的几何面,正常的时候大概准确,当图像旋转角度很大时,它就不那么精准了,你可以去看看牵引的原版

梦江南 发表于 2025-5-15 08:05

花飞飞 发表于 2025-5-14 19:49
换个浏览器试一试,我点击正常的。

早上问好飞飞老师,我只有一个2345浏览器。{:4_201:}
页: [1] 2 3 4
查看完整版本: three.js : 独立控制图形对象