1

I'm trying to replicate the effect shown in this Three.js example but instead of showing the wireframe and an opaque box, I'd like to show just the edges without any faces (like what is shown when using the THREE.EdgesGeometry.) I know that setting the linewidth property doesn't work and that using shaders is necessary but I'm not really sure where to begin. For reference, these are the shaders being used in the above Three.js example:

Vertex Shader:

attribute vec3 center;
varying vec3 vCenter;

void main() {
    vCenter = center;
    gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );

}

Fragment Shader:

varying vec3 vCenter;

float edgeFactorTri() {
    vec3 d = fwidth( vCenter.xyz );
    vec3 a3 = smoothstep( vec3( 0.0 ), d * 1.5, vCenter.xyz );
    return min( min( a3.x, a3.y ), a3.z );
}

void main() {

    gl_FragColor.rgb = mix( vec3( 1.0 ), vec3( 0.2 ), edgeFactorTri() );
    gl_FragColor.a = 1.0;
}

I've gotten as far as figuring out that changing what d gets multiplied by (1.5 in the example) is what determines the thickness of the line but I'm completely lost as to how the vCenter variable is actually used (it's a vec3 that is either [1, 0, 0], [0, 1, 0] or [0, 0, 1]) or what I could use to make the THREE.EdgesGeometry render with thicker lines like in the example.

Here is what happens when I try rendering the edges geometry with these shaders:

<script type="x-shader/x-vertex" id="vertexShader">
            attribute vec3 center;
            varying vec3 vCenter;

            void main() {
                vCenter = center;
                gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );

            }
</script>

<script type="x-shader/x-fragment" id="fragmentShader">
            varying vec3 vCenter;
            uniform float lineWidth;

            float edgeFactorTri() {
                float newWidth = lineWidth + 0.5;
                vec3 d = fwidth( vCenter.xyz );
                vec3 a3 = smoothstep( vec3( 0.0 ), d * newWidth, vCenter.xyz );
                return min( min( a3.x, a3.y ), a3.z );
            }

            void main() {

                gl_FragColor.rgb = mix( vec3( 1.0 ), vec3( 0.2 ), edgeFactorTri() );
                gl_FragColor.a = 1.0;
            }
</script>

Javascript:

size = 150
geometry = new THREE.BoxGeometry(size, size, size);
material = new THREE.MeshBasicMaterial({ wireframe: true });

mesh = new THREE.Mesh(geometry, material);
mesh.position.x = -150;
scene.add(mesh);

//

// geometry = new THREE.BufferGeometry().fromGeometry(new THREE.BoxGeometry(size, size, size));
geometry = new THREE.EdgesGeometry(new THREE.BoxGeometry(size, size, size));
setupAttributes(geometry);

material = new THREE.ShaderMaterial({
    uniforms: { lineWidth: { value: 10 } },
    vertexShader: document.getElementById("vertexShader").textContent,
    fragmentShader: document.getElementById("fragmentShader").textContent
});

material.extensions.derivatives = true;
mesh = new THREE.Mesh(geometry, material);
mesh.position.x = 150;
scene.add(mesh);

//

geometry = new THREE.BufferGeometry().fromGeometry(new THREE.SphereGeometry(size / 2, 32, 16));

setupAttributes(geometry);

material = new THREE.ShaderMaterial({
    uniforms: { lineWidth: { value: 1 } },
    vertexShader: document.getElementById("vertexShader").textContent,
    fragmentShader: document.getElementById("fragmentShader").textContent
});

material.extensions.derivatives = true;

mesh = new THREE.Mesh(geometry, material);
mesh.position.x = -150;
scene.add(mesh);

jsFiddle

As you can see in the fiddle, this is not what I'm looking for, but I don't have a good enough grasp on how the shaders work to know where I'm going wrong or if this approach would work for what I want.

I've looked into this answer but I'm not sure how to use it as a ShaderMaterial and I can't use it as a shader pass (here are the shaders he uses for his answer.)

I've also looked into THREE.MeshLine and this issue doesn't seem to have been resolved.

Any guidance would be greatly appreciated!

1 Answers1

2

You want to modify this three.js example so the mesh is rendered as a thick wireframe.

The solution is to modify the shader and discard fragments in the center portion of each face -- that is, discard fragments not close to an edge.

You can do that like so:

void main() {

    float factor = edgeFactorTri();
    if ( factor > 0.8 ) discard; // cutoff value is somewhat arbitrary
    gl_FragColor.rgb = mix( vec3( 1.0 ), vec3( 0.2 ), factor );
    gl_FragColor.a = 1.0;
}

You can also set material.side = THREE.DoubleSide if you want.

updated fiddle: https://jsfiddle.net/vy0we5wb/4.

three.js r.89

WestLangley
  • 102,557
  • 10
  • 276
  • 276