Three.jsを使って、Webサイトを3Dモデルの画面?に表示させる方法を紹介いたします。動画だったり、自分のWEBサイトだったりを、iframeを使って表示させる方法になります。Demoにあるようなものになります。
【Demo_Phone Model】: https://misora.main.jp/model01/
【Demo_PC Model】: https://misora.main.jp/model02/
応用すれば3DモデルのTVに動画を表示したり、製品の画面の埋め込みに付けるテクニックになります。注意点もございますので、ご参考ください。
1.3Dモデルを作る(Blender)
画面を埋め込む3Dモデルを作成します。
私は、Blenderを使って3Dモデルを作成しました。
おのおので、3Dモデルを用意する必要がございます。そこまで凝ったものは必要ないと思います。フリーの3Dモデルサイトから調達しても問題ございません。
SmartPhone Model

glTFファイルで、作成したモデルを出力します。(smartphone.glb)
(圧縮で出力すると、Three.jsでDRACOLoaderが必要になります。)


Desktop PC Model

glTFファイルで、作成したモデルを出力します。(desktopPC.glb)
(圧縮で出力すると、Three.jsでDRACOLoaderが必要になります。)
2.3DモデルをThree.jsで表示させる
Three.jsで3Dモデル表示させましょう。
threejs.org / Loading 3D models three.js docs
Three.js
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
//import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';const loader = new GLTFLoader();
loader.load( 'models/smartphone.glb', function ( gltf ) {
gltf.scene.name = "phone";
gltf.scene.position.set(0, -12, 9.3)// 各自で調整
gltf.scene.scale.set(48, 48, 48)// 各自で調整
scene.add( gltf.scene );
}, undefined, function ( error ) {
console.error( error );
} );// Light
// 環境光源
const E_light = new THREE.AmbientLight(0xFFFFFF, 0.4);
scene.add(E_light);
// DirectionalLight
const light = new THREE.DirectionalLight( 0xffffff, 4.0 );
light.position.set( -500, 800, 1000 ); //default; light shining from top
light.castShadow = true; // default false
scene.add( light );
//Set up shadow properties for the light
light.shadow.mapSize.width = 512; // default
light.shadow.mapSize.height = 512; // default
light.shadow.camera.near = 1.0; // default
light.shadow.camera.far = 500; // defaultfunction renderLoop() {
//stats.begin();//stats計測
renderer.render(scene, camera) // render the scene using the camera
requestAnimationFrame(rendeLoop) //loop the render function
//stats.end();//stats計測
}
renderLoop() //start rendering
OK!
3.iframeのPlaneを作成(CSS3DRenderer)
IframeをThree.jsで表示させます。正しくは、canvas内に表示させるものでなく、HTMLのDomを擬似的に3Dに見せているものになります。canvasタグの上面にCSS3DRendererのdivタグが重なります。レイヤーの感覚です。
threejs.org / CSS3DRenderer three.js docs
HTMLでCSS3DRendererで表示させたいものを記述します。
html (width,heightの値でサイズを調整)
<div id="css3d">
<iframe id="IFR" class="visible" src="**Your_URL**" width="440" height="900" frameborder="0"></iframe>
</div>three.js
import { CSS3DRenderer, CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer.js';const scene2 = new THREE.Scene();
// Render
const cssrenderer = new CSS3DRenderer({});
cssrenderer.setSize(window.innerWidth, window.innerHeight);
cssrenderer.domElement.style.position = 'absolute';
cssrenderer.domElement.style.top = 0;
cssrenderer.domElement.style.pointerEvents = 'none'
document.body.appendChild(cssrenderer.domElement);
// Object
const cssobj = document.getElementById("css3d");
let css3dobject = new CSS3DObject(cssobj);
// 3Dモデルと重なるように各自で調整
// Scale
css3dobject.scale.set(0.0135, 0.0135, 0.0135);// 3Dモデルと重なるようにサイズを調整しています。
// Position
css3dobject.position.set( -0.1, 0.63, 0);// 3Dモデルと重なるように各自で調整
// Rotation
css3dobject.rotation.set(THREE.MathUtils.degToRad(-5), THREE.MathUtils.degToRad(0), THREE.MathUtils.degToRad(0));// 各自で調整
scene2.add(css3dobject);カメラのZが0以下になると非表示するようにしています。3DモデルのCanvas層の上のレイヤーに表示されているので、モデルに隠れることがありません。なので、適宜にCSSで消す必要があります。
function renderLoop() {
//stats.begin();//stats計測
// *Hide the back side.
if(camera.position.z < 0){
cssobj.style.opacity = 0.0
}else{
cssobj.style.opacity = 1.0
}
// CSS3DRenderer
cssrenderer.render(scene2, camera);
renderer.render(scene, camera) // render the scene using the camera
requestAnimationFrame(rendeLoop) //loop the render function
//stats.end();//stats計測
}
renderLoop() //start rendering
iframeのPlaneができれば、OK!
*Safari で非表示になる問題(CSS3DRender.jsを修正)
viewElement.style.transformOrigin の箇所を削除する
/*CSS3DRender.js*/
~~
const domElement = parameters.element !== undefined ? parameters.element : document.createElement( 'div' );
domElement.style.overflow = 'hidden';
this.domElement = domElement;
const viewElement = document.createElement( 'div' );
//viewElement.style.transformOrigin = '0 0'; // <= *Remove
viewElement.style.pointerEvents = 'none';
domElement.appendChild( viewElement );
const cameraElement = document.createElement( 'div' );
~~4.3Dモデルとiframeをあわせる

上記のもので、3Dモデルとiframeをあわせれば、完成です!
最後に、画面(iframe)のON,OFFのAnimation設定しましょう。Classを追加・削除する方法で、CSSのkeyframesアニメーションを使い、Fade-Inを実現します。
three.js (Animation Switching)
let CamAnime = true;
document.getElementById("Play-Btn").addEventListener("click", () => function(){
console.log("Play-Btn");
if(!CamAnime){
// ON
document.getElementById('IFR').classList.add("visble"); // AddClass
document.getElementById("Play-Btn").innerText = "OFF";
CamAnime = true;
}else{
// OFF
document.getElementById('IFR').classList.remove("visble"); //RemoveClass
document.getElementById("Play-Btn").innerText = "ON";
CamAnime = false;
}
}());CSS (Animation)
#IFR{
visibility:hidden;
}
#IFR.visble{
visibility:visible;
animation-duration: 0.5s;
animation-name: fade-in;
animation-timing-function:ease-out;
}
@keyframes fade-in{
0%{
visibility:hidden;
opacity: 0;
}
50%{
visibility:visible;
opacity: 0.5;
}
100%{
visibility:visible;
opacity: 1;
}
}5.完成!
簡単でしたね!
iframeの平面と3Dモデルをあわせる微調整が地味に大変ですが、CSSのアニメーションとかを凝って制作すればカッコいい感じが作れると思います。ポートフォリオサイトなどで、良く見かけるようになったTipsで、自分でもやってみよと思いチャレンジしました。
仕事などで使えそうな感じなので、備忘録しておきます。

