Three.jsは、WebGLを簡単に扱うためのJavaScriptライブラリであり、シェーダーを用いた視覚効果を簡潔に実装することが可能です。本システムでは、シンプルなカスタムシェーダーを用いて、球体の表面にリアルタイムで変化するテクスチャを適用します。
Three.jsを使用した動的パーリンノイズシェーダーの実装
概要
本稿では、Three.jsを使用して動的に変化するパーリンノイズシェーダーを実装する方法について説明します。Three.jsは、WebGLの抽象化ライブラリであり、視覚的なエフェクトを簡単に実装できるため、シェーダーのカスタマイズも容易です。本実装では、球体の表面にリアルタイムで変化するテクスチャを適用し、視覚的に興味深い効果を得ることを目的としています。
頂点シェーダー(Vertex Shader)
頂点シェーダーは、各頂点の位置をクリップ空間に変換し、フラグメントシェーダーに位置データを渡す役割を担います。以下のコードは、頂点シェーダーの実装例です。
varying vec3 vPosition;
void main() {
vPosition = position;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
varying vec3 vPosition;: この変数はフラグメントシェーダーで使用するために頂点の位置を保存します。
vPosition = position;: 各頂点の位置をvPositionに保存します。
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);: 頂点の位置をクリップ空間に変換し、最終的な位置を設定します。
このコードでは、頂点の位置をvPositionとしてフラグメントシェーダーに渡し、後の処理に利用します。
フラグメントシェーダー(Fragment Shader)
フラグメントシェーダーは、パーリンノイズを生成し、これを用いて動的な色変化を適用します。簡易的なノイズ生成アルゴリズムを用いることで、リアルタイムに変化するテクスチャを実現しています。以下のコードは、フラグメントシェーダーの実装例です。
uniform float uTime;
varying vec3 vPosition;
float random(vec3 p) {
return fract(sin(dot(p, vec3(12.9898, 78.233, 45.543))) * 43758.5453);
}
float noise(vec3 p) {
vec3 i = floor(p);
vec3 f = fract(p);
float n = dot(i, vec3(1.0, 57.0, 113.0));
return mix(mix(mix(random(i + vec3(0.0, 0.0, 0.0)), random(i + vec3(1.0, 0.0, 0.0)), f.x),
mix(random(i + vec3(0.0, 1.0, 0.0)), random(i + vec3(1.0, 1.0, 0.0)), f.x), f.y),
mix(mix(random(i + vec3(0.0, 0.0, 1.0)), random(i + vec3(1.0, 0.0, 1.0)), f.x),
mix(random(i + vec3(0.0, 1.0, 1.0)), random(i + vec3(1.0, 1.0, 1.0)), f.x), f.y), f.z);
}
void main() {
float n = noise(vPosition * 5.0 + uTime);
vec3 color = vec3(0.2, 0.4, 0.6) * n;
gl_FragColor = vec4(color, 1.0);
}
uniform float uTime;: 時間(uTime)の値をシェーダーに渡し、ノイズの動きを制御します。
varying vec3 vPosition;: 頂点シェーダーから受け取った頂点の位置を使用します。
float random(vec3 p): 簡単なランダム関数を定義します。
float noise(vec3 p): 簡単なノイズ生成関数です。パーリンノイズの簡易版で、vPositionとuTimeを使って色の変化を計算します。
gl_FragColor = vec4(color, 1.0);: ノイズに基づいて計算した色を最終的なフラグメント(ピクセル)の色として設定します。
このコードでは、random関数を用いて基本的なノイズを生成し、noise関数を用いてスムーズなノイズを作成します。uTimeは時間を示すユニフォームで、ノイズの動的な変化を実現します。
結論
本稿で示したThree.jsによるパーリンノイズシェーダーの実装は、シンプルでありながら強力な方法で、リアルタイムで動的に変化するテクスチャを実現します。Three.jsを用いることで、複雑なWebGLの処理を簡素化し、視覚的に魅力的なエフェクトを簡単に実装することができます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Three.js Custom Shader Example</title>
<style>
body { margin: 0; }
canvas { display: block; }
</style>
</head>
<body>
<!-- Three.jsを使用するためのスクリプトを読み込む -->
<script src="https://cdn.jsdelivr.net/npm/three@0.153.0/build/three.min.js"></script>
<script>
// シーン、カメラ、レンダラーを初期化
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// パーリンノイズを生成するカスタムシェーダー
var vertexShader = `
varying vec3 vPosition;
void main() {
vPosition = position;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;
var fragmentShader = `
uniform float uTime;
varying vec3 vPosition;
float random(vec3 p) {
return fract(sin(dot(p, vec3(12.9898, 78.233, 45.543))) * 43758.5453);
}
float noise(vec3 p) {
vec3 i = floor(p);
vec3 f = fract(p);
float n = dot(i, vec3(1.0, 57.0, 113.0));
return mix(mix(mix(random(i + vec3(0.0, 0.0, 0.0)), random(i + vec3(1.0, 0.0, 0.0)), f.x),
mix(random(i + vec3(0.0, 1.0, 0.0)), random(i + vec3(1.0, 1.0, 0.0)), f.x), f.y),
mix(mix(random(i + vec3(0.0, 0.0, 1.0)), random(i + vec3(1.0, 0.0, 1.0)), f.x),
mix(random(i + vec3(0.0, 1.0, 1.0)), random(i + vec3(1.0, 1.0, 1.0)), f.x), f.y), f.z);
}
void main() {
float n = noise(vPosition * 5.0 + uTime); // ノイズを計算
vec3 color = vec3(0.2, 0.4, 0.6) * n; // ノイズに基づいた色を設定
gl_FragColor = vec4(color, 1.0); // 最終的な色を設定
}
`;
// シェーダーを作成
var shaderMaterial = new THREE.ShaderMaterial({
uniforms: {
uTime: { type: 'f', value: 0.0 }
},
vertexShader: vertexShader,
fragmentShader: fragmentShader
});
// 球体ジオメトリとマテリアルを作成
var geometry = new THREE.SphereGeometry(1, 32, 32);
var sphere = new THREE.Mesh(geometry, shaderMaterial);
scene.add(sphere);
camera.position.z = 5;
// アニメーションループ
function animate() {
requestAnimationFrame(animate);
// 時間の経過に応じてuTimeを更新
shaderMaterial.uniforms.uTime.value += 0.01;
// 球体を回転させる
sphere.rotation.x += 0.01;
sphere.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();
// ウィンドウのリサイズに対応
window.addEventListener('resize', () => {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
});
</script>
</body>
</html>
特定のアルゴリズムをシェーダー内で直接実装する記述方法。
Three.jsとシェーダーを使ってパーリンノイズを使用し、表面に動的な変化を加えるシェーダーを実装。
Three.jsとカスタムシェーダーを使った実装は、以下のようなアプローチも可能です:
- カスタムシェーダーの使用:
Vertex Shader と Fragment Shader を定義して、特定のビジュアルエフェクトを作成する。
パーリンノイズの生成や動的な変化など、特定のアルゴリズムをシェーダー内で直接実装する。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Perlin Noise Shader with Three.js</title>
<style>
body { margin: 0; }
canvas { display: block; }
</style>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script id="vertexShader" type="x-shader/x-vertex">
varying vec3 vPosition;
void main() {
vPosition = position;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
uniform float uTime;
varying vec3 vPosition;
// パーリンノイズ関数(簡易版)
float random(vec3 p) {
return fract(sin(dot(p, vec3(12.9898, 78.233, 45.543))) * 43758.5453);
}
float noise(vec3 p) {
vec3 i = floor(p);
vec3 f = fract(p);
float n = dot(i, vec3(1.0, 57.0, 113.0));
return mix(mix(mix(random(i + vec3(0.0, 0.0, 0.0)), random(i + vec3(1.0, 0.0, 0.0)), f.x),
mix(random(i + vec3(0.0, 1.0, 0.0)), random(i + vec3(1.0, 1.0, 0.0)), f.x), f.y),
mix(mix(random(i + vec3(0.0, 0.0, 1.0)), random(i + vec3(1.0, 0.0, 1.0)), f.x),
mix(random(i + vec3(0.0, 1.0, 1.0)), random(i + vec3(1.0, 1.0, 1.0)), f.x), f.y), f.z);
}
void main() {
float n = noise(vPosition + uTime);
vec3 color = vec3(0.2, 0.4, 0.6) * n; // ノイズを色に適用
gl_FragColor = vec4(color, 1.0);
}
</script>
<script>
// シーン、カメラ、レンダラーの初期化
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 球体ジオメトリの作成
const geometry = new THREE.SphereGeometry(1, 64, 64);
// シェーダーマテリアルの作成
const material = new THREE.ShaderMaterial({
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragmentShader').textContent,
uniforms: {
uTime: { value: 0.0 }
}
});
// メッシュの作成
const sphere = new THREE.Mesh(geometry, material);
scene.add(sphere);
// カメラの位置
camera.position.z = 3;
// アニメーションループ
function animate(time) {
requestAnimationFrame(animate);
material.uniforms.uTime.value = time * 0.001; // uTimeを更新
renderer.render(scene, camera);
}
animate();
</script>
</body>
</html>