Display wireframe and solid color
Asked Answered
V

4

37

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?

Vacationist answered 21/7, 2015 at 12:31 Comment(4)
Maybe a custom shader?Scamander
can you further explain? How can someone use shader material for this purpose?Vacationist
You can define your own shaders, so you might try with that: aerotwist.com/tutorials/an-introduction-to-shaders-part-1 If not, you can always use a wireframe texture with a solid background.Scamander
have a look at: threejs.org/examples/#webgl_materials_wireframeMccowan
E
83

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

Extent answered 21/7, 2015 at 14:4 Comment(7)
linewidth doesn't change the width of the lines with the EdgesHelper or WireframeHelperThine
@Thine If you are running Windows, it is likely an ANGLE limitation.Extent
Yep, just tested on linux, and the linewidth property works. Kind of annoying though.Thine
if the mesh geometry changes this solution doesn't work.Calderon
@ArmenB: when mesh geometry changes, update the wireframe like this: geoWireframe.setFromObject(objectMesh), where geoWireframe is geo above and objectMesh is mesh above. jbRemarque
This requires extra GPU calls to add the wireframe. It'd be nice if the material shaders supported it all in one material.Speaker
@Remarque That's not a method of the Object3D class, it's specific to only Box3.Partial
B
9

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);
        
};
Banquet answered 25/10, 2018 at 18:26 Comment(0)
C
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;
}
Carrell answered 24/7, 2015 at 12:22 Comment(4)
Using later TS versions you can use the this.fragmentSrc = ` syntax , which allows multiline strings.Addison
Is there an online example using this?Calcine
There is an online example here: homepages.loria.fr/BLevy/GEOGRAM/geobox.htmlCarrell
I'm trying to implement this with Autodesk's viewer but it doesn't work, I asked here about it: #45918111 any ideas why?Calcine
T
3

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...

Treenatreenail answered 7/11, 2017 at 18:35 Comment(1)
createMultiMaterialObject() instantiates a separate Mesh for each material, and consequently, does not "avoid cloning".Extent

© 2022 - 2024 — McMap. All rights reserved.