見出し画像

Vue 3 と Three.js で3D空間にHTMLを表示(CSS3D編)

Vue3とThree.jsで作った3D空間に、HTMLエレメントを表示する方法の、第2段です。

前回の記事 Vue 3 と Three.js で3D空間にHTMLを表示(HTMLMesh編) の続きです。
Vue3 + Three.js|NAYUTA株式会社 シリーズ記事の第5弾です。
https://github.com/NAYUTA-tech/tutorial-three-vue にソースコードも公開しています。

今回作るもの

今回は、前回同様、3D空間にHTMLエレメントを表示します。

左が前回のHTMLMesh、右が今回のCSS3D

HTMLMeshとCSS3Dの違い

HTMLMeshの弱点

おさらいになりますが、前回扱ったHTMLMeshには以下のような弱点がありました。

  • iframeなど、使えないHTMLエレメントが多い

  • CSSで使えないスタイルが多い

CSS3Dの場合、

  • HTML+CSSで表現できるもののほとんどが、CSS3Dでも表現できる

  • 一方で、他の3Dオブジェクトとの前後関係を表現するのが難しい(普通に使うとCSS3Dオブジェクトが壁越しでも見えてしまう)

CSS3DのレンダラーであるCSS3DRendererは、HTMLエレメントをCSSのtransformで変形して、それを通常のWebGLRendererに重ねて表示しているものなので、上記のようなメリット・デメリットが出てくるわけですね。

手順

前回HTMLMeshで作ったオブジェクトを、左に移動してしまいましょう。

addHtmlMesh() {
  this.htmlMesh = new HTMLMesh(this.$refs.modalRef)
  this.htmlMesh.position.set(-4, 5, 5)  # ここを変更

今回使うモジュールをimportして、

import { CSS3DRenderer, CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer.js'

CSS3D用のsceneとrendererを登録

init() {
  // 略

  /**
   * CSS3D
   */
  this.sceneCss = new THREE.Scene()
  this.rendererCss = new CSS3DRenderer()
  this.$refs.css3dRef.appendChild(this.rendererCss.domElement)

  // レンダリング解像度
  // 略
  this.rendererCss.setSize(
    window.innerWidth,
    window.innerHeight
  )

animate()内でもCSS3Dのrendererを登録

animate() {
  // 略
  this.rendererCss.render(this.sceneCss, this.camera)
}

後はCSS3Dオブジェクトを作成して配置して、

mounted() {
  // 略
  this.addCss3dObject()
  // 略
},
addCss3dObject() {
  this.css3dObject = new CSS3DObject(this.$refs.modalCss3dRef)
  this.css3dObject.position.set(4, 5, 5)
  this.css3dObject.scale.setScalar(0.01)

  this.sceneCss.add(this.css3dObject)
},
<template>

  <!-- idを追加 -->
  <div id="container3d" class="fullscreen">

    <canvas ref="canvasRef" class="fullscreen"></canvas>

    <!-- ↓新たに追加 -->
    <div ref="css3dRef"></div>

    <div ref="modalRef" class="modal3d">
      <p>
        Lorem ipsum dolor sit amet consectetur adipisicing<br />
        elit. Possimus expedita necessitatibus, at magni<br />
        optio sequi cumque repellat fugit rem ratione <br />
        laudantium perferendis officiis eveniet alias atque<br />
        debitis, itaque qui. Repellendus!
      </p>
      <button @click="onClick">Hello</button>
    </div>

    <!-- ↓新たに追加 -->
    <div ref="modalCss3dRef" class="modal3d">
      <p>
        Lorem ipsum dolor sit amet consectetur adipisicing<br />
        elit. Possimus expedita necessitatibus, at magni<br />
        optio sequi cumque repellat fugit rem ratione <br />
        laudantium perferendis officiis eveniet alias atque<br />
        debitis, itaque qui. Repellendus!
      </p>
      <button @click="onClick">Hello</button>
    </div>

  </div>
</template>

スタイルを調整すれば、

<style scoped>
/* 新規に追加 */
#container3d {
  overflow: hidden;
}

.fullscreen {
  /* 略 */
}

/* 大幅に変更 */
.modal3d {
  width: 400px;
  height: 300px;
  padding: 20px;
  border: dotted 10px green;
  background-color: black;
  color: #f0f0f0;
  padding: 5px 15px;
  border-radius: 20px;
  letter-spacing: 1px;
}
</style>

冒頭の画像と同様のものが表示されたと思います。

今回追加した<div>は、前回と全く同じものをコピペし、refだけ書き換えたものを使用しています。CSSスタイルも共通です。
それでも異なる表示になったのは、HTMLMeshでは使えないCSSスタイルが多いからです。
CSS3Dでは、ほとんどのCSSスタイルが適用できます。今回実装していませんが、YouTubeの埋め込みも可能です。

CSS3Dと他の3Dオブジェクトとの前後関係を表現する方法

CSS3Dオブジェクトが壁越しでも見えてしまう問題についても、完全ではないものの、解決策はあります。

ここでは説明しませんが、ヒントだけ挙げておきます。

CSS3Dオブジェクトの座標とカメラとの間に、他の3Dオブジェクトが存在する場合、CSS3Dオブジェクトを非表示にしてしまえば解決しそうですね。
Raycasterなんかが使えそうです。