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の弱点
おさらいになりますが、前回扱った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なんかが使えそうです。