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);
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!