36

Is it possible to display the wireframe of the object and also the solid color of its faces on the same object? I found a way using a clone of the object and assign different materials e.g

var geometry = new THREE.PlaneGeometry(plane.width, plane.height,width - 1, height - 1);
var materialWireframe = new THREE.MeshPhongMaterial({color:"red",wireframe:true});
var materialSolid = new THREE.MeshPhongMaterial({color:"green",wireframe:false});
var plane = new THREE.Mesh(geometry, materialWireframe );
var plane1 = plane.clone();
plane1.material = materialSolid ;
plane1.material.needsUpdate = true;

any thoughts?

Edric
  • 24,639
  • 13
  • 81
  • 91
prieston
  • 1,426
  • 2
  • 18
  • 39
  • Maybe a custom shader? – Shomz Jul 21 '15 at 12:33
  • can you further explain? How can someone use shader material for this purpose? – prieston Jul 21 '15 at 12:35
  • You can define your own shaders, so you might try with that: https://aerotwist.com/tutorials/an-introduction-to-shaders-part-1/ If not, you can always use a wireframe texture with a solid background. – Shomz Jul 21 '15 at 12:38
  • have a look at: http://threejs.org/examples/#webgl_materials_wireframe – meirm Jul 21 '15 at 13:13

4 Answers4

80

To render both a model and its wireframe, you can use a pattern like this one:

// mesh
var material = new THREE.MeshPhongMaterial( {
    color: 0xff0000,
    polygonOffset: true,
    polygonOffsetFactor: 1, // positive value pushes polygon further away
    polygonOffsetUnits: 1
} );
var mesh = new THREE.Mesh( geometry, material );
scene.add( mesh )

// wireframe
var geo = new THREE.EdgesGeometry( mesh.geometry ); // or WireframeGeometry
var mat = new THREE.LineBasicMaterial( { color: 0xffffff } );
var wireframe = new THREE.LineSegments( geo, mat );
mesh.add( wireframe );

The use of polygonOffset will help prevent z-fighting between the mesh material and the wireframe line. Consequently, the wireframe will look a lot better.

three.js r.126

WestLangley
  • 102,557
  • 10
  • 276
  • 276
8

This can also be achieved with WireframeGeometry: https://threejs.org/docs/#api/en/geometries/WireframeGeometry. (and give plane and line the same position, you can also play with opacity see the docs).

let geometryWithFillAndWireFrame = () => {

    let geometry = new THREE.PlaneGeometry(250, 250, 10, 10);
    let material = new THREE.MeshBasicMaterial( { color: 0xd3d3d3} );
    let plane = new THREE.Mesh(geometry, material);

    scene.add(plane);

    let wireframe = new THREE.WireframeGeometry( geometry );

    let line = new THREE.LineSegments( wireframe );
        
    line.material.color.setHex(0x000000);
        
    scene.add(line);
        
};
borisdiakur
  • 10,387
  • 7
  • 68
  • 100
3

To do that, a possibility is to use a GLSL fragment shader that changes the fragment color when the fragment is near one edge of the triangle. Here is the GLSL shader that I am using. As input, it takes the barycentric coordinates of the fragment in the triangle, and an edge mask that selects for each edge whether it should be drawn or not. (rem: I had to use it with the compatibility profile for backward compatibility reasons, if you do not want to do that, it can easily be adapted):

(fragment source)

#version 150 compatibility

flat in float diffuse;
flat in float specular;
flat in vec3  edge_mask;
in vec2 bary;
uniform float mesh_width = 1.0;
uniform vec3 mesh_color = vec3(0.0, 0.0, 0.0);
uniform bool lighting = true;
out vec4 frag_color;

float edge_factor(){
    vec3 bary3 = vec3(bary.x, bary.y, 1.0-bary.x-bary.y);
    vec3 d = fwidth(bary3);
    vec3 a3 = smoothstep(vec3(0.0,0.0,0.0), d*mesh_width, bary3);
    a3 = vec3(1.0, 1.0, 1.0) - edge_mask + edge_mask*a3;
    return min(min(a3.x, a3.y), a3.z);
}

void main() {
    float s = (lighting && gl_FrontFacing) ? 1.0 : -1.0;
    vec4  Kdiff = gl_FrontFacing ?
         gl_FrontMaterial.diffuse : gl_BackMaterial.diffuse;
    float sdiffuse = s * diffuse;
    vec4 result = vec4(0.1, 0.1, 0.1, 1.0);
    if(sdiffuse > 0.0) {
       result += sdiffuse*Kdiff +
                 specular*gl_FrontMaterial.specular;
    }
    frag_color = (mesh_width != 0.0) ?
                  mix(vec4(mesh_color,1.0),result,edge_factor()) :
                  result;
}
Luis Perez
  • 27,650
  • 10
  • 79
  • 80
BrunoLevy
  • 2,495
  • 17
  • 30
  • 1
    Using later TS versions you can use the this.fragmentSrc = ` syntax , which allows multiline strings. – Sentinel Nov 28 '16 at 16:34
  • Is there an online example using this? – shinzou Aug 28 '17 at 09:47
  • There is an online example here: http://homepages.loria.fr/BLevy/GEOGRAM/geobox.html – BrunoLevy Aug 28 '17 at 12:10
  • I'm trying to implement this with Autodesk's viewer but it doesn't work, I asked here about it: https://stackoverflow.com/questions/45917611/shader-wireframe-of-an-object any ideas why? – shinzou Aug 29 '17 at 07:20
2

To avoid cloning my object I used a pattern like that :

var mat_wireframe = new THREE.MeshBasicMaterial({color: 0x000000, wireframe: true});
var mat_lambert = new THREE.MeshLambertMaterial({color: 0xffffff, shading: THREE.FlatShading});
var meshmaterials = [ mat_wireframe, mat_lambert ];

and then applied it to my mesh like that :

var myMesh = THREE.SceneUtils.createMultiMaterialObject( mesh_geometry, meshmaterials );
scene.add( myMesh ) ; 

I hope it could help...

Julien_
  • 83
  • 1
  • 7
  • 5
    `createMultiMaterialObject()` instantiates a separate `Mesh` for each material, and consequently, does not "avoid cloning". – WestLangley Nov 07 '17 at 19:28