Edges disappear in playcanvas model-viewer

Hello, guys! In playcanvas model viewer I locally implemented additional functionality, that works similar to wireframe rendering, but highlights only edges. This is how I collect edges:

private collectEdgeInstances(entity: Entity | Array<MeshInstance>)
    {
        const edgeInstances: Array<MeshInstance> = [];

        const getUniqueEdges = (positions, indices) => {
            const edgeMap = new Map();
        
            const addEdge = (start, end) => {
                const key = `${Math.min(start, end)},${Math.max(start, end)}`;
                if (edgeMap.has(key)) {
                    edgeMap.set(key, edgeMap.get(key) + 1);
                } else {
                    edgeMap.set(key, 1);
                }
            };
        
            for (let i = 0; i < indices.length; i += 3) {
                const a = indices[i];
                const b = indices[i + 1];
                const c = indices[i + 2];
        
                addEdge(a, b);
                addEdge(b, c);
                addEdge(c, a);
            }
        
            const edges = [];
            edgeMap.forEach((value, key) => {
                if (value === 1) { // Only add edges that appear in one triangle (boundary edges)
                    const [start, end] = key.split(',').map(Number);
                    edges.push(start, end);
                }
            });
        
            return edges;
        }

        const createEdgeGeometry = (positions, edges) => {
            const vertices = [];

            for (let i = 0; i < edges.length; i += 2) {
                const start = edges[i];
                const end = edges[i + 1];
                
                vertices.push(positions[start * 3], positions[start * 3 + 1], positions[start * 3 + 2]);
                vertices.push(positions[end * 3], positions[end * 3 + 1], positions[end * 3 + 2]);
            }
        
            return vertices;
        }

        var meshes: Array<MeshInstance>;
        if (entity instanceof Entity) {
            const components = entity.findComponents('render');
            for (let i = 0; i < components.length; i++) {
                const render = components[i] as RenderComponent;
                if (render.meshInstances) {
                    meshes = render.meshInstances;                    
                }
            }
        } else {
            meshes = entity;
        }

        for (let m = 0; m < meshes.length; m++) {
            const meshInstance = meshes[m];                        
            var positions : any = [];
            var indices : any = [];
            meshInstance.mesh.getPositions(positions);
            meshInstance.mesh.getIndices(indices);

            const edges = getUniqueEdges(positions, indices);
            const edgeVertices = createEdgeGeometry(positions, edges);

            const edgeMesh = new Mesh(this.app.graphicsDevice);
            const edgeVertexFormat = new VertexFormat(this.app.graphicsDevice, [{
                semantic: SEMANTIC_POSITION,
                components: 3,
                type: TYPE_FLOAT32
            }]);

            const numVerts = edgeVertices.length / 3;
            edgeMesh.vertexBuffer = new VertexBuffer(this.app.graphicsDevice, edgeVertexFormat, numVerts, {
				usage: BUFFER_STATIC
            });
            edgeMesh.vertexBuffer.setData(new Float32Array(edgeVertices));
            edgeMesh.primitive[0].type = PRIMITIVE_LINES;
            edgeMesh.primitive[0].base = 0;
            edgeMesh.primitive[0].count = numVerts;
            edgeMesh.primitive[0].indexed = false;

            const mi = new MeshInstance(edgeMesh, this.edgeMaterial, meshInstance.node);

            edgeInstances.push(mi);
        }

        return edgeInstances;
    }

An this is how I build them (similar to buildWireframeMeshes):

private buildEdgeMeshes() {
        this.edgeMeshInstances = this.getSelectedEdgeInstances();
        this.app.scene.layers.getLayerByName('World').addMeshInstances(this.edgeMeshInstances);
    }

And getSelectedEdgeInstances is as simple as:

private getSelectedEdgeInstances() {
        return this.selectedNode ? this.collectEdgeInstances(this.selectedNode as Entity) : this.collectEdgeInstances(this.meshInstances);
    }

So, the whole logic is totally like with wireframe. It only differs in the way how edges are collected. An the problem I see, is that when I zoom in, edges disappear. And I do not know what may lead to such behaviour. And this is how it all looks like:

As you can see, when I zoomed in, edges disappeared. How can I possible fix it? And BTW is there a better way, then detecting edges like so? To me it looks like, edges are evaluated for too long time. Probably, we can implement some shader, that can do the job much faster? I tried myself to apply edge detector post effect, but it does not work and throws some errors.

Thanks in advance!

Do the edges disappear all together, or they ‘fade’ somehow? How about when camera orbits around? Maybe share a short video.

No, it does not disappear all together. If I zoom in, they hide, if I zoom out, they appear again. And when rotating - same behaviour - show-hide-show-hide - depending on how close we are to the model. (writing from another account)

You can check it here http://84.252.138.255:8085/?load=http://84.252.138.255/files/digitaltwineditor/files/2024/06/2b4c5053-4faf-4d80-ab4c-598c4b7e2c34.glb. Choose settings and toggle Edge.

I’m also interested to know if it is possible to achieve similar edge rendering, using just shaders - so not to compute edges manually every time. I tried myself dozens of shaders from the Web, but they do not work. I’ve see also edge-detector post processing script for playcanvas, but could not make it work in model viewer. Probably, there is a stand alone edge detector shader, that some one specifically implemented for playcanvas. Thanks in advance!

Hi, there! I got it - the whole problem was because of frustumCulling set to true. Now it works as expected. Though, I still have a question about an alternative solution of this task - without calculating edges by hand, but with the help of a shader.