I am trying to render SVG Files with threeJS. As PNG files don't look pretty nice I am using now the SVGRenderer.
My SVG Files looks sth like this:
When I render the Image it appears to be completely black (the colour I have chosen though)
I found the option to render it as a wireframe, but that shows some framelines I don't want to see
My JS looks like this:
var loader = new THREE.SVGLoader();
loader.load( 'media/qpartdark/SVG/qpart2.svg', function ( paths ) {
var group = new THREE.Group();
group.scale.multiplyScalar( 0.25 );
group.position.x = - 70;
group.position.y = 0;
group.position.z = 0;
group.scale.y *= -1;
for ( var i = 0; i < paths.length; i ++ ) {
var path = paths[ i ];
var material = new THREE.MeshBasicMaterial( {
color: path.color,
side: THREE.DoubleSide,
depthWrite: false,
wireframe: false,
wireframeLinewidth: 0.01
} );
var shapes = path.toShapes( true );
for ( var j = 0; j < shapes.length; j ++ ) {
var shape = shapes[ j ];
var geometry = new THREE.ShapeBufferGeometry( shape );
var mesh = new THREE.Mesh( geometry, material );
group.add( mesh );
}
}
scene.add( group );
} );
//
renderer = new THREE.WebGLRenderer( { alpha: true, antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
Do you have any hints how I can render the svg as in the example?
here is the original SVG:
Related
I saw some solutions that accessed the THREE.BoxGeometry faces like that:
var geometry = new THREE.BoxGeometry(n,n,n)
let faces = geometry.faces;
for (let face of faces) {
face.color.set(Math.random() * 0xffffff);
}
But it seems that the faces-array doesn't exist in the current Three.js version r129 (Uncaught TypeError: faces is not iterable).
How can I achieve an easy coloring of the six cube faces?
Try it like so:
let camera, scene, renderer, mesh;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 10 );
camera.position.z = 1;
scene = new THREE.Scene();
const geometry = new THREE.BoxGeometry( 0.2, 0.2, 0.2 ).toNonIndexed();
// vertexColors must be true so vertex colors can be used in the shader
const material = new THREE.MeshBasicMaterial( { vertexColors: true } );
// generate color data for each vertex
const positionAttribute = geometry.getAttribute( 'position' );
const colors = [];
const color = new THREE.Color();
for ( let i = 0; i < positionAttribute.count; i += 3 ) {
color.set( Math.random() * 0xffffff );
// define the same color for each vertex of a triangle
colors.push( color.r, color.g, color.b );
colors.push( color.r, color.g, color.b );
colors.push( color.r, color.g, color.b );
}
// define the new attribute
geometry.setAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
}
function animate() {
requestAnimationFrame( animate );
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.02;
renderer.render( scene, camera );
}
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.129.0/build/three.js"></script>
The previous geometry format was removed from the core with r125. More information in the following post at the three.js forum.
https://discourse.threejs.org/t/three-geometry-will-be-removed-from-core-with-r125/22401
Is there a way to convert an SVG group of paths into its 3D counterpart?
In a way, It's like adding thickness to it.
I used SVGLoader to import my svg path into THREE.JS library:
loader.load(
'./test.svg',
function ( data ) {
const paths = data.paths;
for ( let i = 0; i < paths.length; i ++ ) {
const path = paths[ i ];
const material = new THREE.MeshBasicMaterial( {
color: 0x337ab7,
side: THREE.DoubleSide,
depthWrite: false,
opacity: 1,
transparent: true
} );
const shapes = path.toShapes( true );
for ( let j = 0; j < shapes.length; j ++ ) {
const shape = shapes[ j ];
const geometry = new THREE.ShapeBufferGeometry( shape );
const mesh = new THREE.Mesh( geometry, material );
group.add( mesh );
}
}...
The SVG upload works fine.
Do you know any library or trickery to accomplish this 3D transformation?
Instead of creating ShapeBufferGeometry try it with ExtrudeBufferGeometry. It should be exactly what you are looking for.
I have imported a test SVG using the SVGLoader and am able to tween the SVG object using Tween.js.
I would like to tween the individual paths and polygons within the loaded SVG object - is this possible?
SVGLoader:
var loader = new THREE.SVGLoader();
loader.load( 'svg/test.svg', function ( paths ) {
var group = new THREE.Group();
group.scale.multiplyScalar( 0.25 );
group.position.x = -150;
group.position.y = 70;
group.scale.y *= - 1;
for ( var i = 0; i < paths.length; i ++ ) {
var path = paths[ i ];
var material = new THREE.MeshBasicMaterial( {
color: path.color,
side: THREE.DoubleSide,
depthWrite: false
} );
var shapes = path.toShapes( true );
for ( var j = 0; j < shapes.length; j ++ ) {
var shape = shapes[ j ];
var geometry = new THREE.ShapeBufferGeometry( shape );
var mesh = new THREE.Mesh( geometry, material );
group.add( mesh );
}
}
scene.add( group );
} );
Tween Function:
triggerTweens();
function triggerTweens() {
new TWEEN.Tween( title.position ).to( { x: 10 }, 5000 ).start();
}
I have found the following solution to this. Essentially adding the tween trigger into the loader function and passing the variable mesh, ( not group ) - allows tweening on the constituent paths and polygons etc of the SVG. To take this a step further I will figure out how to perform the tween for each separately which shouldn't be too tricky at this point.
New SVG Loader:
var loader = new THREE.SVGLoader();
loader.load( 'svg/test.svg', function ( paths ) {
var group = new THREE.Group();
group.scale.multiplyScalar( 0.25 );
group.position.x = -150;
group.position.y = 70;
group.scale.y *= - 1;
for ( var i = 0; i < paths.length; i ++ ) {
var path = paths[ i ];
var material = new THREE.MeshBasicMaterial( {
// color: path.color,
color: 0xff0000,
side: THREE.DoubleSide,
depthWrite: false
} );
var shapes = path.toShapes( true );
for ( var j = 0; j < shapes.length; j ++ ) {
var shape = shapes[ j ];
var geometry = new THREE.ExtrudeGeometry(shape, {
depth: 1,
bevelEnabled: false
});
var mesh = new THREE.Mesh( geometry, material );
// Randomly position each constituent mesh to demo
randX = Math.random() * (20000 - -20000) + -20000;
randY = Math.random() * (20000 - -20000) + -20000;
randZ = Math.random() * (20000 - -20000) + -20000;
mesh.position.set( randX, randY, randZ );
group.add( mesh );
}
triggerTweens(mesh); // Trigger tweens passing mesh variable
}
scene.add( group );
} );
New Tween Function:
// Bring all meshes back to position 0,0,0 Over 5000ms
function triggerTweens(mesh) {
new TWEEN.Tween( mesh.position ).to( { x: 0, y: 0, z: 0 }, 5000 ).easing(TWEEN.Easing.Cubic.In).start();
}
I'm trying to load a .svg asset into my three.js scene, as a flat vector layer; I found this example with SVGLoader and SVGRenderer from another post, but I can't make it work.
The svg loaded is stuck in 2d space and not responding to camera movement, I can't access its position.
I tried to switch to WebGLRenderer, but the svg doesn’t get loaded.
The option of loading it as sprite would be good, but I would want the sprite to not face the camera and stay still in 3d space.
var svgManager = new THREE.SVGLoader();
var url = 'https://upload.wikimedia.org/wikipedia/commons/f/f7/Europe_laea_location_map.svg';
function svg_loading_done_callback(doc) {
init();
svg(new THREE.SVGObject(doc));
ico();
animate();
};
svgManager.load(url,
svg_loading_done_callback,
function() {
console.log("Loading SVG...");
},
function() {
console.log("Error loading SVG!");
});
var AMOUNT = 100;
var container, camera, scene, renderer;
function init() {
scene = new THREE.Scene();
renderer = new THREE.SVGRenderer();
renderer.setClearColor(0x00ff00);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
var container = document.getElementById('container');
container.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.z = 1100;
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableZoom = true;
window.addEventListener('resize', onWindowResize, false);
}
function svg(svgObject) {
svgObject.position.x = 510;
svgObject.position.y = -110;
svgObject.position.z = 0;
scene.add(svgObject);
}
function ico() {
geometry = new THREE.IcosahedronGeometry(100, 1)
material = new THREE.MeshNormalMaterial({});
ico = new THREE.Mesh(geometry, material);
ico.position.y = -300;
scene.add(ico);
ico2 = new THREE.Mesh(geometry, material);
ico2.position.y = 500;
ico2.position.x = -500;
ico2.position.z = -50;
scene.add(ico2);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
requestAnimationFrame(animate);
controls.update;
render();
}
function render() {
renderer.render(scene, camera);
}
body {
margin: 0;
}
canvas {
width: 100%;
height: 100%
}
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<div id="container"></div>
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/renderers/SVGRenderer.js"></script>
<script src="https://threejs.org/examples/js/renderers/Projector.js"></script>
<script src="https://threejs.org/examples/js/loaders/SVGLoader.js"></script>
<script src="https://threejs.org/examples/js/libs/stats.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
The SVGLoader and SVGRenderer are two different things. The first loads an SVG file and converts it to three.js shapes (albeit with some limitations, i.e. can read very simple SVGs, does not render strokes but only filled objects, etc), while the latter renders three.js primitives using SVG elements instead of WebGL. In a sense, they are opposites of each other.
So, first of all, you'd need to use the WebGLRenderer for your case.
Then, you need to change the SVG loading callback. It receives an array of paths with which you can render the SVG.
See the changes in functions svg_loading_done_callback, init and svg, and run it in JSFiddle:
var svgManager = new THREE.SVGLoader();
var url = 'https://upload.wikimedia.org/wikipedia/commons/f/f7/Europe_laea_location_map.svg';
function svg_loading_done_callback(paths) {
init();
svg(paths);
ico();
animate();
};
svgManager.load(url,
svg_loading_done_callback,
function() {
console.log("Loading SVG...");
},
function() {
console.log("Error loading SVG!");
});
var AMOUNT = 100;
var container, camera, scene, renderer;
function init() {
scene = new THREE.Scene();
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0x00ff00);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
var container = document.getElementById('container');
container.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.z = 1100;
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableZoom = true;
window.addEventListener('resize', onWindowResize, false);
}
function svg(paths) {
var group = new THREE.Group();
group.position.x = 510;
group.position.y = -110;
group.position.z = 0;
for ( var i = 0; i < paths.length; i ++ ) {
var path = paths[ i ];
var material = new THREE.MeshBasicMaterial( {
color: path.color,
side: THREE.DoubleSide,
depthWrite: false
} );
var shapes = path.toShapes( true );
for ( var j = 0; j < shapes.length; j ++ ) {
var shape = shapes[ j ];
var geometry = new THREE.ShapeBufferGeometry( shape );
var mesh = new THREE.Mesh( geometry, material );
group.add( mesh );
}
}
scene.add( group );
}
function ico() {
geometry = new THREE.IcosahedronGeometry(100, 1)
material = new THREE.MeshNormalMaterial({});
ico = new THREE.Mesh(geometry, material);
ico.position.y = -300;
scene.add(ico);
ico2 = new THREE.Mesh(geometry, material);
ico2.position.y = 500;
ico2.position.x = -500;
ico2.position.z = -50;
scene.add(ico2);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
requestAnimationFrame(animate);
controls.update;
render();
}
function render() {
renderer.render(scene, camera);
}
PS: Check the SVG Loader to see what it's able to parse
I've been trying out Three.js today and I've had trouble getting my Mesh to change according using mesh.rotation.x += 10; for example.
The code below renders a triangle and the camera moves around onMouseMove but inside the render() function, it fails to scale or rotate the Mesh obj.
Appreciate the pointers.
<body>
<div id="container" style="border: #0f0 solid;">
</div>
<script type="text/javascript">
var mouseX = 0, mouseY = 0;
windowHalfX = window.innerWidth / 2,
windowHalfY = window.innerHeight / 2;
// get the DOM element to attach to
var container = document.getElementById("container");
// create a WebGL renderer, camera
// and a scene
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
camera = new THREE.PerspectiveCamera( 25, window.innerWidth / window.innerHeight, 50, 1e7 );
var scene = new THREE.Scene();
var obj;
var geom = new THREE.Geometry();
var stats;
init();
animate();
function init(){
// the camera starts at 0,0,0 so pull it back
camera.position.z = 400;
// start the renderer
// attach the render-supplied DOM element
container.appendChild(renderer.domElement);
// create a new mesh with triangle geometry
// create the sphere's material
var material = new THREE.MeshLambertMaterial(
{
color: 0x00FF00
});
var v1 = new THREE.Vector3(50,0,0);
var v2 = new THREE.Vector3(50,100,0);
var v3 = new THREE.Vector3(0, 50, 0);
geom.vertices.push(new THREE.Vertex(v1));
geom.vertices.push(new THREE.Vertex(v2));
geom.vertices.push(new THREE.Vertex(v3));
geom.faces.push(new THREE.Face3(0,1,2));
geom.computeFaceNormals();
obj = new THREE.Mesh(geom, material);
obj.doubleSided = true;
obj.rotation.x = 0.1;
// add the obj to the scene
scene.addObject(obj);
// create a point light
var pointLight = new THREE.PointLight( 0xFFFFFF );
// set its position
pointLight.position.x = 0;
pointLight.position.y = 0;
pointLight.position.z = 600;
//pointLight.lookAt(obj);
// add to the scene
scene.add(pointLight);
ambientLight = new THREE.AmbientLight( 0xbbbbbb );
scene.add(ambientLight);
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
stats.domElement.style.zIndex = 100;
container.appendChild( stats.domElement );
// draw!
renderer.render(scene, camera);
}
function animate() {
requestAnimationFrame( animate );
render();
stats.update();
}
function onDocumentMouseMove(event) {
mouseX = event.clientX - windowHalfX;
mouseY = event.clientY - windowHalfY;
}
function render() {
camera.position.x += ( mouseX - camera.position.x ) * .05;
camera.position.y += ( - mouseY + 200 - camera.position.y ) * .05;
camera.lookAt( scene.position );
var time = Date.now() * 0.0015;
for ( var i = 0; i < scene.objects.length; i ++ ) {
scene.objects[ i ].rotation.y = time * ( i % 2 ? 1 : -1 );
}
obj.rotation.x += 20;
renderer.clear();
renderer.render( scene, camera );
}
</script>
</body>
From r45 scene.addObject(obj); must be now scene.add(obj);
See Three.js commit log:
2011 10 06 - r45 - Object/Scene.add*() and Object/Scene.remove*() are now Object/Scene.add() and Object/Scene.remove()