exa

ユビキタスソリューション
GPUプログラミング【画像処理から汎用演算まで】

エクサでは、プラネタリウムや科学館、博物館の大型投影施設、PCやモバイル機器向けのシステム構築の実績を通じて 高速なグラフィックス・プログラムの開発に取り組んでいます。ここでは、その要素技術の一つ、GPU プログラミングについて紹介します。

1990年代に「グラフィックスアクセラレータ」と呼ばれていた半導体チップが発展し、 グラフィックス描画処理専用のプロセッサ「GPU(Graphics Processing Unit)」として登場してから数年が経ちました。 その間、nVidia社やATI社といったビデオチップメーカーから次々とGPU搭載ビデオボードが市場に投入され、高性能化や低価格化に伴い、 今では研究用途からビジネス分野、ゲーム業界まで幅広く利用されるようになりました。

近年では、一部の描画機能をプログラムとして組み込む「プログラマブルシェーダ」が登場し、 特殊効果や描画処理をプログラマが独自に実装する「GPUプログラミング」が一般的になりつつあります。GPUプログラミングでは、 今までCPU上で実行していた複雑な処理をGPU上で実行することによってリアルタイム性能を向上させたり、 独自のアルゴリズムを描画処理に組み込むことが可能となります。

そこで本稿では、プログラマブルシェーダを利用したGPUプログラミングについて具体的に説明します。

pagetop

プログラマブルシェーダとは?

一般的に描画処理は、座標変換や陰影処理など複数の工程からなります。これをレンダリングパイプラインと呼びます。 アーキテクチャを図1に示します。プログラマブルシェーダとは、このレンダリングパイプラインの一部の機能をプログラム可能にする技術です。 具体的には、座標変換などの頂点処理を行う「バーテックスシェーダ(Vertex Shader)」と、 実際に表示するピクセルの色や明暗などを決定する「ピクセルシェーダ(Pixel Shader)」の2種類あります。

図1 レンダリングパイプラインのアーキテクチャ
図1 レンダリングパイプラインのアーキテクチャ

シェーダプログラミングでは、「シェーダ言語」を用いてプログラムを記述します。代表的なシェーダ言語を表1に示します。

表1 代表的なシェーダ言語
用語 概要
GLSL OpenGL1.5で拡張仕様、OpenGL2.0で標準仕様になった
HLSL Microsoft社が開発したC言語に似たシェーダ言語。DirectXとの親和性が高い
Cg nVidia社が開発したC言語に似たシェーダ言語
GLSL OpenGL1.5で拡張仕様、OpenGL2.0で標準仕様になった

では、以降より各シェーダの概要を説明します。なお、サンプルの動作環境は以下の通りです。

OS MicrosoftXP SP2
VGA OpenGL 2.0対応のビデオボード
開発環境 VisualC++ 6.0
言語 C++、OpenGL、GLSL
バーテックスシェーダ(Vertex Shader)

モデリングデータの各頂点に対して実行するプログラムです。オリジナルの頂点データを変更することなく座標変換や 頂点カラーの設定などをリアルタイムに処理可能になるため、水面の波紋や人間の表情などの動きをリアルに表現することが可能となります。

ここでは、バーテックスシェーダを利用して3Dモデルの変形を行うサンプルを紹介します。3DモデルにはTeapotを使用します。 変形前のモデルを図2に示します。

図2 変形前のTeapot(ソリッド表示)図2 変形前のTeapot(ワイヤーフレーム表示)
図2 変形前のTeapot(左:ソリッド表示、右:ワイヤーフレーム表示)

では、バーテックスシェーダを使用して3Dモデルを歪ませます。なお、頂点移動と同時に光源による陰影処理(フラットシェーディング)を頂点カラーに反映しています。


リスト1 バーテックスシェーダプログラム

uniform float offset;

void main()
{
	// 頂点座標
	vec4 pos = gl_Vertex;

	// 頂点移動
	// プログラムからオフセット(時々刻々と変化する値)を取得
	pos.x = pos.x + 0.25*sin(3*(pos.z + offset));

	// 出力する頂点座標
	gl_Position = vec4(gl_ModelViewProjectionMatrix * pos);

	// 頂点カラーの設定
	// 環境光
	float ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient;
	float globalAmbient = gl_LightModel.ambient * gl_FrontMaterial.ambient;

	// 拡散反射(Lambertの余弦法則)
	vec3 normal = normalize(gl_NormalMatrix * gl_Normal);
	vec3 lightDir = normalize(vec3(gl_LightSource[0].position));
	float NdotL = max(dot(normal, lightDir), 0.0);
	vec4 diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse;

	// 出力する頂点カラー
	gl_FrontColor = NdotL * diffuse + globalAmbient + ambient;
}
		

出力結果を図3に示します。

図3 変形後のTeapot(ソリッド表示)図3 変形後のTeapot(ワイヤーフレーム表示)
図3 変形後のTeapot(左:ソリッド表示、右:ワイヤーフレーム表示)

ピクセルシェーダ(Pixel Shader)

ラスタライズ処理によって得られた各ピクセルに対して実行するプログラムです。ピクセル単位でレンダリング処理が可能になるため、 従来テクスチャで表現していた人間の肌や毛髪をより実物に近く表現したり、ハイダイナミックレンジレンダリングの ような高精度な陰影処理によって自然な表現が可能となります。

ここでは、ピクセルシェーダを利用して画像処理を行うサンプルを紹介します。図4のオリジナル画像をテクスチャとして読み込み、 ピクセル単位で色空間を変換してセピア調画像とグレースケール画像を生成します。従来のソフトウェア処理に比べて、 よりシンプルなプログラムで実現することが出来ます。

図4 オリジナル画像
図4 オリジナル画像


セピア調変換

RGB色空間をYCbCr色空間に変換し、セピア調画像を生成します。色あせた写真のような表現になります。手順を以下に示します。

  1. RGB を YCbCr に変換し、輝度(Y)を取得する
  2. セピア調になるように色差(CbCr)を調整する
  3. 調整後の YCbCr を RGB に変換する
  4. 求めた RGB を出力する画素値として設定する

リスト2 セピア調変換用ピクセルシェーダプログラム

uniform sampler2D tex;

void main()
{
	// テクスチャから画素値取得
	vec4 tcolor = texture2D(tex, gl_TexCoord[0].st);

	// 輝度算出
	float y, u, v;
	y = tcolor.r * 0.299 + tcolor.g * 0.587 + tcolor.b * 0.114;

	// セピア色調変換
	u = -0.091;
	v = 0.056;
	tcolor.r = y + v * 1.402;
	tcolor.g = y + u * -0.344 + v * -0.714;
	tcolor.b = y + u * 1.772;

	// 出力するピクセル色
	gl_FragColor = tcolor;
}
			

出力結果を図5に示します。

図5 セピア調変換後の画像
図5 セピア調変換後の画像


グレースケール変換

白から黒までの明暗だけで表現するグレースケール画像を生成します。手順を以下に示します。

  1. RGBをYCbCrに変換し、輝度(Y)を取得する
  2. RGBの値をYで置き換える(R=G=B=Y)
  3. 求めたRGBを出力する画素値として設定する

リスト3 グレースケール変換用ピクセルシェーダプログラム

uniform sampler2D tex;

void main()
{
	// テクスチャから画素値取得
	vec4 tcolor = texture2D(tex, gl_TexCoord[0].st);

	// 輝度算出
	float y, u, v;
	y = tcolor.r * 0.299 + tcolor.g * 0.587 + tcolor.b * 0.114;

	// グレースケール変換
	tcolor.r = y;
	tcolor.g = y;
	tcolor.b = y;

	// 出力するピクセル色
	gl_FragColor = tcolor;
}
			

出力結果を図6に示します。

図6 グレースケール変換後の画像
図6 グレースケール変換後の画像

pagetop

GPUの可能性

近年では、GPUをCPUのように利用する「GPGPU(General Purpose GPU)」という動きがあります。すでに紹介されている適用事例を以下に示します。

数値計算 線形台数、高速フーリエ変換、並列処理
信号処理 音響効果
データ解析 ソート、バイナリサーチ

この背景には、GPUがプログラマブルシェーダのアーキテクチャとなってプログラム可能となったことや、 GPUがCPU以上の演算性能を持つようになったことが挙げられます。最近では、GPGPU向けのメタプログラミング言語も登場しています。

pagetop

まとめ

本稿では、GPUプログラミングの概要と各シェーダプログラムの動作について説明しました。GPUプログラミングにより、 グラフィックスの表現力やリアルタイム性が向上しただけではなく、GPGPUのような新しい技術も登場しました。 今後は、別の技術と組み合わせた利用方法なども増えてくると予想されます。

次回は、GPUとオフスクリーンレンダリングの組み合わせについて説明します。エクサは、GPUを利用した案件を多数手がけています。 我々にお手伝いできることがあれば、何なりとお申し付け下さい。

pagetop

参考資料

GPU関連
GPGPU関連
書籍

pagetop