今回は、
Phongシェーダーの改良版である
Blinn-Phongシェーダーを実装します。
Phongシェーダーは反射ベクトルを求めるのですが、
環境によっては、反射ベクトルを計算するのが大変らしい(?)ので、
ライトベクトルと視線ベクトルの中間の角度にあるベクトルで計算するのが
BlinnPhongシェーダーです。
以下 実装
//グローバル変数宣言
float4x4 world; // ワールド行列
float4x4 view; // ビュー行列
float4x4 projection; // 透視変換行列
float3 light = float3( 10.0f, 10.0f, 10.0f ); // ライトの位置
//入力頂点構造体
struct VS_INPUT
{
float4 position : POSITION; //頂点座標
float3 normal : NORMAL; //法線ベクトル
float4 color : COLOR00; //色
};
//出力頂点構造体
struct VS_OUTPUT
{
float4 position : POSITION; //頂点座標
float3 normal : TEXCOORD1; //法線ベクトル
float4 color : COLOR0; //色
float3 light : TEXCOORD0; //ライトベクトル
float3 view : TEXCOORD2; //ビューベクトル
float3 halfAngle : TEXCOORD3; //二等分ベクトル
};
//頂点シェーダー
VS_OUTPUT vs_main(
VS_INPUT input,
uniform float4x4 world,
uniform float4x4 view,
uniform float4x4 projection,
uniform float3 light
)
{
VS_OUTPUT output;
//モデルビュー行列
float4x4 WV = mul( world, view );
//法線ベクトルの座標変換
output.normal = mul(input.normal,(float3x3)WV);
//モデルビュー透視変換行列
float4x4 WVP = mul( WV, projection );
//頂点座標の座標変換
output.position = mul(input.position,WVP);
//色の設定
output.color = input.color;
//視点座標系の頂点座標を求める
float3 PosEye = mul( input.position, WV );
//視点座標系のライト座標を求める
float3 LightPosEye = mul( light, (float3x3)view );
//ライト方向のベクトル
float3 L = normalize( LightPosEye - PosEye );
output.light =L;
//視点方向のベクトル
float3 V = normalize(-PosEye);
output.view = V;
// 追加
//二等分ベクトル
float3 H = normalize( L+V );
output.halfAngle = H;
//ビューベクトルの設定
output.view = V;
return output;
}
//ライトカラー
float4 Ambient = float4( 1.0f, 1.0f, 1.0f, 1.0f );
float4 Diffuse = float4( 1.0f, 1.0f, 1.0f, 1.0f );
float4 Specular = float4( 1.0f, 1.0f, 1.0f, 1.0f );
//マテリアルカラー
float4 Ka = float4( 0.2f, 0.2f, 0.2f, 1.0f );
float4 Kd = float4( 0.6f, 0.6f, 0.6f, 1.0f );
float4 Ks = float4( 1.0f, 1.0f, 1.0f, 1.0f );
float power = 90.0f;
//入力ピクセル構造体
struct PS_INPUT
{
float4 color : COLOR0; //色
float3 normal : TEXCOORD1; //法線
float3 light : TEXCOORD0; //ライトベクトル
float3 view : TEXCOORD2; //ビューベクトル
float3 halfAngle : TEXCOORD3; //二等分ベクトル
};
//ピクセルシェーダー
float4 ps_main(
PS_INPUT input,
uniform float4 Ambient,
uniform float4 Diffuse,
uniform float4 Specular,
uniform float4 Ka,
uniform float4 Kd,
uniform float4 Ks,
uniform float power
) : COLOR0
{
//ライトベクトルの正規化
float3 L = normalize( input.light );
//法線の正規化
float3 N = normalize( input.normal );
//ビューベクトルの正規化
float3 V = normalize( input.view );
//二等分べkとるの正規化
float3 H = normalize( input.halfAngle );
//ディフーズ
float diffuse = max( dot( L, N ), 0 );
//スペキュラ
float specular = pow( max( dot( N, H ), 0 ), power );
//環境光
float4 totalAmbient = Ambient * Ka * input.color;
//拡散反射光
float4 totalDiffuse = Kd * Diffuse * diffuse * input.color;
//鏡面反射光
float4 totalSpecular = Ks * Specular * specular;
return totalAmbient + totalDiffuse + totalSpecular;
}
//テクニックの指定
technique blinnphong
{
//パスの指定
pass Pass_0
{
DepthTestEnable = true; //デプステストを有効
BlendEnable = true; //ブレンドを有効
BlendFunc = float2( SrcAlpha,OneMinusSrcAlpha ); //ブレンド関数を設定
VertexProgram = compile arbvp1 vs_main( world, view, projection, light );
FragmentProgram = compile arbfp1 ps_main( Ambient, Diffuse, Specular, Ka, Kd, Ks, power );
}
}
実行結果ですが、
こちらがPhongシェーダー
こちらがBlinn-Phongシェーダー
PhongとBlinnPhongの違いは、実装の違いで、
反射ベクトルか、ハーフベクトルを鏡面反射の計算に使うかの違いなので、
自分の好きな方を使ってください。
今回は、ここまで。
最終更新:2009年01月31日 21:52