Three.jsの雛形を作成する
目次
解説
私も学習中なので抜け漏れがあるかもしれませんがご了承ください🙇♂️
今回はベースとなるThree.jsを使うための雛形を作成します。
インストール
npm i -D three
TypeScriptのプロジェクトでThree.jsを使うためには、Three.jsの型定義ファイルもインストールする必要があります。
npm i -D @types/three
基礎知識
Three.jsの基本要素
Three.jsはシーン、カメラ、レンダラー、ジオメトリ、マテリアル、メッシュなどの基本的な要素を使って3Dシーンを作成します。
- シーン
- 3Dオブジェクト、カメラ、ライトなどを配置する空間
- カメラ
- シーンを見る視点
- レンダラー
- シーンとカメラを使って描画する
- ジオメトリ
- 3Dオブジェクトの形状
- マテリアル
- オブジェクトの色や質感を決める
- メッシュ
- ジオメトリとマテリアルを組み合わせたもの
GLSLの基本要素
シェーダーはGPUで実行されるプログラムです。
Three.jsではシェーダーを使ってマテリアルを作成することができます。
-
vertexShader
- 頂点シェーダー
-
fragmentShader
- フラグメントシェーダー
-
uv
- テクスチャ座標
- geometryをconsole.logをするとuv座標が確認できる。
-
varying
- 頂点シェーダーからフラグメントシェーダーにデータを渡す
-
gl_Position
- 頂点の位置
-
gl_FragColor
- フラグメントの色
コードを書いてみる
- import文でThree.jsを読み込みます。
import * as THREE from "three";
- Three.js の3Dシーンを作成します。
/** Three.jsの3Dシーンを作成します*/
const scene = new THREE.Scene();
- カメラを設定する カメラの種類もいくつかありますが、今回は透視投影カメラを使います。
- 例
- THREE.PerspectiveCamera : 遠近感が適用されるカメラ
- THREE.OrthographicCamera : 平行投影が適用されるカメラ
/** カメラを設定します */
const camera = new THREE.PerspectiveCamera(
40, // 視野角
window.innerWidth / window.innerHeight, // アスペクト比
0.1, // カメラの視点の最短距離
1000, // カメラの視点の最長距離
);
このままだと最初のwindow幅で固定されてしまうので、リサイズした時に画面幅を合わせるようにします。
/** リサイズした時画面幅を合わせる */
window.addEventListener(
"resize",
() => {
renderer?.setSize(window.innerWidth, window.innerHeight);
if (camera) {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
}
},
false,
);
- レンダラーを作成する WebGLレンダラーを作成し、アンチエイリアシングを有効にします。
/** WebGLレンダラーを作成し、アンチエイリアシングを有効にします */
const renderer = new THREE.WebGLRenderer({
antialias: true,
});
renderer.setSize(window.innerWidth, window.innerHeight);
- エレメントの取得 DOM要素を取得して、レンダラーのDOM要素を追加します。
/** エレメントの取得 */
const containerElement = document.querySelector("#webgl");
if (!containerElement) return; // #webgl要素が存在しない場合は処理を終了
containerElement.appendChild(renderer.domElement);
renderer.setSize(window.innerWidth, window.innerHeight);
- ジオメトリを作成する
ジオメトリは3Dオブジェクトの形状を決めるものです。
いくつか種類はありますが、今回は立方体を作成します。
/** Geometry を作成します */
const geometry = new THREE.BoxGeometry();
他にも様々な種類があるのでドキュメントを参照してください。
- マテリアルを作成する
今回はシェーダーマテリアルを使います。 vertexShaderとfragmentShaderは分かりやすくするため別ファイルに分けて書きます。
vertexShader.glsl
projectionMatrix * modelViewMatrixはカメラの位置とオブジェクトの位置を合わせるために使います。
positionはジオメトリの頂点の位置です。
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
fragmentShader.glsl
vec4(1.0, 0.0, 0.0, 1.0)はRGBAの値です。
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
main.ts
import fragmentShader from "./shaders/fragmentShader.glsl";
import vertexShader from "./shaders/vertexShader.glsl";
/** ShaderMaterialを作成します */
const material = new THREE.ShaderMaterial({
vertexShader,
fragmentShader,
});
- メッシュを作成する
ジオメトリとマテリアルを組み合わせてメッシュを作成します。
/** Mesh を作成します */
const cube = new THREE.Mesh(geometry, material);
- シーンに追加する
最後にシーンに追加します!
これがないと3Dオブジェクトが表示されないので注意が必要です!
/** シーンに追加します */
scene.add(cube);
ここまでで3Dシーンの作成は完了です。
あとは細かい調整やアニメーション周りの設定をすれば、3Dシーンを作成することができます。
デモ
デモを見る
全体のコード
main.ts
import * as THREE from "three";
import fragmentShader from "./shaders/fragmentShader.glsl";
import vertexShader from "./shaders/vertexShader.glsl";
(async () => {
/** Three.jsの3Dシーンを作成します*/
const scene = new THREE.Scene();
/** カメラを設定します */
const camera = new THREE.PerspectiveCamera(
40, // 視野角
window.innerWidth / window.innerHeight, // アスペクト比
0.1, // カメラの視点の最短距離
1000, // カメラの視点の最長距離
);
/** リサイズした時画面幅を合わせる */
window.addEventListener(
"resize",
() => {
renderer?.setSize(window.innerWidth, window.innerHeight);
if (camera) {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
}
},
false,
);
/** WebGLレンダラーを作成し、アンチエイリアシングを有効にします */
const renderer = new THREE.WebGLRenderer({
antialias: true,
});
renderer.setSize(window.innerWidth, window.innerHeight);
/** エレメントの取得 */
const containerElement = document.querySelector("#webgl");
if (!containerElement) return; // #webgl要素が存在しない場合は処理を終了します
containerElement.appendChild(renderer.domElement);
renderer.setSize(window.innerWidth, window.innerHeight);
/** Geometry を作成します */
const geometry = new THREE.BoxGeometry();
console.log(geometry.attributes); // uv座標を確認できます
/** ShaderMaterialを作成します */
const material = new THREE.ShaderMaterial({
vertexShader,
fragmentShader,
});
/** Mesh を作成します */
const cube = new THREE.Mesh(geometry, material);
/** シーンに追加します */
scene.add(cube);
camera.position.z = 5;
/** アニメーションループ*/
function animate() {
requestAnimationFrame(animate);
cube.rotation.x = cube.rotation.x + 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();
})();
vertexShader.glsl
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
fragmentShader.glsl
varying vec2 vUv; // 頂点シェーダから受け取る変数
void main() {
gl_FragColor = vec4(vUv, 1.0, 1.0);
}