3

Consider, I have an SVG vector graphics file (logotype), which I want to load and display in three.js (with WebGL renderer).

What would be the recommended way to approach this?

It seems like I need to load the image and to create a geometry and a mesh from it.

I've managed to load the SVG document using the THREE.SVGLoader, but I can't find any relevant information on how to create a geometry/mesh from it further down the line.

function preload () {

  const svgLoader = new THREE.SVGLoader();

  svgLoader.load('images/logo.svg', svgDocument => {

    // @todo: create a geometry/mesh from svgDocument?

    // @todo: scene.add(logoMesh);

  });

}
Slava Fomin II
  • 26,865
  • 29
  • 124
  • 202
  • Take a look at this [example](https://threejs.org/examples/?q=shapes#webgl_geometry_extrude_shapes2) and its [source code](https://github.com/mrdoob/three.js/blob/da2936540a48774b043a1e617bddbdce5102e417/examples/webgl_geometry_extrude_shapes2.html#L400). – prisoner849 Jan 20 '18 at 23:42
  • @prisoner849 this example manually parses SVG paths. This is a very low-level approach. There should be an easier way. I'm using three.js in order to avoid such low-level programming in the first place. – Slava Fomin II Jan 21 '18 at 10:45
  • Ehm... Is it really hard to put `transformSVGPath(pathStr)` function in a separated script and use it from there? That function returns `THREE.ShapePath()` which can be transformed to array of `THREE.Shape()` with `.toShapes()` method, and then use those shapes with `THREE.ShapeGeometry()`. Very simple. No reason to mention about low-level programming. – prisoner849 Jan 21 '18 at 11:22
  • It's a bad practice to put third-party code directly into your project. If you do this you are becoming responsible for this code maintenance (including security aspects). And to maintain a code you must have a complete understanding of it. – Slava Fomin II Jan 21 '18 at 11:37
  • _"It's a bad practice"_ I'm surprised that you're using Three.js framework, written by many people, instead of writing your own one, to be sure that there are no security issues and so on ;) But I don't insist. If you don't want to use such approach, it's totally up to you. Cheers! ;) – prisoner849 Jan 21 '18 at 11:46
  • @prisoner849 piggybacking on this question as you seem to have some insight. I did all what you describe but am at a loss as to how to convert the ShapePath to a LineSegment or similar, because our SVG actually is just an outline not a solid image. Any idea? – xDreamCoding Jan 22 '18 at 14:54
  • @xDreamCoding You can create another question on SO, or ask it [here](https://discourse.threejs.org) with more details and some live code examples (jsfiddle, codepen etc.) :) – prisoner849 Jan 22 '18 at 15:06
  • @prisoner849 [done](https://discourse.threejs.org/t/simplest-method-of-displaying-an-svg-path-in-webglrenderer/) thanks for any help in advance – xDreamCoding Jan 22 '18 at 15:46

1 Answers1

1

Texture

If you need svg only for texture purposes:

  1. Render svg image to canvas
  2. use that canvas as source of texture
  3. use texture in your scene...

Disclaimer I'm not the author of this code, I just fixed jsfiddle that I've found

window.onload = () => {
  var mesh;
  var scene = new THREE.Scene();

  var camera = new THREE.PerspectiveCamera(50, 500 / 400, 0.1, 1000);
  camera.position.z = 10;

  var renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setSize(500, 400);
  document.body.appendChild(renderer.domElement);

  var svg = document.getElementById("svgContainer").querySelector("svg");
  var svgData = (new XMLSerializer()).serializeToString(svg);

  var canvas = document.createElement("canvas");
  var svgSize = svg.getBoundingClientRect();
  canvas.width = svgSize.width;
  canvas.height = svgSize.height;
  var ctx = canvas.getContext("2d");

  var img = document.createElement("img");
  img.setAttribute("src", "data:image/svg+xml;base64," + window.btoa(unescape(encodeURIComponent(svgData))) );

  img.onload = function() {
    ctx.drawImage(img, 0, 0);

    var texture = new THREE.Texture(canvas);
    texture.needsUpdate = true;

    var geometry = new THREE.SphereGeometry(3, 50, 50, 0, Math.PI * 2, 0, Math.PI * 2);
    var material = new THREE.MeshBasicMaterial({ map: texture });
    material.map.minFilter = THREE.LinearFilter;
    mesh = new THREE.Mesh(geometry, material);
    scene.add(mesh);
  };

  var render = function () {
      requestAnimationFrame(render);
      mesh.rotation.y += 0.01;
      renderer.render(scene, camera);
  };

  render();
}
<script src="https://threejs.org/build/three.min.js"></script>

<div id="svgContainer" style="width: 222px; height: 222px;">
  <svg width="200" height="200" xmlns="http://www.w3.org/2000/svg" version="1.1">
    <rect width="200" height="200" fill="lime" stroke-width="4" stroke="pink" />
    <circle cx="125" cy="125" r="75" fill="orange" />
    <polyline points="50,150 50,200 200,200 200,100" stroke="red" stroke-width="4" fill="none" />
    <line x1="50" y1="50" x2="200" y2="200" stroke="blue" stroke-width="4" />
  </svg>
</div>

Mesh

If you would like to render svg as geometry I'll would suggest use of some libraries e.g. svg-mesh-3d

Example from docs of svg-mesh-3d

    var loadSvg = require('load-svg')
    var parsePath = require('extract-svg-path').parse
    var svgMesh3d = require('svg-mesh-3d')

    loadSvg('svg/logo.svg', function (err, svg) {
      if (err) throw err

      var svgPath = parsePath(svg)
      var mesh = svgMesh3d(svgPath, {
        delaunay: false,
        scale: 4
      })
    })

Blender

Alternative options is to use blender to import svg, (optionally) tune geometry and export it to Three.js using Three.js Blender Export

  • Thank you for your answer. I want to use SVG as a mesh. I've tried to follow the `svg-mesh-3d` example, but the mesh returned by `svgMesh3d()` is not a correct mesh. It has no `position` property for example, so I can't set it's position and when I add it to the scene it's not displayed. How do I assign a material to it? – Slava Fomin II Jan 21 '18 at 10:41
  • svg-mesh-3d produces so called ["simplicial complex"](https://www.npmjs.com/package/mesh-primitives#generic-mesh-modules) that can be converted to Three.js mesh by [three-simplicial-complex](https://github.com/Jam3/three-simplicial-complex) as why there are on position on mesh best open a new question – Bartosz Matuszewski Jan 21 '18 at 12:55