Рубріки: Инструменты

React в 3D: как использовать react-three-fiber

Павел Мищенко

Когда-то, изучая возможности работы с 3D-изображениями в современных браузерах, я наткнулся на Three.js — кроссбраузерную библиотеку JavaScript для создания динамичных 3D-изображений в браузере, как, например, здесь. Изучив документацию и поэкспериментировав с основными элементами библиотеки, я нашел отличный урок Кайла Уэттона How to Build a Color Customizer App.

Я решил применить инструкцию Уэттона к популярной библиотеке React с дополнением react-three-fiber — библиотеки, в которой объединены React и Three.js. 

Примечание: В этой статье я не описывал разработку таких элементов интерфейса, как выбор цвета и модели, в обычном React. Их применение можно увидеть в конечном коде.

Пара слов о react-three-fiber

Примеры проектов с react-three-fiber.

Библиотека react-three-fiber (R3F) позволяет создавать динамические графические изображения с так называемыми многоразовыми компонентами, а также структурировать исходный код.

Эти компоненты реагируют на изменения внутреннего состояния, интерактивны и синхронизируются с экосистемой React. К тому же, работая с react-three-fiber вы практически ничем не ограничены — все, что можно сделать с обычным Three.js, будет работать и в R3F. Производительность в таком случае определяется Three.js и GPU, поэтому R3F не медленнее, чем Three.js.  

Вот ссылки на react-three-fiber:

— Repo

— API

Итак, начнем начнем наш эксперимент с подготовки параметров. 

Подготовка параметров

С помощью компонентов react-three-fiber мы создадим 3D-изображение стула, стоящего на полу со всеми необходимыми бликами и тенями. Для нашей задачи мы будем использовать шаблон create-react-app. 

npx create-react-app my-app
npm install three react-three-fiber

Используем CanvasComponent, в котором создаются все 3D-объекты. Остальное HTML будет в виде UI. 

import {Canvas} from "react-three-fiber";
<Canvas id="rtfCanvas" >
   <Scene/>
</Canvas>

Canvas object — ваше входное окно в Three.js. Он выполняет Three.js-элементы, не выполняя при этом DOM-элементы. Мы можем установить некоторые элементы по дефолту, например, трехмерные иллюстрации проекций лучей (raycaster) или настройки камеры, через свойства Canvas, например: 

<Canvas id="rtfCanvas" camera={{fov: 50}}/>

Но лучше создать компонент Scene на подуровне, в качестве «ребенка», для чистоты структуры проекта и удобства в разработке. В Canvas R3F уже по дефолту встроен THREE.Scene object, так что в Scene мы просто настроим его под себя. Установим поле значения для просмотра 50 и обновим цвет фона Scene. 

Добавим немного тумана такого же цвета, чтобы скрыть края пола:

const Scene = ({newMaterialOpt}) => {
   const {
       scene, camera,
       gl: {domElement}
   } = useThree();
    
   useEffect(() => {
       scene.background = new THREE.Color(0xf1f1f1);
       scene.fog = new THREE.Fog(0xf1f1f1, 20, 100);
       camera.fov = 50;
   }, [])

   return (<> </>)
}

Добавим полукруг и направим свет на объект:

Добавим controls от OrbitControls.js. Функция «расширить» дополняет каталог three-fiber элементами JSX elements. Компоненты, добавленные таким образом, можно использовать в графических иллюстрациях помощью camel casing.

Стартовая конфигурация нашего 3D-изображения выглядит так: 

import {extend, useThree} from "react-three-fiber";
import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls'
import * as THREE from "three";
extend({OrbitControls})
const Scene = ({newMaterialOpt}) => {
    const {
        scene, camera,
        gl: {domElement}
    } = useThree();

   useEffect(() => {
        scene.background = new THREE.Color(0xf1f1f1);
        scene.fog = new THREE.Fog(0xf1f1f1, 20, 100);
        camera.fov = 50;
   }, [])

    return (
        <>
            <orbitControls args={[camera, domElement]}/>
           <hemisphereLight
               skycolor={new THREE.Color(0xffffff)}
               groundColor={new THREE.Color(0xffffff)}
               intensity={0.61}
               position={[0, 50, 0]}
           />
           <directionalLight
               color={new THREE.Color(0xffffff)}
               intensity={0.54}
               position={[-8, 12, 8]}
               castShadow
           />
           <Suspense fallback={null}>
               <ChairMesh newMaterialOpt={newMaterialOpt}/>
               <Floor/>
           </Suspense>
       </>
   )
}

Загружаем модель

Добавим компонент ChairMesh, который будет воспроизводить модель стула. Расширением модели будет .gltf — это требования открытого формата для эффективного воспроизведения и загрузки 3D-контента. Ассеты могут быть представлены как в JSON (.gltf) так и в .glb.

Загружаем модель объекта, используя GLTFLoader из three.js package: 

После загрузки всей информации о переменной модели используем <primitive/>, чтобы добавить уже созданные объекты в изображение.

Получаем следующее состояние объекта модели, используя primitive. На данном этапе наша модель выглядит так:

Теперь добавим пол, на который будет падать тень от стула:

Процесс создания 3D primitive в R3F с помощью jsx немного отличается, но суть та же, что и в обычном ThreeJs.

Во-первых, создаем трехмерную сетку, состоящую в основном из геометрических фигур и материалов. 

Во-вторых, создаем planeGeometry в форме четырехугольника, затем новую meshPhongMaterial и выставляем пару опций: цвет, и блики. Phong — отличный материал, поскольку с его помощью можно регулировать отражения и блики.

Должно получиться вот так: 

Перейдем к теням

Используя дополнительные настройки, создаем тени от стула на полу. Запускаем shadowMap в рендере GL в настройках нашей сцены. Также вручную добавляем shadow mapSize к directionalLight к scene children, поскольку искусственные настройки светотени не работают (на момент создания статьи):

В функции загрузки есть возможность пройтись по параметрам 3D-объекта. Для этого перейдем к функции загрузки и добавим ниже theModel: = gltf.scene. Для каждой части 3D-объекта (ножки, подушки, и т.д.), мы применяем опцию «применить тени».

Этот метод изменения параметров будет использован позже.

Материалы объекта все еще выглядят неправдоподобно, поскольку сейчас используются стандартные материалы из дизайн-редактора (в моем случае — Blender). Во всяком случае тут есть тени: 

Установим материал Phong по умолчанию:

На этом можно было бы и закончить. Но для некоторых объектов может понадобиться загрузить особый цвет или текстуру, поскольку мы не можем покрыть всю модель одним базовым цветом.

Мы будем перебирать наш 3D-объект, используя childID, чтобы найти разные части стула, и применить к ним материал. Эти childID происходят от имен, которые мы дали каждому объекту в Blender. 

Под функцией загрузки добавим функцию, которая берет модель, часть объекта (тип) и материал и устанавливает материал. Также добавим к этой части новое свойство под названием nameID, чтобы использовать его позже.

Попробуем запустить эту функцию после того, как загрузим готовую модель. В useEffect hook выбираем переменную theModel как зависимость для обновления.

Также изменим цвет пола с красного на 0xeeeeee. В итоге получаем это:

В результате мы создали трехмерное изображение со светотенями и управлением мышьюРезультат нашей работы можно увидеть здесь.

If you have found a spelling error, please, notify us by selecting that text and pressing Ctrl+Enter.

Останні статті

Токсичные коллеги. Как не стать одним из них и прекратить ныть

В благословенные офисные времена, когда не было большой войны и коронавируса, люди гораздо больше общались…

07.12.2023

Делать что-то впервые всегда очень трудно. Две истории о начале карьеры PM

Вот две истории из собственного опыта, с тех пор, когда только начинал делать свою карьеру…

04.12.2023

«Тыжпрограммист». Как люди не из ІТ-отрасли обесценивают профессию

«Ты же программист». За свою жизнь я много раз слышал эту фразу. От всех. Кто…

15.11.2023

Почему чат GitHub Copilot лучше для разработчиков, чем ChatGPT

Отличные новости! Если вы пропустили, GitHub Copilot — это уже не отдельный продукт, а набор…

13.11.2023

Как мы используем ИИ и Low-Code технологии для разработки IT-продукта

Несколько месяцев назад мы с командой Promodo (агентство инвестировало в продукт более $100 000) запустили…

07.11.2023

Университет или курсы. Что лучше для получения IT-образования

Пару дней назад прочитал сообщение о том, что хорошие курсы могут стать альтернативой классическому образованию.…

19.10.2023