马黑黑 发表于 2025-6-29 10:29

ThreeJS点材质着色问题

<style>
        .artBox { font-size: 18px; }
        .artBox > p { margin: 10px 0; line-height: 30px; }
        .artBox mark { padding: 4px 6px; background: lightblue; }
        #prevBox { position: fixed; top: 0; right: 0; bottom: 0; left: 0; background: #eee; display: none; padding: 0; overflow: hidden; z-index: 1000; margin: 0; }
        #prevBox::after { position: absolute; content: '关闭预览'; bottom: 10px; left: calc(50% - 40px); padding: 0 4px; width: 80px; height: 30px; line-height: 30px; text-align: center; border: 1px solid #efe; border-radius: 6px; background: #eee; font-size: 14px; box-shadow: 2px 2px 6px rgba(0,0,0,.25); cursor: pointer; }
        iframe { position: relative; width: 100%; height: 100%; border: none; outline: none; box-sizing: border-box; margin: 0; }
</style>

<div id="prevBox"></div>
<div class="artBox">
        <p><mark>THREE.PointsMaterial</mark>以点云的方式“绘制”缓冲几何体的面片。帖子<a href="http://mhh.52qingyin.cn/art/bshow.php?st=3&sd=3&art=mahei_1750931581" target="_blank">《中国史诗》</a>变大变小的播放控制器就是使用点材质+<mark>THREE.SphereGeometry</mark>做成。该帖球体面片上的点材质所有的点都是同一个颜色,里面这么设计:</p>
        <div class="hEdiv"><pre class="hEpre">
const sphereGeometry = new THREE.SphereGeometry();
const pointsMaterial = new THREE.PointsMaterial({ size: 0.05, color: 0x90ee90 });
        </pre></div>
        <p>这里,实例化的点材质 PointsMaterial 是一个点云整体(帖子代码将其命名为 pointsMaterial),我们可以在JS对象集合<mark>{ ... }</mark>里统一配置点材质相关属性,如点材质的个头 size、颜色 color 等等。这很方便,只是,当我们需要每一个点材质个体使用不同的颜色,它无能为力。这需要用到 ThreeJS 已经封装好的<mark>webGL</mark>顶点着色器等相关技术,我们可以为缓冲几何体设计点云各点的颜色。</p>
        <p>下面是一个完整的绘制示例:首先创建球体缓冲几何体,接着获取几何体的顶点总数,再设计对等数量的RGB颜色数组、使用该数组创建一个颜色体系,最后在点材质中应用顶点着色器——</p>
        <div class="hEdiv"><pre class="hEpre" id="pre1">
&lt;script type="module"&gt;
        import { THREE, scene, camera, renderer, clock, basic3, click3 } from 'https://638183.freep.cn/638183/3dev/3/3basic.js';
        basic3();

        const sphereGeometry = new THREE.SphereGeometry(); // 创建球体几何体
        const count = sphereGeometry.attributes.position.count; // 获得顶点数量(多少取决于几何体的配置)
        const colors = new Float32Array(count * 3); // 每个顶点需要3个值(R, G, B)
        // 为每一个点生成随机RGB数据并存入colors数组
        for (let i = 0; i &lt; count; i++) {
                colors = Math.random();   // R
                colors = Math.random(); // G
                colors = Math.random(); // B
        }
        // 使用缓冲几何体的 setAttribute 创建颜色体系
        sphereGeometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
        // 配置点材质
        const pointsMaterial = new THREE.PointsMaterial({
                size: 0.05, // 点的大小
                vertexColors: true, // 使用顶点着色器(关键)
        });
        // 合成点云 THREE.Points 并将其加入到场景
        const pointsBall = new THREE.Points(sphereGeometry, pointsMaterial);
        scene.add(pointsBall);
        renderer.render(scene, camera); // 渲染效果
&lt;/script&gt;
        </pre></div>
        <blockquote><button id="btnPrev1">运行代码</button></blockquote>
        <p>或许,有必要让点云球转起来。这个不复杂,创建一个动画函数并运行它即可:</p>
        <div class="hEdiv"><pre class="hEpre" id="pre2">
&lt;script type="module"&gt;
        import { THREE, scene, camera, renderer, clock, basic3, click3 } from 'https://638183.freep.cn/638183/3dev/3/3basic.js';
        basic3();

        const sphereGeometry = new THREE.SphereGeometry(1, 32, 32); // 创建球体几何体
        const count = sphereGeometry.attributes.position.count; // 获得顶点数量(多少取决于几何体的配置)
        const colors = new Float32Array(count * 3); // 每个顶点需要3个值(R, G, B)
        // 为每一个点生成随机RGB数据并存入colors数组
        for (let i = 0; i &lt; count; i++) {
                colors = Math.random();   // R
                colors = Math.random(); // G
                colors = Math.random(); // B
        }
        // 使用缓冲几何体的 setAttribute 创建颜色体系
        sphereGeometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
        // 配置点材质
        const pointsMaterial = new THREE.PointsMaterial({
                size: 0.05, // 点的大小
                vertexColors: true, // 使用顶点着色器(关键)
        });
        // 合成点云 THREE.Points 并将其加入到场景
        const pointsBall = new THREE.Points(sphereGeometry, pointsMaterial);
        scene.add(pointsBall);
        pointsBall.rotateX(-Math.PI / 6); // 点云球在X轴上倾斜
        renderer.render(scene, camera); // 渲染效果

        // 动画函数
        const animate = () => {
                const delta = clock.getDelta(); // 获取上下帧间隔时差
                pointsBall.rotation.y += delta / 2; // 点云球在Y轴上旋转
                renderer.render(scene, camera); // 渲染
                requestAnimationFrame(animate); // 循环调用函数
        };
       
        animate(); // 运行动画
&lt;/script&gt;
        </pre></div>
        <blockquote><button id="btnPrev2">运行代码</button></blockquote>
        <p>利用webGL顶点着色器给缓冲几何体着色其实不限于点云材质,任何能着色的材质都可以。本文的示例,将点云材质 THREE.PointsMaterial 更换为 THREE.MeshBasicMaterial 一样可以成立——当然需要将合成语句中的 THREE.Points(..., ...) 的 Points 跟着更换为 Mesh,前者是点云网格,后者是常规网格。</p>
</div>

<script type="module">
        import hlight from 'https://638183.freep.cn/638183/web/helight/helight1.js';
        const pres = document.querySelectorAll('.hEpre');
        const divs = document.querySelectorAll('.hEdiv');
        divs.forEach( (div, key) => hlight.hl(div, pres));
       
        const preView = (htmlCode, targetBox) => {
                if (targetBox.innerHTML) return;
                const iframe = document.createElement('iframe');
                htmlCode = htmlCode + '<style>body {margin: 0; }</style>';
                iframe.srcdoc = htmlCode;
                targetBox.appendChild(iframe);
                targetBox.style.display = 'block';
                targetBox.onclick = () => {
                        targetBox.innerHTML = '';
                        targetBox.style.display = 'none';
                }
        };

        const btns = ;
        const idPrevs = ;
        btns.forEach( (btn, key) => {
                btn.onclick = () => preView(idPrevs.textContent, prevBox);
        });
</script>

梦江南 发表于 2025-6-29 11:58

谢谢黑黑老师对代码的详细讲解。{:4_199:}

红影 发表于 2025-6-29 13:25

这个厉害了,可以给点云的每一个点分别着色了。为每一个点生成随机RGB数据,可否加一个透明度?

获得顶点数量(多少取决于几何体的配置)——这个是自动获得的么,没有具体数字呢。

红影 发表于 2025-6-29 13:26

看后面的介绍,这个不但能为点云着色,还能为常规网格着色呢,厉害了{:4_199:}

杨帆 发表于 2025-6-29 13:30

辛苦了!谢谢马老师经典讲授{:4_191:}

马黑黑 发表于 2025-6-30 14:02

杨帆 发表于 2025-6-29 13:30
辛苦了!谢谢马老师经典讲授

{:4_190:}

马黑黑 发表于 2025-6-30 14:02

红影 发表于 2025-6-29 13:26
看后面的介绍,这个不但能为点云着色,还能为常规网格着色呢,厉害了

是的

马黑黑 发表于 2025-6-30 14:03

红影 发表于 2025-6-29 13:25
这个厉害了,可以给点云的每一个点分别着色了。为每一个点生成随机RGB数据,可否加一个透明度?

获得顶 ...

支持透明度,使用 opacity 键值对

马黑黑 发表于 2025-6-30 14:03

梦江南 发表于 2025-6-29 11:58
谢谢黑黑老师对代码的详细讲解。

{:4_190:}

红影 发表于 2025-6-30 19:55

马黑黑 发表于 2025-6-30 14:02
是的

越学越是觉得这里面的东西好多啊。

红影 发表于 2025-6-30 19:56

马黑黑 发表于 2025-6-30 14:03
支持透明度,使用 opacity 键值对

哦哦,用 opacity 就可以啊,那还挺方便。

泡沫 发表于 2025-6-30 21:34

webGL顶点着色器好厉害。
第一次看到RGB三色要分开设定的,这样每个点都随机组合就有无数种颜色了。。
const colors = new Float32Array(count * 3); 这个随机色的设定真是天才算法{:4_173:}

泡沫 发表于 2025-6-30 21:35

头脑风暴持续中。。。接下来去瞅瞅是怎么炸的。。{:4_173:}

马黑黑 发表于 2025-6-30 21:35

泡沫 发表于 2025-6-30 21:35
头脑风暴持续中。。。接下来去瞅瞅是怎么炸的。。

{:4_173:}

泡沫 发表于 2025-6-30 22:25

马黑黑 发表于 2025-6-30 21:35


翘太多课,看晕乎是正常滴{:4_173:}

泡沫 发表于 2025-6-30 22:25

马黑黑 发表于 2025-6-30 21:35


说明炸裂效果深入人心{:4_170:}

马黑黑 发表于 2025-7-1 20:13

泡沫 发表于 2025-6-30 22:25
说明炸裂效果深入人心

{:4_196:}

马黑黑 发表于 2025-7-1 20:13

泡沫 发表于 2025-6-30 22:25
翘太多课,看晕乎是正常滴

正常正常

泡沫 发表于 2025-7-1 20:17

马黑黑 发表于 2025-7-1 20:13


小猪猪笑得欢快,深入人心这么开心{:4_173:}

泡沫 发表于 2025-7-1 20:18

马黑黑 发表于 2025-7-1 20:13
正常正常

看到新效果了。。。天天都有更新换代。。。
页: [1] 2 3
查看完整版本: ThreeJS点材质着色问题