我如何根据点与原点的距离使用 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>
我目前正在开发内置于 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>