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.
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:
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.
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With