首页 文章

从用户上传加载纹理到three.js中的几何体

提问于
浏览
4

我是three.js和javascript(一般编码)的新手,但我在尝试各种可能性方面有很多乐趣,并创建了一些带灯光的小型Three.js场景,从3D JSON模型开发的自定义几何体我的技能能够解决我 Build 3D网站的最终目标 .

我目前完全坚持允许用户从HTML'输入'文件标签上传图像文件的问题,并让该图像出现在已存在于场景中的PlaneGeometry网格上,其中已经加载了虚拟纹理材料 .

我可以让纹理图像出现在平面上没有问题,我有HTML'输入'文件按钮出现在屏幕上,我可以点击它,它会加载一个图像,但它没有链接到three.js中的任何东西,那就是我在哪里陷入困境,我不明白该怎么办 .

我发现这个JSFIDDLE EXAMPLE我可以看到有效但我无法弄清楚如何使用它来为已经存在的几何和材料添加自定义纹理 .

<body>
<!-- three.js container -->
<input id="userImage" type="file" />
<div id="container"></div>
<script type="x-shader/x-vertex" id="vertexShader">
    varying vec2 vUv;

    void main() {
        vUv = uv;
        gl_Position = vec4(uv * 2. - vec2(1, 1), 0., 1.);
    }
</script>
<script type="x-shader/x-fragment" id="fragmentShader">
    precision highp float;

    varying vec2 vUv;
    uniform sampler2D texture1;
    void main() {
        gl_FragColor = texture2D(texture1, vUv.xy);
    }
</script>
<script type="text/javascript" src="js/jquery.min.js"></script>
<script type="text/javascript" src="js/three.min.js"></script>
<script>

    init()

    function init(){
        $("#userImage").change(function () {
            var image = document.createElement( 'img' );
            var texture = new THREE.Texture( image );
            image.onload = function()  {
                texture.needsUpdate = true;
        };

        userImage = $("#userImage")[0];
        if (userImage.files && userImage.files[0]) {
            var reader = new FileReader();

            reader.onload = function (e) {
                image.src = e.target.result;
            };

            reader.readAsDataURL(userImage.files[0]);
        }

        camera = new THREE.OrthographicCamera();
        renderer = new THREE.WebGLRenderer({
            antialias: false
        });
        renderer.setSize(window.innerWidth, window.innerHeight);
        shader = new THREE.ShaderMaterial({
            vertexShader: document.getElementById('vertexShader').textContent,
            fragmentShader: document.getElementById('fragmentShader').textContent,
            uniforms: {
                texture1: {
                    type: "t",
                    value: texture
                }
            }
        });
        scene = new THREE.Scene();
        scene.add(new THREE.Mesh(new THREE.PlaneGeometry(1), shader));

        $('#container').append(renderer.domElement);

            animate();

        function animate() {
            requestAnimationFrame(animate);

            renderer.render(scene, camera);
        }
    }); 
    }
</script>

有没有人会分享一个例子,说明如何将用户上传的纹理加载到已经存在的纹理,材质和对象上?

image of current three.js result with HTML loader at the top

2 回答

  • 1

    使用addEventListener将"change"事件添加到"file" input

    document.getElementById('userImage').addEventListener('change', function(e) {
         .....  
    } );
    

    新文件可以通过event参数的 target.files[0] 属性获取:

    var userImage = e.target.files[0];
    

    使用URL.createObjectURL创建包含URL的DOMString:

    var userImageURL = URL.createObjectURL( userImage );
    

    我建议使用THREE.TextureLoader来加载纹理:

    var loader = new THREE.TextureLoader();
    loader.setCrossOrigin("");
    var texture = loader.load(userImageURL);
    

    请参阅示例,该示例基于您的问题代码:

    init()
      
    function init(){
        document.getElementById('userImage').addEventListener('change', function(e) {
    
        var userImage = e.target.files[0];     
        var userImageURL = URL.createObjectURL( userImage );
    
        container = document.getElementById('container');      
        camera = new THREE.OrthographicCamera();
        renderer = new THREE.WebGLRenderer({ antialias: false });
        renderer.setSize(window.innerWidth, window.innerHeight);
        container.appendChild(renderer.domElement);
    
        var loader = new THREE.TextureLoader();
        loader.setCrossOrigin("");
        var texture = loader.load(userImageURL);
    
        shader = new THREE.ShaderMaterial({
            vertexShader: document.getElementById('vertexShader').textContent,
            fragmentShader: document.getElementById('fragmentShader').textContent,
            uniforms: {
                texture1: { type: "t", value: texture }
            }
        });
        scene = new THREE.Scene();
        scene.add(new THREE.Mesh(new THREE.PlaneGeometry(1), shader));
    
        animate();
        function animate() {
            requestAnimationFrame(animate);
    
            renderer.render(scene, camera);
        }
        } ); 
    }
    
    <input id="userImage" type="file" />
    <div id="container"></div>
    <script type="x-shader/x-vertex" id="vertexShader">
        varying vec2 vUv;
    
        void main() {
            vUv = uv;
            gl_Position = vec4(uv * 2. - vec2(1, 1), 0., 1.);
        }
    </script>
    <script type="x-shader/x-fragment" id="fragmentShader">
        precision highp float;
    
        varying vec2 vUv;
        uniform sampler2D texture1;
        void main() {
            gl_FragColor = texture2D(texture1, vUv.xy);
        }
    </script>
    <script type="text/javascript" src="https://threejs.org/build/three.min.js"></script>
    
  • 0

    这样可以解决问题:

    shader.uniforms.texture1.value = texture
    

    这将进入文件onChange方法,它基本上用上传的纹理替换初始纹理 .

    像这样:

    fileUpload(e) {
        let userImage = e.target.files[0];     
        let userImageURL = URL.createObjectURL( userImage );
    
        let loader = new THREE.TextureLoader();
        loader.setCrossOrigin("");
        let texture = loader.load(userImageURL);
    
        // update texture
        shader.uniforms.texture1.value = texture;
    }
    

    使用输入onChange运行该 fileUpload 方法 . 这样,每次上传图像时都不需要重新启动场景 .

    确保 shader 是一个可以在 fileUpload 方法中访问的变量 .

相关问题