ThreeJS点击生成点仅首次生效问题求助
Hey there, let's break down why your single-click points aren't showing up after the first one, and how to fix it properly.
What's Causing the Issue?
Asynchronous Texture Loading
Every time you calladdPoint(), you're reloading the sprite texture withnew THREE.TextureLoader().load(). This is an asynchronous operation—meaning the texture isn't ready when you create theTHREE.Pointsobject. The first click might work because by the time the renderer draws the next frame, the texture finishes loading and updates the material. But for subsequent clicks, thePointsis created immediately with an unloaded (empty) texture. YouralphaTest: 0.5setting then culls the fully transparent point, making it invisible.Inefficient Object Creation + Single-Vertex Rendering
Creating a brand newBufferGeometry,PointsMaterial, andPointsobject for every single click is terrible for performance (you’ll end up with hundreds of redundant objects) and triggers edge cases in Three.js. Single-vertexPointscan sometimes be overlooked by the renderer or frustum culling, whereas aPointsobject with thousands of vertices (like your loop example) gets prioritized and renders correctly once the texture loads.
Fixes to Get Your Points Showing Up Every Time
1. Load the Texture Once (Not Per Click)
Move texture loading outside of addPoint() so it only happens once during initialization. This avoids async race conditions and redundant network requests.
2. Reuse a Single Points Object
Instead of creating a new Points for each click, maintain one global Points instance and dynamically add vertices to its geometry. This is far more efficient and eliminates the single-vertex rendering issue.
Here's the revised code:
// Global variables for texture and reusable point system let spriteTexture; let points; let verticesArray = []; // Initialize texture and point system when your scene is ready function initPointSystem() { const loader = new THREE.TextureLoader(); loader.load('textures/disc.png', (texture) => { spriteTexture = texture; createBasePoints(); }); } // Create the single, reusable Points object function createBasePoints() { const geometry = new THREE.BufferGeometry(); // Start with an empty vertex array geometry.setAttribute('position', new THREE.Float32BufferAttribute(verticesArray, 3)); const material = new THREE.PointsMaterial({ size: 1, sizeAttenuation: true, map: spriteTexture, alphaTest: 0.5, transparent: true }); material.color.setHSL(1.0, 0.3, 0.7); points = new THREE.Points(geometry, material); group.add(points); } // Updated addPoint to add vertices to the existing system function addPoint(coord) { // Push new vertex coordinates to our global array verticesArray.push(coord.x, coord.y, coord.z); // Update the buffer geometry with the new vertices points.geometry.setAttribute('position', new THREE.Float32BufferAttribute(verticesArray, 3)); // Mark the position attribute as needing an update (critical for the renderer to pick up changes) points.geometry.attributes.position.needsUpdate = true; } // Call this when your scene is initialized initPointSystem();
Why This Works
- The texture is loaded once upfront, so it’s fully ready when we create the
Pointsobject. - We only have one
Pointsinstance, so every new vertex is added to the same geometry—no more single-vertex edge cases. - Updating the existing geometry instead of creating new objects keeps your scene performant even with hundreds of clicks.
内容的提问来源于stack exchange,提问作者rikicecchi

