更新Three.js 3d模型纹理+Fabric.js画布作为纹理

问题描述 投票:0回答:1

我对 Three.js 还很陌生。我正在尝试开发一个项目,在该项目中,我使用预先存在的 3D 模型并修改其材质之一,以便它使用 Fabric.js 画布作为其纹理。但是,我遇到了一个问题,纹理呈现画布的颜色,但我添加的图像都不可见。这是我的代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>

    <style>
        body { margin: 0; }

        #controls {
            position: absolute;
            top: 10px;
            left: 10px;
            z-index: 1;
        }
    </style>
</head>
<body>
    <div id="controls">
        <div class="colorPicker"></div>
        <canvas id="c"></canvas>
        <button id="clear">Borrar</button>
        <input type="file" id="file">
    </div>
    

    <script src="https://cdn.jsdelivr.net/npm/@jaames/iro@5"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/loaders/GLTFLoader.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/controls/OrbitControls.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"></script>

    <script>
        let canvas = new fabric.Canvas('c',  {
            backgroundColor: 'white',
        });
        canvas.setHeight(512);
        canvas.setWidth(512);

        document.getElementById('clear').addEventListener('click', () => {
            !deleteActiveObjects()
        });

        function deleteActiveObjects() {
            const activeObjects = canvas.getActiveObjects();
            if(!activeObjects.length) return false;
            
            if(activeObjects.length) {
                activeObjects.forEach(function(object) {
                    canvas.remove(object);
                });
            } else {
                canvas.remove(activeObjects);
            }
            
            return true;
        }

        document.getElementById('file').addEventListener('change', (e) => {
            const file = e.target.files[0]; // Get the file from the input
            if (file) {
                const reader = new FileReader();
                reader.onload = function(event) {
                    fabric.Image.fromURL(event.target.result, function(img) {
                        img.set({
                            angle: 0,
                            padding: 10,
                            flipX: false
                        });
                        img.scaleToHeight(200);
                        img.scaleToWidth(200);
                        canvas.add(img);
                        canvas.renderAll(); // Redibujar el canvas después de añadir la imagen
                        
                        // Actualizar la textura del material 14
                        const texture = new THREE.CanvasTexture(canvas.getElement());
                        material14.map = texture;
                        material14.map.needsUpdate = true; // Esto asegura que la textura se actualice
                    });
                };
                reader.readAsDataURL(file); // Read the file as a data URL
            }
        });

        function colorPicker(material14) {
            let colorPicker = new iro.ColorPicker(".colorPicker", {
                width: 280,
                color: "rgb(255, 0, 0)",
                borderWidth: 1,
                borderColor: "#fff",
            });
        
            colorPicker.on(["color:change"], function(color) {
                canvas.backgroundColor = color.hexString;
                canvas.renderAll();
        
                // Actualiza la textura del material
                material14.map.needsUpdate = true;
            });
        }

        function initScene() {
            const scene = new THREE.Scene();
            scene.background = new THREE.Color(0xd3d3d3);

            const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
            camera.position.set(0, 1, 5);

            const renderer = new THREE.WebGLRenderer({ antialias: true });
            renderer.setSize(window.innerWidth, window.innerHeight);
            document.body.appendChild(renderer.domElement);

            const controls = new THREE.OrbitControls(camera, renderer.domElement);
            controls.enableDamping = true;
            controls.dampingFactor = 0.05;
            controls.screenSpacePanning = false;
            controls.minDistance = 2;
            controls.maxDistance = 50;
            controls.maxPolarAngle = Math.PI / 2;

            return { scene, camera, renderer, controls };
        }

        function addLights(scene) {
            const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
            scene.add(ambientLight);

            const light = new THREE.DirectionalLight(0xffffff, 1);
            light.position.set(10, 10, 10).normalize();
            scene.add(light);
        }

        function loadModel(scene) {
            const loader = new THREE.GLTFLoader();
            loader.load('assets/models/2x2.glb', function(gltf) {
                model = gltf.scene;
                model.scale.set(0.001, 0.001, 0.001);
                scene.add(model);
                console.log("Modelo cargado correctamente");
        
                const material14 = getMaterialByIndex(model, 14);
                if (material14) {
                    console.log("Material 14 encontrado:", material14);
        
                    // Usa el canvas de Fabric.js como textura
                    const texture = new THREE.CanvasTexture(canvas.getElement());
                    material14.map = texture;
                    material14.map.wrapS = THREE.RepeatWrapping;
                    material14.map.wrapT = THREE.RepeatWrapping;
                    material14.map.minFilter = THREE.LinearFilter;
                    material14.map.magFilter = THREE.LinearFilter;
                    material14.needsUpdate = true;
        
                    colorPicker(material14);
                }
            }, undefined, function(error) {
                console.error("Error al cargar el modelo:", error);
            });
        }

        function getMaterialByIndex(model, index) {
            let materials = [];
            model.traverse(function(child) {
                if (child.isMesh) {
                    materials = materials.concat(child.material);
                }
            });
            return materials[index] || null;
        }

        function animate(scene, camera, renderer, controls) {
            function render() {
                requestAnimationFrame(render);
                controls.update();
                renderer.render(scene, camera);
            }
            render();
        }

        function onWindowResize(camera, renderer) {
            window.addEventListener('resize', function() {
                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();
                renderer.setSize(window.innerWidth, window.innerHeight);
            });
        }

        function init() {
            const { scene, camera, renderer, controls } = initScene();
            addLights(scene);
            loadModel(scene);
            animate(scene, camera, renderer, controls);
            onWindowResize(camera, renderer);
        }

        init();
    </script>
</body>
</html>

我尝试在上传图像时更新材料,但没有任何作用。

javascript three.js 3d fabricjs
1个回答
0
投票

问题更多的是 3D 建模而不是代码本身。调整模型后,我意识到它没有 UV 映射,而这是 Three.js 将纹理正确应用到材质所必需的。这是最终有效的代码:

function loadModel() {
const loader = new THREE.GLTFLoader();
loader.load('assets/models/2x2.glb', function (gltf) {
    model = gltf.scene;
    model.scale.set(0.001, 0.001, 0.001);
    model.position.set(0, 0, 0);

    model.traverse(function (child) {

        if (child.isMesh) {

            if (child.material) {
                if (child.material.name == "mat14.003") {
                    const basicMaterial = new THREE.MeshBasicMaterial({
                        map: child.material.map, 
                        side: THREE.DoubleSide 
                    });

                    child.material = basicMaterial;

                    child.material.map = texture;
                    child.material.map.wrapS = THREE.RepeatWrapping;
                    child.material.map.wrapT = THREE.RepeatWrapping;
                    child.material.map.needsUpdate = true;
                }
            }
        }
    });
    
    scene.add(model);
}, undefined, function (error) {
    console.error(error);
});

}

© www.soinside.com 2019 - 2024. All rights reserved.