Three.js (fiber & drei) — 3D web3 serie

Серия 3D web3
Это второй пост из серии 3D-web3.

1 — Vite config и базовый three.js
2 — Three.js (fiber & drei)
3 — Физика пушки
4 — Web3

Привет, приятель,

Drei pacakge предоставляет нам готовые абстракции для «Fiber».
Это коллекция полезных помощников и полностью функциональных элементов DOM

npm i @react-three/drei

В «Drei docs» есть полезный индекс для проверки того, какие функции уже доступны.

Мы будем использовать:

  • Из «Fiber»: Canvas, useThree, useFrame
  • Из «Drei»: useGLTF, CameraShake, OrbitControls, Stars и Html
  • Из «3»: Vector3

Этот пост разделен на следующие шаги:
1_ Стилизация
2_ Приложение «Canvas» со всеми компонентами
3_ Drei элементы для создания фона «небо», «звезды»
4_ Импорт 3D модели в «Canvas» (с использованием «Suspense» и созданием освещения)
5_ Создание двух различных вариантов использования «камеры»

Шаг1_ Определите область просмотра «Canvas» в App.css

.canvas {
  height: 100vh;
  width: 100vw;
}
Вход в полноэкранный режим Выход из полноэкранного режима

Шаг2_ Внедрите элемент «Canvas», импортированный из «fiber».
Комментарии в коде

import { Suspense, useState } from 'react';
import { Canvas } from '@react-three/fiber';
import { CameraShake, OrbitControls, Sky, Html } from '@react-three/drei';

import './App.css';
import { Lights } from './lights/Lights';
import CameraRig from './camera/CameraRig';
import Stars from './stars/Stars';
import GltfLoader from './gltf/GltfLoader';

const App = () => {

    const [cameraMode, setCameraMode] = useState(true);
    const [move, setmove] = useState(undefined);

    return (
        <div className='row'>
            <div className='col-lg-12 canvas'>
                <Canvas
                    camera={{
                        position: [0, 0, 0],
                        rotation: [0, 0, 0],
                    }}>
                    {/* Details in "Stars" component */}
                    <Stars />
                    {/* Drei component, creates a sky background (does not give ilumination to canvas elements) */}
                    <Sky distance={450000}
                        sunPosition={[2, 1, 10]}
                        inclination={0}
                        azimuth={0.25} />

                    {/* While importing gltf model, we will cath it in a "Suspense" */}
                    <Suspense fallback={null}>
                        {/* Our "GltfLoader" component takes a "position" and "cameraMove" setter to switch between two different camera controls*/}
                        <GltfLoader url={'chair/armchairYellow.gltf'} position={[0, -5, -15]} setCameraMode={() => { setCameraMode(!cameraMode) }} />
                    </Suspense>
                    {/* Several lights types are implemented in canvas */}
                    <Lights />

                    {/* switch between two different camera controls */}
                    {cameraMode ? <><OrbitControls
                        enableZoom={true}
                        enablePan={true}
                        enableRotate={true}
                    />
                        {/* HTML is added only in Orbitcontrols, because creates a conflict using with "CameraShake" or "CameraRig" */}
                        <Html
                            as='h4'
                            position={[0, 0, -15]}
                            transform={true}
                        >
                            <h4 className="htmlcanvas" >Click to switch between two different camera controls</h4>
                        </Html>
                    </>
                        :
                        <>
                            {/* Drei component, creates a balanced movement in our "camera" position */}
                            <CameraShake
                                yawFrequency={0.1}
                                pitchFrequency={0.1}
                                rollFrequency={0.1}
                            />
                            {/* Using "CameraRig", "camera" position reacts to our mouse movement */}
                            <CameraRig setmove={setmove} />
                        </>
                    }
                </Canvas>
            </div>
        </div>
    );
}

export default App
Войти в полноэкранный режим Выйти из полноэкранного режима

Шаг3_ «небо», «звезды» Drei элементы для создания фона

Stars.jsx
useRef для присоединения к «mesh», который является встроенным «элементом DOM» по волокну, что позволяет манипулировать объектом внутри.

import { Stars } from '@react-three/drei';
import { useFrame } from '@react-three/fiber';
import { useRef } from 'react';

const StarsBG = () => {

    const ref = useRef();
    const ref2 = useRef();

    useFrame(() => {
        ref.current.rotation.y += 0.001
        ref2.current.rotation.y -= 0.0005
    })

    return (
        <>

            <Stars
                ref={ref}
                radius={4}
                depth={100}
                count={5000}
                factor={3}
                saturation={0.9}
                fade
                speed={.1}
                color={3}
            />
            <Stars
                ref={ref2}
                radius={2}
                depth={200}
                count={5000}
                factor={3}
                saturation={0.9}
                fade
                speed={.2}
                color={3}
            />
        </>
    );
}

export default StarsBG;


Вход в полноэкранный режим Выход из полноэкранного режима

Компонент Sky уже определен в нашем «Canvas».

<Sky distance={450000}
                        sunPosition={[2, 1, 10]}
                        inclination={0}
                        azimuth={0.25} />
Войти в полноэкранный режим Выход из полноэкранного режима

Шаг4_ Импорт 3D модели в «холст» (использование «Suspense» и создание освещения)

Мы будем использовать «useGLTF» от «drei». Предоставим url и «/draco-gltf» в качестве параметров и определим его в элементе «primitive»

GltfLoader.jsx

Передайте атрибут onClick для вызова метода setCameraMode. Для переключения между двумя разными режимами управления камерой (объясняется в шаге 5)

import { useGLTF } from "@react-three/drei";

function GltfLoader(props) {
  const gltf = useGLTF(props.url, "/draco-gltf");
  useGLTF.preload(props.url);
  return (
    <primitive
      scale={0.05}
      object={gltf.scene}
      dispose={null}
      position={props.position}
      onClick={props.setCameraMode}
    />
  );
}
export default GltfLoader;

Войдите в полноэкранный режим Выйдите из полноэкранного режима

Заверните его с помощью «Suspense» для обработки процесса при импорте gltf-модели

Теперь создайте освещение, используя несколько волоконных элементов.
Используйте «useRef», чтобы придать вращение освещению, используя «useFrame».

Lights.jsx

import { useFrame } from '@react-three/fiber';
import { useRef } from 'react'

export const Lights = () => {

    const ref = useRef();
    useFrame(() => {
        // give rotation to the ilumination
        ref.current.rotation.y -= 0.005
    })

    return (
        <group
            ref={ref}>
            {/* Ambient Light illuminates lights for all objects */}
            <ambientLight intensity={0.3} />
            {/* Diretion light */}
            <directionalLight position={[10, 10, 5]} intensity={1} />
            <directionalLight
                castShadow
                position={[0, 10, 0]}
                intensity={1.5}
                shadow-mapSize-width={1024}
                shadow-mapSize-height={1024}
                shadow-camera-far={50}
                shadow-camera-left={-10}
                shadow-camera-right={10}
                shadow-camera-top={10}
                shadow-camera-bottom={-10}
            />
            {/* Spotlight Large overhead light */}
            <spotLight intensity={1} position={[1000, 0, 0]} castShadow />
        </group>
    );
};

Вход в полноэкранный режим Выход из полноэкранного режима

Шаг5_ Создание двух различных вариантов использования «камеры»
Мы будем использовать состояния:
1_ move (для установки движения камеры в каждом кадре)
2_ cameraMode (для переключения между двумя различными управлениями камеры).

Использование if else в одной строке для переключения режима

Вариант1_ мы будем использовать следующие элементы из «drei» в нашем «Canvas»:
«OrbitControls» — Дает нам управление камерой: вращение (левая кнопка мыши) и положение (правая кнопка мыши).

«Html» — Позволяет нам использовать основные узлы DOM для создания текста.

«Html» создает конфликт, если мы используем его с «CameraShake» или «CameraRig». Поэтому мы показываем текст только в варианте1.

Вариант2_ мы будем использовать следующие элементы:
«CameraShake» от «drei» — создает сбалансированное движение в нашей «камере» Vector3
«CameraRig» (созданный нами). Но сначала проверьте код.

Этот код уже находится в элементе «canvas» файла App.jsx

<Canvas>
...code
{cameraMove ? <><OrbitControls
                        enableZoom={true}
                        enablePan={true}
                        enableRotate={true}
                    />
                        {/* HTML is added only in Orbitcontrols, because creates a conflict using with "CameraShake" or "CameraRig" */}
                        <Html
                            as='h4'
                            position={[0, 0, -15]}
                            transform={true}
                        >
                            <h4 className="htmlcanvas" >Click to switch between two different camera controls</h4>
                        </Html>
                    </>
                        :
                        <>
                            {/* Drei component, creates a balanced movement in our "camera" position */}
                            <CameraShake
                                yawFrequency={0.1}
                                pitchFrequency={0.1}
                                rollFrequency={0.1}
                            />
                            {/* Using "CameraRig", "camera" position reacts to our mouse movement */}
                            <CameraRig setmove={setmove} />
                        </>
                    }
                </Canvas>
Войти в полноэкранный режим Выйти из полноэкранного режима

И последнее, используйте алгоритм «lerp» для линейной интерполяции в нашей камере «Vector3», импортированный из «three».

Этот код позволит нам перемещать камеру, имитируя движение мыши.

CameraRig.js

import { useFrame, useThree } from "@react-three/fiber";
import { Vector3 } from "three";

// Using "CameraRig", "camera" position reacts to our mouse movement
function CameraRig(props) {
  const vec = new Vector3();
  const { camera, mouse } = useThree();
  useFrame(() => {
    // using "lerp" algorithm to do a linear interpolation between three variables (x,y,z) and  given a fraction (0.05 in this case)
    camera.position.lerp(vec.set(mouse.x * 20, mouse.y * 20, mouse.z), 0.05);
    props.setmove(camera.position);
  });
}

export default CameraRig;

Вход в полноэкранный режим Выход из полноэкранного режима

Возобновление компонентов: App, Stars, GltfLoader, Lights, CameraRig

Код в песочнице
Не рендерится из-за проблемы с CodeSandBox «ide».
ошибка: Unexpected token < in JSON at position 0
Для получения дополнительной информации, проверьте в stackoverslow или в этом посте.

Я удалил gltf-модель, чтобы показать быстрый предварительный просмотр.

Web3 будет добавлен в следующем посте

Надеюсь, это было полезно.

Оцените статью
devanswers.ru
Добавить комментарий