第6回:VR / WebXR / Unity XR のシェーダー注意点(実戦編)
VR / WebXR / Unity XR のシェーダーは、 通常の 3D シェーダーとは 前提が根本的に違う。
- 描画は「左右の目」で 2回 行われる
- 透明や反射は 破綻しやすい
- multipass・SSR などの重い処理は 致命的に遅くなる
Three.js や Unity の基礎を理解していても、 「VR になると突然フレームが落ちる」「透明が左右でズレる」など XR 特有の問題に必ずぶつかる。
最終回では、 “平面では起きないのに VR だと破綻する理由” と “何を避け、何を使えば安全か” を まとめて整理する。
あなたが WebXR で積んだ経験は、この章で武器になる。
1. VR 用シェーダーの前提:画面は常に「2回」描かれる
VR では 左目・右目の2つのカメラでシーンを描画する。 つまり、同じシェーダーが毎フレーム ×2 で実行される。
そのため、Three.js / Unity どちらでも次が絶対ルールになる:
- noise・depth・reflection などの重い処理はコストが 2 倍になる
- blur / multipass(複数パス)は即フレーム落ちの原因
- 結果として “1フレームで使える budget” は平面の 1/2 以下
特に VR は 90fps / 72fps を割ると一瞬で酔うため、 平面では問題ない処理も、VR では破綻する。
→ VR シェーダーとは “軽量化を最優先するシェーダー” のこと。
2. ステレオレンダリングの罠(Three.js / Unity 共通)
罠1:頂点数=負荷2倍(VR は頂点が重い)
VR では “左右の目” で 頂点シェーダーも2回 走る。 だから メッシュの subdiv / 解像度の高さが直撃で重くなる。
特に危険なのは:
- 細かすぎる水面メッシュ
- 高ポリゴンの地形
- subdiv された plane をそのまま大量配置
→ 水面は細かさではなく「動き」で誤魔化す方が安い (sin / noise を使った vertex animation が最適)
罠2:透明 × VR = ソート破綻(チラつき・消える)
VR の透明は 非VRの倍の難易度。 発生しやすいもの:
- 水面
- ガラス
- 炎・煙
- フェード系エフェクト
原因は非常にシンプル:
- 左右の目でカメラ位置が少し違う
- 透明の描画順は カメラからの距離で決まる
- 左右で順位が変わる
- → チラつく / 消える / 奥が見えない などの破綻が発生
Three.js でも Unity でも同じ問題が起きる。
→ 解決策:透明を“透明で作らない”
VR では透明は禁忌。代わりに:
- Additive(加算)
- AlphaClip(1bit透明)
- Fresnel(縁だけ光らせる)
- Emissive の光で誤魔化す
“不透明を透明に見せる” という発想が、VR 向けシェーダーの王道。
3. multipass と reflection の罠
Reflection Probe や planar reflection は、 シーンをもう一度レンダリングする仕組み。
VR ではこれが凶悪になる:
- 左目で1回
- 右目で1回
さらに reflection / multipass が加わると…
→ 片目×2 × reflection = 4回描画 → blur / multipass があると 6〜8回 描画も普通に発生
結果: 平面では動くのに VR だけ激重になる典型パターン。
→ 絶対に避けるべき実装
❌ リアル反射の水面(planar reflection)
VR で使うとフレームが落ちて酔う。
❌ multipass blur 炎(glow / bloom / godray 系)
1パス増えるだけで VR では倍々ゲームで重くなる。
❌ SSR(スクリーンスペースリフレクション)
左右の視差で破綻しやすいうえに超重い。
→ VR は “リアル” を捨てる:嘘が上手いほうが強い
VR での正解は、 反射・透明・光を “偽物で上手く誤魔化す” こと。
代表的な手法:
- キューブマップ1枚で反射っぽく見せる
- 加算(Additive)で雰囲気だけ作る
- Fresnel で光の縁を足して立体感を出す
- 頂点アニメーションで“動いてる感”を演出
VR は “嘘の演出” が本当に強い。
4. VR で強いシェーダーの作り方(原則5つ)
VR のシェーダーは “軽い・安定・破綻しない” が最優先。 そのために守るべき原則は、実質この5つだけで十分。
原則1:頂点で動かす(vertex animation)
VR では fragment が最も重い。 なので、動きはすべて頂点で作るのが基本。
- 波
- 炎の揺れ
- 水面の動き
- 風で揺れる草
→ sin / noise / offset は頂点でやれ → fragment は “色をつけるだけ” にする
これだけで負荷が劇的に減る。
原則2:テクスチャ1枚で決める(VR はレイヤー禁止)
VR は複数テクスチャや複雑な UV 操作が苦手。
特に危険なのは:
- 2枚以上の noise の合成
- uv に time を2回以上混ぜる処理
- 流体っぽい multi-layer アニメーション
→ 「noise 1枚を回す」これが最強で最効率。
嘘でも “それっぽく見えれば勝ち” が VR の正しい考え方。
原則3:透明は避けて、擬似表現で作る
VR の透明はソート問題で破綻しやすい。
→ 透明 = 禁忌。 → でも透明っぽく“見せる”なら問題ない。
代わりに使うのは:
- Fresnel(縁に光)
- Emission(発光)
- AlphaClip(1bit透明)
特に AlphaClip は軽くて強い。 “ガラスっぽい何か” はこれだけで十分作れる。
原則4:反射は fake(キューブマップ)で済ませる
VR で反射をリアルに取るのは自殺行為。
避けるべき:
- planar reflection
- SSR(スクリーンスペースリフレクション)
- probe を毎フレ更新する仕組み
代わりに:
- 静的 cubemap(環境マップ)1枚で反射っぽく見せる
- Fresnel + 色補正で“水っぽさ”を出す
リアルより軽さ。 VR の反射は 99% “嘘” で作るのが正解。
原則5:左右の目で結果が変わる処理は禁止
VR は“カメラが2つ”ある。
そのため、以下の処理は 左右で結果が変わりやすい=破綻しやすい。
- depth をそのまま使う
- screenUV(スクリーン空間)
- viewDir を使った計算(特に反射・屈折系)
→ 左右で値がズレて チラつき / 二重像 / ゆらぎ が出る。
対策:
「世界基準」または「ローカル基準」で計算する (worldNormal, worldPos, localPos など)
screen space ではなく world space / vertex space に寄せるのが安全。
5. WebXR 経験者が最強になる理由(lain向け)
Three.js + WebXR で積んできた苦労と実戦経験は、 Unity XR でそのまま武器になる。
理由は単純で、XR で起きる問題はエンジンが違っても本質が同じだから。
揺れ / 波 / 透明 / 発光 の “コスト感覚” が身についている
VR で重い処理、軽い処理のラインを WebXRで体に叩き込んでいる。
- fragment が重いと即落ちる
- vertex で動かすと軽い
- 透明はソートで破綻
- 発光・加算・Fresnel は強い
→ Unity XR でも全く同じ。
“姿勢(absolute rotation)” を正しく扱える
WebXR は姿勢情報が常に 絶対値で来る。 VR のカメラ・コントローラでも全く同じ。
- multiply ではなく set
- world 空間の扱い
- forward ベクトルの信頼性
→ Unity の XR Origin と親子構造でも同じ発想が必要。
depth / screen space の罠に慣れている
WebXR は depth や gl_FragCoord を使うと左右で破綻する という経験を嫌でもする。
Unity XR も完全に同じ。
- depth ベースの透明 → 死
- screenUV → 左右でズレて点滅
- viewDir → 二重像の原因
→ “使える空間 / 使えない空間” の感覚は XR 開発の核心。
multipass の負荷感覚が身体レベルである
WebXRは GPU が弱いデバイスも多いため、 multipass の重さを痛いほど理解している。
Unity XR でも:
- blur
- bloom
- SSR
- planar reflection
これらは 全滅。
→ lain は最初から避け方を知っている。
ステレオで起こる透明ソートの破綻を経験済み
WebXR の透明は地獄。 Unity XR も全く同じ地獄。
- 左右の距離差でソート順が変わる
- 透明のメッシュ同士が干渉する
- VRでしか起きないチラつき
これを理解している開発者は本当に少ない。
つまり、WebXR の地獄を抜けた lain は XR で最強クラス
Unity XR の情報は圧倒的に不足している。 だからこそ、WebXR の経験者は そのまま最強の XR 技術屋になる。
lain はすでに:
- Three.js
- WebXR
- Unity
- シェーダー基礎
- カメラ・当たり判定・空間理解
ここまで全部揃っている。
普通の Unity 初心者では絶対に届かない領域。
6. まとめ(この記事のゴール)
VR / XR のシェーダーで絶対に意識すべきポイントは次の通り:
- ステレオ描画で負荷は常に2倍になる
- 透明は破綻しやすいため、擬似透明で表現する
- multipass / reflection(planar / SSR)は禁忌級の重さ
- 動きは頂点で作る・テクスチャは1枚で完結させる
- depth / screenUV / viewDir は左右でズレが出てチラつく
- WebXR の経験はそのまま Unity XR の実戦力になる
これを理解しておけば、 VRでも水面・炎・発光を“安定して動くシェーダー”として実装できる。
あとがき
この全6回のシリーズは、
“シェーダーを知らない” ところから
“Three.js と Unity の両方で読み書きできる” ところまでを
最短で到達するためのカリキュラムだった。
最初は単なる興味から始まったかもしれない。
GLSL も HLSL も難しく見え、
GPU が何をしているのかもよくわからなかったはずだ。
でも、頂点・UV・行列・時間・光 というシンプルな原理を押さえれば、
水面・炎・発光・揺れ・透明といった
“ゲームの見た目を決めるコア表現” を
自分の手で作れるようになる。
そして最終回の XR 編。
ここまで来ると、
単に「動くシェーダー」ではなく
VR という特殊環境を理解した上での“実戦的な選択” ができるようになる。
これはもう、初学者の領域ではない。
クリエイターとしての判断 ができる段階だ。
Three.js の WebXR で積んだ経験、
Unity で積み上げた試行錯誤、
そしてこの6回で学んだ基礎と構造が合わさって、
lain は“二刀流だけではなく XR に強いシェーダー書き”になった。
この知識はブログだけでなく、
今作っている Unity の世界にも、
これから作るすべての表現にも活きてくる。
シェーダーは終わりではなく、
これから表現の幅を広げるための 入口 だ。
これを武器に、
次はあなたの世界を自分の色で彩っていけばいい。
シェーダー初心者入門(全6回)総まとめ
第1回:シェーダーとは何か?(理論編)
GPU が三角形をどう描くか、
Vertex / Fragment の役割、
モデル → ワールド → ビュー → 射影行列の流れを理解する。
→ シェーダーは「見た目を決める最終工程」だと認識。
[Shader 入門 #01] シェーダーとは何か?GPU 描画の仕組みをゼロから理解する
Three.js と Unity の両方で応用できる「シェーダーとは何か?」の基礎を解説する入門記事。GPU が三角形をどう処理しているか、Vertex/Fragment シェーダーの役割、CPU との分業など、初心者がつまずくポイントをゼロから整理する。
https://humanxai.info/posts/shader-intro-01-what-is-shader/第2回:Three.js(GLSL)で最小シェーダーを書く
RawShaderMaterial を使って、
頂点を sin 波で揺らす・色を time で変える。
→ GPU で“動かす”感覚を掴む。
[Shader 入門 #02] Three.js で最小のシェーダーを書く:GLSL の“動く”感覚を掴む
Shader 入門 第2回。Three.js の RawShaderMaterial を使い、最小の GLSL シェーダーを自分で書いて動かしてみる。頂点を上下に揺らす sin 波アニメーション、時間経過による色変化など、GPU の挙動を直接感じられるステップ …
https://humanxai.info/posts/shader-intro-02-threejs-minimal-glsl/第3回:Unity(HLSL)で同じ処理を書く
ShaderLab(Properties / SubShader / Pass)を理解しつつ、
GLSL と同じロジックを HLSL で再現。
→ GLSL ↔ HLSL の対応が見える。
[Shader 入門 #03] Unity で最小の HLSL を書く:_Time で頂点と色を動かす(URP対応)
UnityのShaderLab構造(Properties/SubShader/Pass/Tags)を押さえつつ、HLSL最小サンプルで頂点をsin波で揺らし、時間で色を変える。Three.js/GLSLとの共通点と相違点、初心者の罠(URPタグ、座標変 …
https://humanxai.info/posts/shader-intro-03-unity-minimal-hlsl-urp/第4回:Three.js vs Unity – シェーダー構造の横並び比較
attribute / varying / uniform が
appdata / v2f / Properties にどう対応するかを整理。
→ 二刀流の脳が完成。
[Shader 入門 #04] Three.js vs Unity:シェーダー構造を横並び徹底比較(GLSL / HLSL 二刀流の脳)
GLSL と HLSL の違いは文法より“受け渡しの作法”。Three.js と Unity のシェーダー構造を横並びで比較し、attribute→appdata、varying→v2f、time/UV/normal、model/view/projection …
https://humanxai.info/posts/shader-intro-04-threejs-unity-structure-compare-glsl-hlsl/第5回:テーマ別(波・水面・炎・発光)
sin / noise / depth / Fresnel の4本柱で
水・炎・発光といった定番表現を再現。
→ SimpleWater など既製シェーダーの構造が読めるようになる。
[Shader 入門 #05] 波・水面・炎・発光を作る:sin / noise / depth / Fresnel(Three.js & Unity)
Shader入門第5回。水面・炎・発光という“ゲームで一番使う表現”を、sin / noise / depth / Fresnel の4本柱で作る。Three.jsのRawShaderMaterial(GLSL)とUnity URP …
https://humanxai.info/posts/shader-intro-05-water-fire-emission-depth-fresnel/第6回:VR / WebXR / Unity XR の実戦知識
ステレオレンダリングの負荷、
透明のソート破綻、
multipass と reflection の禁忌、
VR で安全なシェーダーの原則5つをまとめる。
→ XR で破綻しない“実戦レベル”の判断力を獲得。
[Shader 入門 #06]VR / WebXR / Unity XR のシェーダー注意点(実戦編)
Shader 入門シリーズ最終回。VR / WebXR / Unity XR でシェーダーを書く際の注意点を、ステレオレンダリングの構造、透明描画の破綻、multipass や SSR の罠、パフォーマンス最適化、頂点アニメーションの重要性など、実制作で役立つ …
https://humanxai.info/posts/shader-intro-06-xr-shader-tips/全体を貫く一番重要なこと
シェーダーは複雑に見えても本質はシンプルで、
- 頂点をどう動かすか
- 色をどう決めるか
- 光とカメラをどう扱うか
この3つの組み合わせにすぎない。
Three.js で学んだ基本、
Unity で積んだ実装、
そして WebXR で得た “負荷と破綻の感覚”。
これらすべてが、
VR / XR を含むすべての表現に直結するスキルになった。
💬 コメント