Improving the performance of high density lines

I am using LineSegments2 along with LineGeometry and LineMaterial to display the fat lines. The count of vertices for each line may differ. The problem I am facing is, if the count of lines increases to 1000 and more, the fps drops to 10. How can I improve the performance?

Are you doing multiple lines in a single geometry?
1000 sounds low, unless you’re doing a single geometry+material for each one.

You should be using 1 geometry and 1 material if possible.

1 Like

Thanks for your response @manthrax. Yes, I am using single geometry+material for each one. Is there any example which shows how to use single geometry and material for all the lines? The reason I was using single geometry+material for each line is because I am creating them on the go i.e., based on the mouse clicks. The user clicks to create vertex and join those vertices to create lines.
I am not sure how can I optimize this since I am new to Threejs.

You might be better off just using an array of [start,end,start,end] points, and then when the list is changed, rebuild the geometry.

Here’s a class I use for dynamic debug line drawing:
It’s not super efficient but its sorta meant to be quick and dirty.
I preallocate a buffer of 1 million lines, and then just update the count and rewrite the array when its flagged as needing update:

export default class DebugDrawer {
    set color(v) {
        this._currentColor = v;
        this._color.set(v)
    }    
    get color() {
        return this._currentColor
    }
    constructor({THREE}) {
        let {sin, cos, PI, max, min, abs} = Math;
        let vec3 = (x,y,z)=>new THREE.Vector3(x,y,z)
        let v0 = vec3()
        let v1 = vec3()
        let v2 = vec3()
        let cursor = vec3()
        this._color = new THREE.Color('white');
        let material = new THREE.LineBasicMaterial({
            color: 0xffffff,
            vertexColors: true,
            toneMapped: false,
            
            //opacity:.2,
            transparent:true,
            blending:THREE.AdditiveBlending
        });
        this.lines = new THREE.LineSegments(new THREE.BufferGeometry(),material);
        this.lines.frustumCulled = false;
        let geometry = this.geometry = this.lines.geometry;
        let points = new THREE.BufferAttribute(new Float32Array(3000000),3).setUsage(THREE.DynamicDrawUsage)
        let colors = new THREE.BufferAttribute(new Float32Array(3000000),3).setUsage(THREE.DynamicDrawUsage)
        geometry.setAttribute('position', points)
        geometry.setAttribute('color', colors)
        let top = 0;
        let lastVersion=0;
        let version=0;
        this.lines.onBeforeRender=()=>{
            if(version==lastVersion)return
            geometry.setDrawRange(0, top>points.count ? Infinity : top)
            points.needsUpdate = true;
            colors.needsUpdate = true;
            lastVersion = version;
        }
        let vt = (p,c=this._color)=>{
            points.setXYZ(top%points.count, p.x, p.y, p.z)
            colors.setXYZ(top%points.count, c.r, c.g, c.b)
            top++;
        }
        let moveto = this.moveto = (p,d,q)=>((d !== undefined) && cursor.set(p, d, q)) || cursor.copy(p)
        let lineto = this.lineto = (p,d,q)=>{
            vt(cursor);
            moveto(p, d, q);
            vt(cursor);
            version++;
        }
        this.cls = ()=>{
            top = 0;
            version++;
        }
        this.circle = (radius=.1,sides=16)=>{
            v2.copy(cursor)
            for (let i = 0; i <= sides; i++) {
                v1.copy(v2);
                let th = i * PI * 2 / sides
                v1.x += sin(th) * radius
                v1.z += cos(th) * radius
                if (!i)
                    moveto(v1);
                else
                    lineto(v1);
            }
            cursor.copy(v2);
        }
}

Then you can call it like:

let dd = new DebugDrawer({THREE})
scene.add(dd.lines);

dd.color = 'red';
dd.moveto(0,0,0);
dd.lineto(10,10,10);

2 Likes