🐧

Three.jsの記法まとめ

2022/10/17に公開

はじめに

この記事は、Three.jsを用いた3Dオブジェクトを作成する方法やシェーダーの記法の整理を目的としています。
解説を目的とした記事ではないので、ご覧される際はあくまでメモ程度と捉えて頂けますと幸いです。
尚、全てのコードはnpmでthreejsモジュールをインストールしている前提で記載しています。

Three.jsを用いた3Dオブジェクトの生成

テンプレート(コメントあり)

JavaScript
//モジュールの読み込み(パスは環境によって調整)
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import GUI from "lil-gui";

/*
//canvasを定義
*/
const canvas = document.getElementById("canvas"); 

/*
サイズを定義
*/
const sizes = {
  width:innerWidth,
  height:innerHeight,
}

/*
//シーンを定義
*/
const scene = new THREE.Scene();

/*
//カメラ
*/
//デフォルトではカメラは、原点に位置しており、z軸マイナス方向に向いている
const fov = 75;//視野角(degree)
const aspect = sizes.width / sizes.height;//アスペクト比
const near = 0.1;//撮影開始距離
const far = 3000;//撮影終了距離
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);//カメラを定義
scene.add(camera);//カメラをシーンに追加
camera.position.z = 2;//カメラの位置を変更

/*
レンダラーを定義
*/
const renderer = new THREE.WebGLRenderer({
  canvas: canvas,
  antialias: true, //アンチエイリアスを適応
});
renderer.setSize(sizes.width, sizes.height);//サイズを指定
renderer.setPixelRatio(window.devicePixelRatio);//アスペクト比を指定

//ジオメトリを定義
const geometry = new THREE.BoxGeometry(1, 1, 1);

//マテリアルを定義(ライトの影響を受けるものと、受けないものがある)
const material = new THREE.MeshToonMaterial();

//メッシュ化
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);//シーンに追加

/*
ライトを定義
*/
//ライトのデフォルト位置は原点
const light = new THREE.DirectionalLight('white', 1);
scene.add(light);//シーンに追加
light.position.set(-1, 2, 4);

const color = 0xFFFFFF;
const intensity = 0.2;
const ambientLight = new THREE.AmbientLight(color, intensity);
scene.add(ambientLight);

//レンダリングする
renderer.render(scene, camera);

/*
UIデバッグを実装
*/
const gui = new GUI(); //UIデバッグ用のlil-guiを定義

/*
マウスでカメラ操作を有効化する
*/
const orbitControls = new OrbitControls(camera,renderer.domElement);//マウス操作用のorbitControlsを定義

/*
ブラウザのリサイズに対応
*/
window.addEventListener("resize", () => {
  sizes.width = window.innerWidth;
  sizes.height = window.innerHeight;
 
  camera.aspect = sizes.width / sizes.height;
  camera.updateProjectionMatrix();
 
  renderer.setSize(sizes.width, sizes.height);
  renderer.setPixelRatio(window.devicePixelRatio);
});

//アニメーションを定義
function animate(time) {
  time *= 0.001;//経過時間をmsからsに変換
  renderer.render(scene, camera);
  requestAnimationFrame(animate)
}

animate();

テンプレート(コメント最小限,記述簡略化)

JavaScript(コピペ用)
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import GUI from "lil-gui";

let camera, scene, renderer;
const clock = new THREE.Clock();

//サイズを定義
const sizes = {
  width: innerWidth,
  height: innerHeight,
};

init();
animate();

function init() {
  const canvas = document.getElementById("canvas");

  //シーンを定義
  scene = new THREE.Scene();

  //カメラを定義
  const fov = 75,
    aspect = sizes.width / sizes.height,
    near = 0.1,
    far = 3000;
  camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
  camera.position.z = -2;
  scene.add(camera);

  //ライトを定義
  const light = new THREE.DirectionalLight("white", 1);
  const ambientLight = new THREE.AmbientLight("white",1);
  scene.add(light, ambientLight);

  //マテリアルを定義
  const material = new THREE.MeshToonMaterial();

  //ジオメトリを定義
  const geometry = new THREE.BoxGeometry(1, 1, 1);

  //メッシュ化
  const mesh = new THREE.Mesh(geometry, material);
  scene.add(mesh);

  //レンダラーを定義
  renderer = new THREE.WebGLRenderer({
    canvas: canvas,
    antialias: true, //アンチエイリアスを適応
  });
  renderer.setSize(sizes.width, sizes.height); //サイズを指定
  renderer.setPixelRatio(window.devicePixelRatio); //アスペクト比を指定
  renderer.render(scene, camera);

  //マウスでカメラ操作を有効化する
  const orbitControls = new OrbitControls(camera, renderer.domElement);
  orbitControls.minDistance = 2;
  orbitControls.maxDistance = 30;

  //リサイズ対応
  window.addEventListener( 'resize', onWindowResize );
}

//UIデバッグを実装
function setUpGui() {
  const gui = new GUI();
}

//リサイズ対応関数
function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize( window.innerWidth, window.innerHeight );
}


//アニメーション実行用関数
function animate() {
  requestAnimationFrame(animate);
  render();
}

//アニメーション用関数
function render() {
  const delta = clock.getDelta();
  //経時的にしたい処理
  renderer.render( scene, camera );
}

Three.js カスタマイズ

カメラの操作

JavaScript
//カメラの位置を変更(デフォルトは原点)
camera.position.set(x,y,z);
camera.position.x = 1;

//視野角の変更
camera.fov = 60;

//カメラの向く方向を変更(デフォルトは x=0,y=0,z軸負方向)
// 原点方向を見つめる
camera.lookAt(new THREE.Vector3(0, 0, 0));

fog(霧)の追加

カメラの開始距離と終点距離と一致させて記述。その間にあるオブジェクトを指定した色で遠くに行く程霞ませることができる。

JavaScript
// フォグを設定
// new THREE.Fog(色, 開始距離, 終点距離);
scene.fog = new THREE.Fog(0x000000, 50, 2000);

マテリアルの操作

JavaScript
//テクスチャ
import textureImage from "./texture/texture.jpg";
let texture = new THREE.TextureLoader().load(textureImage);

const material = new THREE.MeshBasicMaterial({
  color: 'red',//色を指定
  transparent: true,//透明度を有効化
  opacity: 0.5,//透明度を指定
  side: THREE.DoubleSide,//裏面も描画
  map:texture//テクスチャの設定
});

メッシュ(3Dオブジェクト)の操作

JavaScript
//サイズを変更
mesh.scale.set(x,y,z);
mesh.scale.x = 2;

//回転
mesh.rotation.set(x,y,z);
mesh.rotation.x = 2;

//移動
mesh.position.set(x,y,z);
mesh.position.x = 2;

//複製
const mesh2 = mesh.clone();

ライトの操作

JavaScript
//ライトの位置を操作
light.position.set(x,y,z);
light.position.x = 5;

//色の変更
light.color.set(0xff0000);

レンダラーの操作

JavaScript
//背景色を設定
renderer.setClearColor(new THREE.Color(0xEEEEEE));

//透明度を有効化
 renderer = new THREE.WebGLRenderer({
    alpha:true,
  });
//canvas背景透過
 renderer.setClearColor(0x000000, 0);

renderer.setSize(sizes.width, sizes.height); //サイズを指定
renderer.setPixelRatio(window.devicePixelRatio); //アスペクト比を指定

lil-guiの設定

JavaScript
//lil-guiを定義
const gui = new GUI()

//色の変更デバッグ
gui.addColor(material,"color");

//addの使用
gui.add(デバッグする値,min,max,step).name("名前");

//位置をデバッグ
gui.add(box.position,"x",-10,10,1).name("translateX");

//要素の存在
gui.add(box,"visible").name("box");
gui.add(light,"visible").name("light");

//フォルダーを定義
const position = gui.addFolder( 'Position' );

//フォルダーにデバッグを追加
position.add(box.position, "x", -10, 10, 1);

オブジェクトに影を落とす

JavaScript

//影の描画を有効化
renderer.shadowMap.enabled = true;

// ライトに影を有効にする
light.castShadow = true;

// 影を受け付ける
meshFloor.receiveShadow = true;

// 影を落とす
box.castShadow = true;

//影の解像度を設定(2の累乗)
light.shadow.mapSize.width = 2048;
light.shadow.mapSize.height = 2048;

オブジェクトのグループ化

javaScript
//グループの定義
const wrap = new THREE.Group(); 
wrap.add(mesh1,mesh2); // 任意のObject3Dを追加 
scene.add(wrap); 

座標の取得

JavaScript
//ワールド座標の取得
// object3D は任意の3Dオブジェクト。
const world = object3D.getWorldPosition(new THREE.Vector3());

キューブ環境マッピング

JavaScript
//jsファイル冒頭で画像のインポート
import envTop from "./envMap/top.png";
import envBottom from "./envMap/bottom.png";
import envRight from "./envMap/right.png";
import envLeft from "./envMap/left.png";
import envFront from "./envMap/front.png";
import envBack from "./envMap/back.png";

//envimages配列に格納
const urls = [envRight,envLeft,envTop,envBottom,envBack,envFront,];

//環境キューブマッピングを実行,マッピングモードを指定
 const textureCube = new THREE.CubeTextureLoader().load(urls);
 textureCube.mapping = THREE.CubeRefractionMapping;
 scene.background = textureCube;

鏡面仕上げ

環境マッピング設定がしてある必要あり。

中身が詰まったガラスの質感:
MeshLambertMaterialとMeshPhongMaterialとMeshBasicMaterialで設定可能。
MeshPhysicalMaterialでは、thicknessを上げることで設定可能になる。

空洞のガラスの質感:
MeshPhysicalMaterialで設定可能

JavaScript
//キューブ環境マッピングのコードを流用

//
 const crystal = new THREE.MeshLambertMaterial({
    color: 0xf5f5f5,
    envMap: textureCube,
    refractionRatio: 0.9,//屈折率(デフォルト:0.98)
    reflectivity: 0.9,//反射率(デフォルト:1)
  });
  
  //こちらはキューブ環境マッピングなしでも設定可能
  const vidro = new THREE.MeshPhysicalMaterial({
    transmission: 1,//透過率
    metalness: 0.1,//金属製
    roughness: 0.1,//粗さ
    ior: 1.2,//屈折率
    thickness: 0.1,//厚み
    specularIntensity: 1,//反射量
    specularColor: 0xff7f50,//反射色
  });

Blenderモデルの導入

ちょっとBlender使ったことないのでアレですが
https://free3d.com/ja/3d-models/blender
ここから.plyデータでインストールする前提で。

JavaScript
import { PLYLoader } from "three/examples/jsm/loaders/PLYLoader";

//PLYLoderを定義
const loader = new PLYLoader();

//モデルの読み込み
  loader.load("./ply/Model_ply.ply", function (geometry) {
    const model = new THREE.Mesh(geometry,material);
    scene.add(model);
  });

BufferGeometryを用いた操作

線を引く

JavaScript
//頂点を定義
const points = [];
points.push( new THREE.Vector3( - 10, 0, 0 ) );
points.push( new THREE.Vector3( 0, 10, 0 ) );
points.push(new THREE.Vector3(10, 0, 0));

//ジオメトリを定義
const geometry = new THREE.BufferGeometry().setFromPoints(points);

//マテリアルを定義
const material = new THREE.LineBasicMaterial( { color: 0x0000ff } );

//メッシュ化
const line = new THREE.Line( geometry, material );

// シーンに追加
scene.add(line);

シェーダーの基本記法

JavaScript
//テクスチャをインポート
import textureImage from "./texture/texture.jpg";

//テクスチャを定義
let texture = new THREE.TextureLoader().load(textureImage);

//ShaderMaterialを定義
const material = new THREE.ShaderMaterial({
  vertexShader: `
  uniform float uFrequency;//jsファイルから変数を受け取る
  varying vec2 vUv;//変数を渡す

  void main() {
    vec4 modelPosition = modelMatrix * vec4(position,1.0);

    vUv = uv;//uv座標を格納

    vec4 viewPosition = viewMatrix * modelPosition;
    vec4 projectionPosition = projectionMatrix * viewPosition;
    gl_Position = projectionPosition;
  }
  `,
  fragmentShader: `
  uniform vec3 uColor;
  uniform sampler2D uTexture;
  varying vec2 vUv;//変数を受け取る
    void main(){
    // gl_FragColor = vec4(uColor,1.0);
    vec4 textureColor = texture2D(uTexture,vUv);
    gl_FragColor = vec4(textureColor);
  }
  `,
  side: THREE.DoubleSide,
  uniforms: {
    uFrequency: { value: 10.0 },
    uColor:{value:new THREE.Color('red')},
    uTexture:{value:texture},
  }
});

頻出GLSL関数

texture2D

fragmentShader
vec4 texture2D (sampler2D, vec2)

sampler2D:色を取り出したいテクスチャ
vec2:テクセル情報を取り出したいuv座標

参考サイト

Discussion