Shader(シェーダー)とは、GPU上で実行され、頂点、フラグメント、または計算タスクなどのグラフィックデータを処理するプログラムです。CPU上の通常のコードとは異なり、シェーダーは数百ものコアで並列に動作し、各要素を独立して処理します。Khronos Group(2025年)によると、シェーダーはモバイルゲームからシステムアプリケーションのUIエフェクトまで、最新の3Dアプリケーションの100%で使用されています。開発者はGLSL、HLSL、Metal Shading Language、SPIR-Vなどの専門言語でシェーダーを記述します。
重要ポイント
Shader(シェーダー)は、専門言語で記述されGPU上で実行される小さなプログラムです。各シェーダーは、頂点、フラグメント(ピクセル)、またはワークアイテムという1つのデータ要素を他の要素から独立して処理します。GPUの並列処理により、何千ものシェーダーインスタンスを同時に実行できます。
この用語は1980年代にPixarがRenderManでサーフェスの外観を制御するプログラムを説明するために導入しました。リアルタイムのシェーダーは、プログラマブルパイプラインを備えた最初のグラフィックカードであるNVIDIA GeForce 3(2001年)とともに登場しました。シェーダー以前は、グラフィックスは色、テクスチャ、ブレンドモードといった固定パラメータで設定されていました。
Unity Technologies(2025年)によると、平均的なモバイルゲームは50~200のシェーダーを使用しています。各シェーダーには、異なるプラットフォーム、品質レベル、ライティング設定に対応するバリアントがあります。バリアント数はプロジェクトあたり10,000に達することもあります。
シェーダーのアーキテクチャは、入力データ(属性、uniform変数、テクスチャ)、プログラムコード、出力データ(色、位置)に分けられます。Uniform変数はシェーダー実行前にCPUから設定され、フレームのすべての要素で不変です。属性は要素ごとのデータ(頂点座標、法線、UV座標)です。
SIMT(Single Instruction, Multiple Threads)は、GPU上でのシェーダー実行モデルです。1つの命令が1回ロードされ、異なるデータに対して数百のスレッドで実行されます。例えば、1000ピクセルに対するFragment Shaderは、同じコードを持つ1000のスレッドを実行しますが、各スレッドは独自のUV座標と補間された属性を受け取ります。
SIMTの重要な結果として、シェーダー内の分岐はコストが高くなります。ifブロック内で半分のスレッドが一方の分岐に、残り半分が他方の分岐に進む場合、GPUは両方の分岐をすべてのスレッドに対して順次実行します。パフォーマンスは50%に低下します。シェーダー内の動的分岐を避けるか、CPUでの事前計算によって最小化してください。
最新のGPUは、グラフィックパイプラインまたは計算パイプラインの各ステージに対応する複数のタイプのシェーダーをサポートしています。主要な種類である頂点シェーダー、フラグメントシェーダー、Compute、そして最新のMeshシェーダーを見ていきましょう。
| シェーダーの種類 | パイプラインステージ | 処理内容 | 言語 |
|---|---|---|---|
| Vertex Shader | 頂点 | 各頂点 | GLSL, HLSL |
| Fragment Shader | フラグメント | 各ピクセル | GLSL, HLSL |
| Compute Shader | 計算 | 任意のデータ | GLSL, HLSL |
| Geometry Shader | ジオメトリ | プリミティブ | GLSL, HLSL |
| Tessellation Shader | テッセレーション | パッチ | GLSL, HLSL |
| Mesh Shader | Meshパイプライン | Task + Mesh | HLSL, MSL |
Vertex Shaderはジオメトリの各頂点を処理します:座標変換、ライティング計算、フラグメントシェーダーへのデータ受け渡し。これはグラフィックパイプラインの必須ステージであり、これなしでは頂点は画面に表示されません。Vertex Shaderは各フレームの各頂点に対して呼び出されます。
Fragment Shader(またはPixel Shader)は最もリソースを消費するステージです。テクスチャ、ライティング、マテリアルを使用して各フラグメントの最終的な色を計算します。Fragment Shaderは、ジオメトリで覆われた各ピクセルに対して、適用されたすべてのテクスチャとエフェクトを考慮して実行されます。このシェーダーの最適化が最大のパフォーマンス向上をもたらします。
Compute Shaderは、GPU上での任意の計算のための汎用シェーダーです。グラフィックシェーダーとは異なり、ジオメトリやピクセルに結びつきません。Compute Shaderは、ワークグループを介して任意のデータバッファを操作します。物理演算、パーティクルシミュレーション、ポストエフェクト、ニューラルネットワーク推論に使用されます。
Mesh Shaderは、NVIDIA Turing(2018年)とMetal 3で登場した最新のシェーダータイプです。Mesh Shaderは、頂点シェーダー、ジオメトリシェーダー、テッセレーションシェーダーを単一のメッシュパイプラインに置き換えます。Task Shaderと連携して動作します:Task Shaderがレンダリングするメッシュグループを決定し、Mesh Shaderがその場でジオメトリを生成します。
シェーダーは、GPUのマシンコードにコンパイルされる専門言語で記述されます。言語の選択はプラットフォームとAPIに依存します。主要な言語であるGLSL、HLSL、Metal Shading Language、および中間形式のSPIR-Vを見ていきましょう。
GLSLは、OpenGL、OpenGL ES、WebGL向けのシェーダー言語です。構文はCをベースに、ベクター型(vec2、vec4、mat4)と組み込み関数(texture、normalize、reflect)が追加されています。GLSL ESは、モバイルデバイス向けに精度が制限されたバージョンです(lowp、mediump、highp)。Khronos(2025年)によると、GLSLはクロスプラットフォーム対応により、最も広く普及しているシェーダー言語であり続けています。
#version 300 es
precision mediump float;
in vec2 v_texCoord;
uniform sampler2D u_texture;
out vec4 fragColor;
void main() {
fragColor = texture(u_texture, v_texCoord);
}
シンプルなフラグメントシェーダーは、UV座標からテクスチャの色を読み取ります。precision mediumpは、GPUにfloatの半精度を使用するよう指示し、モバイルデバイスでの実行を25~40%高速化します。
HLSLは、MicrosoftのDirectX向けシェーダー言語です。構文はC++に近く、クラス、構造体、テンプレートをサポートしています。HLSLはWindowsアプリケーションで使用され、Shader Model 6.7+以降はRay Tracing、Mesh Shaders、Sampler Feedbackをサポートしています。モバイル開発ではHLSLは直接使用されませんが、Vulkan向けにSPIR-Vにコンパイルされます。
MSL(Metal Shading Language)は、Appleのシェーダー言語であり、C++14をベースとしています。GLSLやHLSLとは異なり、MSLはアプリケーションと一緒にコンパイルされるため、デバイス上でのJITコンパイルが不要です。MSLはポインタ、テンプレート、C++標準ライブラリをサポートしています。iPhone、iPad、Mac、Apple TVなど、すべてのAppleデバイスで使用されています。
#include <metal_stdlib>
using namespace metal;
struct VertexOut {
float4 position [[position]];
float2 texCoord;
};
fragment float4
myFragment(VertexOut in [[stage_in]],
texture2d<float> tex [[texture(0)]]) {
constexpr sampler s = sampler(filter::linear);
return tex.sample(s, in.texCoord);
}
MSLは[[position]]、[[stage_in]]、[[texture(N)]]属性を使用してリソースをバインドします。Appleのコンパイラは、レジスタ数とキャッシュを考慮して、現在のGPUに最適化されたコードを生成します。
SPIR-Vは、Vulkan向けの標準的な中間バイナリシェーダーフォーマットです。シェーダーはGLSLまたはHLSLで記述され、SPIR-VにコンパイルされてVulkanアプリケーションに読み込まれます。SPIR-Vは特定の言語に結びついておらず、Rust、Python、OpenCL CからSPIR-Vへのコンパイラも存在します。
モバイルプラットフォームでのシェーダーは、デスクトップと比較して制限があります:レジスタ数が少なく、精度が制限され、一部の命令がサポートされていません。Android(Vulkan/OpenGL ES)とiOS(Metal)でのシェーダーの特性を見ていきましょう。
Androidは、OpenGLにGLSL ESを、VulkanにSPIR-Vを使用します。OpenGL ES 3.2はデフォルトでmediumpのシェーダーをサポートしています。Vulkanでは、glslangValidatorを介してGLSLからSPIR-Vへの明示的なコンパイルが必要です。Androidでは、シェーダーは文字列リソースまたはコンパイル済みのSPIR-Vファイルから読み込まれます。
Qualcomm Adreno GPUはドライバーレベルでシェーダーを最適化します。推奨事項:色とUVにはmediumpを、位置にのみhighpを使用し、頂点シェーダーでのテクスチャサンプリングを避け、計算をベクターにグループ化します(4x floatの代わりにvec4)。Qualcomm(2025年)によると、これらの最適化により30~50%の向上が得られます。
iOSは専らMetal Shading Languageを使用します。シェーダーはXcodeを介してアプリケーションと一緒にマシンコードにコンパイルされます。MetalはシェーダーのプロファイリングのためのShader DebuggerとGPU Captureを提供します。Apple GPU(TBDR)には、early fragment test、memoryless render targets、programmable blendingといった固有の最適化があります。
Apple WWDC 2024によると、iOSシェーダーではfloatの代わりにhalfを使用し、レジスタプレッシャーを制限し(最大64レジスタ)、依存テクスチャサンプリングを避けることが重要です。iOSシェーダーはコンパクトである必要があり、理想的なサイズは50~100のALU命令です。
ゲームエンジンのUnityとUnreal Engineは、ビジュアルシェーダーエディタ(Shader Graph、Material Editor)と抽象言語(ShaderLab、USF)を提供しています。開発者は高レベル言語でシェーダーを記述し、エンジンがターゲットプラットフォーム用にコンパイルします。Unityは中間言語としてHLSLを、UnrealはUSF(Unreal Shader Format)を使用します。
シェーダーの最適化は、モバイルデバイス向けグラフィック開発の重要なステップです。適切に記述されていないシェーダーはFPSを60から20に低下させる可能性があります。主要な最適化ルールとテクニックを見ていきましょう。
必要最小限の精度を使用します:色とUVにはlowp、法線とライティングにはmediump、位置と行列にのみhighpを使用します。ARM(2025年)によると、Mali GPUではmediump演算はhighpの2倍高速です。GLSL ESでは、これは精度修飾子を介して設定されます。
テクスチャサンプリングはシェーダー内で最もコストの高い操作です(ALU命令の1~2サイクルに対して10~30サイクル)。サンプリング数を減らすには、テクスチャをアトラスに結合し、テクスチャの代わりにuniform配列にデータを格納し、微分命令を使用して隣接ピクセルの結果をキャッシュします。
動的分岐(uniform変数を使用したif文)はSIMTパイプラインのパフォーマンスを低下させます。分岐を数学関数に置き換えます:mix()、step()、smoothstep()、clamp()。これらの関数は1~2命令で実行されるのに対し、分岐はスレッドの発散により8~16命令かかる可能性があります。
// シェーダー内の動的分岐: 悪い例
if (u_enableLight) {
color *= computeLighting(normal);
}
// 数学的な分岐置き換え: 良い例
color *= mix(1.0, computeLighting(normal),
float(u_enableLight));
mix()は2つの値の間で線形補間を実行します。u_enableLight=0の場合、1.0を返します—色は変わりません。u_enableLight=1の場合、ライティングの結果を返します。分岐なし—すべてのスレッドが同じコードを実行します。
モバイル開発向けの実践的なシェーダー例を見ていきましょう—シンプルなシェーディングからプロシージャルテクスチャ生成まで。各例は特定のテクニックを示しています。
フォンモデルは、拡散反射と鏡面反射の成分を持つ古典的なライティングモデルです。Vertex Shaderが各頂点のライティングを計算し、Fragment Shaderは結果を補間するだけです(Gouraudシェーディング)。コストが低いためモバイルデバイスに適しています。
#version 300 es
layout(location = 0) in vec4 a_position;
layout(location = 1) in vec3 a_normal;
uniform mat4 u_mvp;
uniform mat4 u_modelView;
out vec3 v_color;
void main() {
vec3 normal = normalize(mat3(u_modelView) * a_normal);
vec3 lightDir = normalize(vec3(0.0, 1.0, 1.0));
float diff = max(dot(normal, lightDir), 0.0);
v_color = vec3(0.8, 0.2, 0.2) * (0.3 + diff * 0.7);
gl_Position = u_mvp * a_position;
}
シェーダーは拡散ライティングを計算します:法線と光の方向のドット積。結果は環境光(0.3)と拡散光(0.7)の成分と混合されます。頂点あたり10~15のALU命令—モバイルGPUで60 FPSでの100,000ポリゴンに十分対応できます。
プロシージャルテクスチャは、ファイルから読み込まずにFragment Shaderで生成されます。ビデオメモリを節約し、アニメーションを簡素化します。例—セルサイズを変更可能なチェッカーボード。わずか数個のALU命令—GPUへの負荷は最小限です。
#version 300 es
precision mediump float;
in vec2 v_uv;
uniform float u_cells;
out vec4 fragColor;
void main() {
vec2 cell = floor(v_uv * u_cells);
float isWhite = mod(cell.x + cell.y, 2.0);
fragColor = mix(vec4(0.1, 0.1, 0.1, 1.0),
vec4(0.9, 0.9, 0.9, 1.0),
isWhite);
}
8つのALU命令—モバイルデバイスに理想的なシェーダーです。u_cellsは軸あたりのセル数を指定します。floorとmodは安価な演算で、すべてのモバイルGPUで1サイクルで実行されます。
よくある質問
シェーダーはSIMTモードでGPU上で実行されます—1つの命令が数百のスレッドで実行されます。C++の通常のプログラムはMIMDモードでCPU上で実行されます。シェーダーはファイルシステム、入出力、動的メモリにアクセスできません。バッファとテクスチャを介してデータを受け取り、位置または色を返します。
GLSL—OpenGL ESとWebGL向け、クロスプラットフォーム。Metal Shading Language—iOS向け。HLSL—UnityとUnreal Engineの基礎。GLSLから始めてください—基礎の理解はどのシェーダー言語にも応用できます。
主な原因:高精度(mediumpの代わりにhighp)、過剰なテクスチャサンプリング、動的分岐、レジスタ制限の超過。シェーダー分析にはRenderDocまたはXcode GPU Frame Debuggerを使用してください。
Compute Shaderは、GPU上での任意の計算のためのシェーダーです:パーティクル物理、画像処理、布地シミュレーション。Fragment Shaderとは異なり、ピクセルに結びつかず、任意のバッファに書き込むことができます。
はい、ビジュアルエディタを介して可能です:UnityのShader Graph、Unreal EngineのMaterial Editor、またはMetal Shader Converter。これらはノードベースのスキーマからシェーダーコードを生成します。高度な最適化にはシェーダー言語の理解が必要です。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。