马黑黑 发表于 2025-5-10 09:47

three.js : 画球

本帖最后由 马黑黑 于 2025-5-10 10:07 编辑 <br /><br /><style>
        .artBox { font-size: 20px; }
        .artBox > p { margin: 10px 0; }
        .artBox button { box-shadow: 2px 2px 8px rgba(0,0,0,.5); cursor: pointer; }
        .artBox button:hover { box-shadow: 2px 2px 12px rgba(0,0,0,.5); font-weight: bold; }
</style>

<div class="artBox">
        <p>以下是 three.js 画球体最简单的方法:</p>
        <div class="divBox"><pre class="preBox">
&lt;script type="module"&gt;

import * as THREE from 'https://esm.sh/three'; // 导入three模块

// 场景、相机和渲染器
const scene = new THREE.Scene(); // 场景
// 透视相机
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5; // 相机位置
const renderer = new THREE.WebGLRenderer({ antialias: true }); // 渲染器
renderer.setSize(window.innerWidth, window.innerHeight); // 渲染范围
document.body.appendChild(renderer.domElement); // 渲染器加入body标签

const sphere = new THREE.SphereGeometry(); // 实例化球体几何体
const material = new THREE.MeshNormalMaterial(); // 实例化材质
const ball = new THREE.Mesh(sphere, material); // 用几何体+材质实例化网格对象(球体)
//ball.rotateX(Math.PI / 4); // 球体X轴旋转45度
scene.add(ball); // 球体加入到场景

renderer.render(scene, camera); // 渲染效果

&lt;/script&gt;

&lt;style&gt; body { margin: 0; } &lt;/style&gt;
</pre></div>
        <blockquote><button class="btnPre">运行代码</button></button></blockquote>
        <p>实际绘制球体的JS代码总共也就是10来行,一个有颜色的球体就展现在我们眼前。</p>
        <p>细心的朋友可能已经发现:这个球体没有着色,可运行的效果却是一个彩球。嗯,这里我们实例化的 three.js 材质对象是 MeshNormalMaterial,虽然它的名称中有 Normal(正常)一词,它却是一种特殊的材料,法向量(与面垂直的向量)材质,自带光源和颜色,其优点是可以用来快速构建一个3d对象。</p>
        <p>画圆球的关键是几何体的创建。three.js 的 SphereGeometry 对象用于创建球形几何体,然后N多的面去填充其表面,因此它实际上有很多配置参数,我们上例都是用了默认配置。在下一个例子,我们将给它配置:半径,水平分段数、垂直分段数、水平起始角、水平扫描角度、垂直起始角、垂直扫描角度等齐整的参数,并用基础材料 MessBasicMaterial 对象构建球形几何体:</p>
        <div class="divBox"><pre class="preBox">
&lt;script type="module"&gt;

import * as THREE from 'https://esm.sh/three'; // 导入three模块

// 创建场景、相机和渲染器
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 创建圆球
const total = 8; // 圆球总数
// 实例化球体几何体
const geometry = new THREE.SphereGeometry(2, 32, 32, 0, 2 * Math.PI, 0, 2 * Math.PI);
// 添加 total 个圆球
for (let i = 0; i &lt; total; i++) {
        // 实例化球体材质 :基础材质
        const material = new THREE.MeshBasicMaterial({
               color: Math.random() * 0xffffff, // 随机颜色
                transparent: true, // 使用透明度
                opacity: 0.8, // 透明度为 0.2
                wireframe: true // 使用线框
        });
        // 实例化网格对象(球 = 几何体+材质)
        const ball = new THREE.Mesh(geometry, material);
        // 安排下球的位置(根据顺序绕圆心布置)
        const radian = (360 / total) * (Math.PI / 180);
        ball.position.set(
                8 * Math.sin(radian * i),
                8 * Math.cos(radian * i),
                0
        );
        scene.add(ball); // ball 加入到场景
}

// 设置相机位置
camera.position.z = 30;

renderer.render(scene, camera);

&lt;/script&gt;

&lt;style&gt; body { margin: 0; } &lt;/style&gt;
        </pre></div>
        <blockquote><button class="btnPre">运行代码</button></button></blockquote>
        <p>上例我们绘制了 8 个绕圆心排列的线框圆球。之所以使用线框,是因为基础材质 MessBasicMaterial 对象呈现出来的只是一个平面,加入线框球体的观感才可以渲染出来。要绘制更漂亮的球体,需要使用其它材质并配上光源,情况就相对复杂了。</p>
        <p>以上两组代码均可存为本地 .html 文档,或将代码拿到 <a href="http://mhh.52qingyin.cn/api/pencilcode/editor.html" target="_blank">简易编辑器</a> 运行。</p>
</div>

<script type="module">
        import hlight from 'https://638183.freep.cn/638183/web/helight/helight1.js';

        const divs = document.querySelectorAll('.divBox');
        const pres = document.querySelectorAll('.preBox');
        const btns = document.querySelectorAll('.btnPre');

        divs.forEach((div, key) => {
                hlight.hl(div, pres);
                btns.onclick = () => {
                        const value = pres.textContent;
                        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-10 10:18

默认的创建球体很不错,代码特别简洁。 MeshNormalMaterial,这个材质对象真好,形成的颜色那么漂亮,过渡均匀。默认的没线框,感觉更像实体,但这个实体在边缘部位感觉不那么圆,而加了线框的虽然同样不那么圆,感觉很好接受{:4_187:}

马黑黑 发表于 2025-5-10 10:22

红影 发表于 2025-5-10 10:18
默认的创建球体很不错,代码特别简洁。 MeshNormalMaterial,这个材质对象真好,形成的颜色那么漂亮,过渡 ...
抗锯齿+足够的水平和垂直分段数,法向量材质的圆也可以是边缘平滑的,你可以试下:将第二个例子的几何体对象参数挪到第一个例子,并尝试修改水平和垂直分段数,它们俩最好一致,比如都是32或都是100,默认是8

红影 发表于 2025-5-10 10:23

8个线框圆球也好美,密密麻麻的那么多的线框,如此简单就出现了,若要一个个三角去画,真要画到天荒地老去了,而使用three.js,想要多少个就有多少个,太厉害了{:4_199:}

马黑黑 发表于 2025-5-10 10:24

红影 发表于 2025-5-10 10:23
8个线框圆球也好美,密密麻麻的那么多的线框,如此简单就出现了,若要一个个三角去画,真要画到天荒地老去 ...

所以很多封装都是有必要的

红影 发表于 2025-5-10 10:24

相机的位置有什么讲究么?看你前一个取了5,后一个取了30,是在不同位置上观看吧,后面的球比较多,所以拉得远点看?

马黑黑 发表于 2025-5-10 10:30

红影 发表于 2025-5-10 10:24
相机的位置有什么讲究么?看你前一个取了5,后一个取了30,是在不同位置上观看吧,后面的球比较多,所以拉 ...

相机相当于观者的眼睛,它在屏幕的前方,基于与屏幕垂直的Z轴方向。它的位置设置有诸多考量,一是相机自身的角度,二是希望看到的对象的大小(越近看到的越大,近到 0 单位距离时啥也看不到),三是尽可能减少分散于场景各处或偏亮场景中心的3d对象的变形程度,等等等等。

红影 发表于 2025-5-10 10:55

马黑黑 发表于 2025-5-10 10:22
抗锯齿+足够的水平和垂直分段数,法向量材质的圆也可以是边缘平滑的,你可以试下:将第二个例子的几何体 ...

嗯嗯,分段足够细,就感觉更平滑了{:4_187:}

红影 发表于 2025-5-10 10:55

马黑黑 发表于 2025-5-10 10:24
所以很多封装都是有必要的

这些封装真的做到了简化了制作{:4_187:}

红影 发表于 2025-5-10 10:56

马黑黑 发表于 2025-5-10 10:30
相机相当于观者的眼睛,它在屏幕的前方,基于与屏幕垂直的Z轴方向。它的位置设置有诸多考量,一是相机自 ...

这里面讲究很听多的呢{:4_204:}

马黑黑 发表于 2025-5-10 11:35

红影 发表于 2025-5-10 10:55
嗯嗯,分段足够细,就感觉更平滑了

参数较多,其中的起始经纬、经纬跨度也可以适当调整,会得到不同的形状

马黑黑 发表于 2025-5-10 11:37

红影 发表于 2025-5-10 10:56
这里面讲究很听多的呢

一个圆球看着圆咕隆咚的,其学问可多了去,而人类对圆的真正认识,是对圆周率的发现,而发现圆周率的手段,是割圆法。能不讲究吗?

马黑黑 发表于 2025-5-10 11:37

红影 发表于 2025-5-10 10:55
这些封装真的做到了简化了制作

那必须的,不然封装啥

红影 发表于 2025-5-10 13:57

马黑黑 发表于 2025-5-10 11:35
参数较多,其中的起始经纬、经纬跨度也可以适当调整,会得到不同的形状

我用pencil code去演示,好像就第一次能出来,修改了再演示,就看不到效果了{:4_203:}

红影 发表于 2025-5-10 13:59

马黑黑 发表于 2025-5-10 11:37
一个圆球看着圆咕隆咚的,其学问可多了去,而人类对圆的真正认识,是对圆周率的发现,而发现圆周率的手段 ...

圆是很奇妙的形状,圆周率甚至是永无止境的。

红影 发表于 2025-5-10 14:01

马黑黑 发表于 2025-5-10 11:37
那必须的,不然封装啥

把共性的东西封起来,然后便于研究非共性的了。

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

红影 发表于 2025-5-10 14:01
把共性的东西封起来,然后便于研究非共性的了。

大概酱紫

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

红影 发表于 2025-5-10 13:59
圆是很奇妙的形状,圆周率甚至是永无止境的。

无理数,永不终止

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

红影 发表于 2025-5-10 13:57
我用pencil code去演示,好像就第一次能出来,修改了再演示,就看不到效果了

那可能是修改错误了

红影 发表于 2025-5-10 14:54

马黑黑 发表于 2025-5-10 14:13
大概酱紫

可以带来很多研究的便捷。
页: [1] 2 3 4 5 6 7
查看完整版本: three.js : 画球