2
3

2Dフラクタルノイズで地形を生成して、山を回転させるアニメーションのゲーム。

Last updated at Posted at 2024-09-11

2Dフラクタルノイズで地形を生成して、山を回転させるアニメーションのゲーム。

スペースキーを押すことで新しい地形を再生成します。

image.png

スクリーンショット 2024-09-12 052323.png

Three.jsを使って3Dで生成された山の地形を描画し、それを回転させるアニメーションを実現しています。また、スペースキーを押すと異なる地形が再生成されます。

Three.js を使って3D地形を生成し、アニメーションさせています。
フラクタルノイズ を用いて山のような地形を生成しています。
スペースキーを押すと、新しい地形がランダムに生成されます。

シーン、カメラ、レンダラー の作成:

シーンは3Dオブジェクトを配置する空間。
カメラはシーンを観察する視点を提供。
レンダラーはWebGLを使ってシーンをブラウザに描画。

地形メッシュとマテリアル:

PlaneGeometryを使って100x100の分割されたメッシュを作成。
地形の色は緑(0x228B22)で、ワイヤーフレームではなく実際の地形として描画。
光源:

AmbientLight(環境光)で全体を照らし、PointLight(ポイント光源)で太陽のような光を表現。

ノイズの生成:

Perlinノイズをベースにしたフラクタルノイズで山の地形を作成しています。各頂点のz軸をこのノイズで計算し、高低差を作り出しています。

アニメーション:

地形はplane.rotation.zの値を少しずつ増やして回転させています。これにより、地形が回転しているように見えます。

スペースキーでの地形再生成:

キーボードのスペースキーを押すと、新しいノイズパターンを使って地形を再生成します。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>3D Terrain Rotation</title>
  <style>
    body { margin: 0; overflow: hidden; }
    canvas { display: block; }
  </style>
</head>
<body>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></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.PlaneGeometry(10, 10, 100, 100); // 100x100のメッシュ
    const material = new THREE.MeshStandardMaterial({ color: 0x228B22, wireframe: false });
    const plane = new THREE.Mesh(geometry, material);
    plane.rotation.x = -Math.PI / 2; // 平面を横に回転させる
    scene.add(plane);

    // 環境光源とポイント光源(太陽のような光)
    const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); // 環境光
    scene.add(ambientLight);

    const pointLight = new THREE.PointLight(0xffffff, 1, 100);
    pointLight.position.set(5, 10, 5); // 光源の位置
    scene.add(pointLight);

    // カメラ位置の設定
    camera.position.z = 10;
    camera.position.y = 5;
    camera.lookAt(plane.position);

    // ノイズ生成用の関数 (2Dフラクタルノイズ)
    function fade(t) {
      return t * t * t * (t * (t * 6 - 15) + 10);
    }

    function lerp(t, a, b) {
      return a + t * (b - a);
    }

    function grad(hash, x, y) {
      const h = hash & 15;
      const u = h < 8 ? x : y;
      const v = h < 4 ? y : x;
      return ((h & 1) === 0 ? u : -u) + ((h & 2) === 0 ? v : -v);
    }

    const permutation = [...Array(256).keys()];
    for (let i = permutation.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [permutation[i], permutation[j]] = [permutation[j], permutation[i]];
    }
    const p = [...permutation, ...permutation];

    function perlin(x, y) {
      const X = Math.floor(x) & 255;
      const Y = Math.floor(y) & 255;

      x -= Math.floor(x);
      y -= Math.floor(y);

      const u = fade(x);
      const v = fade(y);

      const A = p[X] + Y;
      const B = p[X + 1] + Y;

      return lerp(v, lerp(u, grad(p[A], x, y), grad(p[B], x - 1, y)),
                    lerp(u, grad(p[A + 1], x, y - 1), grad(p[B + 1], x - 1, y - 1)));
    }

    // フラクタルノイズの生成
    function fractalNoise(x, y, octaves, persistence) {
      let total = 0;
      let frequency = 1;
      let amplitude = 1;
      let maxValue = 0;

      for (let i = 0; i < octaves; i++) {
        total += perlin(x * frequency, y * frequency) * amplitude;
        maxValue += amplitude;
        amplitude *= persistence;
        frequency *= 2;
      }

      return total / maxValue;
    }

    // 地形の生成関数
    function generateTerrain() {
      const positions = geometry.attributes.position.array;

      for (let i = 0; i < positions.length; i += 3) {
        const x = positions[i];
        const y = positions[i + 1];

        // フラクタルノイズによってz軸の位置を更新
        const noiseValue = fractalNoise(x * 0.2, y * 0.2, 5, 0.5);
        positions[i + 2] = noiseValue * 2; // Z軸を更新し、山のように変形
      }

      geometry.attributes.position.needsUpdate = true;
    }

    // 初期の地形を生成
    generateTerrain();

    // アニメーション用の変数
    function animate() {
      requestAnimationFrame(animate);

      // 山の回転
      plane.rotation.z += 0.005;  // z軸で回転

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

    // ウィンドウリサイズ時にレンダラーとカメラのアスペクト比を調整
    window.addEventListener('resize', () => {
      renderer.setSize(window.innerWidth, window.innerHeight);
      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();
    });

    // スペースキーで地形の再生成
    window.addEventListener('keydown', (event) => {
      if (event.code === 'Space') {
        generateTerrain();
      }
    });

    // アニメーションを開始
    animate();
  </script>
</body>
</html>

参考。

GLSL(OpenGL Shading Language)には、様々な組み込み関数とユーティリティ関数が用意されています。これらの関数は、シェーダー内での計算やデータ処理を助けるために設計されています。以下に、GLSLの主要な関数とそのカテゴリを示します。

数学関数

算術関数

abs(x): 絶対値
sign(x): 符号
min(x, y): 最小値
max(x, y): 最大値
clamp(x, minVal, maxVal): 値を範囲内に制限
mix(x, y, a): 線形補間

三角関数

sin(x): サイン
cos(x): コサイン
tan(x): タンジェント
asin(x): アークサイン
acos(x): アークコサイン
atan(x): アークタンジェント
atan(y, x): 2変数アークタンジェント

指数・対数関数

pow(x, y): 指数
exp(x): 指数関数
log(x): 自然対数
log2(x): 2を底とする対数
sqrt(x): 平方根
inversesqrt(x): 逆平方根

絶対値・浮動小数点関数

float(x): 浮動小数点数への変換
int(x): 整数への変換
round(x): 四捨五入
floor(x): 小数点以下切り捨て
ceil(x): 小数点以下切り上げ

ベクトル・行列関数

ベクトル関数

length(x): ベクトルの長さ
normalize(x): ベクトルの正規化
dot(x, y): ドット積
cross(x, y): クロス積
reflect(I, N): 反射ベクトル
refract(I, N, eta): 屈折ベクトル

行列関数

transpose(x): 行列の転置
inverse(x): 行列の逆行列
determinant(x): 行列の行列式

テクスチャ関数

サンプリング関数

texture2D(sampler2D, coord): 2Dテクスチャサンプリング
texture3D(sampler3D, coord): 3Dテクスチャサンプリング
textureCube(samplerCube, coord): キューブマップテクスチャサンプリング

テクスチャ関連

textureSize(sampler2D, level): テクスチャのサイズ取得
texelFetch(sampler2D, P, lod): 指定されたレベルのテクセル取得

数値演算関数

浮動小数点数演算

mod(x, y): 剰余
fmod(x, y): 浮動小数点剰余
fract(x): 小数部分
trunc(x): 切り捨て整数

ビット操作関数(GLSL 4.0以降)

bitCount(x): ビット数
bitFieldExtract(x, offset, count): ビットフィールド抽出
bitFieldInsert(x, insert, offset, count): ビットフィールド挿入
bitReverse(x): ビット反転

その他の関数

制御関数

if(condition) { ... }: 条件付き実行
for(init; condition; increment) { ... }: ループ
while(condition) { ... }: 繰り返し

switch(expression) { case value: ... break; ... default: ... }: スイッチ文
discard;: 現在のフラグメントを描画しない

エラー処理

assert(condition);: 条件が満たされない場合にエラーを発生させる(GLSL 4.30以降)

関数の定義

void functionName(params) { ... }: GLSLでは、シェーダー内で関数を定義し、呼び出すことができます。引数や戻り値の型を指定し、複雑な処理を分割して管理することができます。

シェーダー内での利用
シェーダー内でこれらの関数を利用することで、さまざまなグラフィックス処理を効率的に行うことができます。例えば、頂点シェーダーでは、頂点の位置計算や変換を行い、フラグメントシェーダーでは、ピクセルごとの色計算やテクスチャサンプリングを行います。

GLSLコードのサンプル

以下に、いくつかのGLSLの関数を使ったシンプルなシェーダーの例を示します。

頂点シェーダーのサンプル


#version 300 es
layout(location = 0) in vec4 a_position; // 頂点位置
layout(location = 1) in vec4 a_color;    // 頂点カラー

out vec4 v_color; // フラグメントシェーダーへの出力

void main() {
    gl_Position = a_position; // 頂点の位置を設定
    v_color = a_color;       // 頂点カラーをフラグメントシェーダーへ渡す
}

フラグメントシェーダーのサンプル


#version 300 es
precision mediump float; // 浮動小数点の精度指定

in vec4 v_color; // 頂点シェーダーからの入力

out vec4 fragColor; // 最終的なフラグメント色

void main() {
    fragColor = v_color; // 頂点カラーをそのまま出力
}

まとめ
GLSLの組み込み関数は、グラフィックスシェーダーでの計算や処理を効率的に行うための強力なツールです。

2
3
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
2
3