【问题标题】:load texture from user upload to geometry in three.js将用户上传的纹理加载到three.js中的几何图形
【发布时间】:2018-10-30 02:15:51
【问题描述】:

我是 three.js 和 javascript(一般编码)的新手,但我在尝试各种可能性时获得了很多乐趣,并创建了一些带灯光的小 Three.js 场景,来自 3D JSON 模型的自定义几何培养我的技能,以解决我构建 3D 网站的最终目标。

我目前完全停留在允许用户从 HTML 'input' 文件标签上传图像文件的问题上,并让该图像出现在场景中已经存在的具有虚拟纹理的 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

【问题讨论】:

    标签: javascript html three.js


    【解决方案1】:

    这样就可以了:

    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 方法中访问的变量。

    【讨论】:

      【解决方案2】:

      使用addEventListener 添加“更改”事件 到“文件”input:

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

      新文件可以通过事件参数的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>

      【讨论】:

      • 谢谢兔子!您的示例确实帮助我了解了在从“userImage”中选择纹理后如何加载初始场景,这很好用!!!您使用示例代码向窗口添加了另一个 domElement,但我正在研究如何替换现有场景中的纹理并更新场景,以便共享先前纹理(克隆 tex)的任何对象也具有其纹理更改。我已经尝试过清除整个场景(我无法弄清楚如何),但我希望能够通过加载新图像以非破坏性方式更改纹理。
      猜你喜欢
      • 2019-08-23
      • 1970-01-01
      • 2017-03-28
      • 2014-01-08
      • 2016-03-08
      • 2016-10-27
      • 2012-11-25
      • 2013-02-23
      • 2019-09-19
      相关资源
      最近更新 更多