はじめに

戦略技術センターでは、XR・メタバース・デジタルツイン・空間コンピューティングといった3D分野のアプリケーションを、ウェブアプリ開発技術ベースで開発するための開発基盤の構築に取り組んできました。

初期の開発基盤では疎結合なアーキテクチャと、アバターやマルチプレイのシステムといった汎用的な基本コンポーネントのライブラリを実装することで、ウェブベースの3Dアプリ開発でのコンポーネント設計・実装の一貫性や保守性、品質の向上を実現しました。

一方で、開発者がアーキテクチャやライブラリを用いて実装したコンポーネントを組み合わせて3D空間を構築する工程には、ウェブベースの3Dアプリ開発の中心技術である React Three Fiber(R3F)に起因する問題があります。具体的には、空間構成情報がアプリケーションと密結合してしまう点、そして空間構築の方法がコードベースで直感的でない点です。

本稿では上記の空間構築の問題を解決するために新たに開発基盤に実装した「メタレンダラ」と「シーンエディタ」について紹介します。

メタレンダラは空間構成データから動的に空間を描画するレンダラで、空間構成とアプリの分離を実現します。シーンエディタは空間構築のためのGUIエディタで直感的な操作での空間構成データの編集を実現します。

想定読者

アプリケーション開発者

背景

R3F中心のウェブベース開発への移行

弊社の3Dアプリ開発は、2024年に Unity ベースから React Three Fiber(R3F)を中心としたウェブベースの開発に移行しました。

ウェブベースのアプリケーション開発で得られる代表的なメリットは次の通りです。

  • 高速なビルドやホットリロード
  • 豊富なOSSライブラリを活用できるReact/Webエコシステム
  • インストール不要のウェブアプリとしての展開のしやすさ

ウェブベースの移行は、従来のUnityベースのネイティブアプリ開発が抱えていた課題を解決し、開発サイクルとリリースサイクルの高速化を実現しました。

ウェブベースへの移行に関する詳細は、別途公開している記事をご参照ください。

React Three Fiberとは

React Three Fiber は、ウェブ向け3DCGライブラリである Three.js を、Reactコンポーネントとして扱えるようにするラッパー的位置づけのライブラリです。R3Fでは空間構成(どのような空間であるのかという情報)を、通常のReactでページやUIを構築するのと同様に、JSXで宣言的に記述することができます。

import React from 'react'
import { Canvas } from '@react-three/fiber'

// 赤い箱型オブジェクトが配置されたシーンのコンポーネント
export function Scene() {
  return (
    <Canvas>
      <mesh position={[0, 0, 0]}>
        <boxGeometry />
        <meshBasicMaterial color='red' />
      </mesh>
      <directionalLight position={[10, 10, 10]} />
    </Canvas>
  );
}

上記のR3Fの実装例に相当するThree.jsでの実装は下記の通りです。Three.jsでは空間構成は手続き的に記述されます。

//シーンを生成
const scene = new THREE.Scene();

// 赤い箱型のオブジェクトを生成
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: "red" });
const cube = new THREE.Mesh(geometry, material);
directionalLight.position.set(0, 0, 0);

// 光源の生成
const directionalLight = new THREE.DirectionalLight();
directionalLight.position.set(10, 10, 10);

// オブジェクトと光源をシーンに追加
scene.add(cube)
scene.add(directionalLight);

R3Fの宣言的な記述は、どのような空間構成であるのかを直接的に表現しているため、Three.js の手続き的な記述と比べて、より扱いやすい空間構築の方式であるといえます。

課題

R3Fベースの開発は、Unityベースの開発と比べて、開発サイクルとリリースサイクルの高速化を実現した一方で、「空間」を扱うという点で2つの課題があります。

空間とアプリの密結合

1つ目の課題は「空間とアプリの密結合」です。R3Fでは、空間構成はJSXとして記述されます。つまり、空間構成がアプリケーションのプログラム内にハードコードされ、空間とアプリが密結合な状態となります。

そのため、開発者は空間を追加・更新する際には、必ずコードを編集する必要があります。すでにリリース済みのアプリの場合は、再度ビルドしてデプロイし直さなければなりません。

空間を扱うアプリにおいて、空間の開発・提供を効率化することは重要な課題です。特にメタバースのようなアプリケーションではコンテンツとしての空間を継続的に開発・提供することが求められます。

3Dアプリの開発効率と運用効率の向上を実現するためには、空間とアプリの密結合を解消する必要があります。

非直感的な空間構築

2つ目の課題は「非直感的な空間構築」です。R3Fで空間内のオブジェクトの位置や向きを調整するためには、コードの該当箇所を変更して結果を確認するという手順を踏む必要があり、空間構築に時間がかかります。

Unityでは専用のGUIエディタが用意されており、マウス操作で直接的に空間内のオブジェクトの配置を変更するなどして、視覚的かつ直感的な空間構築が可能でした。一方でR3Fにはそのような標準エディタが存在しません。弊社は過去のプロジェクトで、メタバースアプリにエディタ機能を組み込んだことがありましたが、それは他のアプリでは使うことのできない汎用性に乏しいものでした。

ウェブベースの3Dアプリにおける空間構築を効率化するために、直感的かつ汎用的な空間構築を可能にする手段を実現する必要があります。

課題の解決

上記の課題に対する解決策として、「データから空間を描画するレンダラ」と「直感的な空間構築を実現するGUIエディタ」を、空間構築のための基盤技術として開発基盤上に実装しました。前者をメタレンダラ、後者をシーンエディタと呼びます。

メタレンダラは、空間構成を定義したYAML形式のデータをもとに空間を描画するレンダラです。YAML形式のデータには、R3F や drei などが提供する3Dコンポーネントからなる空間構成を宣言的に記述します。

シーンエディタは、空間構成データを編集するためのGUIエディタです。リアルタイムでレンダリング結果を確認しながら、空間構成データを編集することができます。また、Unityのように空間内のオブジェクトを直接ドラッグして配置を変更することも可能です。

開発者は、シーンエディタで作成したデータをYAMLファイルとして出力し、アプリケーションに組み込まれたメタレンダラにロードさせることで、アプリケーション上で空間を提供することができます。

以降は、メタレンダラとシーンエディタ、それぞれについて解説します。

メタレンダラ

メタレンダラとは?

メタレンダラは、R3F開発における「空間とアプリの密結合」の問題を解消するために設計されたReactコンポーネントです。空間構成データをpropsとして受け取り、動的にReact要素を生成することで、構成とロジックの分離を実現します。

基本的な使い方

メタレンダラコンポーネントの使い方は次の通りです。

import { Box } from '@react-three/drei';
import { Canvas } from '@react-three/fiber';
import { MetaRenderer } from '@repo/meta-renderer';
import { ElementType } from 'react';

export default function App() {
  // 空間構成データ
  const data = {
    type: 'Canvas',
    children: [
      {
        type: 'Box',
        properties: {
          position: [0, 0, 0],
        },
      },
    ],
  };

  // コンポーネントレジストリ
  const componentRegistry = new Map<string, ElementType>([
    ['Canvas', Canvas],
    ['Box', Box]
  ]);

  // メタレンダラコンポーネント
  return <MetaRenderer data={data} componentRegistry={componentRegistry} />;
}

メタレンダラコンポーネント(MetaRenderer)には、描画する空間構成を定義したデータ(data)と、描画に必要なReact要素を登録したコンポーネントレジストリ (componentRegistry) をpropsとして渡します。

空間構成データの構造

空間構成データは、以下のような基本構造を持ちます

  • type: 使用するReact要素名(例:mesh, Canvas, Box)
  • properties: 該当のReact要素に渡すprops(例:position, scaleなど)
  • children: 子要素の配列。子要素も基本構造(type, properties, children)に従い記述。

この構造のもとで、空間構成データは宣言的に記述されます。

レンダリングシステム

メタレンダラは、上記のデータ構造を解析し、React要素を生成するReact組み込みの関数であるReact.createElement関数を動的に呼び出すことで、空間構成データをもとにした空間のレンダリングを実現します。

下記はメタレンダラコンポーネントの簡易実装です。

function MetaRenderer({ data, componentRegistry }) {
  return renderComponent(data, componentRegistry);
}

function renderComponent(data, componentRegistry) {
  // 1. typeの解決(コンポーネントレジストリからElementTypeを取得)
  const elementType = componentRegistry.get(data.type);

  // 2. propsオブジェクトを生成
  const props = data.properties;

  // 3. 子要素に対して再帰的にrenderComponent関数の呼び出し
  const children = data.children.map((child) => renderComponent(child, registry));

  // 4. React要素の生成
  return React.createElement(elementType, props, ...children);
}

通常のReactでは、JSXがトランスパイラによって React.createElement関数を実行するコードに静的に変換されますが、メタレンダラでは、実行時に読み込まれたYAML形式の構成データをもとに、動的に React.createElement関数を呼び出す一連の処理が決定されます。

空間構成のYAMLファイル化

空間構成データは、アプリから分離したYAMLファイル上に記述して扱うことが可能です。

空間構成を定義したYAMLの例は次の通りです。

scene:
  type: Canvas
    children:
      - type: mesh
        properties:
          position: [0, 0, 0]
        children:
          - type: sphereGeometry
            properties:
              args: [1, 32, 32]
          - type: meshStandardMaterial
            properties:
              color: 'red'
      - type: ambientLight
      - type: directionalLight
        properties:
          position: [1, 1, 1]

これは下記のJSXと同等の空間構成を実現します。

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


function Scene() {
  return (
    <Canvas>
      <mesh position={[0, 0, 0]}>
        <sphereGeometry args={[1, 32, 32]} />
        <meshStandardMaterial color='red' />
      </mesh>
      <ambientLight />
      <directionalLight position={[1, 1, 1]} />
    </Canvas>
  )
}

実際に上記のYAMLファイルをアプリで読み込み、パースしたデータをメタレンダラコンポーネントに渡すことで、下画像のように赤い球体の3DCGを表示することができます。

YAMLファイルの参照

空間構成をYAMLファイルとして扱う新たな空間開発の方式のもとで、開発体験や実用性を向上させるために、ファイル間の参照機能も実現しました。

参照機能のために上述するデータの基本構造で示した属性に_ref属性を追加しました。メタレンダラは_ref属性で指定されたURL先にあるYAMLファイルのデータを参照します。これにより、開発者は、空間構成ファイルの論理的な分割や、ファイル間でのデータ定義の再利用をすることができます。

type: Canvas
children:
  # 別ファイルのデータを参照
  - _ref: https://example.com/objects.yaml?type=red-sphere


  # 別ファイルのデータを参照した上でpropertiesを上書き
  - _ref: https://example.com/lighting.yaml?type=directional
    properties:
      position: [1, 2, 1]

メタレンダラが実現すること

メタレンダラは、YAML形式のデータからの動的な空間描画を実現することで、空間構成をアプリケーションから分離しました。

空間構成が外部ファイル・外部データとして管理されることで、構成変更をアプリケーションのビルドやデプロイなしで行えるようになり、開発サイクルの効率化が実現されます。

また、空間構成がコードから分離されることで、特定の空間構成を実現するために、アプリ内のコンポーネントに分離困難な依存関係を埋め込むような設計を避けやすくなるため、コンポーネントの再利用性が確保されます。

さらに、データとロジックを分離するメタレンダラの仕組みは、純粋なReactの内部関数であるcreateElement 関数を呼び出すというシンプルな構成で成立しています。そのため、メタレンダラは3Dコンポーネントに限らず、一般的なReactアプリにおける2D UIやHTML要素なども同様にYAML形式のデータからレンダリングすることが可能です。

メタレンダラはアプリケーション開発の生産性と設計の健全性に対して改善をもたらす実用的かつ汎用的な仕組みといえます。

シーンエディタ

シーンエディタとは?

シーンエディタは、メタレンダラのレンダリング結果をリアルタイムで確認しながら空間構成データを編集できるGUIエディタです。

シーンエディタの画面は、画面左側からデータリスト、YAMLエディタ、ビューポートで構成されています。

各UIの基本機能は次の通りです。

  • データリスト:空間構成データの作成・削除・選択
  • YAMLエディタ:選択中の空間構成データのYAMLの表示・編集
  • ビューポート:選択中の空間構成データのレンダリング結果の表示

直感的な空間構築

シーンエディタは編集中の空間構成データのレンダリング結果をリアルタイムで確認できますが、オブジェクトの位置・向き・大きさといったパラメータを、YAMLエディタ上だけでイメージ通りに調整するのは難しいです。そこでシーンエディタに空間内のオブジェクトの位置・向き・大きさをビューポートから直接マウス操作で変更できる機能を実装しました。

シーンエディタでは空間内のオブジェクトをクリックで選択することで、ハンドルが表示されます。

上の画像では緑色の箱型オブジェクト上に位置調整のためのハンドルが表示されています。ユーザはハンドルをドラッグ操作することによって空間内のオブジェクトの位置を変更することができます。オブジェクトの操作後、画面右上のパネル内のSaveボタンを押すことにより、ビューポート上での操作結果がYAMLに反映されます。

この機能を活用することにより、ユーザは空間内のオブジェクトを直接操作して直感的に空間や複合的なオブジェクトの構築を実施することができます。

シーンエディタが実現すること

シーンエディタは、空間構成データをGUI上で編集できる環境を提供することで、直感的な空間構築を可能にします。ビューポート上でオブジェクトの位置・向き・大きさを直接操作できるため、空間の構成を視覚的に把握しながら編集できる点が大きな特徴です。

このような操作性により、デザイナーやプランナーなどの非エンジニアでも空間構成に関与しやすくなり、専門的な知識がなくても空間設計に参加できる環境が整います。

空間構成はYAML形式で記述されるため、一定の記述ルールへの習熟は必要ですが、アプリケーションの開発環境を起動し、コード内の空間構成を特定して編集する従来の方法に比べれば、編集のハードルは大きく下がります。GUIによる編集とリアルタイムプレビューの組み合わせにより、構成変更の試行錯誤も容易になります。

さらに、シーンエディタ自体がウェブアプリとして動作するため、チーム内での展開が容易です。特別な開発環境を必要とせず、ブラウザ上で動作することで、エンジニア以外のメンバーにもすぐに利用してもらうことができます。

これらの特徴により、エンジニアと非エンジニアが同じ空間構成データを共有・編集できる環境が整い、職種を超えた協業が促進されます。空間構築のプロセスがより開かれたものとなり、チーム全体での開発体験の質が向上します。

 

おわりに

本記事では、ウェブベースの3Dアプリ開発における空間構築の基盤技術として、「メタレンダラ」と「シーンエディタ」を紹介しました。

メタレンダラは、空間構成データを動的にレンダリングすることで、従来のR3Fベースの開発において課題となっていた空間構成とアプリケーションロジックの分離を実現します。一方、シーンエディタは、GUI上で空間構成を編集できる環境を提供することで、より直感的かつ柔軟な空間設計を可能にし、メタレンダラを活用したアプリ開発の実用性を高めています。

これらの技術は、メタバースや空間コンピューティングといった分野に向けて設計されたものですが、その根底にある思想は、ソフトウェア開発全般に応用可能な普遍性を持っています。

メタレンダラのように、アプリケーションの振る舞いの構成をコードから切り離し、データとして扱えるようにすることは、高い柔軟性、再利用性、拡張性を有するアプリケーションの実現のための重要なアプローチです。

また、構成をコードではなくデータとして扱えるようにすることで、アプリケーションの内部実装に依存せずに構成を編集・管理できるシーンエディタのようなツールの開発が容易になり、開発効率の向上や非エンジニアとの協業を促進する環境を整えることが可能になります。

この記事が、皆様の新たな技術的視点やヒントとなれば幸いです。