概要
今回、株式会社GENEROSITY様でインターンシップをさせていただき、8thwallを使用して、以下のような箱の中身が見えるWebARを制作しました。制作期間は2週間程度でした。
制作においては、透明のマテリアルや、半透明のマテリアルの表現に苦戦しました。
それも単に透明なだけでなく、今回のシーンには リンゴ飴(半透明の飴+不透明のリンゴ)に加え、アップルジュース(透明の液体+透明のガラス) が含まれていたので、さらに難易度が上がりました。
本来、リアルタイムレンダリングでは描画順の設定により透明/半透明のオブジェクトは問題が発生しがちなので避けられます。しかし、どうしてもリンゴ飴とアップルジュースを出したかったことから、頑張って実装することにしました。
シェーダーを書いての対処もできるのだと思いますが、私はHTML,CSS,Javascriptについて全くの初心者だったので、今回は8thwallのモジュールとBlenderを使って、なるべく情報量の多いリッチな見た目にしていきたいと思います。
目次
01_開発環境
02_Blenderでモデルを用意する
03_8thWall標準のガラスマテリアルを適用してみる
04_8thWallでRealtimeReflectionsの設定をする
~前編はここまで~
05_HDR素材を用意する
06_Blenderでベイク用シェーダーの設定をする
07_BlenderでBake-A-Nodeを使用しベイクする
08_Photoshopでレタッチをする
09_Blenderでシェーダーを再設定する
10_8thWallにアップロード&配置
11_出力結果
12_まとめ
13_参考
01_開発環境
- Windows11
- 8thwall (HTML,CSS,Javascript,A-frame)
- Blender 4.0.2
アドオン追加 → Bake-A-Node - Adobe Photoshop 24.0
- テストデバイス
iPhone 13 Pro Max
02_Blenderでモデルを用意する
何はともあれモデルが無いと始まらないので、まずはモデルを用意していきます。
今回必要なモデルは以下の5つです
・リンゴ飴のリンゴ部分
・リンゴ飴の飴部分
・リンゴ飴の棒(割り箸)
・リンゴジュースのボトル
・リンゴジュースのコップ
リンゴは購入することにしましたが、他はモデリングします。
アセットの購入
リンゴのモデルはこちらを使用させていただきました。
アセットの中には何故か伝播が1.0になっており、読み込んだ際に透けるものがあるので注意します。このアセットも同様です。
その他モデリング
リンゴに棒を差し、飴をまとわせます。アップルジュースも簡単にモデリングします。
デシメート&UV展開も済ませておきます。
8th Wallの軽量化についてはこちらの記事が参考になります。
終わったらシェーディングに取り掛かります。
03_8thWall標準のガラスマテリアルを適用してみる
8thwallにはガラス表現のモジュールが既にあります。
色付きガラスも表現できるそうなので、一旦こちらを使用してみましょう。
こちらのリンクをクリックし、Clone Projectをクリックします。
ワークスペース、URL、ライセンスタイプを表示し、Createボタンを押します。
表示されたら、body.html
の中でcorrectGlass
に指定されているglass_converted.glb
をダウンロードして、Blenderで開きます。
こちらのモデルに適用されているマテリアルをそのまま自分のモデルに適用させていきましょう。
適用できたら8th Wallに移り、アセットをシーンに配置していきます。
回転については、Zが前方に来るようにモデリングしなかったため、オブジェクトを配置する際は
rotation="90 0 0"
のようにrotationのXの値を+90しました。
しかし
これだけでは求めているような見た目にはなりませんでした。後ろの背景によって中身の半透明の色が正しく見えたり見えなかったりするほか、あまりガラスらしくキラキラとした見た目にはなっていません。今回の制作においては、他のアセットと同じように、もっと情報量が欲しいところです。
試しにメタリックと粗さを上げて再配置してみると以下のようになりました。
この方向での調整では情報量の向上はあまり見込め無そうです。
調べたところ……
反射していないのは、環境マップが与えられていないことが原因でした。
そのため、同じくキューブマップであるこちらを適用してみます。
04_8thWallでRealtimeReflectionsの設定をする
先ほどのRealtime Reflectionsのプロジェクトを元に、反射の設定をします。
モジュールの追加
Modulesの横のプラスボタンを押します。
中央左のReflections Moduleをクリックします。
importをクリックします。
追加されました。
Filesの横のプラスマークからNewFileをクリックし、responsive-immersive.js
と命名します。
以下のコードを追加します。8th Wallのサンプルプロジェクトのプログラムを元にして、ID指定部分を少しいじってリンゴ飴の飴部分(飴と飴以外で別のアセットにしています)とジュースのアセットを登録します。また、デバック用にconsole.log
を付け加えています。
// This component is an example of how to separate behavior by device category
// using 8th Wall Engine sessionAttributes
console.log('reflection') // eslint-disable-line no-console
const responsiveImmersiveComponent = {
init() {
console.log('reflectionが読み込まれました') // eslint-disable-line no-console
const onAttach = ({sessionAttributes}) => {
const realtimeBall1 = document.getElementById('appleCandyCandy')
const realtimeBall2 = document.getElementById('appleJuice')
const s = sessionAttributes
// eslint-disable-next-line max-len
const isDesktop = !s.cameraLinkedToViewer && !s.controlsCamera && !s.fillsCameraTexture && !s.supportsHtmlEmbedded && s.supportsHtmlOverlay && !s.usesMediaDevices && !s.usesWebXr
// eslint-disable-next-line max-len
const isHMD = s.cameraLinkedToViewer && s.controlsCamera && !s.fillsCameraTexture && s.supportsHtmlEmbedded && !s.supportsHtmlOverlay && !s.usesMediaDevices && s.usesWebXr
// eslint-disable-next-line max-len
const isMobile = !s.cameraLinkedToViewer && !s.controlsCamera && s.fillsCameraTexture && !s.supportsHtmlEmbedded && s.supportsHtmlOverlay && s.usesMediaDevices && !s.usesWebXr
if (isDesktop) {
console.log('デスクトップ版が読み込まれました') // eslint-disable-line no-console
// Desktop specific behavior goes here
realtimeBall1.setAttribute('reflections', 'type: static')
realtimeBall2.setAttribute('reflections', 'type: static')
} else if (isHMD) {
console.log('HMD版が読み込まれました') // eslint-disable-line no-console
// Head mounted display specific behavior goes here
const isVRHMD = this.el.sceneEl.xrSession.environmentBlendMode === 'opaque'
// eslint-disable-next-line max-len
const isARHMD = this.el.sceneEl.xrSession.environmentBlendMode === 'additive' || 'alpha-blend'
realtimeBall1.setAttribute('reflections', 'type: static')
realtimeBall2.setAttribute('reflections', 'type: static')
} else if (isMobile) {
console.log('モバイル版が読み込まれました') // eslint-disable-line no-console
// Mobile-specific behavior goes here
realtimeBall1.setAttribute('reflections', 'type: realtime')
realtimeBall2.setAttribute('reflections', 'type: realtime')
}
}
const onxrloaded = () => {
XR8.addCameraPipelineModules([{'name': 'responsiveImmersive', onAttach}])
console.log('onxrloadedが読み込まれました') // eslint-disable-line no-console
}
window.XR8 ? onxrloaded() : window.addEventListener('xrloaded', onxrloaded)
console.log('reflectionが完全に読み込まれました') // eslint-disable-line no-console
},
}
export {responsiveImmersiveComponent}
console.log('reflectionのページの最後です') // eslint-disable-line no-console
app.js
に以下の記述を加え、responsive-immersive.js
でexportしたresponsiveImmersiveComponent
をbody.html
で使用できるようにします。
import {responsiveImmersiveComponent} from './responsive-immersive'
AFRAME.registerComponent('responsive-immersive', responsiveImmersiveComponent)
body.html
に情報が渡ったので、のタグの中responsive-immersive
を置きます。
responsive-immersive
これでentityでリアルタイム反射の設定が出来るようになりました。ライトとアセットは以下のような設定になっています。(アセットは他にもあるので一部抜粋)
<a-entity
xr-light
light="type: directional;
castShadow: true;
shadowMapHeight:2048;
shadowMapWidth:2048;
shadowCameraTop: 10;
target: #group;
shadowRadius: 10"
intensity="1.0"
xrextras-attach="target: group; offset: 0 15 0;"
shadow>
</a-entity>
<a-entity
id="group">
<a-entity
id="appleCandyCandy"
gltf-model="#appleCandy-Candy-glb"
position= "-0.45 0.9 0.0"
rotation="90 0 0"
scale="2.5 2.5 2.5"
animation-mixer = "timeScale: 0"
shadow></a-entity>
<a-entity
id="appleJuice"
gltf-model="#appleJuice-glb"
position= "-0.4 0.4 0.5"
rotation="90 0 0"
scale="2.8 2.8 2.8"
reflections="type: static"
animation-mixer = "timeScale: 0"
shadow></a-entity
</a-entity>
注意
responsive-immersive.js
で指定する
const realtimeBall1 = document.getElementById('appleCandyCandy')
const realtimeBall2 = document.getElementById('appleJuice')
といったID(ここではappleCandyCandy
やappleJuice
)は、entity内で指定したID
<a-entity
id="appleCandyCandy"
<a-entity
id="appleJuice"
であることに注意してください。アセットを最初に読み込んだ時のID
<a-asset-item id="appleCandy-Candy-glb"
src="assets/appleCandy_Candy_05.glb"></a-asset-item>
<a-asset-item id="appleJuice-glb"
src="assets/glass_test_forQiita_12.glb"></a-asset-item>
(ここではappleCandy-Candy-glb
やappleJuice-glb
)ではないので注意。
出力結果の比較
反射に関しては少しずつ感じになってきました。環境マップはあった方がよさそうです。しかし、やはり他のアセットに比べて情報量が足りないため、カラーテクスチャによって情報量を追加していくことにします。
後編に続きます