Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Odd artifacts and Empty texture in extruded shape in three.js

Tags:

three.js

I have tried to give 3D feeling to SVG paths (lineto and moveto) by extruding shapes using Three.js, but the process causes some artifacts that I cannot remove.

What could cause odd artifacts in my rendered 3D shape? Is there a way to remove them?

The artifacts are marked with arrows in the sample image below.

enter image description here

The live example is here: http://jsfiddle.net/pHn2B/24/

And the code is here:

// Picking with Callback

// three.js r.52
var container,
    info,
    camera,
    scene,
    light,
    geometry,
    mesh,
    projector,
    renderer,
    controls;
    objects = [];

// dom
container = document.createElement( 'div' );
document.body.appendChild( container );

// info
info = document.createElement( 'div' );
info.style.position = 'absolute';
info.style.top = '10px';
info.style.width = '100%';
info.style.textAlign = 'center';
info.innerHTML = "drag to rotate camera; click to select";
container.appendChild( info );

// renderer
renderer = new THREE.CanvasRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );

// scene
scene = new THREE.Scene();

// camera
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.set( 0, 300, 500 );
scene.add( camera );

// controls
controls = new THREE.OrbitControls( camera );

// light
scene.add( new THREE.AmbientLight( 0x222222 ) );

// light
light = new THREE.PointLight( 0xaaaaaa );
light.position = camera.position;
scene.add( light );

// geometry
geometry = new THREE.CubeGeometry( 100, 100, 500 );

// material
material = new THREE.MeshLambertMaterial( { color: 0xff0000, ambient: 0xff0000, overdraw: true } );

// mesh
mesh = new THREE.Mesh( geometry, material );
mesh.position.set( -100, -100, 200 );
mesh.name = "Red Object";
mesh.callback = function() { info.innerHTML = this.name; }
scene.add( mesh );

objects.push( mesh );

// geometry
////////////
    // CUSTOM //
    ////////////

var starPoints2 = new THREE.Shape();

starPoints2.moveTo(307.94,275.49);
starPoints2.lineTo(296.26,275.23);
starPoints2.lineTo(286.64,272.99);
starPoints2.lineTo(279.78,269.31);
starPoints2.lineTo(274.14,263.55);
starPoints2.lineTo(271.65,260.21);
starPoints2.lineTo(269.2,261.06);
starPoints2.lineTo(254.83,268.51);
starPoints2.lineTo(242.11,272.97);
starPoints2.lineTo(227.59,275.23);
starPoints2.lineTo(209.91,275.48);
starPoints2.lineTo(197.47,273.63);
starPoints2.lineTo(187.91,270.13);
starPoints2.lineTo(180.48,265.09);
starPoints2.lineTo(175.32,258.88);
starPoints2.lineTo(172.2,251.44);
starPoints2.lineTo(171.1,242.23);
starPoints2.lineTo(172.24,233.63);
starPoints2.lineTo(175.49,226.24);
starPoints2.lineTo(181,219.54);
starPoints2.lineTo(189.42,213.3);
starPoints2.lineTo(201.36,207.73);
starPoints2.lineTo(217.23,203.25);
starPoints2.lineTo(238.28,200.1);
starPoints2.lineTo(269.37,198.47);
starPoints2.lineTo(269.98,182.93);
starPoints2.lineTo(268.74,171.32);
starPoints2.lineTo(266.05,163.7);
starPoints2.lineTo(261.58,157.72);
starPoints2.lineTo(255.24,153.24);
starPoints2.lineTo(247.06,150.32);
starPoints2.lineTo(235.44,149.13);
starPoints2.lineTo(224.71,150.05);
starPoints2.lineTo(215.91,153);
starPoints2.lineTo(210.23,156.86);
starPoints2.lineTo(207.64,160.85);
starPoints2.lineTo(207.19,165.28);
starPoints2.lineTo(209.34,169.86);
starPoints2.lineTo(212.01,174.15);
starPoints2.lineTo(212.14,177.99);
starPoints2.lineTo(209.8,181.78);
starPoints2.lineTo(204.22,185.79);
starPoints2.lineTo(197.62,187.68);
starPoints2.lineTo(188.65,187.43);
starPoints2.lineTo(182.41,185.39);
starPoints2.lineTo(178.45,181.77);
starPoints2.lineTo(176.2,176.9);
starPoints2.lineTo(176.03,170.64);
starPoints2.lineTo(178.2,164.13);
starPoints2.lineTo(183.09,157.69);
starPoints2.lineTo(191.04,151.36);
starPoints2.lineTo(202.01,145.82);
starPoints2.lineTo(216.09,141.57);
starPoints2.lineTo(232.08,139.24);
starPoints2.lineTo(250.07,139.18);
starPoints2.lineTo(266.13,141.23);
starPoints2.lineTo(279.05,145.06);
starPoints2.lineTo(289.15,150.3);
starPoints2.lineTo(295.91,156.19);
starPoints2.lineTo(300.73,163.41);
starPoints2.lineTo(303.85,172.47);
starPoints2.lineTo(305.07,183.78);
starPoints2.lineTo(305.07,241.97);
starPoints2.lineTo(306,251.51);
starPoints2.lineTo(308.18,256.39);
starPoints2.lineTo(311.72,259.09);
starPoints2.lineTo(317.31,260.01);
starPoints2.lineTo(324.71,259.01);
starPoints2.lineTo(332.45,255.86);
starPoints2.lineTo(335.57,257.53);
starPoints2.lineTo(337.6,260.44);
starPoints2.lineTo(336.94,262.33);
starPoints2.lineTo(328.27,268.74);
starPoints2.lineTo(317.89,273.41);
starPoints2.lineTo(307.94,275.49);
/*
starPoints2.moveTo(245.79,125.33);
starPoints2.lineTo(232.93,124.53);
starPoints2.lineTo(222.21,121.74);
starPoints2.lineTo(213.14,117.11);
starPoints2.lineTo(207.36,111.92);
starPoints2.lineTo(203.7,105.75);
starPoints2.lineTo(201.94,98.18);
starPoints2.lineTo(202.34,90.12);
starPoints2.lineTo(204.86,83.4);
starPoints2.lineTo(210.01,76.81);
starPoints2.lineTo(217.49,71.33);
starPoints2.lineTo(227.17,67.31);
starPoints2.lineTo(238.35,65.2);
starPoints2.lineTo(243.99,64.95);
starPoints2.lineTo(255.92,66.06);
starPoints2.lineTo(266.21,69.28);
starPoints2.lineTo(274.98,74.44);
starPoints2.lineTo(280.64,80.19);
starPoints2.lineTo(284.02,86.85);
starPoints2.lineTo(285.26,94.52);
starPoints2.lineTo(284.27,102.84);
starPoints2.lineTo(281.24,109.66);
starPoints2.lineTo(276.03,115.43);
starPoints2.lineTo(267.89,120.46);
starPoints2.lineTo(257.68,123.93);
starPoints2.lineTo(245.79,125.33);
*/
var smileyEye1Path = new THREE.Path();

smileyEye1Path.moveTo(221.69,258.13);
smileyEye1Path.lineTo(215.2,255.08);
smileyEye1Path.lineTo(210.86,250.57);
smileyEye1Path.lineTo(208.4,244.49);
smileyEye1Path.lineTo(207.92,237.03);
smileyEye1Path.lineTo(209.69,230.71);
smileyEye1Path.lineTo(213.82,224.85);
smileyEye1Path.lineTo(220.9,219.34);
smileyEye1Path.lineTo(230.95,214.67);
smileyEye1Path.lineTo(245.76,210.86);
smileyEye1Path.lineTo(266.59,208.36);
smileyEye1Path.lineTo(269.48,208.76);
smileyEye1Path.lineTo(269.99,212.88);
smileyEye1Path.lineTo(269.99,244.81);
smileyEye1Path.lineTo(269.34,247.02);
smileyEye1Path.lineTo(266.07,250.04);
smileyEye1Path.lineTo(255.27,255.23);
smileyEye1Path.lineTo(242.52,258.58);
smileyEye1Path.lineTo(230.57,259.43);
smileyEye1Path.lineTo(221.69,258.13);

/*
smileyEye1Path.moveTo(238.44,116.65);
smileyEye1Path.lineTo(231.99,114.29);
smileyEye1Path.lineTo(227.23,110.22);
smileyEye1Path.lineTo(223.94,104.53);
smileyEye1Path.lineTo(222.41,96.92);
smileyEye1Path.lineTo(223.05,88.57);
smileyEye1Path.lineTo(225.65,82.21);
smileyEye1Path.lineTo(230.07,77.36);
smileyEye1Path.lineTo(235.93,74.4);
smileyEye1Path.lineTo(243.68,73.34);
smileyEye1Path.lineTo(246.08,73.43);
smileyEye1Path.lineTo(253.37,75.08);
smileyEye1Path.lineTo(258.65,78.43);
smileyEye1Path.lineTo(262.47,83.41);
smileyEye1Path.lineTo(264.59,90.25);
smileyEye1Path.lineTo(264.64,98.93);
smileyEye1Path.lineTo(262.63,106.12);
smileyEye1Path.lineTo(258.87,111.5);
smileyEye1Path.lineTo(253.73,115.1);
smileyEye1Path.lineTo(246.81,116.94);
smileyEye1Path.lineTo(238.44,116.65);
*/
var starShape = starPoints2;

starShape.holes.push( smileyEye1Path );

    var extrusionSettings = {
        //size: 1, height: 1, curveSegments: 100, step = 10,
        // font, weight, style,
        amount:20,
        bevelEnabled: true, 
        bevelThickness: 0.5, 
        bevelSize: 0.5,
        bevelSegments: 8,
        // extrudePath:
        // bendPath:
        material: 0,
        extrudeMaterial: 1
        //,
        //uvGenerator: BoundingUVGenerator
       //uvGenerator: THREE.ExtrudeGeometry.WorldUVGenerator
    };

    var starGeometry = new THREE.ExtrudeGeometry( starShape, extrusionSettings );

    var materialFront = new THREE.MeshLambertMaterial( { color: 0xffff00, ambient: 0xffff00, overdraw: false, transparent:false, opacity: 1.0, side: THREE.DoubleSide } );
    var materialSide = new THREE.MeshLambertMaterial( { color: 0xff8800, ambient: 0xff8800, overdraw: false, transparent:false, opacity: 1.0, side: THREE.DoubleSide } );

//var crateTexture = new THREE.ImageUtils.loadTexture( 'http://www.kahkonen.com/asiakkaat/crate2.gif');
//var crateTexture = new THREE.ImageUtils.generateDataTexture(10,10,{r:255,g:0,b:0});

//var materialFront = new THREE.MeshBasicMaterial( { map: crateTexture } );

var materialArray = [ materialFront, materialSide ];
var materialArray = [ materialFront, materialSide ];

var starMaterial = new THREE.MeshFaceMaterial(materialArray);

    var star = new THREE.Mesh( starGeometry, starMaterial );

    star.position.set(-150,-150,0);
    scene.add(star);
/*  
    // add a wireframe to model
    var wireframeTexture = new THREE.MeshBasicMaterial( { color: 0x000000, wireframe: true, transparent: false } ); 
    var star = new THREE.Mesh( starGeometry, wireframeTexture );
    star.position.set(50,10,0);
    scene.add(star);
*/
objects.push( star );

// projector
projector = new THREE.Projector();

// listeners
document.addEventListener( 'mousedown', onDocumentMouseDown, false)

// keyboard handler
function onDocumentMouseDown( event ) {

    event.preventDefault();

    var vector = new THREE.Vector3( 
        ( event.clientX / window.innerWidth ) * 2 - 1, 
        - ( event.clientY / window.innerHeight ) * 2 + 1, 
        0.5 );

    projector.unprojectVector( vector, camera );

    var ray = new THREE.Ray( camera.position, vector.subSelf( camera.position ).normalize() );

    var intersects = ray.intersectObjects( objects );    

    if ( intersects.length > 0 ) {

        intersects[0].object.callback();

    }

}

// render
function render() {

    controls.update()

    renderer.render( scene, camera );
}

// animate            
(function animate() {

    requestAnimationFrame( animate );
    render();
}());

I have tried to remove artifacts by appending texture, but texture is not shown at all:

enter image description here

Other problem (that may or may not be related to artifact-problem) is that background can be seen through edges.

Texture is not a must, but removing artifacts is.

The code that I have used to add texture is below and full code in the fiddle (link is after the code):

var crateTexture = new THREE.ImageUtils.loadTexture( 'http://www.kahkonen.com/asiakkaat/crate.gif'); var crateTexture = new THREE.ImageUtils.generateDataTexture(10,10,{r:255,g:0,b:0}); var materialFront = new THREE.MeshBasicMaterial( { map: crateTexture } );

// http://jsfiddle.net/pHn2B/27/

Sample images are from Chrome. The same behavior occurs in Firefox too.

like image 482
Timo Kähkönen Avatar asked Apr 19 '13 13:04

Timo Kähkönen


2 Answers

This is a know limitation of CanvasRenderer caused by z-buffering problems. It is made worse by your geometry which has many elongated faces. The model renders correctly with WebGLRenderer.

ExtrudeGeometry was originally written for text, and if you look at the UVs it generates, it just uses the x- and y-components of the vertex positions for UVs, which in your case, produces values that are outside the range [ 0, 1 ]. You have the option of providing your own UV-generator in a callback function.

Make sure you can successfully add a texture to a cube first.

three.js r.58

like image 90
WestLangley Avatar answered Sep 23 '22 22:09

WestLangley


Just for further reference. The answer of @Westlangley lead me to the right direction to get texture on the shape. The key was scaling values from one range to another range, from world coordinates to shapes inner UV range, which must be 0-1.

So I copied THREE.ExtrudeGeometry.WorldUVGenerator and renamed it to THREE.ExtrudeGeometry.BoundingBoxUVGenerator and replaced in member function generateTopUV() these lines:

        return [
            new THREE.Vector2( ax, ay ),
            new THREE.Vector2( bx, by ),
            new THREE.Vector2( cx, cy )
        ];

with these:

    var bb = extrudedShape.getBoundingBox();
    var bb_minX = bb.minX;
    var bb_minY = bb.minY;
    var bb_width = bb.maxX - bb_minX;
    var bb_height = bb.maxY - bb_minY;
        if (bb_width == 0) bb_width = 1;
        if (bb_height == 0) bb_height = 1;
        return [new THREE.Vector2( (ax - bb_minX) / bb_width,  (ay - bb_minY) / bb_height  ),
            new THREE.Vector2( (bx - bb_minX) / bb_width,  (by - bb_minY) / bb_height  ),
            new THREE.Vector2( (cx - bb_minX) / bb_width,  (cy - bb_minY) / bb_height  )
        ];

and the texture "magically" appeared on the right place!

( For someone who have tried similarly named function from https://github.com/mrdoob/three.js/issues/1811 . It does not work, because range conversion is made some odd way. )

This works also in cases where the vertice coordinates are really in range 0-1, and in cases where the coordinates are negative. And if for some reason width and height are 0, returns 0.

For the most of the possible purposes this is far more useful than the default THREE.ExtrudeGeometry.WorldUVGenerator. And I cannot even imagine any purposes for the default generator, because it works only for vertices that have coordinates in the range 0-1. But I'm new to three.js, and there can be reasons that I cannot imagine yet.

like image 31
Timo Kähkönen Avatar answered Sep 25 '22 22:09

Timo Kähkönen