頂点シェーダー


title: "頂点シェーダー" description: "頂点シェーダーを使ったジオメトリの変形を学びます。波うつ平面や動的な頂点操作を実践します。" chapter: "10-threejs" order: 2 challenge: description: "平面ジオメトリの頂点をy方向に変位させて波のような地形を作ってください。sin関数を使い、位置のx,z座標と時間に基づいた波を生成しましょう。" hints:

  • "頂点シェーダー内で position.y を sin 関数で変位させます。"
  • "float wave = sin(position.x * 2.0 + u_time) * 0.3; のように波の高さを計算します。"
  • "x方向とz方向の両方で波を作ると立体的になります。"

頂点シェーダー

頂点シェーダーの役割

フラグメントシェーダーがピクセルの色を決めるのに対し、頂点シェーダーはメッシュの各頂点の位置を決めます。頂点の位置を動的に変更することで、ジオメトリを変形させることができます。

基本構造

uniform float u_time;
varying vec2 vUv;
varying float vElevation;

void main() {
    vUv = uv;

    // 頂点位置のコピーを操作
    vec3 pos = position;
    pos.y += sin(pos.x * 3.0 + u_time) * 0.2;

    vElevation = pos.y;

    gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}

重要なのは position を直接変更せず、コピー(vec3 pos = position;)を操作することです。

波うつ平面

PlaneGeometry にセグメントを多く設定し(例: 64x64)、各頂点を波のように動かします。

頂点シェーダーで2方向の波を合成します:

uniform float u_time;
varying vec2 vUv;
varying float vElevation;

void main() {
    vUv = uv;
    vec3 pos = position;

    float wave1 = sin(pos.x * 2.0 + u_time) * 0.3;
    float wave2 = sin(pos.y * 3.0 + u_time * 1.5) * 0.15;
    pos.z = wave1 + wave2;

    vElevation = pos.z;
    gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}

フラグメントシェーダーで高さに応じた色をつけます:

varying vec2 vUv;
varying float vElevation;

void main() {
    vec3 low = vec3(0.1, 0.2, 0.5);
    vec3 high = vec3(0.9, 0.95, 1.0);
    float t = smoothstep(-0.3, 0.3, vElevation);
    vec3 color = mix(low, high, t);
    gl_FragColor = vec4(color, 1.0);
}

頂点変位(ディスプレースメント)

ノイズ関数を使って有機的な変形も可能です:

float hash(vec2 p) {
    return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
}

void main() {
    vUv = uv;
    vec3 pos = position;

    float noise = hash(pos.xy * 0.5 + u_time * 0.1);
    pos.z += noise * 0.5;

    gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}

法線の再計算

頂点を変位させると法線が正しくなくなります。正確なライティングには法線の再計算が必要です。簡易的な方法として、隣接頂点との差分から法線を近似できます。微小なオフセット(delta)で隣の点の変位を求め、外積で法線を計算します。

まとめ

  • 頂点シェーダーでpositionを変更してジオメトリを変形できる
  • varying変数で変位量などをフラグメントシェーダーに渡せる
  • 十分なセグメント数のジオメトリが必要(変位は頂点単位で行われる)
  • 変位後は法線の再計算が必要になることがある