我如何根据点与原点的距离使用 dat.gui 在 three.js 中对点进行不同的着色?

How can I colour points differently in three.js using dat.gui depending on their distance from the origin?

我目前正在开发内置于 three.js 中的 3D Pi Estimator,其源代码可在 https://jsfiddle.net/fny94e7w/.

中查看
let num_points = 5000;
            let in_sphere = 0;
            let out_sphere = 0;
            let geometry_point = new THREE.SphereGeometry( 0.1, 3, 2 );
            for (let i = 0; i < num_points; i++) {
                let material_point = new THREE.MeshBasicMaterial( { color: 0xffffff } ); // Default
                let point = new THREE.Mesh( geometry_point, material_point );
                let point_pos_x = (Math.random() - 0.5) * 20;
                let point_pos_y = (Math.random() - 0.5) * 20;
                let point_pos_z = (Math.random() - 0.5) * 20;
                if (Math.pow(point_pos_x, 2) + Math.pow(point_pos_y, 2) + Math.pow(point_pos_z, 2) <= 100) {
                    point.material.color.setHex( 0x42c5f5 );
                    in_sphere++;
                }
                else {
                    point.material.color.setHex( 0xffffff );
                    out_sphere++;
                }
                point.position.set(point_pos_x,
                                   point_pos_y,
                                   point_pos_z);
                scene.add( point );
            }

正如您从该片段中看到的那样,每个点的颜色都不同,具体取决于它是否位于以原点为中心的球体内。我正在寻找关于如何使用 dat.gui 制作 GUI 的解决方案,以便我可以交互式地自定义球体内外的点的颜色。

我也不确定我如何或是否可以用 InstancedMesh 产生这个,这可能会导致性能的显着提升(这个模拟挣扎超过 ~15000 点) - 我不知道如何使用 InstancedMesh 来实现这一点,因为不同的点颜色取决于位置,如果有解决方案可以使 dat.gui.[= 的问题更容易解决,我们将不胜感激。 18=]

这是对您的代码的修改,它使用 InstancedMesh

body {
  overflow: hidden;
  margin: 0;
}

#info {
  position: absolute;
  top: 10px;
  width: 100%;
  text-align: center;
  z-index: 100;
  display: block;
  color: white;
  font-family: 'Courier New', monospace;
  s
}

#lil-gui {
  left: 10px;
  top: 70px;
}
      
<div id="info">3d pi estimator <br> dots in sphere: 0 • dots outside sphere: • pi estimate: </div>
<script type="module">
import * as THREE from 'https://cdn.skypack.dev/three@v0.136.0';
import { OrbitControls } from 'https://cdn.skypack.dev/three@v0.136.0/examples/jsm/controls/OrbitControls.js';
import { GUI } from "https://cdn.skypack.dev/three@0.136.0/examples/jsm/libs/lil-gui.module.min.js";


const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );

const renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );

const controls = new OrbitControls( camera, renderer.domElement );
controls.autoRotate = true;
camera.position.z = 30;
camera.position.y = 15;
controls.update();

const geometry_cube = new THREE.BoxGeometry( 20, 20, 20 );
const edges_cube = new THREE.EdgesGeometry( geometry_cube );
const cube = new THREE.LineSegments( edges_cube, new THREE.LineBasicMaterial( { color: 0xffffff } ) );
scene.add( cube );

const geometry_sphere = new THREE.SphereGeometry( 10, 32, 16 );
const material_sphere = new THREE.MeshBasicMaterial( { color: 0xffffff } );
const sphere = new THREE.Mesh( geometry_sphere, material_sphere );
sphere.material.transparent = true;
sphere.material.opacity = 0.5;
scene.add( sphere );

let num_points = 30000;
let in_sphere = 0;
let out_sphere = 0;
let geometry_point = new THREE.SphereGeometry( 0.1, 3, 2 );
let colorController = {
  color1: "#ffffff",
  color2: "#42c5f5"
};
let uniforms = {
  color1: {value: new THREE.Color(colorController.color1)},
  color2: {value: new THREE.Color(colorController.color2)}
};
let material_point = new THREE.MeshBasicMaterial( { 
  color: 0xffffff,
  onBeforeCompile: shader => {
    shader.uniforms.color1 = uniforms.color1;
    shader.uniforms.color2 = uniforms.color2;
    shader.vertexShader = `
      attribute float colorIdx;
      varying float vColorIdx;
      ${shader.vertexShader}
    `.replace(
      `#include <begin_vertex>`,
      `#include <begin_vertex>
        vColorIdx = colorIdx;
      `
    );
    //console.log(shader.vertexShader);
    shader.fragmentShader = `
      uniform vec3 color1;
      uniform vec3 color2;
      varying float vColorIdx;
      ${shader.fragmentShader}
    `.replace(
      `vec4 diffuseColor = vec4( diffuse, opacity );`,
      `
      vec3 finalColor = mix(color1, color2, vColorIdx);
      finalColor *= diffuse;
      vec4 diffuseColor = vec4( finalColor, opacity );
      `
    );
    //console.log(shader.fragmentShader);
  }
} );
let point = new THREE.InstancedMesh( geometry_point, material_point, num_points );
let dummy = new THREE.Object3D();
let colorIdx = [];
for (let i = 0; i < num_points; i++) {
    dummy.position.random().subScalar(0.5).multiplyScalar(20);
    dummy.updateMatrix();
    point.setMatrixAt(i, dummy.matrix);
    if (dummy.position.length() <= 10) {
        colorIdx.push(1);
        in_sphere++;
    }
    else {
        colorIdx.push(0);
        out_sphere++;
    }
}
geometry_point.setAttribute("colorIdx", new THREE.InstancedBufferAttribute(new Float32Array(colorIdx), 1));
scene.add(point);

let gui = new GUI({autoplace:false});
gui.domElement.id = "lil-gui"
gui.addColor(colorController, "color1").onChange(val => {uniforms.color1.value.set(val)});
gui.addColor(colorController, "color2").onChange(val => {uniforms.color2.value.set(val)});

document.getElementById("info").innerHTML = "3d pi estimator <br>" + "dots in sphere: " + in_sphere + " • dots outside sphere: " + out_sphere + " • pi estimate: " + ((6 * in_sphere) / (in_sphere + out_sphere)); 

function animate() {
    requestAnimationFrame( animate );
    controls.update(); // Required because of auto-rotation
    renderer.render( scene, camera );
};

animate();
</script>