【问题标题】:Trying to load obj & mtl file with Three.js in React Native尝试在 React Native 中使用 Three.js 加载 obj 和 mtl 文件
【发布时间】:2018-10-15 19:56:48
【问题描述】:

主要目标:将从 Maya 导出的动画模型加载到 React Native 应用程序中 导出的文件: obj、mtl 和 png 文件

我在我的 React Native 项目中设置了https://github.com/react-community/react-native-webgl,它工作正常。

现在,当我尝试使用 MTLLoader 加载 MTL 文件时,出现以下错误:

Can't find variable: document

显然,MTLLoader 正在调用 TextureLoader,它在内部调用了一些具有“文档”引用的加载函数。那么有什么办法可以解决这个问题呢?

这是我正在使用的两个文件:

三个.js

const THREE = require("three");
global.THREE = THREE;
if (!window.addEventListener)
    window.addEventListener = () => { };
// require("three/examples/js/renderers/Projector");
require("three/examples/js/loaders/MTLLoader");
require("three/examples/js/loaders/OBJLoader");
export default THREE;

ThreeView.js

import React, { Component } from "react";
import { StyleSheet, View } from "react-native";
import { WebGLView } from "react-native-webgl";
import THREE from "./three";
import { image } from "src/res/image";

export default class ThreeView extends Component {
    requestId: *;
    componentWillUnmount() {
    cancelAnimationFrame(this.requestId);
}
onContextCreate = (gl: WebGLRenderingContext) => {
    const rngl = gl.getExtension("RN");

    const { drawingBufferWidth: width, drawingBufferHeight: height } = gl;
    const renderer = new THREE.WebGLRenderer({
        canvas: {
            width,
            height,
            style: {},
            addEventListener: () => { },
            removeEventListener: () => { },
            clientHeight: height
        },
        context: gl
    });
    renderer.setSize(width, height);
    renderer.setClearColor(0xffffff, 1);

    let camera, scene;
    let cube;

    function init() {
        camera = new THREE.PerspectiveCamera(75, width / height, 1, 1100);
        camera.position.y = 150;
        camera.position.z = 500;
        scene = new THREE.Scene();

        var mtlLoader = new THREE.MTLLoader();
        mtlLoader.load('female-croupier-2013-03-26.mtl', function (materials) {
            materials.preload();

            var objLoader = new THREE.OBJLoader();
            objLoader.setMaterials(materials);
            objLoader.load('female-croupier-2013-03-26.obj', function (object) {
                scene.add(object);
            }, onLoading, onErrorLoading);
        }, onLoading, onErrorLoading);
    }
    const onLoading = (xhr) => {
        console.log((xhr.loaded / xhr.total * 100) + '% loaded');
    };
    const onErrorLoading = (error) => {
        console.log('An error happened', error);
    };
    const animate = () => {
        this.requestId = requestAnimationFrame(animate);
        renderer.render(scene, camera);

        // cube.rotation.y += 0.05;

        gl.flush();
        rngl.endFrame();
    };

    init();
    animate();
};
render() {
    return (
        <View style={styles.container}>
            <WebGLView
                style={styles.webglView}
                onContextCreate={this.onContextCreate}
            />
        </View>
    );
}
}

const styles = StyleSheet.create({
container: {
    flex: 1,
    backgroundColor: "#fff",
    alignItems: "center",
    justifyContent: "center"
},
webglView: {
    width: 300,
    height: 300
}
});

【问题讨论】:

  • 您可以覆盖 ImageLoader 中的加载函数,并使用var image = new Image() 而不是document.createElementNS。
  • @2pha :“图像”是什么意思?反应原生图像组件?还是 Html 图片?我必须导入它吗?
  • 我不知道如何导入正确的“图片”,所以现在我收到了Can't find variable: Image
  • Image 是用于创建 html 图像元素的构造函数,可替代调用 document.createElement。它不应该被导入。虽然我来自 html/web 的角度,但我不知道它是否可以在 React Native 中工作(我从未使用过它)。这只是一个尝试的建议。
  • 我不认为它会像这样工作。 HTML 元素在 React Native 应用程序中不可用。

标签: react-native three.js


【解决方案1】:

我有类似的设置并遇到了同样的问题。我的选择是切换到 JSONLoader,它不需要在 react-native 中呈现文档。所以,我只是在 Blender 中加载了我的模型,并带有三个 js 插件,然后将其导出为 json。看看这个向 Blender 添加三个 js 插件的过程

https://www.youtube.com/watch?v=mqjwgTAGQRY

一切顺利

【讨论】:

    【解决方案2】:

    正如其他人所说,这个错误是由于threejs试图使用react-native没有的浏览器功能引起的。

    到目前为止,我已经能够通过猴子修补纹理加载器以在 react-native-webgl 中使用加载器来加载纹理(这是您收到错误的阶段)。将此添加到您的 init 函数中(最好在顶部附近)。

    //make sure you have defined renderer and rngl
    
    /*
    
    const renderer = new THREE.WebGLRenderer(...)
    
    const rngl = gl.getExtension("RN");
    
    */
    
    const loadTexture = async function(url, onLoad, onProgress, onError) {
          let textureObject = new THREE.Texture();
          console.log("loading",url,'with fancy texture loader');
          let properties = renderer.properties.get(textureObject);
    
    
          var texture = await rngl.loadTexture({yflip: false, image: url});
    
          /*
          rngl.loadTexture({ image: url })
            .then(({ texture }) => {
            */
              console.log("Texture [" + url + "] Loaded!")
              texture.needsUpdate = true;
              properties.__webglTexture = texture;
              properties.__webglInit = true;
              console.log(texture);
                  
              
              if (onLoad !== undefined) {
                //console.warn('loaded tex', texture);
                onLoad(textureObject);
              }
              
      
          //});
    
        
          return textureObject;
      
      }
      
      THREE.TextureLoader.prototype.load = loadTexture;   

    这解决了加载纹理的问题,我可以看到它们在 Charles 中加载,但它们仍然没有在模型上渲染,所以我被困在这一点上。从技术上讲,这是一个正确的答案,但是一旦实施,您就会被卡住。我希望你能发表评论并告诉我你已经走得更远了。

    【讨论】:

      【解决方案3】:

      这可能会让你更接近:

      GLTF 格式支持嵌入纹理图像(作为 base64)。如果您的资产管道允许,您可以转换为 GLTF,然后加载到三个/react-native。

      我不得不为“decodeUriComponent”和“atob”提供一些“window”polyfill,因为 GLTFLoader 使用 FileLoader 来解析 base64:

      我已经成功加载了嵌入式缓冲区,但是您需要更多的 polyfill 来加载纹理。 TextureLoader 使用 ImageLoader,它使用 document.createElementNS

      【讨论】:

        【解决方案4】:

        您正在使用使用 TextureLoader 的 MTLLoader,而 TextureLoader 使用 ImageLoader。 图像加载器使用 document.createElementNS() 函数。

        我解决这个问题的方法是直接调用 THREEjs TextureLoader:

        let texture = new THREE.Texture(
             url //URL = a base 64 JPEG string in this case     
             );
        

        (对于Texture的使用请查看Texture documentation)

        然后我使用来自 React native 的 Image 类(而不是需要构建 DOM 的 THREEjs Image)将其作为属性赋予 Texture:

        import { Image } from 'react-native';
        
        var img = new Image(128, 128);
        img.src = url;
        texture.normal = img;
        

        然后最后将纹理映射到目标材质上:

        const mat = new THREE.MeshPhongMaterial();
        mat.map = texture;
        

        react native documentation 会解释如何使用 react native Image 元素,它支持 base64 编码的 JPEG。

        也许有一种方法可以让您挑出需要 TextureLoader 的部分,并用这个答案替换该部分。让我知道结果如何。

        旁注,我还没有尝试在我的 webGLView 中显示它,但在日志中它看起来像普通的 threejs 对象,值得一试

        【讨论】:

          猜你喜欢
          • 2014-10-03
          • 1970-01-01
          • 2013-07-16
          • 1970-01-01
          • 1970-01-01
          • 2017-10-04
          • 2019-01-18
          • 2017-06-13
          • 2017-07-17
          相关资源
          最近更新 更多