BLOG

ブログ

Three.jsでガラスのような質感の表現方法

こんにちは!64スマブラ大会inマーベリックスでチャンピオンになった菱村です!

前回はThree.jsの簡単なパーティクルアニメーションについてご紹介しました。

今回はThree.jsでガラスのような質感を表現する方法をご紹介します。
光の屈折で歪める表現方法を試していく過程で作成したデモになります。

↓完成デモはこちら(スマホの場合はControlsを閉じてご確認ください!)


Three.jsの基本的な部分(シーンを作成してメッシュを描画する など)については、前回の記事で簡単に触れているため、今回は割愛させていただきます。

HTML・CSS

今回用意したHTML・CSSは↓になります。

<div class="wrapper">
  <!-- ここに描画される -->
  <canvas id="canvas" class="canvas"></canvas>
</div>

<script type="importmap">
  {
    "imports": {
      "three": "https://unpkg.com/three@0.165.0/build/three.module.js",
      "fontLoader": "https://unpkg.com/three@0.165.0/examples/jsm/loaders/FontLoader.js",
      "textGeometry": "https://unpkg.com/three@0.165.0/examples/jsm/geometries/TextGeometry.js",
    }
  }
</script>
<script type="module" src="js/function.js"></script>
.canvas {
  position: fixed;
  top: 0;
  left: 0;
  width: 100% !important;
  height: 100vh !important;
  background: #000000;
}

CDNでThree.jsのほかに、FontLoader.jsとTextGeometry.jsを読み込んでいます。
名前から想像できるかもしれませんが、どちらもThree.jsでテキストを描画するために必要なもので、ガラスの質感表現には関係ありません…!

また、Three.jsのバージョンは0.165.0を使用しています。
古いバージョンだと対応していないプロパティがあったりするので、見え方がかなり変わってしまいます。

Three.jsでテキストを表示する

最初に、Three.jsの初期設定〜背景にテキストを表示するまでの部分です。

import * as THREE from "three";
import { FontLoader } from "fontLoader";
import { TextGeometry } from "textGeometry";

// ページの読み込みを待ってから実行
window.addEventListener("DOMContentLoaded", init);

function init() {
  // 描画サイズ
  const width = window.innerWidth;
  const height = window.innerHeight;

  // 描画先
  const canvas = document.querySelector("#canvas");

  // レンダラーを作成
  const renderer = new THREE.WebGLRenderer({
    canvas: canvas,
    antialias: true,
    devicePixelRatio: window.devicePixelRatio,
  });
  renderer.setSize(width, height);
  renderer.setClearColor(0x000000, 0);

  // シーンを作成
  const scene = new THREE.Scene();

  // 光源
  const directionalLight = new THREE.DirectionalLight(0xffffff, 4);
  directionalLight.position.set(0, 2, 3);
  scene.add(directionalLight);
  scene.add(new THREE.AmbientLight(0xffffff, 1));

  // カメラを作成
  const camera = new THREE.PerspectiveCamera(45, width / height);
  camera.position.set(0, 0, +60);

  // テキストを表示
  const fontLoader = new FontLoader();
  fontLoader.load("../fonts/Roboto_Condensed_Bold.json", function (font) {
    const textGeometry = new TextGeometry("MAVERICKS", {
      font: font,
      size: 10,
      depth: 1,
    });
    textGeometry.center();

    const textMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff });
    const textMesh = new THREE.Mesh(textGeometry, textMaterial);
    textMesh.position.z = -10;

    scene.add(textMesh);
  });

  // 毎フレーム時に実行されるループイベント
  animate();

  function animate() {
    render();
    requestAnimationFrame(animate);
  }

  function render() {
    renderer.render( scene, camera );
  }
}

canvasにテキストを表示する方法として、今回は以下のようにしています。

  • FontLoaderでフォント(jsonファイル)を読み込む
  • 読み込んだフォントをTextGeometryを使用して、メッシュを生成

フォントのjsonファイルは、今回はGoogleFontsからダウンロードしたttfファイルをjsonに変換して用意しました。
変換はFacetype.jsが便利でした!

ガラス質感のリングを生成する

先ほどのjsの animate(); より前に、以下を追記します。

  // メッシュ
  const geometry = new THREE.TorusGeometry(10, 5, 24, 130);

  const params = {
    color: 0xffffff,
    transmission: 1.3,
    opacity: 1,
    metalness: 0,
    roughness: 0,
    ior: 1.6,
    thickness: 5,
    specularIntensity: 1,
    specularColor: 0xffffff,
    dispersion: 5,
  };

  const material = new THREE.MeshPhysicalMaterial({
    color: params.color,
    transmission: params.transmission,
    opacity: params.opacity,
    metalness: params.metalness,
    roughness: params.roughness,
    ior: params.ior,
    thickness: params.thickness,
    specularIntensity: params.specularIntensity,
    specularColor: params.specularColor,
    side: THREE.DoubleSide,
    dispersion: params.dispersion,
  });

  const torus = new THREE.Mesh(geometry, material);
  torus.rotateY(0.6);

  const group = new THREE.Group();
  group.add(torus);
  scene.add(group);

paramsで設定している各プロパティについての説明は、公式ドキュメントのMeshPhysicalMaterialに記載されています。(英語の説明だとイメージしにくいという方は、本記事冒頭の完成デモのGUIから各プロパティの値を変更できますので、ぜひ試してみてください)

マテリアルに設定した各プロパティの値の他、光の当て方や光の強さによっても見える質感が変わってくると感じました。

ガラス質感のリングを回転させる

ここからはおまけになりますが、光の屈折での歪みをより感じやすくするために、リングを回転させます。

function animate() の中に、以下のコードを追加します。

torus.rotation.x += 0.02;


また、マウスの動きに連動して回転させる部分については、以下のように記述しました。

// マウス連動
renderer.domElement.addEventListener("mousemove", function (e) {
  group.rotation.x = e.pageY / 100.0;
  group.rotation.y = e.pageX / 100.0;
});

マウスの垂直位置(pageY)とリングのx軸方向の回転、マウスの水平位置(pageX)とリングのy軸方向の回転が連動するようになっています。

おわりに

今回のデモを作成したのは、テキストや画像を歪ませた表現をしてみたいと思ったのがきっかけです。形状だけでなく色も歪ませることでき、とてもきれいだと感じました。
WebGLでは多彩な表現が可能なので、もっと多くの表現方法を試していきたいと思います!

最後まで読んでいただきありがとうございました!