High Level Shading Language(ハイレベル シェーディング ランゲージ、略称: HLSL)[1]はマイクロソフトによって開発された、Direct3D (DirectX) で使われるプログラマブルシェーダーのためのプロプライエタリなシェーディング言語である。High Level Shader Language という呼び方もされている[2][3][4]。ただしMSDNの日本語版ドキュメントでは、英語版の原文が「High Level Shader Language」となっている箇所だけでなく「High Level Shading Language」となっている箇所でも、上位レベル シェーダー言語(じょういレベルシェーダーげんご)という訳語を使用している[5][6]。
HLSLはOpenGLで使われるシェーディング言語であるGLSLと(機能的には)類似の物である。また、NVIDIAと協力して開発されたことから、言語文法がCg(C for Graphics)言語に非常によく似ている。
Direct3D 7まではグラフィックスチップ(GPU)に実装された固定パイプラインおよびハードウェア機能を駆使して3Dグラフィックスシーンを構築していたが、グラフィックス表現の柔軟性を向上させるべく、Direct3D 8ではプログラマブルシェーダーが搭載された[7]。これによって、グラフィックスアプリケーション開発者がグラフィックス描画アルゴリズムをソフトウェアによってカスタマイズすることが可能となった。しかし、Direct3D 8で使用できるシェーダー言語はアセンブリ言語(低水準言語)であったため、開発効率やプログラムコードの再利用性に限界があった。それを解消するべく、C/C++風の宣言文や制御文などの記法を可能とした高水準言語HLSLが開発されることとなった。HLSLはDirect3D 9で初めて導入され、その後もDirect3Dとともに機能拡張が続けられている。
GDC 2016ではDirectX 12の今後の発展、そして新たな次期DirectXとシェーダーモデル6の開発に関して言及があり、HLSL言語仕様にも大きな機能追加が予定されていることが発表された[8]。シェーダーモデル6.0ではWaveと呼ばれるSIMTスレッドグループの概念と各グループ内でのデータ交換(シャッフル)のための組み込み命令が導入され、NVIDIA GPUのWarpおよびAMD GPUのWavefrontを標準化した[9]。
HLSLによってプログラム可能なグラフィックスパイプラインステージは、対応するDirect3Dのバージョンおよびシェーダーモデル(後述)によって異なる。
頂点シェーダーはアプリケーションによって提供(入力)される頂点それぞれについて実行され、主にオブジェクト空間から視空間への頂点座標変換やテクスチャ座標の生成、また頂点の接線や従法線や法線ベクトルのような光線の係数の計算などの処理を担当する。頂点シェーダーを通して頂点のグループ (三角形であれば通常は3個) が入力された時、出力座標はその領域内で画面上のピクセルを決めるために補間される。この処理はラスタライゼーションとして知られている。これらのピクセルそれぞれがピクセルシェーダーを通ることで、結果としてスクリーン上の各点の色が計算される。
また、Direct3D 10/11/12対応ハードウェアにおいてDirect3D 10/11/12インターフェイスを使うアプリケーションは、頂点シェーダーステージの後にジオメトリシェーダーを指定することもできる。ジオメトリシェーダーはラスタライズの前にプリミティブの増減や種類の変更を行なうことができる。
なお、HLSLで使用可能なパーリンノイズ生成関数であるnoise()は、テクスチャシェーダーと呼ばれる特殊なシェーダーステージでのみ利用が可能となっている[10]。
Windows 10 October 2018 Update (version 1809) では、DirectX Raytracing (DXR) と呼ばれるリアルタイムレイトレーシング技術が実装された[11]。DXRにハードウェアレベルで対応するGPU上では、シェーダーモデル6.3のレイトレーシングパイプラインを記述するためのHLSLシェーダーが利用可能となる[12]。
HLSLの基本的な文法はC/C++に準ずるが、グラフィックスプログラムを記述するのに適した専用のベクトル・行列型や関数を備えている。数学関数の中にはC/C++標準ライブラリと同様のものも含まれる。また、Direct3D 10ではAPIの大幅な設計変更が行なわれたこともあり、Direct3D 9用のHLSLと比較して、Direct3D 10以降用のHLSLではオブジェクト指向に基づいた言語仕様の再設計がなされ、多数の仕様変更が加えられている[13]。
Direct3D 10以降ではHLSLにてクラスを定義し、C++のようにデータ(メンバー変数)と振る舞い(メンバー関数)を関連付けることが可能だが、アクセス指定子によるカプセル化や継承および仮想関数といった機能は備えていない。
Direct3D 11ではシェーダーの組み合わせ爆発問題を解消するべく、HLSLにてインターフェイスの定義と実装によるポリモーフィズムを疑似的に実現する「動的シェーダーリンク(Dynamic Shader Linkage)」と呼ばれる機能が追加された[14]。
以下にDirect3D 10/11向けHLSLを用いた、単純なHalf Lambert照明モデルおよびPhong照明モデル(Blinn-Phong)の頂点シェーダーおよびピクセルシェーダーのプログラムを示す。なお分岐によって照明モデルを切り替えているが、これはいわゆるウーバーシェーダー(uber-shader)なので、実行速度効率などは考慮していないことに注意されたい。
// Shader Constants.
matrix TrWorldViewProj;
matrix TrWorld;
float4 LightPosition;
float3 EyePosition;
float4 DiffuseColor;
float4 SpecularColor;
float SpecularPower;
bool IsPhongModel;
struct BasicVSOutput
{
float4 Pos : SV_POSITION;
float3 WPos : TEXCOORD1;
float3 WNormal : NORMAL0;
};
typedef BasicVSOutput BasicPSInput;
// Vertex Shader Program.
BasicVSOutput BasicVS(float3 pos : POSITION0, float3 normal : NORMAL0)
{
BasicVSOutput output = (BasicVSOutput)0;
output.Pos = mul(float4(pos, 1), TrWorldViewProj);
output.WPos = mul(float4(pos, 1), TrWorld).xyz;
output.WNormal = mul(normal, (float3x3)TrWorld);
return output;
}
float4 CalcLambert(float3 light, float3 wnormal)
{
// Half Lambert.
float lambert = dot(light, wnormal);
lambert = lambert * 0.5f + 0.5f;
lambert *= lambert;
return lambert * DiffuseColor;
}
float4 BasicLambert(BasicPSInput input)
{
const float3 light = normalize(LightPosition.xyz - input.WPos);
const float3 normal = normalize(input.WNormal);
return CalcLambert(light, normal);
}
float4 BasicPhong(BasicPSInput input)
{
// Phong lighting with specular.
const float3 eye = normalize(EyePosition - input.WPos);
const float3 light = normalize(LightPosition.xyz - input.WPos);
const float3 halfway = normalize(light + eye);
const float3 normal = normalize(input.WNormal);
const float specular = pow(max(dot(normal, halfway), 0.0), SpecularPower);
return CalcLambert(light, normal) + specular * SpecularColor;
}
// Pixel Shader Program.
float4 BasicPS(BasicPSInput input) : SV_TARGET0
{
if (IsPhongModel)
{
return BasicPhong(input);
}
else
{
return BasicLambert(input);
}
}
例のように、ピクセルシェーダーによってピクセル単位の正規化法線ベクトルを求めることにより、Direct3D 7以前の固定機能シェーダーでは実現が難しかったPer-Pixelライティングが容易に実装可能となっている。 もちろん、使用するシェーダーモデルおよび対応するハードウェアによっては、より複雑で長大なアルゴリズムを実装することもできる。リアルタイムグラフィックスゆえにハードウェア性能に応じたトレードオフにはなるが、単純な局所照明(ローカルイルミネーション)だけでなく、より厳密な物理ベースのレンダリング方程式に基づいた、大域照明(グローバルイルミネーション)モデルをHLSLによるプログラマブルシェーダーで実装することで、より現実に近いリアルタイム3Dコンピューターグラフィックスを実現することも可能となる。さらに、Direct3D 11 (DirectCompute) ではコンピュートシェーダーを使って、GPUにグラフィックス用途以外の汎用計算を行なわせるGPGPUプログラムをHLSLで記述することも可能となる。
なお、HLSLソースファイルには通例.hlsl拡張子が付けられ、ヘッダーファイルには.hlsli拡張子が付けられる[15]。
HLSLプログラムは主にホストとなるC++アプリケーションプログラムコードからDirect3D APIを使って入力と出力を管理する必要があるので、単体で動作させることはできない。なお、単体のコンパイラはマイクロソフトから無償提供されているDirectX SDK(あるいはバージョン8.0以降のWindows SDK)に付属する。プロプライエタリなHLSLコンパイラfxc.exeによって出力されるのは、グラフィックスハードウェアのベンダーに依存しない共通バイトコード(中間表現)であるため、一度コンパイルしておけば異なるハードウェアであっても(コンパイル時に想定されていた機能を満たす限り)動作させることができる[16]。HLSLプログラムをサポートするのはDirect3D 9以降をサポートするシステムに限られるため、かつてはWindows OSおよびXbox 360以降のXboxシリーズが主な動作環境であったが、Vulkan向けのシェーダープログラムを事前コンパイルして中間表現SPIR-Vを生成するコンパイラglslangValidator[17]がGLSLとHLSLの両方に対応したことなどもあり、クロスプラットフォームでのHLSLの活用が進んでいる。
アプリケーションの実行時にHLSLソースコードをコンパイルしてバイトコードを生成する機能を組み込むためのD3DCompilerランタイムも提供されている[18]。
他に、ゲームエンジンのUnityでは、シェーダープログラムの記述にHLSLが使用されている(かつてはCgが使用されていたため、一部に名残がある)[19][20]。なお、Windows上でDirectComputeベース (DirectX 11ベース) のコンピュートシェーダーを使用することができるが、Cgはコンピュートシェーダーに対応していないため、コンピュートシェーダーの記述には当初からHLSLが使用されていた[21]。UnityはCg/HLSLからGLSLへのトランスレーションが可能なため、OpenGL 4.3やOpenGL ES 3.1のコンピュートシェーダーを用いる場合でもGLSLではなくHLSLを使用することが推奨されていた[22]。
LLVM/ClangベースのHLSLコンパイラdxc.exeもGitHub上で開発が進められている[23]。こちらはDirectX Intermediate Language (DXIL) と呼ばれる別の中間言語コード[24]を生成するが、これまでのfxc.exeが生成するバイトコード (DXBC) とは互換性がない。
HLSL自体は、シェーダー関数および各シェーダーステージのエントリーポイント(いわゆるメイン関数)を記述するために使われるが、この複数のシェーダーステージをまとめて管理・適用する「エフェクト」と呼ばれる仕組みも存在する。つまり、例えば2つの頂点シェーダーエントリーポイントVS1(), VS2()と2つのピクセルシェーダーエントリーポイントPS1(), PS2()を単一のHLSLソースプログラムファイル(通例.fx拡張子が付けられ、エフェクトファイルと呼ばれる)に記述し、さらにVS1+PS1, VS2+PS2, VS1+PS2, VS2+PS1といったシェーダーステージの組み合わせ(パス)のほか、各種レンダリングステートの設定をエフェクトファイル中に記述して関連付けることができる。エフェクトを扱うAPIはDirect3D 10のコアライブラリもしくはDirect3D 9/11のエクステンションライブラリ(D3DX)に用意されており、レンダリングパイプラインの管理をC++コードから分離することができる。
Windowsデスクトップアプリケーションフレームワークの1つであるWPFでは、グラフィックスのレンダリングにDirect3Dが使用されているが、GUIウィジェットにブラー(ぼかし)やドロップシャドウといったエフェクト(フィルター)を適用することが可能となっている。さらにWPFではユーザープログラマーがHLSLで作成したピクセルシェーダーを使用してカスタムエフェクトを適用することもできる[25]。
WPF 3.5まではシェーダーモデル2.0のピクセルシェーダーのみがサポートされていたが、WPF 4ではシェーダーモデル3.0のピクセルシェーダーも使用できるようになった[26] [27]。
DirectXファミリーの1つ、2次元コンピュータグラフィックスAPIであるDirect2Dでは、バージョン1.1にてエフェクト機能が実装されたが、HLSLによるカスタムエフェクトを作成・利用することも可能となっている[28]。
Direct3Dプログラマブルシェーダーを実行するには、Direct3D 8以降に対応したハードウェアが必要となる。ただし、Direct3D 9までの場合、頂点シェーダーだけはソフトウェアすなわちCPUでエミュレートすることもできる(D3DCREATE_SOFTWARE_VERTEXPROCESSING
)ため、固定機能ピクセルシェーダーと組み合わせることによりDirect3D 7以前の古いハードウェアでプログラマブルシェーダーを実行することも可能である。また、Direct3D 10.1以降では比較的高速なソフトウェアレンダリングエンジンであるWARPデバイスも実装されているため、GPUが対応していなくてもCPUにDirect3Dレンダリングを実行させることもできる。
Direct3D対応ハードウェア(グラフィックスカード)の世代によって、GPU上にてハードウェアレベルで実行可能なシェーダープログラムの仕様(制約、機能など)が異なる。この仕様はシェーダーモデルと呼ばれ、新しい世代のシェーダーモデルをサポートするハードウェアは基本的に古い世代のシェーダーモデルもサポートする[29]が、ベンダーごとに拡張された2.0a/2.0bなどの例外も存在する。 なおHLSLがDirect3Dに搭載されたのはバージョン9以降だが、シェーダーモデル2.0以降でないとHLSLを使えないというようなことはなく、HLSLを使用してシェーダーモデル1.xレベルのプログラムを記述することも可能である。また、Direct3D 10では、アセンブリ言語によるシェーダープログラム開発が廃止され、シェーダーの記述にはHLSLのみが使用できるようになった[30]。
シェーダーモデル3.0には頂点テクスチャフェッチ(Vertex Texture Fetch, VTF)と呼ばれる機能が存在するが、DirectX 9.0c世代で対応したのはNVIDIAのハードウェアのみで、ATIのハードウェアではサポートされなかった。逆に、浮動小数点バッファにおけるアンチエイリアス機能は、NVIDIAハードウェアではサポートされず、ATIハードウェアのみでの対応となっていた[31] [32]。他にも、(DirectX 11で標準化される前の)テッセレーション機能[33]がATIハードウェア上のみでサポートされる[34] [35] [36]など、シェーダーモデル3.0までは(たとえDirectX 9.0c対応を謳っていても)機能面において各社の足並みがそろわない状態にあり、これらの機能を利用するアプリケーション開発者は使用したい機能が実際にハードウェアでサポートされているかどうかをあらかじめDirect3DのCaps (Capabilities) 取得APIを使って一つ一つ調べなければならなかった。このようにベンダーごとに各機能の対応レベルがバラバラとなっていた悲惨な状況は、次のバージョンのDirect3D 10以降で要求仕様が厳格化されたことで、ある程度解消されることになる[37]。
なお、Direct3D 10.1 APIでは4.xプロファイルのシェーダープログラムに加えてダウンレベルの2.0プロファイルが使用可能であり、Direct3D 11/12 APIでは5.xおよび4.xプロファイルに加えてダウンレベルの2.0プロファイルが使用可能だが、いずれも3.0プロファイルに関しては使用できない[38] [39] [40] [41]。
以下の表はハードウェアが対応しているDirectXバージョンと、そのハードウェアがサポートする各シェーダーステージの最上位バージョン(プロファイル)間の関係を示している。 後述するように、実行可能なシェーダープログラムの最大命令数やレジスタ数、リソーススロット数などは新しいバージョンのほうが大きくなり、より柔軟で長大なプログラムを記述することができるようになる。
DirectX Version | Pixel Shader | Vertex Shader | Geometry Shader | Hull Shader | Domain Shader | Compute Shader |
---|---|---|---|---|---|---|
8.0 | 1.0, 1.1 | 1.0 | - | - | - | - |
8.1 | 1.2, 1.3, 1.4 | 1.1 | - | - | - | - |
9.0 | 2.0 | 2.0 | - | - | - | - |
9.0a | 2.0a | 2.0a | - | - | - | - |
9.0b | 2.0b | 2.0 | - | - | - | - |
9.0c | 3.0 | 3.0 | - | - | - | - |
10.0 | 4.0 | 4.0 | 4.0 | - | - | 4.0 |
10.1 | 4.1 | 4.1 | 4.1 | - | - | 4.1 |
11.0-11.2 | 5.0 | 5.0 | 5.0 | 5.0 | 5.0 | 5.0 |
11.3, 12 | 5.1 | 5.1 | 5.1 | 5.1 | 5.1 | 5.1 |
12 | 6.0 | 6.0 | 6.0 | 6.0 | 6.0 | 6.0 |
なおコンピュートシェーダー(DirectCompute)はDirectX 11にて導入されたステージであり、DirectX 10 APIを経由して利用することはできないが、ドライバーが対応していればDirectX 10.x世代のハードウェア上でもDirectX 11 APIを経由してダウンレベル(機能制限付き)のコンピュートシェーダーを実行することが可能となる。
シェーダーモデル5.1[42]はDirectX 12 APIをドライバーレベルでサポートするすべてのハードウェア(機能レベル11_0以上が必須)で使用可能だが、Root Signatureに関する機能はDirectX 11.3では使用できず、DirectX 12専用の機能となる[43]。また、ROV (Rasterizer Order View) に関するオブジェクトは、ROV対応ハードウェアでしか使用できない[44]。
シェーダーモデル6.0[9]の組み込み関数は機能レベル12_0の要件として追加されている。
PS 1.0-1.3 | PS 1.4 | PS 2.0 | PS 2.0a | PS 2.0b | PS 3.0[45] | PS 4.0[46], 4.1, 5.0 | |
---|---|---|---|---|---|---|---|
依存テクスチャ制限 | 4 | 6 | 8 | 無制限 | 4 | 無制限 | 無制限 |
テクスチャ命令制限 | 4 | 6*2 | 32 | 無制限 | 無制限 | 無制限 | 無制限 |
Position register | No | No | No | No | No | Yes | Yes |
命令スロット数 | 8 + 4 | 8 + 4 | 32 + 64 | 512 | 512 | ≥ 512 | ≥ 65536 |
実行命令数 | 8+4 | 6*2+8*2 | 32 + 64 | 512 | 512 | 65536 | 無制限 |
テクスチャの間接参照数 | 4 | 4 | 4 | 無制限 | 4 | 無制限 | 無制限 |
Interpolated registers | 2 + 8 | 2 + 8 | 2 + 8 | 2 + 8 | 2 + 8 | 10 | 32 |
命令予測 | No | No | No | Yes | No | Yes | No |
Index input registers | No | No | No | No | No | Yes | Yes |
一時レジスタ(Temp registers) | 2 | 6 | 12から32 | 22 | 32 | 32 | 4096 |
定数レジスタ(Constant registers) | 8 | 8 | 32 | 32 | 32 | 224 | 16x4096 |
Arbitrary swizzling | No | No | No | Yes | No | Yes | Yes |
Gradient instructions | No | No | No | Yes | No | Yes | Yes |
Loop count register | No | No | No | No | No | Yes | Yes |
Face register (2-sided lighting) | No | No | No | No | No | Yes | Yes |
動的フロー制御 | No | No | No | No | No | 24 | Yes |
ビット演算 | No | No | No | No | No | No | Yes |
整数演算 | No | No | No | No | No | No | Yes |
実行命令数において"32 + 64"というのは"32のテクスチャ命令と64の算術命令"を意味する。
VS 1.1 | VS 2.0 | VS 2.0a | VS 3.0[45] | VS 4.0[46], 4.1, 5.0 | |
---|---|---|---|---|---|
命令スロット数 | 128 | 256 | 256 | ≥ 512 | 4096 |
最大命令実行数 | 不明 | 65536 | 65536 | 65536 | 65536 |
命令予測 | No | No | Yes | Yes | Yes |
一時レジスタ(Temp registers) | 12 | 12 | 13 | 32 | 4096 |
定数レジスタ(Constant registers) | ≥ 96 | ≥ 256 | ≥ 256 | ≥ 256 | 16x4096 |
静的フロー制御 | 不明 | Yes | Yes | Yes | Yes |
動的フロー制御 | No | No | Yes | Yes | Yes |
動的フロー制御の深度 | No | No | 24 | 24 | Yes |
Vertex Texture Fetch | No | No | No | Yes | Yes |
テクスチャサンプラーの数 | N/A | N/A | N/A | 4 | 128 |
Geometry instancing support | No | No | No | Yes | Yes |
ビット演算 | No | No | No | No | Yes |
整数演算 | No | No | No | No | Yes |
なおVS 2.0bは存在しない[47]。
以下の表はDirect3Dプログラマブルシェーダーに対応している各社グラフィックスカードの概略であり、対応するピクセルシェーダーバージョンとDirectXのバージョンを示している。グラフィックスチップは前述のとおり、一般的には上位互換性がある。例えばPS 3.0対応のチップはPS 2.0やPS 1.1にも対応している。
なお開発に専用APIやOpenGL/GLSLを使用するゲーム専用機やモバイル機器は、たとえプログラマブルシェーダー対応であってもDirect3D/HLSLに対応しているとは限らないが、グラフィックスチップの世代を示すときは便宜上シェーダーモデルを使うことがある。
なおOpenGL対応レベルに関しては、ナンバリングがDirectXのバージョンやシェーダーモデルと正確に対応するわけではなく、細かい機能における差異が多数存在するが、世代としてはおおよそ下記の通りとなる。