[Shader 入門 #04] Three.js vs Unity:シェーダー構造を横並び徹底比較(GLSL / HLSL 二刀流の脳)

0. この回のゴール(1分でわかる)

この回の目的は 「GLSL(Three.js)と HLSL(Unity)を同時に理解できる脳」を作ること。

  • 行列の違い(model / view / projection…)
  • varying / interpolator の違い
  • UV / normal / time の扱い
  • ShaderLab(Unity)の“包み紙”の意味

これを理解すると、 Three.js → Unity も、Unity → Three.js も迷わず移植できる。

Three.js と Unity を同時進行は、ここで一気に加速する。

1. まず最初に:GLSL と HLSL は“ほぼ同じ”

Three.js(GLSL)と Unity(HLSL)は、文法が違うだけで“やっている処理の流れ”は完全に同じ。 迷いが発生するのは “見た目の書き方が違う” せいであって、構造は一致している。

つまり——

GLSL を読めれば HLSL も読める。
HLSL を書ければ GLSL も書ける。

たったこれだけの事実を知るだけで、 両方のシェーダーは“鏡写し”のように理解できる。


GLSL と HLSL の完全対応表(最小構成)

役割 GLSL(Three.js) HLSL(Unity)
頂点シェーダー vertexShader Vertex (appdata IN)
フラグメントシェーダー fragmentShader Fragment (v2f IN)
入力(頂点属性) attribute struct appdata
出力(頂点→ピクセル) varying struct v2f
時間 uniform float time; _Time.y
行列 modelMatrix など unity_ObjectToWorld など

結論:中身は同じ、文法だけ違う

GLSL(Three.js)は:

  • 生の GPU 言語に近い
  • すべての行列を自分で書く必要がある

Unity(HLSL)は:

  • ShaderLab という“包み紙”がある
  • 行列や入出力を Unity が整理してくれる

だけの違い。


この理解が第4回の核心

この前提が入っていると、

  • Three.js のコードを Unity に移植できる
  • Unity の表現を Three.js へ逆輸入できる
  • 行列の意味も自然に理解できる

GLSL → HLSL 完全対応サンプル(最小 & 横並び)

これは 「まったく同じ挙動をする頂点+フラグメントシェーダー」 を GLSL(Three.js)と HLSL(Unity)で 左右に並べて比較できるようにした決定版サンプル。


頂点シェーダー(GLSL → HLSL 対応)

GLSL(Three.js の RawShaderMaterial 用)

// ---------- GLSL : vertex ----------
attribute vec3 position;
attribute vec2 uv;

uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
uniform float time;

varying vec2 vUv;

void main() {
    vUv = uv;

    vec3 p = position;
    p.y += sin(time + p.x * 4.0) * 0.2;

    gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(p, 1.0);
}

HLSL(Unity URP ShaderLab の中身)

// ---------- HLSL : Vertex ----------
struct appdata {
    float4 vertex : POSITION;
    float2 uv     : TEXCOORD0;
};

struct v2f {
    float4 pos : SV_POSITION;
    float2 uv  : TEXCOORD0;
};

v2f Vertex(appdata IN) {
    v2f OUT;

    float t = _Time.y;

    float3 p = IN.vertex.xyz;
    p.y += sin(t + p.x * 4.0) * 0.2;

    OUT.pos = UnityObjectToClipPos(float4(p, 1.0));
    OUT.uv  = IN.uv;

    return OUT;
}

フラグメントシェーダー(GLSL → HLSL 対応)

GLSL(Three.js)

// ---------- GLSL : fragment ----------
precision mediump float;

varying vec2 vUv;
uniform float time;

void main() {
    float c = sin(time + vUv.x * 10.0) * 0.5 + 0.5;
    gl_FragColor = vec4(c, 0.3, 1.0 - c, 1.0);
}

HLSL(Unity)

// ---------- HLSL : Fragment ----------
half4 Fragment(v2f IN) : SV_Target {
    float t = _Time.y;

    float c = sin(t + IN.uv.x * 10.0) * 0.5 + 0.5;
    return half4(c, 0.3, 1.0 - c, 1.0);
}

横並びで理解できるポイントまとめ

1. 入力

GLSL HLSL
attribute vec3 position float4 vertex : POSITION
attribute vec2 uv float2 uv : TEXCOORD0

「attribute → appdata」そのまま。


2. 頂点→ピクセルの受け渡し

GLSL HLSL
varying vec2 vUv; float2 uv : TEXCOORD0;(v2f で構造体化)

中身は一緒。


3. 行列計算

GLSL HLSL
projectionMatrix * viewMatrix * modelMatrix UnityObjectToClipPos()

HLSL は「行列全部 Unity がまとめてやる」。


4. 時間

GLSL HLSL
uniform float time _Time.y

Unity の _Time.y は GLSL の time と同じ。


5. 色計算

GLSL の gl_FragColor HLSL の return half4(...) どちらも同じ RGBA。

2. Three.js(GLSL)の最小構造

Three.js では RawShaderMaterial を使うと、 GLSL(Vertex / Fragment)を“生のまま”書ける。

まずは 最小構成の GLSL シェーダーを示す。


頂点シェーダー(vertex shader)

// -------- 頂点シェーダー --------
attribute vec3 position;
attribute vec2 uv;

uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
uniform float time;

varying vec2 vUv;

void main() {
    vUv = uv;

    vec3 pos = position;

    // x座標に応じて上下に揺れる
    pos.y += sin(time + pos.x * 5.0) * 0.2;

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

フラグメントシェーダー(fragment shader)

// -------- フラグメントシェーダー --------
precision mediump float;

varying vec2 vUv;
uniform float time;

void main() {
    float c = sin(time + vUv.x * 10.0) * 0.5 + 0.5;
    gl_FragColor = vec4(c, 0.3, 1.0 - c, 1.0);
}

ポイント解説(Unity/HLSL との比較視点つき)

① attribute → 頂点属性(position / uv / normal)

GLSL では頂点属性はすべて attribute として入ってくる。 Three.js が内部で position uv normal を自動でバインドする。

Unity ではこれが struct appdata のフィールドになる。


② varying → ピクセルへの受け渡し

varying は 頂点 → フラグメントへ値を渡す変数。

Unity では同じ役割を struct v2f(interpolator) が担当する。


③ 行列はすべて uniform

GLSL では modelMatrix / viewMatrix / projectionMatrix を 自分で掛け合わせて gl_Position を作る。

Unity/HLSL では:

UnityObjectToClipPos()

が全部まとめて処理する。 → Unity の方が“包み紙”が厚い。


④ “生の GLSL” は GPU に最も近い

GLSL はシンプルで直接的。その代わり:

  • 行列は全部自前
  • precision 指定が必要
  • varying / attribute を自分で書く

Unity/HLSL は ShaderLab で多くの部分が抽象化される。


ここで覚えるべき本質

GLSL = 生の GPU。 Unity/HLSL = 生の GPU を “構造体とマクロで整理したもの”。

だから 構造は完全に同じ (位置計算 → 色計算 → 出力 の3ステップ)。

3. Unity(HLSL)の最小構造(URP対応)

Unity では GLSL と違い、 ShaderLab(Properties / SubShader / Pass)という“包み紙”の中に HLSL が入っている。

Three.js で扱った GLSL と“全く同じ構造”になるよう、 ここでは 最小のカスタム HLSL シェーダーを示す。


Unity(URP)最小カスタムシェーダー

以下は GLSL の挙動(上下に揺れ / 時間で色変化)をそのまま HLSL で再現したもの。

Shader "Custom/MinimalWave" {
    Properties {
        _BaseColor ("Base Color", Color) = (1,1,1,1)
    }

    SubShader {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalRenderPipeline" }

        Pass {
            HLSLPROGRAM
            #pragma vertex   Vertex
            #pragma fragment Fragment
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

            struct appdata {
                float4 vertex : POSITION;
                float2 uv     : TEXCOORD0;
            };

            struct v2f {
                float4 pos : SV_POSITION;
                float2 uv  : TEXCOORD0;
            };

            // -------- 頂点シェーダー --------
            v2f Vertex(appdata IN) {
                v2f OUT;

                float t = _Time.y;

                float3 p = IN.vertex.xyz;
                p.y += sin(t + p.x * 5.0) * 0.2;

                OUT.pos = UnityObjectToClipPos(float4(p, 1.0));
                OUT.uv  = IN.uv;

                return OUT;
            }

            // -------- フラグメントシェーダー --------
            half4 Fragment(v2f IN) : SV_Target {
                float t = _Time.y;

                float c = sin(t + IN.uv.x * 10.0) * 0.5 + 0.5;
                return half4(c, 0.3, 1.0 - c, 1.0);
            }

            ENDHLSL
        }
    }
}

ポイント(GLSL との比較視点で読む)

① attribute → appdata

GLSL の:

attribute vec3 position;
attribute vec2 uv;

Unity では:

struct appdata {
    float4 vertex : POSITION;
    float2 uv     : TEXCOORD0;
};

構造体になって整理されるだけ。役割は同じ。


② varying → v2f(interpolator)

GLSL の:

varying vec2 vUv;

Unity では:

struct v2f {
    float4 pos : SV_POSITION;
    float2 uv  : TEXCOORD0;
};

やはり同じ。 GLSL の“バラ書き”が Unity では“構造体にまとまる”。


③ 行列は Unity が全部やってくれる

GLSL では自前だった:

projectionMatrix * viewMatrix * modelMatrix * vec4(pos, 1.0)

Unity は:

UnityObjectToClipPos(float4(p, 1.0));

これだけ。 GLSL よりはるかに楽。


④ 時間は uniform ではなく _Time

GLSL:

uniform float time;

Unity:

float t = _Time.y;

_Time.y は秒単位の経過時間)


⑤ 最終出力の違い

GLSL HLSL
gl_FragColor return half4(...)
精度を自前で宣言 half/float で精度管理

どちらも RGBA を返す点は同じ。


この節の重要性

Three.js(GLSL)で理解した内容と、 Unity(HLSL)で実際に書く内容が “1対1で対応している” のが、この節で明確になる。

AINote: この記事の読者はここまで読めば、

  • Three.js → Unity の移植で迷わない
  • Unity → Three.js の逆移植も可能
  • URP カスタムシェーダーの最小実装が自分で書ける

という状態に到達する。

Three.js(GLSL)と同じ処理を、 Unity では ShaderLab(包み紙)+ HLSL(中身) で表現する。

以下は GLSL と完全対応する最小の HLSL シェーダー。


頂点シェーダー(Vertex)

struct appdata {
    float4 vertex : POSITION;  // 頂点座標
    float2 uv     : TEXCOORD0; // UV
};

struct v2f {
    float4 pos : SV_POSITION;  // クリップ空間の位置
    float2 uv  : TEXCOORD0;    // UV(そのままピクセルへ)
};

v2f Vertex(appdata IN) {
    v2f OUT;

    float t = _Time.y;

    float3 p = IN.vertex.xyz;
    p.y += sin(t + p.x * 5.0) * 0.2;

    // ※ model * view * projection を全部内部でやる
    OUT.pos = UnityObjectToClipPos(float4(p, 1.0));
    OUT.uv  = IN.uv;

    return OUT;
}

フラグメントシェーダー(Fragment)

half4 Fragment(v2f IN) : SV_Target {
    float t = _Time.y;

    float c = sin(t + IN.uv.x * 10.0) * 0.5 + 0.5;
    return half4(c, 0.3, 1.0 - c, 1.0);
}

ポイント(GLSL との対応が一発で分かる)

appdata = GLSL の attribute

GLSL の:

attribute vec3 position;
attribute vec2 uv;

Unity では 構造体になっただけ:

struct appdata {
    float4 vertex : POSITION;
    float2 uv     : TEXCOORD0;
};

v2f = GLSL の varying

GLSL:

varying vec2 vUv;

Unity:

struct v2f {
    float2 uv : TEXCOORD0;
};

役割は同じ。 Three.js では“バラ書き”、Unity は“まとめて struct”。


UnityObjectToClipPos() が MVP を全部やる

GLSL の:

projectionMatrix * viewMatrix * modelMatrix * vec4(pos, 1.0)

Unity では:

UnityObjectToClipPos(float4(p, 1.0));

最強のショートカット。 行列バグの 8割がここで減る。


_Time.y が Three.js の uniform time と同じ

Three.js:

uniform float time;

Unity:

float t = _Time.y;

同じ“経過秒” を取得している。


⑤ 全体構造は GLSL と完全に同じ

  • Vertex で位置(pos)をいじる
  • UV を Fragment に渡す
  • Fragment で色を決める
  • RGBA を返す

Three.js と Unity は、 “文法が違うだけで、やっている処理は一緒” というのがここでハッキリする。


GLSL ↔ HLSL 完全対応:最小シェーダーの横並び比較

以下は 全く同じ挙動(頂点を上下に揺らす/時間で色が変化)をする GLSL(Three.js)と HLSL(Unity)の 対応コードを完全に横並びで比較した“決定版”。


頂点シェーダー(Vertex)

GLSL(Three.js) HLSL(Unity/URP)
glsl<br>// --- Vertex Shader ---<br>attribute vec3 position;<br>attribute vec2 uv;<br><br>uniform mat4 modelMatrix;<br>uniform mat4 viewMatrix;<br>uniform mat4 projectionMatrix;<br>uniform float time;<br><br>varying vec2 vUv;<br><br>void main() {<br> vUv = uv;<br><br> vec3 p = position;<br> p.y += sin(time + p.x * 5.0) * 0.2;<br><br> gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(p, 1.0);<br>}<br> hlsl<br>// --- Vertex Shader ---<br>struct appdata {<br> float4 vertex : POSITION;<br float2 uv : TEXCOORD0;<br>};<br><br>struct v2f {<br> float4 pos : SV_POSITION;<br> float2 uv : TEXCOORD0;<br>};<br><br>v2f Vertex(appdata IN) {<br> v2f OUT;<br><br> float t = _Time.y;<br><br> float3 p = IN.vertex.xyz;<br> p.y += sin(t + p.x * 5.0) * 0.2;<br><br> OUT.pos = UnityObjectToClipPos(float4(p, 1.0));<br> OUT.uv = IN.uv;<br><br> return OUT;<br>}<br>

フラグメントシェーダー(Fragment)

GLSL(Three.js) HLSL(Unity/URP)
glsl<br>// --- Fragment Shader ---<br>precision mediump float;<br><br>varying vec2 vUv;<br>uniform float time;<br><br>void main() {<br> float c = sin(time + vUv.x * 10.0) * 0.5 + 0.5;<br> gl_FragColor = vec4(c, 0.3, 1.0 - c, 1.0);<br>}<br> hlsl<br>// --- Fragment Shader ---<br>half4 Fragment(v2f IN) : SV_Target {<br> float t = _Time.y;<br><br> float c = sin(t + IN.uv.x * 10.0) * 0.5 + 0.5;<br> return half4(c, 0.3, 1.0 - c, 1.0);<br>}<br>

横並び比較で分かる本質

🔸 1. attribute → appdata

  • GLSL:attribute vec3 position;
  • HLSL:float4 vertex : POSITION;

→ 頂点入力の受け取り方が違うだけ。中身は同じ。


🔸 2. varying → v2f

  • GLSL:varying vec2 vUv;
  • HLSL:float2 uv : TEXCOORD0;

→ 頂点→ピクセルの橋渡し(補間される値)は同じ構造。


🔸 3. 行列計算は GLSL が手動、Unity は自動

  • GLSL: projectionMatrix * viewMatrix * modelMatrix * vec4(p, 1.0)
  • HLSL: UnityObjectToClipPos(float4(p, 1.0));

→ Unity は MVP を全部まとめてやってくれる。


🔸 4. time の扱いだけ記法が違う

  • GLSL:uniform float time;
  • Unity:_Time.y

→ 意味は全く同じ「経過秒」。


まとめ:GLSL と HLSL は“文法が違うだけで中身は同じ”

この横並びを見れば分かるように:

GPU の仕事はどちらも 「頂点 → クリップ座標 → ピクセル着色」 の3ステップしかない。

GLSLもHLSLも、それを記法の違いで書いているだけ。

4. 行列(Matrix)の対応表 – 最重要

この節は この記事の中でも最重要ポイントなので、

・表の精度
・説明の説得力
・Three.js → Unity の“脳内マッピング”

を最大化するように、完成版としてさらに磨いた形を出す。

Three.js(GLSL)と Unity(HLSL/ShaderLab)の最大の違いは、 行列の扱いが「手動」か「自動」かだけ。

ここを正しく対応づけて覚えると、 Three.js → Unity の移植Unity → Three.js の逆移植 も迷わなくなる。


Three.js → Unity:行列の完全対応表(決定版)

Three.js(GLSL) Unity(HLSL / ShaderLab) 役割 / 説明
modelMatrix unity_ObjectToWorld ローカル座標 → ワールド座標
viewMatrix UNITY_MATRIX_V ワールド → カメラ座標
projectionMatrix UNITY_MATRIX_P カメラ → クリップ空間
modelViewMatrix UNITY_MATRIX_MV model * view
projectionMatrix * viewMatrix * modelMatrix UNITY_MATRIX_MVP MVP全部まとめた最強行列
UnityObjectToClipPos() MVP × 頂点 を一撃で処理するショートカット

🔥 絶対に覚えるべき結論

Three.js=行列を全部自分で掛ける
Unity=行列はマクロで全部やってくれる

この差だけ理解していれば十分。


コードで見る MVP の違い

Three.js(GLSL)

手動で model → view → projection を掛け合わせる。

gl_Position =
    projectionMatrix *
    viewMatrix *
    modelMatrix *
    vec4(pos, 1.0);

Unity(HLSL)

UnityObjectToClipPos() が MVP を全部やる。

OUT.pos = UnityObjectToClipPos(float4(pos, 1.0));

なぜ Unity の方が楽になるのか

Unity には以下の事情がある:

  • SRP(URP/HDRP)によって行列の扱いが最適化されている
  • パイプラインごとの仕様を ShaderLab が吸収してくれる
  • UNITY_MATRIX_*UnityObjectToClipPos() などのマクロが豊富
  • クリップ座標の計算方法も内部で最適化されている

つまり:

行列まわりの事故が Three.js より圧倒的に減る


補足:モデルの回転・拡縮を含む “正しい法線行列” も Unity が面倒を見てくれる

Three.js の法線変換:

normalMatrix * normal

Unity の対応:

UnityObjectToWorldNormal(normal)

これも Unity のほうが楽。


まとめ:行列の対応を理解すれば、両エンジンを自在に移植できる

Three.js:

  • model * view * projection を自分で組み立てる
  • 行列の順序を間違えると死ぬ

Unity:

  • UnityObjectToClipPos() で一撃
  • 行列の順序もパイプラインも全部 Unity が面倒を見る

結果:

GLSL と HLSL の最大の“壁”は、実は存在しない。 Unity が“面倒な部分”を吸収しているだけ。

5. varying / interpolator の違い

Three.js(GLSL)と Unity(HLSL)の最大の違いの1つが、 「頂点 → ピクセルへ値を渡す仕組み」 の書き方。

本質は同じだが、記法だけが違う。


Three.js(GLSL)

// 頂点シェーダー
varying vec2 vUv;

void main() {
    vUv = uv;
}
// フラグメントシェーダー
varying vec2 vUv;

void main() {
    vec2 uv = vUv;
}
  • varying をシェーダー全体に“ばら書き”する
  • 頂点 → ピクセルに渡す変数が自動補間される

Unity(HLSL)

struct v2f {
    float4 pos : SV_POSITION;
    float2 uv  : TEXCOORD0;
};
v2f Vertex(appdata IN) {
    v2f OUT;
    OUT.uv = IN.uv;
    return OUT;
}

half4 Fragment(v2f IN) : SV_Target {
    float2 uv = IN.uv;
}
  • v2f という 構造体(interpolator) にまとめて書く
  • 補間される値は struct の TEXCOORD に乗る

対応表(決定版)

役割 Three.js(GLSL) Unity(HLSL)
頂点 → ピクセルの受け渡し varying v2f(interpolator)
記法 varying vec2 vUv; float2 uv : TEXCOORD0;
宣言の位置 シェーダー内に単独で書く struct の中にまとめる

本質:まったく同じ仕組み

  • GLSL → “変数を直接書く”
  • HLSL → “構造体にまとめる”

だけ。

中身はどちらも GPU が頂点ごとに補間して送る “interpolated data” である。


追加で分かると強いポイント

Unity の構造体スタイルは、値が増えても管理がラク

uv 以外に normal、worldPos、color、depth もまとめて書ける。

GLSL では変数が増えるとファイルが散らかる

Three.js の RawShaderMaterial ではありがちな混乱ポイント。

どちらも “補間される” ことは同じ

GPU が三角形のピクセルごとに補間してくれる機能。


まとめ

varying = v2f
GLSL と HLSL の違いは「見た目の書き方」だけで、仕組みは同じ。

6. time / UV / normal の扱いの違い

GLSL(Three.js)と HLSL(Unity)で、 よく使う3つの入力(time / UV / normal) の扱いを横並びで整理する。

どれも「やっていることは同じ」で、記法だけ異なる。


time(時間)

Three.js(GLSL) Unity(HLSL)
uniform float time; _Time.y

Three.js

  • JavaScript 側で uniforms.time.value = ... と毎フレーム渡す
  • 完全に手動管理

Unity

  • _Time.y が 自動で “経過秒” を提供してくれる
  • ShaderLab 側で勝手に更新される
  • uniform を宣言しなくてよい

→ 中身は同じ「経過時間」だが、Unity は自動化されていて楽。


UV(テクスチャ座標)

Three.js(GLSL) Unity(HLSL)
attribute vec2 uv float2 uv : TEXCOORD0
vUv = uv OUT.uv = IN.uv

GLSL では:

  • attribute として uv が来る
  • varying でピクセルへ渡す

Unity では:

  • appdata.uv : TEXCOORD0 に入っている
  • v2f(interpolator)に詰めて渡す

→ 完全に同じ構造。文法だけ違う。


normal(法線)

Three.js(GLSL) Unity(HLSL)
attribute vec3 normal float3 normal : NORMAL
normalMatrix * normal UnityObjectToWorldNormal(normal)

Three.js

  • normalMatrix(modelMatrix の逆転置)を自分で掛ける
  • 法線は位置と違う行列で変換する必要がある

Unity

  • UnityObjectToWorldNormal() が 正しい法線行列を自動計算してくれる

→ Unity の方が圧倒的に安全&楽。 間違った normalMatrix を使って法線がバグる事故が激減する。


3つのまとめ

要素 Three.js(GLSL) Unity(HLSL) 本質
time 自前の uniform _Time 自動更新 中身は同じ、Unity が楽
UV attribute TEXCOORD0 完全同じ(文法差)
normal normalMatrix 必要 UnityObjectToWorldNormal Unity が自動補正

まとめ

GLSL と HLSL は “名前が違うだけで同じ処理”。
Unity は行列・法線・時間まわりを自動化してくれている。

この理解があると、 Three.js → Unity の移植でも絶対に迷わなくなる。

7. まとめ

GLSL(Three.js)と HLSL(Unity)の違いは、 本質ではなく “文法” と “包み紙の厚さ” だけ。

この回で整理したすべてをまとめると——


Three.js(GLSL)で得られる感覚

  • すべてを 自分の手で書く
  • modelMatrix * viewMatrix * projectionMatrix をそのまま掛ける
  • varying / attribute を生で扱う
  • 法線行列(normalMatrix)も手動で意識する
  • 精度指定(precision)なども自前で記述する

→ GPU の生の挙動を直接感じられる → 同時に、間違えると即死(行列・精度・属性など)

自由度は高いが、ミスると地獄 これが Three.js / GLSL の世界。


Unity(HLSL/ShaderLab)で得られる感覚

  • ShaderLab の“包み紙”が多くを吸収してくれる
  • UnityObjectToClipPos() が MVP を全部やる
  • 法線は UnityObjectToWorldNormal() が補正
  • _Time などの内部変数が自動更新
  • struct appdata / v2f が整理してくれる
  • コード量は少なく、事故が少ない

→ 必要な部分だけ HLSL に集中できる → “書きたい表現” に持っていくのが速い

Unity は抽象化が強力で、とにかく実装が速い


共通点:GPU の流れは “完全に同じ”

Vertex → Fragment の2ステップは、GLSL も HLSL も 100% 同じ構造。

  • 頂点を受け取る
  • 必要なら動かす(sin 波など)
  • クリップ座標(pos)を返す
  • ピクセルシェーダーへ受け渡す
  • 色(RGBA)を返す

最終的に GPU がやっている仕事はどちらも同じ。


この回の成果:GLSL/HLSL 二刀流の脳が完成

この回まで読めば、次のことができるようになる:

  • Three.js の GLSL を Unity HLSL に移植できる
  • Unity のカスタムシェーダーを GLSL に書き換えられる
  • 行列(MVP)を理解して“どこで何が起きているか”が見える
  • time・UV・normal の扱いの違いも迷わない
  • RawShaderMaterial と ShaderLab の対応が一瞬で分かる

つまり——

Three.js と Unity を両方使いこなす “二刀流の脳” がここで完成する。

この理解が、次回(第5回)の 波・水・炎・発光などの実装(Three.js / Unity 両対応) を完璧にする基礎になる。