3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【WebAR】8th Wallでの半透明オブジェクト表示の代替案(前編)

Last updated at Posted at 2024-02-07

概要

今回、株式会社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になっており、読み込んだ際に透けるものがあるので注意します。このアセットも同様です。

その他モデリング

リンゴに棒を差し、飴をまとわせます。アップルジュースも簡単にモデリングします。

モデリング結果_01.png

デシメート&UV展開も済ませておきます。
8th Wallの軽量化についてはこちらの記事が参考になります。

終わったらシェーディングに取り掛かります。

03_8thWall標準のガラスマテリアルを適用してみる

8thwallにはガラス表現のモジュールが既にあります。
色付きガラスも表現できるそうなので、一旦こちらを使用してみましょう。

半透明ガラス_01.png

こちらのリンクをクリックし、Clone Projectをクリックします。

クローンプロジェクト_01.png

ワークスペース、URL、ライセンスタイプを表示し、Createボタンを押します。

デモユース_01.png

ガラスプロジェクト画面.png

表示されたら、body.htmlの中でcorrectGlassに指定されているglass_converted.glbをダウンロードして、Blenderで開きます。

コレクトグラス_01.png

blenderグラスマテリアル_01.png

こちらのモデルに適用されているマテリアルをそのまま自分のモデルに適用させていきましょう。

適用できたら8th Wallに移り、アセットをシーンに配置していきます。

回転については、Zが前方に来るようにモデリングしなかったため、オブジェクトを配置する際はrotation="90 0 0" のようにrotationのXの値を+90しました。

しかし

これだけでは求めているような見た目にはなりませんでした。後ろの背景によって中身の半透明の色が正しく見えたり見えなかったりするほか、あまりガラスらしくキラキラとした見た目にはなっていません。今回の制作においては、他のアセットと同じように、もっと情報量が欲しいところです。

試しにメタリックと粗さを上げて再配置してみると以下のようになりました。

この方向での調整では情報量の向上はあまり見込め無そうです。

調べたところ……

反射していないのは、環境マップが与えられていないことが原因でした。

そのため、同じくキューブマップであるこちらを適用してみます。

04_8thWallでRealtimeReflectionsの設定をする

先ほどのRealtime Reflectionsのプロジェクトを元に、反射の設定をします。

モジュールの追加

Modulesの横のプラスボタンを押します。

モジュール追加前_01.png

中央左のReflections Moduleをクリックします。

モジュール一覧画面_01.png

importをクリックします。

モジュールのインポート_01.png

追加されました。

モジュール設定_01.png

Filesの横のプラスマークからNewFileをクリックし、responsive-immersive.js と命名します。

fileの追加_01.png

以下のコードを追加します。8th Wallのサンプルプロジェクトのプログラムを元にして、ID指定部分を少しいじってリンゴ飴の飴部分(飴と飴以外で別のアセットにしています)とジュースのアセットを登録します。また、デバック用にconsole.logを付け加えています。

responsive-immersive.js
// 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したresponsiveImmersiveComponentbody.htmlで使用できるようにします。

app.js
import {responsiveImmersiveComponent} from './responsive-immersive'
AFRAME.registerComponent('responsive-immersive', responsiveImmersiveComponent)

body.htmlに情報が渡ったので、のタグの中responsive-immersiveを置きます。

body.html
    responsive-immersive

これでentityでリアルタイム反射の設定が出来るようになりました。ライトとアセットは以下のような設定になっています。(アセットは他にもあるので一部抜粋)

body.html
  <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>
body.html
    <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で指定する

responsive-immersive.js
      const realtimeBall1 = document.getElementById('appleCandyCandy')
      const realtimeBall2 = document.getElementById('appleJuice')

といったID(ここではappleCandyCandyappleJuice)は、entity内で指定したID

body.html
      <a-entity 
        id="appleCandyCandy"
        
      <a-entity 
        id="appleJuice"

であることに注意してください。アセットを最初に読み込んだ時のID

body.html
    <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-glbappleJuice-glb)ではないので注意。

出力結果の比較

GlassComparisonVideo_02-ezgif.com-optimize.gif

反射に関しては少しずつ感じになってきました。環境マップはあった方がよさそうです。しかし、やはり他のアセットに比べて情報量が足りないため、カラーテクスチャによって情報量を追加していくことにします。

後編に続きます

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?