DEV Community

loading...
Cover image for react-three-fiber: Planet Mars

react-three-fiber: Planet Mars

danielkrupnyy profile image Daniel Krupnyy ・4 min read

As long as I can remember, I have always been attracted by Space. But then i heard about programming...

Hy there! Today we will create mars planet using three.js and react-three-fiber.

Link to the finished project

What are three.js and react-three-fiber?

This tutorial is for those who already know the basics of three.js. There i won't go into details since there are already so many introductory guides. Today i want to focus on practise.

But in brief:

  1. three.js — it's JavaScript library for creating 3D graphics.
  2. react-three-fiber — is a React renderer for three.js on the web and react-native.

Here we go!

First! The structure of our files in this guide:

files structure

Now let's dwell on the details. In our project, we need to create three main components:

  1. Sphere — this will be the planet Mars
  2. SkyBox — this is our space, we will use CubeTextureLoader() to create it. For this component we need to get 6 images for the background of each side of the cube.
  3. CameraControls — very important component. This will give us the ability to rotate and scale our Sphere (Mars) as we want.

Sphere creating

Let's start with the Sphere component:

import React, { useRef } from "react";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { useFrame, useLoader } from "react-three-fiber";

const Sphere = () => {
  const planet = useRef();

  const { nodes } = useLoader(GLTFLoader, "models/mars.glb");

  useFrame(() => (planet.current.rotation.y += 0.0002));

  return (
    <mesh
      ref={planet}
      visible
      position={[0, 0, 0]}
      // Adding data from mars.glb to the geometry and material of the sphere
      geometry={nodes.Cube008.geometry}
      material={nodes.Cube008.material}
    />
  );
};

export default Sphere;

We are using a ready-made gltf 3D file that creates the geometry and material for our sphere. We can get it from the official NASA website. To work with the gltf file we use the GLTFLoader from three.js and the useLoader() hook from react-three-fiber. Also we use the useFrame() hook that add rotation for our planet.

SkyBox creating

Notice how SkyBox returns null this is because we will not be creating any new objects with this component in our scene. Instead, we will use it as a controller to set a property in our scene, as we will see in the next step when we load and apply the skybox textures.

import { useThree } from "react-three-fiber";
import { CubeTextureLoader } from "three";

// Loads the skybox texture and applies it to the scene.
const SkyBox = () => {
  const { scene } = useThree();
  const loader = new CubeTextureLoader();
  // The CubeTextureLoader load method takes an array of urls representing all 6 sides of the cube.
  const texture = loader.load([
    "/images/front.jpg",
    "/images/back.jpg",
    "/images/top.jpg",
    "/images/bottom.jpg",
    "/images/left.jpg",
    "/images/right.jpg",
  ]);

  // Set the scene background property to the resulting texture.
  scene.background = texture;
  return null;
};

export default SkyBox;

To start we need to get a reference of our Three.JS scene and for that we use useThree() hook. Then we create an instance of the CubeTextureLoader and then call the load method with an array containing the six URLs of your images. This will return a CubeTexture. The CubeTexture we assign to the global scene.background which we get a reference to with useThree(), and that's it our skybox is finished.

Skybox Textures

Also important to talk about Skybox textures creating. For this purpose i was using Spacescape program. This is a simple space landscape generator. You can use it or create assets in Photoshop or something.

Camera Controls

And the last thing about camera control. Here we are using OrbitControls, which allows the camera to rotate around the target.

import React, { useRef } from "react";
import { extend, useThree, useFrame } from "react-three-fiber";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";

extend({ OrbitControls });

const CameraControls = () => {
  const {
    camera,
    gl: { domElement },
  } = useThree();

  // Ref to the controls, so that we can update them on every frame with useFrame
  const controls = useRef();

  camera.position.z = 999;

  useFrame(() => controls.current.update());

  return (
    <orbitControls
      ref={controls}
      args={[camera, domElement]}
      autoRotate={false}
      enableZoom={false}
    />
  );
};

export default CameraControls;

Finish

Now we can use all created components in to the App component:

import React, { Suspense } from "react";
import { Canvas } from "react-three-fiber";
import "./styles.css";

import { CameraControls, Sphere, SkyBox } from "./components";

const App = () => {
  return (
    <>
      <Canvas className="canvas">
        <CameraControls />
        <directionalLight intensity={1} />
        <ambientLight intensity={0.6} />
        <Suspense fallback="loading">
          <Sphere />
        </Suspense>
        <SkyBox />
      </Canvas>
    </>
  );
};

export default App;

And also add Styles in to styles.css:

* {
  box-sizing: border-box;
}

html,
body,
#root {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
}

Well that's all. Thanks for reading =)

Discussion

pic
Editor guide