javascriptsvgthree.js

Change color of an svg dynamically and apply as texture in three.js


I'm trying to create a configurator with three.js but I'm at the point that I need to change the color of the product dynamically, I thought that I can use an svg as texture, change the fill property of the svg and update the texture easily, but I'm not getting good results.

At the moment what I have is:

But I'm trying to make it dynamic, you choose a color and it automatically updates the svg applied as texture

Any ideas?

Thanks!


Solution

  • I forked someone else's fiddle to show the solution. I added a click function which modifies the SVG and redraws the material on the model.

    See http://jsfiddle.net/L4pepnj7/

    Click anywhere on the example to change the color of the circle in the SVG.

    The operation which converts the SVG to an encodedURI is to intensive to have in a constant loop, but you can put the color change in a click event while the renderer is doing it's own thing. I added a function called "clickBody" to the existing fiddle which changes the color of one of the SVG elements.

    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");
    var material;
    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);
        material = new THREE.MeshBasicMaterial({
            map: texture
        });
        material.map.minFilter = THREE.LinearFilter;
        mesh = new THREE.Mesh(geometry, material);
        scene.add(mesh);
    };
    
    
    
    var colors = ["red", "orange", "yellow", "green", "blue"];
    
    
    var c = 0;
    
    function clickBody() {
        document.getElementById("test").setAttribute("fill", colors[c]);
        var svgData = (new XMLSerializer()).serializeToString(svg);
        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);
            material.map = texture;
            material.map.needsUpdate = true;
            renderer.render(scene, camera);
        }
        c = c + 1;
        if (c == colors.length) {
            c = 0;
        }
    }
    document.body.addEventListener("click", clickBody)
    
    var render = function() {
        requestAnimationFrame(render);
        mesh.rotation.y += 0.01;
        renderer.render(scene, camera);
    };
    
    
    render();