Issue
I’m currently working on a 3D Pi Estimator built in three.js, for which the source code can be seen at 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 );
}
As you can see from this snippet, each point is coloured differently depending on whether it lies within a sphere centred at the origin. I am looking for a solution on how I can make a GUI using dat.gui
, such that I can interactively customise the colour of points that are outside and inside the sphere.
I am also unsure as to how or if I could produce this with InstancedMesh
which could lead to a significant boost in performance (this simulation struggles with more than ~15000 points) – I don’t know how to make this with InstancedMesh
because of the different dot colours depending on position, and would appreciate if there was a solution to this which in turn could make it easier to solve the issue with dat.gui
.
Solution
Here is the modification of your code, that uses 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/[email protected]';
import { OrbitControls } from 'https://cdn.skypack.dev/[email protected]/examples/jsm/controls/OrbitControls.js';
import { GUI } from "https://cdn.skypack.dev/[email protected]/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>
Answered By – prisoner849
Answer Checked By – Marie Seifert (BugsFixing Admin)