I have got the following issue with an object containing many points (created with THREE.Points
). An example would be https://threejs.org/examples/?q=points#webgl_interactive_points
. Now, I am trying to hide a specific point if I click on it. Using the ´intersect-method I'm able to enlarge these or change their color: using
particles.geometry.attributes.size.array[INDEX]and setting
.needsUpdate`. However, I complete invisibility is not possible. The goal is to hide the points by the INDEX it has in the 'buffer geometry'. Below you can find a code snippet of the example.
var vertices = new THREE.BoxGeometry( 200, 200, 200, 16, 16, 16 ).vertices;
var positions = new Float32Array( vertices.length * 3 );
var colors = new Float32Array( vertices.length * 3 );
var sizes = new Float32Array( vertices.length );
var vertex;
var color = new THREE.Color();
for ( var i = 0, l = vertices.length; i < l; i ++ ) {
vertex = vertices[ i ];
vertex.toArray( positions, i * 3 );
color.setHSL( 0.01 + 0.1 * ( i / l ), 1.0, 0.5 );
color.toArray( colors, i * 3 );
sizes[ i ] = PARTICLE_SIZE * 0.5;
}
var geometry = new THREE.BufferGeometry();
geometry.setAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
geometry.setAttribute( 'customColor', new THREE.BufferAttribute( colors, 3 ) );
geometry.setAttribute( 'size', new THREE.BufferAttribute( sizes, 1 ) );
//
var material = new THREE.ShaderMaterial( {
uniforms: {
color: { value: new THREE.Color( 0xffffff ) },
pointTexture: { value: new THREE.TextureLoader().load( "textures/sprites/disc.png" ) }
},
vertexShader: document.getElementById( 'vertexshader' ).textContent,
fragmentShader: document.getElementById( 'fragmentshader' ).textContent,
alphaTest: 0.9
} );
//
particles = new THREE.Points( geometry, material );
scene.add( particles );
Changing the 'size' on hover works as the following:
// function render() {
particles.rotation.x += 0.0005;
particles.rotation.y += 0.001;
var geometry = particles.geometry;
var attributes = geometry.attributes;
raycaster.setFromCamera( mouse, camera );
intersects = raycaster.intersectObject( particles );
if ( intersects.length > 0 ) {
if ( INTERSECTED != intersects[ 0 ].index ) {
attributes.size.array[ INTERSECTED ] = PARTICLE_SIZE;
INTERSECTED = intersects[ 0 ].index;
attributes.size.array[ INTERSECTED ] = PARTICLE_SIZE * 1.25;
attributes.size.needsUpdate = true;
}
I have looked at shaders (in my opinion way too complex for this task) and trying to create transparent material. Is there something overlooked?
Much thanks in advance.
As an option, using of an additional buffer attribute for visibility and processing it in shaders, does the trick:
body {
overflow: hidden;
margin: 0;
}
<script type="module">
import * as THREE from "https://threejs.org/build/three.module.js";
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 100);
camera.position.set(0, 0, 10);
let renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0x444444);
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
let g = new THREE.PlaneBufferGeometry(10, 10, 10, 10);
g.index = null;
let pos = g.getAttribute("position");
let colors = [];
let color = new THREE.Color();
let sizes = [];
let visibility = [];
for(let i = 0; i < pos.count; i++){
color.set( Math.random() * 0x888888 + 0x888888);
colors.push(color.r, color.g, color.b);
sizes.push( Math.random() * 0.5 + 0.5);
visibility.push(1);
}
g.setAttribute("color", new THREE.Float32BufferAttribute(colors, 3));
g.setAttribute("sizes", new THREE.Float32BufferAttribute(sizes, 1));
g.setAttribute("visibility", new THREE.Float32BufferAttribute(visibility, 1));
let m = new THREE.PointsMaterial({
vertexColors: true,
onBeforeCompile: function(shader){
shader.vertexShader = `
attribute float sizes;
attribute float visibility;
varying float vVisible;
${shader.vertexShader}`
.replace(
`gl_PointSize = size;`,
`gl_PointSize = size * sizes;
vVisible = visibility;
`
);
shader.fragmentShader = `
varying float vVisible;
${shader.fragmentShader}`
.replace(
`#include <clipping_planes_fragment>`,
`
if (vVisible < 0.5) discard;
#include <clipping_planes_fragment>`
)
}
});
let p = new THREE.Points(g, m);
scene.add(p);
let oldIndex = 0;
setInterval(function(){
let newIndex = THREE.Math.randInt(0, pos.count - 1);
g.attributes.visibility.setX(oldIndex, 1);
g.attributes.visibility.setX(newIndex, 0);
oldIndex = newIndex;
g.attributes.visibility.needsUpdate = true;
}, 500);
renderer.setAnimationLoop(animate);
function animate(){
renderer.render(scene, camera);
}
</script>