Search code examples
javascriptthree.jstexturesexpo

Issue with applying a picture on Sphere


I've read quite a few topics on here, googled, watched youtube.... Just can't make my Sphere to get a texture. Running this code only shows me white Sphere but no texture... Please help the newbie out <3

Full code below:

import { View as GraphicsView } from 'expo-graphics';
import ExpoTHREE, { THREE } from 'expo-three';
import React from 'react';

export default class App extends React.Component {
  componentWillMount() {
    THREE.suppressExpoWarnings();
  }

  render() {
    // Create an `ExpoGraphics.View` covering the whole screen, tell it to call our
    // `onContextCreate` function once it's initialized.
    return (
      <GraphicsView
        onContextCreate={this.onContextCreate}
        onRender={this.onRender}
      />
    );
  }

  // This is called by the `ExpoGraphics.View` once it's initialized

  onContextCreate = async ({
    gl,
    canvas,
    width,
    height,
    scale: pixelRatio,
  }) => {
    this.renderer = new ExpoTHREE.Renderer({ gl, pixelRatio, width, height });
    this.renderer.setClearColor(0x00cbff)
    this.scene = new THREE.Scene();
    this.camera = new THREE.PerspectiveCamera(120, width / height, 0.1, 1000);
    this.camera.position.z = 5;
    const loader = new THREE.TextureLoader();
    const geometry = new THREE.SphereGeometry(3, 50, 50, 0, Math.PI * 2, 0, Math.PI * 2);
    const material = new THREE.MeshPhongMaterial({map: loader.load('https://threejsfundamentals.org/threejs/resources/images/wall.jpg')});

    this.cube = new THREE.Mesh(geometry, material);
    this.scene.add(this.cube);

    this.scene.add(new THREE.AmbientLight(0x000000));

    const light = new THREE.DirectionalLight(0xffffff, 0.5);
    light.position.set(3, 3, 3);
    this.scene.add(light);
  };

  onRender = delta => {
    this.cube.rotation.x += 3.5 * delta;
    this.cube.rotation.y += 2 * delta;
    this.renderer.render(this.scene, this.camera);
  };
}

Solution

  • Not sure if it's an abject requirement, but I believe expo needs to load its resources from a local store rather than remote URLs.

    First, download the wall.jpg file and store it locally (in my case, a folder in the root directory called 'assets'.

    Now, you need to configure expo to add jpg files to your bundle. Open app.json in your app's root directory and update or add to the "packagerOpts" so that jpg is included:

        "packagerOpts": {
          "assetExts": ["jpg"]
        },
    

    You may also need to create a file in your app's root directory named "metro.config.js" which contains the following:

    module.exports = {
      resolver: {
        assetExts: ["jpg"]
      }
    }
    

    Now that your packager is including JPG files in your expo app bundle, you can use ExpoTHREE's built in loadAsync function to load your jpg:

    const texture = await ExpoTHREE.loadAsync(
      require('./assets/wall.jpg')
    );
    const geometry = new THREE.SphereGeometry(3, 50, 50, 0, Math.PI * 2, 0, Math.PI * 2);
    const material = new THREE.MeshPhongMaterial({map: texture});
    

    With those changes your code looks to be working on my end. You can use the same process to add different types of files to your bundle and load them for use in ExpoTHREE. Just add the various filetypes to the arrays in your app.json and metro.config.js and they'll be bundled for use in your app.