[Shader 入門 #01] シェーダーとは何か?GPU 描画の仕組みをゼロから理解する

はじめに

Three.jsの頃から、何となく Shader を触ってましたが、あたらめて学ぼうと入門講座をスタートしました。

全6回で、少しずつやってきます。

Unityにもシェーダーがあるようなので、出来るだけThree.jsとUnityの両方で役立つ講座にする予定です。

1. なぜシェーダーを学ぶのか?

ゲームでも Web でも、画面に映っているものはすべて「シェーダー」が描いている。 Three.js でも Unity でも例外はない。

  • 光の反射
  • 水のゆらぎ
  • 影の落ち方
  • 炎やオーロラのようなエフェクト
  • 発光、透明、波、ノイズ

こういった“見た目の魔法”は、すべてシェーダーで作られる。

Three.js で GLSL を触ってから Unity に来ると、 「文法は違うのに仕組みは同じだ」 と気づく瞬間が必ずある。

理由はシンプルで、GPU が世界を描く仕組み自体は共通だから。

この連載では、Three.js(GLSL) と Unity(HLSL) の両方で、 同じテーマ・同じ考え方でシェーダーを学んでいく。

まず最初の本記事では、

“そもそもシェーダーって何なのか?”

この根本を、図解レベルでわかりやすく押さえる。

ここが理解できると、 どのゲームエンジンやライブラリに行っても迷わなくなる。

2. GPU がやっていること:三角形の加工工程

シェーダーを理解するうえで、まず知っておきたいのは次の事実です。

世界のあらゆる3Dモデルは、最終的に“三角形の集合体”として描かれている。

キャラクター、地形、木、建物、水面—— どれだけ複雑に見えても、レンダリングの瞬間には必ず「三角形のメッシュ」に分解されます。

では、この三角形たちを誰が画面に描いているのか?

それが GPU(グラフィックス専用プロセッサ) です。


GPU の本質的な仕事

GPU の役割は、とてもシンプルに言うと、

「大量の三角形を、爆速並列処理で塗りつぶす」

これだけです。

ただし、この処理量がとんでもない。

  • 1フレームで数万〜数百万の三角形
  • その1つ1つをピクセル単位で計算
  • しかも毎秒60回(または90〜120回)

こんな処理、CPU では到底間に合いません。

だから GPU は“並列計算に特化した別脳みそ”になっている。


GPU が三角形を描くときの処理の流れ

GPU は次のような工程で画面を描きます。

  1. 頂点の位置を決める(Vertex Shader)
  2. 三角形に切り分ける
  3. 表面の色を決める(Fragment Shader)
  4. ピクセルに塗りつぶす

ここで出てきた Vertex Shader と Fragment Shader が、まさに今回学ぶ「シェーダー」。

つまりシェーダーとは、

GPU の三角形加工工程を“自分好みに書き換える”ための小さなプログラム

というわけです。


例:水面を揺らす場合(頂点シェーダーの仕事)

  • 三角形の頂点を sin 波で上下させる
  • 波の大きさを時間で変える
  • 風の強さで揺らぎ方を変える

これは全部 Vertex Shader の担当。


例:炎や発光を作る場合(フラグメントシェーダーの仕事)

  • 炎の色のゆらぎを noise で作る
  • 発光の強さを中心からの距離で変える
  • 深い水ほど青くする

これは Fragment Shader の仕事。


GPU の仕組みがわかると、シェーダーの“怖さ”が消える

「シェーダーって難しそう…」 と思いがちですが、正体はただのこれです。

三角形の形をいじるプログラム(頂点) 三角形の色をいじるプログラム(フラグメント)

たったこれだけ。

中身は Three.js(GLSL)でも Unity(HLSL)でも同じです。

3. Vertex / Fragment シェーダーとは何か?

— シェーダーの“2つの役割”を理解する

シェーダーは種類が多いように見えますが、 まず覚えるべきは たった2種類だけ です。

  • Vertex Shader(頂点シェーダー)
  • Fragment Shader(ピクセルシェーダー)

この2つが理解できれば、シェーダー全体の7割は理解できます。

Vertex Shader(頂点シェーダー)— 形を変える担当

頂点シェーダーは、名前の通り “頂点(Vertex)をどう動かすか” を決めます。

たとえば:

  • 波でメッシュを上下に揺らす
  • キャラクターを伸ばす/縮める
  • ノイズで地形をデコボコにする
  • モデルを回転・拡大・移動させる

三角形の“形そのもの”を変えたいときは、全部 Vertex Shader の仕事。

【例:波の頂点揺らぎ(GLSLイメージ)】

pos.y += sin(pos.x * 10.0 + time) * 0.1;

【例:Unity(HLSL)でも原理は同じ】

pos.y += sin(pos.x * 10 + _Time.y) * 0.1;

文法は違っても、 「頂点の位置を時間で動かす」 という原理は同じ。

Three.js で理解したことが Unity で活きる理由はここにある。


Fragment Shader(フラグメントシェーダー)— 色を塗る担当

fragment = “破片” つまり、画面を構成する 1ピクセル1ピクセルの色を決める ためのシェーダー。

担当する仕事は:

  • 色の計算
  • ライティング(光の当たり方)
  • 発光(emission)
  • 水の透明度・深度
  • ノイズを使った炎
  • グラデーション

見た目の美しさに関わる処理は、ほとんど Fragment Shader。

【例:光の色を変えるイメージ(GLSL)】

vec3 color = vec3(0.0, 0.2, 1.0);
gl_FragColor = vec4(color, 1.0);

【Unityでも本質は同じ(HLSL)】

return float4(color, 1);

Vertex と Fragment は“セットで1枚の絵”になる

  1. Vertex が 形を決める
  2. Fragment が 色を決める
  3. GPU が 三角形を画面に塗りつぶす

この3ステップで、初めてあなたの作った世界が画面に現れる。

形 × 色 = シェーダーの本質。


まず Vertex と Fragment だけ理解すればシェーダーは怖くない

  • “頂点を動かすのが Vertex”
  • “色を決めるのが Fragment”

この2つさえハッキリ理解できれば、 Three.js でも Unity でもシェーダーは闇ではなくなる。

次回の実装編(Three.js)で、 実際にこの2つがどう動くかを体験してもらう。

4. CPU と GPU の役割:なぜシェーダーが必要か?

シェーダーを理解する上で、 「CPU と GPU がどう役割分担しているか」 これは避けて通れない重要テーマです。

Three.js でも Unity でも、 この分業を理解すると「なぜシェーダーを書く必要があるのか」が一気にクリアになります。


CPU は“ゲームの頭脳”、GPU は“描画の筋肉”

ゲームやWebアプリ全体を管理しているのは CPU。 一方、画面のピクセルを爆速で処理しているのが GPU。

役割をざっくり分けるとこうなる:


◆ CPU の仕事(頭脳)

  • ゲームロジック
  • スクリプトの Update()
  • 衝突判定
  • AI
  • プレイヤーの入力処理
  • データ構造の管理

「ものを考える」「判断する」仕事は全部 CPU。

Three.js なら JavaScript Unity なら C#

ここが CPU の世界。


◆ GPU の仕事(筋肉)

  • 三角形を並列処理で変形
  • ピクセルを塗りつぶす
  • 光と影の計算
  • フラグメント単位で色の処理
  • 法線による反射・屈折の計算
  • 水・炎・発光などの視覚効果の生成

「とにかく膨大な量を高速で処理する」が GPU の仕事。

1フレームで数千万ピクセル 毎秒60〜120フレーム つまり一秒で数十億回の並列計算をする。

CPU には絶対に不可能な量。


じゃあ、CPU と GPU はどう対話しているのか?

CPU から GPUには、 “パラメータ” と “メッシュ情報” を渡すだけ。

GPU はそれを受け取って黙々と計算する。

ここで登場するのがシェーダー特有のデータ:


Uniform(ユニフォーム)とは?

CPU → GPU に渡す 一定の値

例:

  • 時間(time)
  • 光の強さ
  • マテリアルの色
  • 風の強さ
  • テクスチャ

Three.js でも Unity でも必ず出てくる。


attribute / input(頂点データ)とは?

メッシュが持つ固定の値。

例:

  • 頂点位置(position)
  • UV座標
  • 法線(normal)

どのエンジンでも同じ。


varying / 出力(補間値)とは?

頂点 → フラグメントに渡される値。

例:

  • ライティング用のベクトル
  • 波の高さ
  • 色の補間情報

Three.js では varying Unity では interpolator と呼ばれるが役割は同じ。


CPU は命令するだけ。実際に描くのは GPU。

例として「水面」を作る場合:

CPU(JavaScript / C#)

  • time を渡す
  • 風の強さを渡す
  • メッシュをセットする
  • Draw() だけ呼ぶ

GPU(GLSL / HLSL)

  • time を使って波を計算
  • 法線で光の反射を計算
  • 深さで色を変える
  • すべてのピクセルを塗る

波そのものを計算してるのは GPU。 CPU はただの“司令塔”。


なぜシェーダーが必要なのか?

答えはシンプル:

GPU を使って好きな見た目を実現するには、 GPU で動く “シェーダーコード” が必要だから。

Unity のマテリアルや Three.js の MeshBasic… これらの便利機能は「既製品のシェーダー」の集合体。

だから、 自分だけの表現(水、炎、発光、変形)を作りたいなら 既製品ではなく“自前のシェーダー”が必要になる。

5. シェーダーは何が難しくて、何が簡単なのか?

多くの人がシェーダーに苦手意識を持つ理由は、 「何が難しくて、何が簡単なのか」 を最初に知らないまま飛び込むからです。

ここで“本当の難所”と“実は簡単な部分”を切り分けておきます。 この認識だけで、シェーダー学習のストレスが大幅に下がります。


難しいところ①:文法が C 言語っぽくて独特

GLSL(Three.js)も HLSL(Unity)も、 JavaScript や C# と書き方がかなり違います。

  • vec3float4 など独自の型
  • 行列計算(matrix multiply)
  • varying / uniform など専門用語
  • 括弧が多くて読みにくい

「読んだ瞬間に拒否感が出る」 これは誰でも最初に経験する壁です。


難しいところ②:デバッグがしづらい

  • コンソールログがない
  • break point できない
  • エラーメッセージが不親切
  • 画面が真っ黒になったら何が起きたか分からない

“失敗した時に状況が分かりづらい” これが難しさを倍増させています。


難しいところ③:Unity は抽象レイヤーが厚い

Unity のシェーダーは “ShaderLab” という独自の記述形式で、 最初は構造がとても複雑に見えます。

  • Properties
  • SubShader
  • Pass
  • Tags
  • URP / HDRP の違い

この構造に慣れるまでが地味にキツい。

逆に言うと、 Three.js → Unity の順に学ぶと理解がスムーズ (あなたがまさに経験した流れ)


でも安心してほしい。ここから下は“全部簡単”です。


簡単なところ①:やってることは単純

シェーダーの仕事は本質的にただこれだけ:

  • 頂点を動かす(Vertex)
  • 色を塗る(Fragment)

シンプルな2工程の繰り返しでしかない。


簡単なところ②:数学は中学レベルで十分#

必要な数学はこの程度:

  • sin / cos(波)
  • 0〜1 の補間(Lerp)
  • 長さや正規化(normalize)
  • ベクトルの向きと角度
  • ノイズ(Perlin/Simplex)は概念だけ理解すればOK

Three.js をやってきた人なら すでに全部経験済み。


簡単なところ③:シェーダーは“パターン化”されている

水面、炎、発光、透明、ノイズ…… こういったエフェクトは 全部テンプレ化 されています。

  • 水 → sin 波 + 法線
  • 炎 → noise + time
  • 発光 → 色の加算
  • 地形 → noise + height map

つまり、一度理解すれば 別の表現も同じコードの応用で作れる。


簡単なところ④:Three.js と Unity はやることが同じ

文法は違っても、

  • time を渡す
  • 頂点を動かす
  • UV を使う
  • 法線で光を計算する
  • color を返す

この“仕組み”は全く同じ。

だからこの連載は Three.js(GLSL)で学んで → Unity(HLSL)で理解が深まる という構成にしている。


結論:シェーダーは「最初の入口」だけが難しい。

その後は、 仕組みを理解すれば“作りたいものを作るだけ”の世界になる。

最初の壁を越えるための助走として、 第1回は「概念だけ」を丁寧に整理している。

次回から実際のコードに触れて、 「動く楽しさ」を体験してもらう。

6. まとめ:第2回から実装編へ

今回の第1回では、 シェーダーを学ぶための“土台となる概念”だけを整理しました。

  • 世界は三角形でできている
  • GPU が三角形を加工して画面を描く
  • Vertex(形)と Fragment(色)がシェーダーの基本
  • CPU と GPU には役割分担がある
  • Three.js と Unity は仕組みが同じ
  • 難しいのは入り口だけで、理解してしまえば楽しい

これらが腑に落ちると、 シェーダーは“ブラックボックス”ではなくなります。

次回(第2回)からいよいよ実装に入る

第2回では Three.js の RawShaderMaterial を使い、

  • 頂点を sin 波で動かす
  • 色を時間で変える
  • GLSL の最小サンプルを動かす

という“最初の動くシェーダー”を作ります。

初心者が一番つまづきやすい 「どこに何を書けば動くのか?」 を明確にしつつ、実際の画面変化を体験します。


さらに、第3回では Unity(HLSL) 版として同じものを実装

  • Unity の ShaderLab の構造
  • HLSL の基本文法
  • Three.js の GLSL とコード比較
  • “両対応でシェーダーを書ける” 感覚をつかむ

この流れで Three.js → Unity の二刀流 を自然に身につけられます。


この連載の目的は“シェーダーの本質を恐れずに使えるようになること”

水・炎・発光・ノイズ・波—— どれも特別な才能が必要なわけではありません。

必要なのは「どんな仕組みで動いているか」の理解だけ。

そして第1回で、それを理解する準備は整いました。


それでは次回、実際に動くシェーダーを書いていきましょう。