#029 ABookBizを使ってアプリを動かそう!オブジェクトVR編
「ABookBizを使ってアプリを動かそう!」の第6弾です。
過去の記事のまとめはこちらです。
今回はABookBizを利用して、オブジェクトVRを表示してみたいと思います。
そんな方にぜひこの記事を読んでいただきたいです。
オブジェクトVRとは?
オブジェクトVRとは、Web上で写真を3Dのように回転させ、被写体を360度あらゆる角度から観察できる技術のことです。
通常のVRと異なり、オブジェクトVRは特定の物体に焦点を当て、ユーザーがその物体を自由に操作し、拡大縮小や回転させることが可能です。これにより、製品やアート作品などの詳細な確認ができ、オンラインショッピングやデザインの分野での利用が増えています。
撮影方法
オブジェクトVRの撮影方法は、主に次のステップで行われます。
①準備: 撮影する物体を選び、適切な照明と背景を設定します。物体が均等に照らされ、背景が一貫していることが重要です。
②カメラの設定: 物体の周りを一定の角度で囲むようにカメラを配置します。多くの場合、ターンテーブルを使用し、物体を回転させながら撮影します。これにより、あらゆる角度からの写真を撮ることができます。
③撮影: カメラを一定間隔で移動させながら、物体の写真を撮影します。これにより、360度全方位の画像が得られます。
④画像の編集: 撮影した画像を編集し、一つの連続したインタラクティブな3Dモデルとして統合します。これには、専用のソフトウェアやツールが必要です。
活用例
オブジェクトVRは様々な分野で活用されています。以下はその具体例です。
このように、オブジェクトVRは様々な分野で活用されている技術であり、活用できれば大幅な業務改善も可能です。
オブジェクトVRについて理解できたと思いますので、次は実際に作成して、ABookBizで動かしてみたいと思います。
3DモデルからオブジェクトVR画像を作成
先ほど解説した、ターンテーブルを利用した撮影方法は、実在する物体をオブジェクトVRにする場合を想定しています。
一方で、3Dモデルを利用すれば、機材不要で簡単にオブジェクトVRを作成することが可能です。
今回は、手軽にできる3Dモデルを利用したオブジェクトVR作成を試してみたいと思います。
作成準備
オブジェクトVRに必要なものは画像ファイルのみです。
しかし、単純に画像を作成しようとすると、モデリングソフトを使って、ターンテーブルで撮影するようにカメラ位置を変更しながら画像を出力する必要があります。
それでは時間がかかるため、自動で画像を出力するプログラムを作成していきます。
今回もChatGPTを利用して、サクッとプログラミングしてもらいました。
htmlファイル
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GLTF Rotation Screenshot</title>
<style>
body { margin: 0; }
canvas { display: block; }
#uploadContainer {
position: absolute;
top: 10px;
left: 10px;
z-index: 10;
background-color: rgba(255, 255, 255, 0.8);
padding: 10px;
border-radius: 5px;
}
#inputContainer {
position: absolute;
top: 65px;
left: 10px;
z-index: 10;
background-color: rgba(255, 255, 255, 0.8);
padding: 10px;
border-radius: 5px;
}
</style>
</head>
<body>
<div id="uploadContainer">
<input type="file" id="fileInput" accept=".gltf, .glb">
<button id="startButton">Start Rotation & Capture</button>
</div>
<div id="inputContainer">
<label for="cameraDistance">Camera Distance:</label>
<input type="number" id="cameraDistance" name="cameraDistance" min="1" max="100" value="2" step="0.1">
<br>
<label for="screenshotCount">Screenshot Count:</label>
<input type="number" id="screenshotCount" name="screenshotCount" min="1" value="15" step="1">
<br>
<label for="backgroundColor">Background Color:</label>
<input type="color" id="backgroundColor" name="backgroundColor" value="#EDEDED">
</div>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/GLTFLoader.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.7.1/jszip.min.js"></script>
<script>
// シーン、カメラ、レンダラーを設定
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ preserveDrawingBuffer: true });
renderer.setSize(800, 800);
document.body.appendChild(renderer.domElement);
// 環境光と方向光の追加
const ambientLight = new THREE.AmbientLight(0x404040, 2); // 強度を2に設定
scene.add(ambientLight);
const directionalLight1 = new THREE.DirectionalLight(0xffffff, 1);
directionalLight1.position.set(1, 1, 1).normalize();
scene.add(directionalLight1);
const directionalLight2 = new THREE.DirectionalLight(0xffffff, 1);
directionalLight2.position.set(-1, -1, -1).normalize();
scene.add(directionalLight2);
const pointLight1 = new THREE.PointLight(0xffffff, 1, 100);
pointLight1.position.set(10, 10, 10);
scene.add(pointLight1);
const pointLight2 = new THREE.PointLight(0xffffff, 1, 100);
pointLight2.position.set(-10, -10, -10);
scene.add(pointLight2);
// 真上からのライトを追加
const topLight = new THREE.DirectionalLight(0xffffff, 10);
topLight.position.set(0, 10, 0);
scene.add(topLight);
// 背景色の設定
const backgroundColorInput = document.getElementById('backgroundColor');
backgroundColorInput.addEventListener('input', (event) => {
renderer.setClearColor(event.target.value);
});
// 初期背景色の設定
renderer.setClearColor(backgroundColorInput.value);
// カメラ距離の調整
const cameraDistanceInput = document.getElementById('cameraDistance');
cameraDistanceInput.addEventListener('input', (event) => {
updateCameraPosition(event.target.value);
});
let model;
let bbox;
let imagesCaptured = 0;
let totalImages = 16; // デフォルト値
const loader = new THREE.GLTFLoader();
function updateCameraPosition(distanceFactor) {
if (bbox) {
const size = bbox.getSize(new THREE.Vector3());
const center = bbox.getCenter(new THREE.Vector3());
camera.position.set(center.x, center.y, center.z + size.z * distanceFactor);
camera.lookAt(center);
}
}
document.getElementById('fileInput').addEventListener('change', () => {
const fileInput = document.getElementById('fileInput');
const file = fileInput.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (event) => {
const contents = event.target.result;
const blob = new Blob([contents], { type: file.type });
const url = URL.createObjectURL(blob);
loader.load(url, (gltf) => {
if (model) {
scene.remove(model);
}
model = gltf.scene;
scene.add(model);
// Calculate bounding box
bbox = new THREE.Box3().setFromObject(model);
updateCameraPosition(cameraDistanceInput.value);
}, undefined, (error) => {
console.error(error);
});
};
reader.readAsArrayBuffer(file);
}
});
document.getElementById('startButton').addEventListener('click', () => {
const screenshotCountInput = document.getElementById('screenshotCount');
totalImages = parseInt(screenshotCountInput.value, 10);
imagesCaptured = 0;
captureImages();
});
const captureImages = () => {
if (imagesCaptured < totalImages) {
model.rotation.y += (Math.PI * 2) / totalImages;
renderer.render(scene, camera);
saveScreenshot();
imagesCaptured++;
setTimeout(captureImages, 500); // 少し待ってから次のスクリーンショットを取得
} else {
createZip();
}
};
const screenshots = [];
const saveScreenshot = () => {
const imgData = renderer.domElement.toDataURL("image/png");
screenshots.push(imgData.split(',')[1]);
};
const createZip = () => {
const zip = new JSZip();
for (let i = 0; i < totalImages; i++) {
const fileName = `screenshot_${String(i + 1).padStart(2, '0')}01.png`;
zip.file(fileName, screenshots[i], { base64: true });
}
zip.generateAsync({ type: 'blob' }).then((content) => {
const link = document.createElement('a');
link.href = URL.createObjectURL(content);
link.download = 'screenshots.zip';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
});
};
const animate = function () {
requestAnimationFrame(animate);
renderer.render(scene, camera);
};
animate();
</script>
</body>
</html>
こちらが実行した画面です。htmlなので、どのブラウザでも実行することができます。
以下のように画像が出力されます。
あとは作成した画像をABookBizにアップロードし、プレゼン資料にオブジェクトVRを埋め込めば完成です。
オブジェクトVRを動かしてみよう
例として、以下のプレゼン資料にオブジェクトVRを埋め込んでみたいと思います。
編集画面でオブジェクトを配置します。
用意した画像をセットすれば完了です。
簡単ですよね!
それでは実際の画面を見てみましょう。
オブジェクトをタップすると実際に動かせるようになります。
このようにABookBizではノーコードで魅力的なコンテンツを簡単に制作することができます。
さいごに
いかがでしたか?
今回はABookBizの機能の一つであるオブジェクトVRを動かしてみました。
今後もABookBizはさらにアップデートされていきます。ぜひABookBizを使い倒して、営業活動にお役立てください。
それでは!