MV1SetUseOrigShader を使用すると SetUseVertexShader や SetUsePixelShader で設定できるオリジナルの
プログラマブルシェーダーを使用して3Dモデルを描画することができます。
ここではオリジナルの3Dモデル描画シェーダーを書くための足がかりとなるサンプルを幾つか載せています。
頂点シェーダーの入力形式はどうやって決まるのかやピクセルシェーダーのテクスチャは何番目のサンプラーに
設定されるのかなどの情報については MV1SetUseOrigShader の解説を参照してください。
頂点シェーダーでの射影行列やビュー行列などのデフォルトの定数については LoadVertexShader の解説を、
ピクセルシェーダーのデフォルトの定数については LoadPixelShader の解説を参照してください。
ライティング無し描画
・剛体メッシュのライティング無し描画
・1頂点へ影響を与えるフレームの数が1~4個のスキニングメッシュのライティング無し描画
・剛体メッシュのライティング無しフォグあり描画
・剛体メッシュのライティング無しフォグあり描画(シェーダーモデル3.0版)
固定機能パイプライン互換のディレクショナルライトあり描画
・剛体メッシュのディレクショナルライトあり描画
・1頂点へ影響を与えるフレームの数が1~4個のスキニングメッシュのディレクショナルライトあり描画
固定機能パイプライン互換のポイントライトやスポットライトや複数の組み合わせなどの描画
・剛体メッシュのポイントライトあり描画
・剛体メッシュのスポットライトあり描画
・剛体メッシュのディレクショナルライトとポイントライトあり描画
・剛体メッシュのスポットライトとポイントライトあり描画
ピクセルシェーダーでライティングを行うフォンシェーディング描画
・剛体メッシュのディレクショナルライトありフォンシェーディング描画
・剛体メッシュのポイントライトありフォンシェーディング描画
・剛体メッシュのポイントライトありフォンシェーディング描画(ポイントライト6つ版)
・剛体メッシュのスポットライトありフォンシェーディング描画
トゥーンレンダリング描画
・MMD互換のトゥーンレンダリング スキニングメッシュでディレクショナルライト一つ
・MMD互換のトゥーンレンダリング スキニングメッシュでディレクショナルライトとスポットライトとポイントライト一つづつ
・MMD互換のトゥーンレンダリング スキニングメッシュでディレクショナルライト一つのフォンシェーディング
・MMD互換のトゥーンレンダリング スキニングメッシュでディレクショナルライトとスポットライトとポイントライト一つづつのフォンシェーディング
・MMD互換のトゥーンレンダリング スキニングメッシュでディレクショナルライト一つとスフィアマップのフォンシェーディング
法線マップ付きモデルの描画
・法線マップ付き剛体メッシュのディレクショナルライトあり描画
・法線マップ付き剛体メッシュのディレクショナルライトとスポットライトとポイントライト一つづつ描画
ライティング無し描画
剛体メッシュのライティング無し描画
<実行に必要なファイル一式>
人型モデルのように一つの頂点が複数のフレーム( ボーン )の影響を受けない背景のようなモデルを
ライティング無しでただテクスチャの色がそのまま表示されるようなシェーダーを使用するサンプルプログラムです。
ライティングも無くマテリアルの設定も無視しているので陰影が無くテクスチャの色がそのまま表示されています。
C++のプログラム
#include "DxLib.h"
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
int ModelHandle ;
int PixelShaderHandle ;
int VertexShaderHandle ;
float RotateAngle ;
// ウインドウモードで起動
ChangeWindowMode( TRUE ) ;
// Direct3D9Ex を使用する
SetUseDirect3DVersion( DX_DIRECT3D_9EX ) ;
// DXライブラリの初期化
if( DxLib_Init() < 0 )
{
// エラーが発生したら直ちに終了
return -1 ;
}
// プログラマブルシェーダーモデル2.0が使用できない場合はエラーを表示して終了
if( GetValidShaderVersion() < 200 )
{
// エラー表示
DrawString( 0, 0, "プログラマブルシェーダー2.0が使用できない環境のようです", GetColor( 255,255,255 ) ) ;
// キー入力待ち
WaitKey() ;
// DXライブラリの後始末
DxLib_End() ;
// ソフト終了
return 0 ;
}
// 頂点シェーダーを読み込む
VertexShaderHandle = LoadVertexShader( "NormalMesh_NoLightVS.vso" ) ;
// ピクセルシェーダーを読み込む
PixelShaderHandle = LoadPixelShader( "NormalMesh_NoLightPS.pso" ) ;
// 剛体メッシュモデルを読み込む
ModelHandle = MV1LoadModel( "NormalBox.mqo" ) ;
// 描画先を裏画面にする
SetDrawScreen( DX_SCREEN_BACK ) ;
// モデルを回転される値を初期化
RotateAngle = 0.0f ;
// モデルの描画にオリジナルシェーダーを使用する設定をONにする
MV1SetUseOrigShader( TRUE ) ;
// 使用する頂点シェーダーをセット
SetUseVertexShader( VertexShaderHandle ) ;
// 使用するピクセルシェーダーをセット
SetUsePixelShader( PixelShaderHandle ) ;
// モデルを画面の中心にセット
MV1SetPosition( ModelHandle, VGet( 320.0f, 240.0f, 0.0f ) ) ;
// ESCキーが押されるまでループ
while( ProcessMessage() == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0 )
{
// 画面を初期化
ClearDrawScreen() ;
// モデルを回転値を加算
RotateAngle += 0.02f ;
// モデルの回転値をモデルにセット
MV1SetRotationXYZ( ModelHandle, VGet( 0.0f, RotateAngle, 0.0f ) ) ;
// モデルを描画
MV1DrawModel( ModelHandle ) ;
// 裏画面の内容を表画面に反映させる
ScreenFlip() ;
}
// 読み込んだ頂点シェーダーの削除
DeleteShader( VertexShaderHandle ) ;
// 読み込んだピクセルシェーダーの削除
DeleteShader( PixelShaderHandle ) ;
// 読み込んだモデルの削除
MV1DeleteModel( ModelHandle ) ;
// DXライブラリの後始末
DxLib_End() ;
// ソフトの終了
return 0 ;
}
頂点シェーダーのプログラム
// 頂点シェーダーの入力
struct VS_INPUT
{
float4 Position : POSITION ; // 座標( ローカル空間 )
float3 Normal : NORMAL0 ; // 法線( ローカル空間 )
float4 Diffuse : COLOR0 ; // ディフューズカラー
float4 Specular : COLOR1 ; // スペキュラカラー
float4 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
} ;
// 頂点シェーダーの出力
struct VS_OUTPUT
{
float4 Position : POSITION ;
float2 TexCoords0 : TEXCOORD0 ;
} ;
// C++ 側で設定する定数の定義
float4 cfProjectionMatrix[ 4 ] : register( c2 ) ; // ビュー → 射影行列
float4 cfViewMatrix[ 3 ] : register( c6 ) ; // ワールド → ビュー行列
float4 cfTextureMatrix[ 3 ][ 2 ] : register( c88 ) ; // テクスチャ座標操作用行列
float4 cfLocalWorldMatrix[ 3 ] : register( c94 ) ; // ローカル → ワールド行列
// main関数
VS_OUTPUT main( VS_INPUT VSInput )
{
VS_OUTPUT VSOutput ;
float4 lWorldPosition ;
float4 lViewPosition ;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ローカル座標をワールド座標に変換
lWorldPosition.x = dot( VSInput.Position, cfLocalWorldMatrix[ 0 ] ) ;
lWorldPosition.y = dot( VSInput.Position, cfLocalWorldMatrix[ 1 ] ) ;
lWorldPosition.z = dot( VSInput.Position, cfLocalWorldMatrix[ 2 ] ) ;
lWorldPosition.w = 1.0f ;
// ワールド座標をビュー座標に変換
lViewPosition.x = dot( lWorldPosition, cfViewMatrix[ 0 ] ) ;
lViewPosition.y = dot( lWorldPosition, cfViewMatrix[ 1 ] ) ;
lViewPosition.z = dot( lWorldPosition, cfViewMatrix[ 2 ] ) ;
lViewPosition.w = 1.0f ;
// ビュー座標を射影座標に変換
VSOutput.Position.x = dot( lViewPosition, cfProjectionMatrix[ 0 ] ) ;
VSOutput.Position.y = dot( lViewPosition, cfProjectionMatrix[ 1 ] ) ;
VSOutput.Position.z = dot( lViewPosition, cfProjectionMatrix[ 2 ] ) ;
VSOutput.Position.w = dot( lViewPosition, cfProjectionMatrix[ 3 ] ) ;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// テクスチャ座標変換行列による変換を行った結果のテクスチャ座標をセット
VSOutput.TexCoords0.x = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 0 ] ) ;
VSOutput.TexCoords0.y = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 1 ] ) ;
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータを返す
return VSOutput ;
}
ピクセルシェーダーのプログラム
// ピクセルシェーダーの入力
struct PS_INPUT
{
float2 TexCoords0 : TEXCOORD0 ;
} ;
// ピクセルシェーダーの出力
struct PS_OUTPUT
{
float4 Color0 : COLOR0 ;
} ;
// C++ 側で設定するテクスチャの定義
sampler DiffuseMapTexture : register( s0 ) ; // ディフューズマップテクスチャ
float4 cfFactorColor : register( c5 ) ; // 不透明度等
// main関数
PS_OUTPUT main( PS_INPUT PSInput )
{
PS_OUTPUT PSOutput ;
float4 TextureDiffuseColor ;
// テクスチャカラーの読み込み
TextureDiffuseColor = tex2D( DiffuseMapTexture, PSInput.TexCoords0 ) ;
// 出力カラー = テクスチャカラー
PSOutput.Color0 = TextureDiffuseColor ;
// 出力アルファ = テクスチャアルファ * 不透明度
PSOutput.Color0.a = TextureDiffuseColor.a * cfFactorColor.a ;
// 出力パラメータを返す
return PSOutput ;
}
1頂点へ影響を与えるフレームの数が1~4個のスキニングメッシュのライティング無し描画
<実行に必要なファイル一式>
人型モデルのように一つの頂点が複数のフレーム( ボーン )の影響を受けない背景のようなモデルを
ライティング無しでただテクスチャの色がそのまま表示されるようなシェーダーを使用するサンプルプログラムです。
このプログラムで使用しているモデルは目以外の色付けは全てマテリアルで行っていて、このサンプルではマテリアルも無視して
テクスチャの色をそのまま描画しているだけなので結果として真っ白のデフォルトテクスチャの色そのままになっています。
スキニングメッシュと剛体メッシュのシェーダープログラムで違いがあるのは頂点シェーダーだけで
最初に合成行列を作成してそれをローカル→ワールド座標の変換行列として使うか( スキニングメッシュ )、
それとも定数で設定されているローカル→ワールド座標の変換行列をそのまま使うか( 剛体メッシュ )、
と、記述の違いも僅かです。
尚、このページのサンプルでは1頂点へ影響を与えるフレームの数が1~8個のスキニングメッシュに関する説明がありませんが、
違いは入力頂点データに BlendIndices1 と BlendWeight1 が増えるのと( 頂点データ形式に関しては LoadVertexShader の解説を
参照してください )、頂点シェーダーで影響するフレームの数が1~4個の場合に合成行列を作成している箇所
// 複数のフレームのブレンド行列の作成
lLocalWorldMatrix[ 0 ] = cfLocalWorldMatrix[ VSInput.BlendIndices0.x + 0 ] * VSInput.BlendWeight0.x;
lLocalWorldMatrix[ 1 ] = cfLocalWorldMatrix[ VSInput.BlendIndices0.x + 1 ] * VSInput.BlendWeight0.x;
lLocalWorldMatrix[ 2 ] = cfLocalWorldMatrix[ VSInput.BlendIndices0.x + 2 ] * VSInput.BlendWeight0.x;
lLocalWorldMatrix[ 0 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.y + 0 ] * VSInput.BlendWeight0.y;
lLocalWorldMatrix[ 1 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.y + 1 ] * VSInput.BlendWeight0.y;
lLocalWorldMatrix[ 2 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.y + 2 ] * VSInput.BlendWeight0.y;
lLocalWorldMatrix[ 0 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.z + 0 ] * VSInput.BlendWeight0.z;
lLocalWorldMatrix[ 1 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.z + 1 ] * VSInput.BlendWeight0.z;
lLocalWorldMatrix[ 2 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.z + 2 ] * VSInput.BlendWeight0.z;
lLocalWorldMatrix[ 0 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.w + 0 ] * VSInput.BlendWeight0.w;
lLocalWorldMatrix[ 1 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.w + 1 ] * VSInput.BlendWeight0.w;
lLocalWorldMatrix[ 2 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.w + 2 ] * VSInput.BlendWeight0.w;
こちらが
// 複数のフレームのブレンド行列の作成
lLocalWorldMatrix[ 0 ] = cfLocalWorldMatrix[ VSInput.BlendIndices0.x + 0 ] * VSInput.BlendWeight0.x;
lLocalWorldMatrix[ 1 ] = cfLocalWorldMatrix[ VSInput.BlendIndices0.x + 1 ] * VSInput.BlendWeight0.x;
lLocalWorldMatrix[ 2 ] = cfLocalWorldMatrix[ VSInput.BlendIndices0.x + 2 ] * VSInput.BlendWeight0.x;
lLocalWorldMatrix[ 0 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.y + 0 ] * VSInput.BlendWeight0.y;
lLocalWorldMatrix[ 1 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.y + 1 ] * VSInput.BlendWeight0.y;
lLocalWorldMatrix[ 2 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.y + 2 ] * VSInput.BlendWeight0.y;
lLocalWorldMatrix[ 0 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.z + 0 ] * VSInput.BlendWeight0.z;
lLocalWorldMatrix[ 1 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.z + 1 ] * VSInput.BlendWeight0.z;
lLocalWorldMatrix[ 2 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.z + 2 ] * VSInput.BlendWeight0.z;
lLocalWorldMatrix[ 0 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.w + 0 ] * VSInput.BlendWeight0.w;
lLocalWorldMatrix[ 1 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.w + 1 ] * VSInput.BlendWeight0.w;
lLocalWorldMatrix[ 2 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.w + 2 ] * VSInput.BlendWeight0.w;
lLocalWorldMatrix[ 0 ] += cfLocalWorldMatrix[ VSInput.BlendIndices1.x + 0 ] * VSInput.BlendWeight1.x;
lLocalWorldMatrix[ 1 ] += cfLocalWorldMatrix[ VSInput.BlendIndices1.x + 1 ] * VSInput.BlendWeight1.x;
lLocalWorldMatrix[ 2 ] += cfLocalWorldMatrix[ VSInput.BlendIndices1.x + 2 ] * VSInput.BlendWeight1.x;
lLocalWorldMatrix[ 0 ] += cfLocalWorldMatrix[ VSInput.BlendIndices1.y + 0 ] * VSInput.BlendWeight1.y;
lLocalWorldMatrix[ 1 ] += cfLocalWorldMatrix[ VSInput.BlendIndices1.y + 1 ] * VSInput.BlendWeight1.y;
lLocalWorldMatrix[ 2 ] += cfLocalWorldMatrix[ VSInput.BlendIndices1.y + 2 ] * VSInput.BlendWeight1.y;
lLocalWorldMatrix[ 0 ] += cfLocalWorldMatrix[ VSInput.BlendIndices1.z + 0 ] * VSInput.BlendWeight1.z;
lLocalWorldMatrix[ 1 ] += cfLocalWorldMatrix[ VSInput.BlendIndices1.z + 1 ] * VSInput.BlendWeight1.z;
lLocalWorldMatrix[ 2 ] += cfLocalWorldMatrix[ VSInput.BlendIndices1.z + 2 ] * VSInput.BlendWeight1.z;
lLocalWorldMatrix[ 0 ] += cfLocalWorldMatrix[ VSInput.BlendIndices1.w + 0 ] * VSInput.BlendWeight1.w;
lLocalWorldMatrix[ 1 ] += cfLocalWorldMatrix[ VSInput.BlendIndices1.w + 1 ] * VSInput.BlendWeight1.w;
lLocalWorldMatrix[ 2 ] += cfLocalWorldMatrix[ VSInput.BlendIndices1.w + 2 ] * VSInput.BlendWeight1.w;
になるという二点の違いのみです。
C++のプログラム
#include "DxLib.h"
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
int ModelHandle ;
int PixelShaderHandle ;
int VertexShaderHandle ;
int AnimIndex ;
float AnimCounter ;
// ウインドウモードで起動
ChangeWindowMode( TRUE ) ;
// Direct3D9Ex を使用する
SetUseDirect3DVersion( DX_DIRECT3D_9EX ) ;
// DXライブラリの初期化
if( DxLib_Init() < 0 )
{
// エラーが発生したら直ちに終了
return -1 ;
}
// プログラマブルシェーダーモデル2.0が使用できない場合はエラーを表示して終了
if( GetValidShaderVersion() < 200 )
{
// エラー表示
DrawString( 0, 0, "プログラマブルシェーダー2.0が使用できない環境のようです", GetColor( 255,255,255 ) ) ;
// キー入力待ち
WaitKey() ;
// DXライブラリの後始末
DxLib_End() ;
// ソフト終了
return 0 ;
}
// 頂点シェーダーを読み込む
VertexShaderHandle = LoadVertexShader( "SkinMesh4_NoLightVS.vso" ) ;
// ピクセルシェーダーを読み込む
PixelShaderHandle = LoadPixelShader( "SkinMesh4_NoLightPS.pso" ) ;
// スキニングメッシュモデルを読み込む
ModelHandle = MV1LoadModel( "DxChara.x" ) ;
// アニメーション0をアタッチ
AnimIndex = MV1AttachAnim( ModelHandle, 0 ) ;
// アニメーションカウンタをリセット
AnimCounter = 0.0f ;
// 描画先を裏画面にする
SetDrawScreen( DX_SCREEN_BACK ) ;
// モデルの描画にオリジナルシェーダーを使用する設定をONにする
MV1SetUseOrigShader( TRUE ) ;
// 使用する頂点シェーダーをセット
SetUseVertexShader( VertexShaderHandle ) ;
// 使用するピクセルシェーダーをセット
SetUsePixelShader( PixelShaderHandle ) ;
// モデルの見える位置にカメラを配置
SetCameraPositionAndTarget_UpVecY( VGet( 0.0f, 700.0f, -1100.0f ), VGet( 0.0f, 350.0f, 0.0f ) ) ;
// ESCキーが押されるまでループ
while( ProcessMessage() == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0 )
{
// 画面を初期化
ClearDrawScreen() ;
// アニメーション時間を進める
AnimCounter += 100.0f ;
if( AnimCounter > MV1GetAnimTotalTime( ModelHandle, 0 ) )
{
AnimCounter -= MV1GetAnimTotalTime( ModelHandle, 0 ) ;
}
MV1SetAttachAnimTime( ModelHandle, AnimIndex, AnimCounter ) ;
// モデルを描画
MV1DrawModel( ModelHandle ) ;
// 裏画面の内容を表画面に反映させる
ScreenFlip() ;
}
// 読み込んだ頂点シェーダーの削除
DeleteShader( VertexShaderHandle ) ;
// 読み込んだピクセルシェーダーの削除
DeleteShader( PixelShaderHandle ) ;
// 読み込んだモデルの削除
MV1DeleteModel( ModelHandle ) ;
// DXライブラリの後始末
DxLib_End() ;
// ソフトの終了
return 0 ;
}
頂点シェーダーのプログラム
// 頂点シェーダーの入力
struct VS_INPUT
{
float4 Position : POSITION ; // 座標( ローカル空間 )
int4 BlendIndices0 : BLENDINDICES0 ; // スキニング処理用 Float型定数配列インデックス
float4 BlendWeight0 : BLENDWEIGHT0 ; // スキニング処理用ウエイト値
float3 Normal : NORMAL0 ; // 法線( ローカル空間 )
float4 Diffuse : COLOR0 ; // ディフューズカラー
float4 Specular : COLOR1 ; // スペキュラカラー
float4 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
} ;
// 頂点シェーダーの出力
struct VS_OUTPUT
{
float4 Position : POSITION ;
float2 TexCoords0 : TEXCOORD0 ;
} ;
// C++ 側で設定する定数の定義
float4 cfProjectionMatrix[ 4 ] : register( c2 ) ; // ビュー → 射影行列
float4 cfViewMatrix[ 3 ] : register( c6 ) ; // ワールド → ビュー行列
float4 cfTextureMatrix[ 3 ][ 2 ] : register( c88 ) ; // テクスチャ座標操作用行列
float4 cfLocalWorldMatrix[ 162 ] : register( c94 ) ; // ローカル → ワールド行列
// main関数
VS_OUTPUT main( VS_INPUT VSInput )
{
VS_OUTPUT VSOutput ;
float4 lLocalWorldMatrix[ 3 ] ;
float4 lWorldPosition ;
float4 lViewPosition ;
// 複数のフレームのブレンド行列の作成
lLocalWorldMatrix[ 0 ] = cfLocalWorldMatrix[ VSInput.BlendIndices0.x + 0 ] * VSInput.BlendWeight0.x;
lLocalWorldMatrix[ 1 ] = cfLocalWorldMatrix[ VSInput.BlendIndices0.x + 1 ] * VSInput.BlendWeight0.x;
lLocalWorldMatrix[ 2 ] = cfLocalWorldMatrix[ VSInput.BlendIndices0.x + 2 ] * VSInput.BlendWeight0.x;
lLocalWorldMatrix[ 0 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.y + 0 ] * VSInput.BlendWeight0.y;
lLocalWorldMatrix[ 1 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.y + 1 ] * VSInput.BlendWeight0.y;
lLocalWorldMatrix[ 2 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.y + 2 ] * VSInput.BlendWeight0.y;
lLocalWorldMatrix[ 0 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.z + 0 ] * VSInput.BlendWeight0.z;
lLocalWorldMatrix[ 1 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.z + 1 ] * VSInput.BlendWeight0.z;
lLocalWorldMatrix[ 2 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.z + 2 ] * VSInput.BlendWeight0.z;
lLocalWorldMatrix[ 0 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.w + 0 ] * VSInput.BlendWeight0.w;
lLocalWorldMatrix[ 1 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.w + 1 ] * VSInput.BlendWeight0.w;
lLocalWorldMatrix[ 2 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.w + 2 ] * VSInput.BlendWeight0.w;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ローカル座標をワールド座標に変換
lWorldPosition.x = dot( VSInput.Position, lLocalWorldMatrix[ 0 ] ) ;
lWorldPosition.y = dot( VSInput.Position, lLocalWorldMatrix[ 1 ] ) ;
lWorldPosition.z = dot( VSInput.Position, lLocalWorldMatrix[ 2 ] ) ;
lWorldPosition.w = 1.0f ;
// ワールド座標をビュー座標に変換
lViewPosition.x = dot( lWorldPosition, cfViewMatrix[ 0 ] ) ;
lViewPosition.y = dot( lWorldPosition, cfViewMatrix[ 1 ] ) ;
lViewPosition.z = dot( lWorldPosition, cfViewMatrix[ 2 ] ) ;
lViewPosition.w = 1.0f ;
// ビュー座標を射影座標に変換
VSOutput.Position.x = dot( lViewPosition, cfProjectionMatrix[ 0 ] ) ;
VSOutput.Position.y = dot( lViewPosition, cfProjectionMatrix[ 1 ] ) ;
VSOutput.Position.z = dot( lViewPosition, cfProjectionMatrix[ 2 ] ) ;
VSOutput.Position.w = dot( lViewPosition, cfProjectionMatrix[ 3 ] ) ;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// テクスチャ座標変換行列による変換を行った結果のテクスチャ座標をセット
VSOutput.TexCoords0.x = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 0 ] ) ;
VSOutput.TexCoords0.y = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 1 ] ) ;
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータを返す
return VSOutput ;
}
ピクセルシェーダーのプログラム
// ピクセルシェーダーの入力
struct PS_INPUT
{
float2 TexCoords0 : TEXCOORD0 ;
} ;
// ピクセルシェーダーの出力
struct PS_OUTPUT
{
float4 Color0 : COLOR0 ;
} ;
// C++ 側で設定するテクスチャの定義
sampler DiffuseMapTexture : register( s0 ) ; // ディフューズマップテクスチャ
float4 cfFactorColor : register( c5 ) ; // 不透明度等
// main関数
PS_OUTPUT main( PS_INPUT PSInput )
{
PS_OUTPUT PSOutput ;
float4 TextureDiffuseColor ;
// テクスチャカラーの読み込み
TextureDiffuseColor = tex2D( DiffuseMapTexture, PSInput.TexCoords0 ) ;
// 出力カラー = テクスチャカラー
PSOutput.Color0 = TextureDiffuseColor ;
// 出力アルファ = テクスチャアルファ * 不透明度
PSOutput.Color0.a = TextureDiffuseColor.a * cfFactorColor.a ;
// 出力パラメータを返す
return PSOutput ;
}
剛体メッシュのライティング無しフォグあり描画
<実行に必要なファイル一式>
人型モデルのように一つの頂点が複数のフレーム( ボーン )の影響を受けない背景のようなモデルを
ライティング無しでただテクスチャの色がそのまま表示されるようなシェーダーにフォグを付けたサンプルプログラムです。
フォグを追加するには SetFogEnable, SetFogColor, SetFogStartEnd といった通常のフォグ関数でパラメータを設定した上で、
頂点シェーダーの出力に float Fog : FOG ; を追加してフォグのパラメータをビューポジションのZ座標から算出します。
( DXライブラリ側で事前計算したフォグパラメータを定数を使用すれば、追加するシェーダーコードは2行だけです )
フォグの効果はライティングの有無やシェーディングの方式に関わらず適用されるので、他のサンプルでもこのサンプルの
フォグに関する部分のみをコピーすればそのままフォグを適用することができます。
<注意!>
FOG パラメータを追加して適用するフォグ処理はシェーダーモデル2.0でのみ使用できます。
シェーダーモデル3.0では自前でフォグ計算をしてください。
C++のプログラム
#include "DxLib.h"
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
int ModelHandle ;
int PixelShaderHandle ;
int VertexShaderHandle ;
float RotateAngle ;
float Z, ZAdd ;
// ウインドウモードで起動
ChangeWindowMode( TRUE ) ;
// Direct3D9Ex を使用する
SetUseDirect3DVersion( DX_DIRECT3D_9EX ) ;
// DXライブラリの初期化
if( DxLib_Init() < 0 )
{
// エラーが発生したら直ちに終了
return -1 ;
}
// プログラマブルシェーダーモデル2.0が使用できない場合はエラーを表示して終了
if( GetValidShaderVersion() < 200 )
{
// エラー表示
DrawString( 0, 0, "プログラマブルシェーダー2.0が使用できない環境のようです", GetColor( 255,255,255 ) ) ;
// キー入力待ち
WaitKey() ;
// DXライブラリの後始末
DxLib_End() ;
// ソフト終了
return 0 ;
}
// 頂点シェーダーを読み込む
VertexShaderHandle = LoadVertexShader( "NormalMesh_NoLight_FogVS.vso" ) ;
// ピクセルシェーダーを読み込む
PixelShaderHandle = LoadPixelShader( "NormalMesh_NoLight_FogPS.pso" ) ;
// 剛体メッシュモデルを読み込む
ModelHandle = MV1LoadModel( "NormalBox.mqo" ) ;
// フォグを有効にする
SetFogEnable( TRUE ) ;
// フォグの色を黄色にする
SetFogColor( 255, 255, 0 ) ;
// フォグの開始距離を0、終了距離を1500にする
SetFogStartEnd( 0.0f, 1500.0f ) ;
// 描画先を裏画面にする
SetDrawScreen( DX_SCREEN_BACK ) ;
// モデルを回転される値を初期化
RotateAngle = 0.0f ;
// モデルの描画にオリジナルシェーダーを使用する設定をONにする
MV1SetUseOrigShader( TRUE ) ;
// 使用する頂点シェーダーをセット
SetUseVertexShader( VertexShaderHandle ) ;
// 使用するピクセルシェーダーをセット
SetUsePixelShader( PixelShaderHandle ) ;
// Z値の値を初期化
Z = 0.0f ;
ZAdd = 10.0f ;
// ESCキーが押されるまでループ
while( ProcessMessage() == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0 )
{
// 画面を初期化
ClearDrawScreen() ;
// 画面を黄色で塗りつぶす
DrawBox( 0, 0, 640, 480, GetColor( 255, 255, 0 ), TRUE ) ;
// モデルを回転値を加算
RotateAngle += 0.02f ;
// モデルの回転値をモデルにセット
MV1SetRotationXYZ( ModelHandle, VGet( 0.0f, RotateAngle, 0.0f ) ) ;
// モデルのZ位置を変更
Z += ZAdd ;
if( Z > 1500.0f || Z < 0.0f )
ZAdd = -ZAdd ;
// モデルを座標をセット
MV1SetPosition( ModelHandle, VGet( 320.0f, 240.0f, Z ) ) ;
// モデルを描画
MV1DrawModel( ModelHandle ) ;
// 裏画面の内容を表画面に反映させる
ScreenFlip() ;
}
// 読み込んだ頂点シェーダーの削除
DeleteShader( VertexShaderHandle ) ;
// 読み込んだピクセルシェーダーの削除
DeleteShader( PixelShaderHandle ) ;
// 読み込んだモデルの削除
MV1DeleteModel( ModelHandle ) ;
// DXライブラリの後始末
DxLib_End() ;
// ソフトの終了
return 0 ;
}
頂点シェーダーのプログラム
// 頂点シェーダーの入力
struct VS_INPUT
{
float4 Position : POSITION ; // 座標( ローカル空間 )
float3 Normal : NORMAL0 ; // 法線( ローカル空間 )
float4 Diffuse : COLOR0 ; // ディフューズカラー
float4 Specular : COLOR1 ; // スペキュラカラー
float4 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
} ;
// 頂点シェーダーの出力
struct VS_OUTPUT
{
float4 Position : POSITION ;
float2 TexCoords0 : TEXCOORD0 ;
float Fog : FOG ; // フォグ
} ;
// C++ 側で設定する定数の定義
float4 cfProjectionMatrix[ 4 ] : register( c2 ) ; // ビュー → 射影行列
float4 cfViewMatrix[ 3 ] : register( c6 ) ; // ワールド → ビュー行列
float4 cfTextureMatrix[ 3 ][ 2 ] : register( c88 ) ; // テクスチャ座標操作用行列
float4 cfLocalWorldMatrix[ 3 ] : register( c94 ) ; // ローカル → ワールド行列
float4 cfFog : register( c10 ) ; // フォグ用パラメータ( x:end/(end - start) y:-1/(end - start) z:density w:自然対数の低 )
// main関数
VS_OUTPUT main( VS_INPUT VSInput )
{
VS_OUTPUT VSOutput ;
float4 lWorldPosition ;
float4 lViewPosition ;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ローカル座標をワールド座標に変換
lWorldPosition.x = dot( VSInput.Position, cfLocalWorldMatrix[ 0 ] ) ;
lWorldPosition.y = dot( VSInput.Position, cfLocalWorldMatrix[ 1 ] ) ;
lWorldPosition.z = dot( VSInput.Position, cfLocalWorldMatrix[ 2 ] ) ;
lWorldPosition.w = 1.0f ;
// ワールド座標をビュー座標に変換
lViewPosition.x = dot( lWorldPosition, cfViewMatrix[ 0 ] ) ;
lViewPosition.y = dot( lWorldPosition, cfViewMatrix[ 1 ] ) ;
lViewPosition.z = dot( lWorldPosition, cfViewMatrix[ 2 ] ) ;
lViewPosition.w = 1.0f ;
// ビュー座標を射影座標に変換
VSOutput.Position.x = dot( lViewPosition, cfProjectionMatrix[ 0 ] ) ;
VSOutput.Position.y = dot( lViewPosition, cfProjectionMatrix[ 1 ] ) ;
VSOutput.Position.z = dot( lViewPosition, cfProjectionMatrix[ 2 ] ) ;
VSOutput.Position.w = dot( lViewPosition, cfProjectionMatrix[ 3 ] ) ;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// テクスチャ座標変換行列による変換を行った結果のテクスチャ座標をセット
VSOutput.TexCoords0.x = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 0 ] ) ;
VSOutput.TexCoords0.y = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 1 ] ) ;
// 線形フォグ計算
VSOutput.Fog = lViewPosition.z * cfFog.y + cfFog.x ;
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータを返す
return VSOutput ;
}
ピクセルシェーダーのプログラム
// ピクセルシェーダーの入力
struct PS_INPUT
{
float2 TexCoords0 : TEXCOORD0 ;
} ;
// ピクセルシェーダーの出力
struct PS_OUTPUT
{
float4 Color0 : COLOR0 ;
} ;
// C++ 側で設定するテクスチャの定義
sampler DiffuseMapTexture : register( s0 ) ; // ディフューズマップテクスチャ
float4 cfFactorColor : register( c5 ) ; // 不透明度等
// main関数
PS_OUTPUT main( PS_INPUT PSInput )
{
PS_OUTPUT PSOutput ;
float4 TextureDiffuseColor ;
// テクスチャカラーの読み込み
TextureDiffuseColor = tex2D( DiffuseMapTexture, PSInput.TexCoords0 ) ;
// 出力カラー = テクスチャカラー
PSOutput.Color0 = TextureDiffuseColor ;
// 出力アルファ = テクスチャアルファ * 不透明度
PSOutput.Color0.a = TextureDiffuseColor.a * cfFactorColor.a ;
// 出力パラメータを返す
return PSOutput ;
}
剛体メッシュのライティング無しフォグあり描画(シェーダーモデル3.0版)
<実行に必要なファイル一式>
一つ上の『剛体メッシュのライティング無しフォグあり描画』の、シェーダーモデル3.0版です。
シェーダーモデル3.0では、頂点シェーダーのセマンティクス『FOG』が使用できないので、
セマンティクス『TEXCOORD1』を使用してピクセルシェーダーでフォグの処理を実現しています。
C++のプログラム
#include "DxLib.h"
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
int ModelHandle ;
int PixelShaderHandle ;
int VertexShaderHandle ;
float RotateAngle ;
float Z, ZAdd ;
// ウインドウモードで起動
ChangeWindowMode( TRUE ) ;
// Direct3D9Ex を使用する
SetUseDirect3DVersion( DX_DIRECT3D_9EX ) ;
// DXライブラリの初期化
if( DxLib_Init() < 0 )
{
// エラーが発生したら直ちに終了
return -1 ;
}
// プログラマブルシェーダーモデル2.0が使用できない場合はエラーを表示して終了
if( GetValidShaderVersion() < 200 )
{
// エラー表示
DrawString( 0, 0, "プログラマブルシェーダー2.0が使用できない環境のようです", GetColor( 255,255,255 ) ) ;
// キー入力待ち
WaitKey() ;
// DXライブラリの後始末
DxLib_End() ;
// ソフト終了
return 0 ;
}
// 頂点シェーダーを読み込む
VertexShaderHandle = LoadVertexShader( "NormalMesh_NoLight_Fog_SV3VS.vso" ) ;
// ピクセルシェーダーを読み込む
PixelShaderHandle = LoadPixelShader( "NormalMesh_NoLight_Fog_SV3PS.pso" ) ;
// 剛体メッシュモデルを読み込む
ModelHandle = MV1LoadModel( "NormalBox.mqo" ) ;
// フォグを有効にする
SetFogEnable( TRUE ) ;
// フォグの色を黄色にする
SetFogColor( 255, 255, 0 ) ;
// フォグの開始距離を0、終了距離を1500にする
SetFogStartEnd( 0.0f, 1500.0f ) ;
// 描画先を裏画面にする
SetDrawScreen( DX_SCREEN_BACK ) ;
// モデルを回転される値を初期化
RotateAngle = 0.0f ;
// モデルの描画にオリジナルシェーダーを使用する設定をONにする
MV1SetUseOrigShader( TRUE ) ;
// 使用する頂点シェーダーをセット
SetUseVertexShader( VertexShaderHandle ) ;
// 使用するピクセルシェーダーをセット
SetUsePixelShader( PixelShaderHandle ) ;
// Z値の値を初期化
Z = 0.0f ;
ZAdd = 10.0f ;
// ESCキーが押されるまでループ
while( ProcessMessage() == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0 )
{
// 画面を初期化
ClearDrawScreen() ;
// 画面を黄色で塗りつぶす
DrawBox( 0, 0, 640, 480, GetColor( 255, 255, 0 ), TRUE ) ;
// モデルを回転値を加算
RotateAngle += 0.02f ;
// モデルの回転値をモデルにセット
MV1SetRotationXYZ( ModelHandle, VGet( 0.0f, RotateAngle, 0.0f ) ) ;
// モデルのZ位置を変更
Z += ZAdd ;
if( Z > 1500.0f || Z < 0.0f )
ZAdd = -ZAdd ;
// モデルを座標をセット
MV1SetPosition( ModelHandle, VGet( 320.0f, 240.0f, Z ) ) ;
// モデルを描画
MV1DrawModel( ModelHandle ) ;
// 裏画面の内容を表画面に反映させる
ScreenFlip() ;
}
// 読み込んだ頂点シェーダーの削除
DeleteShader( VertexShaderHandle ) ;
// 読み込んだピクセルシェーダーの削除
DeleteShader( PixelShaderHandle ) ;
// 読み込んだモデルの削除
MV1DeleteModel( ModelHandle ) ;
// DXライブラリの後始末
DxLib_End() ;
// ソフトの終了
return 0 ;
}
頂点シェーダーのプログラム
// 頂点シェーダーの入力
struct VS_INPUT
{
float4 Position : POSITION ; // 座標( ローカル空間 )
float3 Normal : NORMAL0 ; // 法線( ローカル空間 )
float4 Diffuse : COLOR0 ; // ディフューズカラー
float4 Specular : COLOR1 ; // スペキュラカラー
float4 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
} ;
// 頂点シェーダーの出力
struct VS_OUTPUT
{
float4 Position : POSITION ;
float2 TexCoords0 : TEXCOORD0 ;
float Fog : TEXCOORD1 ; // フォグパラメータ
} ;
// C++ 側で設定する定数の定義
float4 cfProjectionMatrix[ 4 ] : register( c2 ) ; // ビュー → 射影行列
float4 cfViewMatrix[ 3 ] : register( c6 ) ; // ワールド → ビュー行列
float4 cfTextureMatrix[ 3 ][ 2 ] : register( c88 ) ; // テクスチャ座標操作用行列
float4 cfLocalWorldMatrix[ 3 ] : register( c94 ) ; // ローカル → ワールド行列
float4 cfFog : register( c10 ) ; // フォグ用パラメータ( x:end/(end - start) y:-1/(end - start) z:density w:自然対数の低 )
// main関数
VS_OUTPUT main( VS_INPUT VSInput )
{
VS_OUTPUT VSOutput ;
float4 lWorldPosition ;
float4 lViewPosition ;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ローカル座標をワールド座標に変換
lWorldPosition.x = dot( VSInput.Position, cfLocalWorldMatrix[ 0 ] ) ;
lWorldPosition.y = dot( VSInput.Position, cfLocalWorldMatrix[ 1 ] ) ;
lWorldPosition.z = dot( VSInput.Position, cfLocalWorldMatrix[ 2 ] ) ;
lWorldPosition.w = 1.0f ;
// ワールド座標をビュー座標に変換
lViewPosition.x = dot( lWorldPosition, cfViewMatrix[ 0 ] ) ;
lViewPosition.y = dot( lWorldPosition, cfViewMatrix[ 1 ] ) ;
lViewPosition.z = dot( lWorldPosition, cfViewMatrix[ 2 ] ) ;
lViewPosition.w = 1.0f ;
// ビュー座標を射影座標に変換
VSOutput.Position.x = dot( lViewPosition, cfProjectionMatrix[ 0 ] ) ;
VSOutput.Position.y = dot( lViewPosition, cfProjectionMatrix[ 1 ] ) ;
VSOutput.Position.z = dot( lViewPosition, cfProjectionMatrix[ 2 ] ) ;
VSOutput.Position.w = dot( lViewPosition, cfProjectionMatrix[ 3 ] ) ;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// 線形フォグ計算
VSOutput.Fog = lViewPosition.z * cfFog.y + cfFog.x ;
// テクスチャ座標変換行列による変換を行った結果のテクスチャ座標をセット
VSOutput.TexCoords0.x = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 0 ] ) ;
VSOutput.TexCoords0.y = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 1 ] ) ;
// 線形フォグ計算
VSOutput.Fog = lViewPosition.z * cfFog.y + cfFog.x ;
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータを返す
return VSOutput ;
}
ピクセルシェーダーのプログラム
// ピクセルシェーダーの入力
struct PS_INPUT
{
float2 TexCoords0 : TEXCOORD0 ;
float Fog : TEXCOORD1 ; // フォグパラメータ
} ;
// ピクセルシェーダーの出力
struct PS_OUTPUT
{
float4 Color0 : COLOR0 ;
} ;
// C++ 側で設定するテクスチャの定義
sampler DiffuseMapTexture : register( s0 ) ; // ディフューズマップテクスチャ
float4 cfFactorColor : register( c5 ) ; // 不透明度等
float4 cfFogColor : register( c8 ) ; // フォグカラー
// main関数
PS_OUTPUT main( PS_INPUT PSInput )
{
PS_OUTPUT PSOutput ;
float4 TextureDiffuseColor ;
// テクスチャカラーの読み込み
TextureDiffuseColor = tex2D( DiffuseMapTexture, PSInput.TexCoords0 ) ;
// 出力カラー = テクスチャカラー
PSOutput.Color0 = TextureDiffuseColor ;
// フォグ効果
PSOutput.Color0.rgb = lerp( cfFogColor.rgb, PSOutput.Color0.rgb, saturate( PSInput.Fog ) );
// 出力アルファ = テクスチャアルファ * 不透明度
PSOutput.Color0.a = TextureDiffuseColor.a * cfFactorColor.a ;
// 出力パラメータを返す
return PSOutput ;
}
固定機能パイプライン互換のディレクショナルライトあり描画
剛体メッシュのディレクショナルライトあり描画
<実行に必要なファイル一式>
人型モデルのように一つの頂点が複数のフレーム( ボーン )の影響を受けない背景のようなモデルに
ディレクショナルライトを当ててライブラリ側で設定されているマテリアルも適用して描画するサンプルプログラムです。
色々なパラメータを足したり掛けたりしていますが、固定機能パイプラインのライティング計算仕様に基づいています。
シェーダーを使う場合はいずれオリジナルのライティング処理を書くことになると思うので
「一応これで固定機能パイプラインと同じ動作します」という程度の意味のサンプルです。
C++のプログラム
#include "DxLib.h"
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
int ModelHandle ;
int PixelShaderHandle ;
int VertexShaderHandle ;
float RotateAngle ;
// ウインドウモードで起動
ChangeWindowMode( TRUE ) ;
// Direct3D9Ex を使用する
SetUseDirect3DVersion( DX_DIRECT3D_9EX ) ;
// DXライブラリの初期化
if( DxLib_Init() < 0 )
{
// エラーが発生したら直ちに終了
return -1 ;
}
// プログラマブルシェーダーモデル2.0が使用できない場合はエラーを表示して終了
if( GetValidShaderVersion() < 200 )
{
// エラー表示
DrawString( 0, 0, "プログラマブルシェーダー2.0が使用できない環境のようです", GetColor( 255,255,255 ) ) ;
// キー入力待ち
WaitKey() ;
// DXライブラリの後始末
DxLib_End() ;
// ソフト終了
return 0 ;
}
// 頂点シェーダーを読み込む
VertexShaderHandle = LoadVertexShader( "NormalMesh_DirLightVS.vso" ) ;
// ピクセルシェーダーを読み込む
PixelShaderHandle = LoadPixelShader( "NormalMesh_DirLightPS.pso" ) ;
// 剛体メッシュモデルを読み込む
ModelHandle = MV1LoadModel( "NormalBox.mqo" ) ;
// 描画先を裏画面にする
SetDrawScreen( DX_SCREEN_BACK ) ;
// モデルを回転される値を初期化
RotateAngle = 0.0f ;
// モデルの描画にオリジナルシェーダーを使用する設定をONにする
MV1SetUseOrigShader( TRUE ) ;
// 使用する頂点シェーダーをセット
SetUseVertexShader( VertexShaderHandle ) ;
// 使用するピクセルシェーダーをセット
SetUsePixelShader( PixelShaderHandle ) ;
// モデルを画面の中心にセット
MV1SetPosition( ModelHandle, VGet( 320.0f, 240.0f, 0.0f ) ) ;
// ESCキーが押されるまでループ
while( ProcessMessage() == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0 )
{
// 画面を初期化
ClearDrawScreen() ;
// モデルを回転値を加算
RotateAngle += 0.02f ;
// モデルの回転値をモデルにセット
MV1SetRotationXYZ( ModelHandle, VGet( 0.0f, RotateAngle, 0.0f ) ) ;
// モデルを描画
MV1DrawModel( ModelHandle ) ;
// 裏画面の内容を表画面に反映させる
ScreenFlip() ;
}
// 読み込んだ頂点シェーダーの削除
DeleteShader( VertexShaderHandle ) ;
// 読み込んだピクセルシェーダーの削除
DeleteShader( PixelShaderHandle ) ;
// 読み込んだモデルの削除
MV1DeleteModel( ModelHandle ) ;
// DXライブラリの後始末
DxLib_End() ;
// ソフトの終了
return 0 ;
}
頂点シェーダーのプログラム
// 頂点シェーダーの入力
struct VS_INPUT
{
float4 Position : POSITION ; // 座標( ローカル空間 )
float3 Normal : NORMAL0 ; // 法線( ローカル空間 )
float4 Diffuse : COLOR0 ; // ディフューズカラー
float4 Specular : COLOR1 ; // スペキュラカラー
float4 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
} ;
// 頂点シェーダーの出力
struct VS_OUTPUT
{
float4 Position : POSITION ;
float4 Diffuse : COLOR0 ;
float4 Specular : COLOR1 ;
float2 TexCoords0 : TEXCOORD0 ;
} ;
// マテリアルパラメータ
struct VS_CONST_MATERIAL
{
float4 Diffuse ; // マテリアルディフューズカラー
float4 Specular ; // マテリアルスペキュラカラー
float4 Power ; // マテリアルスペキュラハイライトの強さ
} ;
// ライトパラメータ
struct VS_CONST_LIGHT
{
float4 Position ; // 座標( ビュー空間 )
float3 Direction ; // 方向( ビュー空間 )
float4 Diffuse ; // ディフューズカラー
float4 Specular ; // スペキュラカラー
float4 Ambient ; // アンビエントカラーとマテリアルのアンビエントカラーを乗算したもの
float4 Range_FallOff_AT0_AT1 ; // x:有効距離の二乗 y:スポットライト用FallOff z:距離による減衰処理用パラメータ0 w:距離による減衰処理用パラメータ1
float4 AT2_SpotP0_SpotP1 ; // x:距離による減衰処理用パラメータ2 y:スポットライト用パラメータ0( cos( Phi / 2.0f ) ) z:スポットライト用パラメータ1( 1.0f / ( cos( Theta / 2.0f ) - cos( Phi / 2.0f ) ) )
} ;
// C++ 側で設定する定数の定義
float4 cfAmbient_Emissive : register( c1 ) ; // マテリアルエミッシブカラー + マテリアルアンビエントカラー * グローバルアンビエントカラー
float4 cfProjectionMatrix[ 4 ] : register( c2 ) ; // ビュー → 射影行列
float4 cfViewMatrix[ 3 ] : register( c6 ) ; // ワールド → ビュー行列
float4 cfTextureMatrix[ 3 ][ 2 ] : register( c88 ) ; // テクスチャ座標操作用行列
float4 cfLocalWorldMatrix[ 3 ] : register( c94 ) ; // ローカル → ワールド行列
VS_CONST_MATERIAL cfMaterial : register( c11 ) ; // マテリアルパラメータ
VS_CONST_LIGHT cfLight : register( c14 ) ; // 有効ライト0番のパラメータ
// main関数
VS_OUTPUT main( VS_INPUT VSInput )
{
VS_OUTPUT VSOutput ;
float4 lWorldPosition ;
float4 lViewPosition ;
float3 lWorldNrm ;
float3 lViewNrm ;
float3 lLightHalfVec ;
float4 lLightLitParam ;
float4 lLightLitDest ;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ローカル座標をワールド座標に変換
lWorldPosition.x = dot( VSInput.Position, cfLocalWorldMatrix[ 0 ] ) ;
lWorldPosition.y = dot( VSInput.Position, cfLocalWorldMatrix[ 1 ] ) ;
lWorldPosition.z = dot( VSInput.Position, cfLocalWorldMatrix[ 2 ] ) ;
lWorldPosition.w = 1.0f ;
// ワールド座標をビュー座標に変換
lViewPosition.x = dot( lWorldPosition, cfViewMatrix[ 0 ] ) ;
lViewPosition.y = dot( lWorldPosition, cfViewMatrix[ 1 ] ) ;
lViewPosition.z = dot( lWorldPosition, cfViewMatrix[ 2 ] ) ;
lViewPosition.w = 1.0f ;
// ビュー座標を射影座標に変換
VSOutput.Position.x = dot( lViewPosition, cfProjectionMatrix[ 0 ] ) ;
VSOutput.Position.y = dot( lViewPosition, cfProjectionMatrix[ 1 ] ) ;
VSOutput.Position.z = dot( lViewPosition, cfProjectionMatrix[ 2 ] ) ;
VSOutput.Position.w = dot( lViewPosition, cfProjectionMatrix[ 3 ] ) ;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// ライトの処理 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// 法線をビュー空間の角度に変換 =========================================( 開始 )
// ローカルベクトルをワールドベクトルに変換
lWorldNrm.x = dot( VSInput.Normal, cfLocalWorldMatrix[ 0 ].xyz ) ;
lWorldNrm.y = dot( VSInput.Normal, cfLocalWorldMatrix[ 1 ].xyz ) ;
lWorldNrm.z = dot( VSInput.Normal, cfLocalWorldMatrix[ 2 ].xyz ) ;
// ワールドベクトルをビューベクトルに変換
lViewNrm.x = dot( lWorldNrm, cfViewMatrix[ 0 ].xyz ) ;
lViewNrm.y = dot( lWorldNrm, cfViewMatrix[ 1 ].xyz ) ;
lViewNrm.z = dot( lWorldNrm, cfViewMatrix[ 2 ].xyz ) ;
// 法線をビュー空間の角度に変換 =========================================( 終了 )
// ライトディフューズカラーとライトスペキュラカラーの角度減衰計算 =======( 開始 )
// 法線とライトの逆方向ベクトルとの内積を lLightLitParam.x にセット
lLightLitParam.x = dot( lViewNrm, -cfLight.Direction ) ;
// ハーフベクトルの計算 norm( ( norm( 頂点位置から視点へのベクトル ) + ライトの方向 ) )
lLightHalfVec = normalize( normalize( -lViewPosition.xyz ) - cfLight.Direction ) ;
// 法線とハーフベクトルの内積を lLightLitParam.y にセット
lLightLitParam.y = dot( lLightHalfVec, lViewNrm ) ;
// スペキュラ反射率を lLightLitParam.w にセット
lLightLitParam.w = cfMaterial.Power.x ;
// ライトパラメータ計算
lLightLitDest = lit( lLightLitParam.x, lLightLitParam.y, lLightLitParam.w ) ;
// ライトディフューズカラーとライトスペキュラカラーの角度減衰計算 =======( 終了 )
// ライトの処理 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ディフューズカラー =
// ディフューズ角度減衰計算結果 *
// ライトのディフューズカラー *
// マテリアルのディフューズカラー +
// ライトのアンビエントカラーとマテリアルのアンビエントカラーを乗算したもの +
// マテリアルのアンビエントカラーとグローバルアンビエントカラーを乗算したものとマテリアルエミッシブカラーを加算したもの
VSOutput.Diffuse = lLightLitDest.y * cfLight.Diffuse * cfMaterial.Diffuse + cfLight.Ambient + cfAmbient_Emissive ;
// ディフューズアルファはマテリアルのディフューズカラーのアルファをそのまま使う
VSOutput.Diffuse.w = cfMaterial.Diffuse.w ;
// スペキュラカラー = スペキュラ角度減衰計算結果 * ライトのスペキュラカラー * マテリアルのスペキュラカラー
VSOutput.Specular = lLightLitDest.z * cfLight.Specular * cfMaterial.Specular ;
// テクスチャ座標変換行列による変換を行った結果のテクスチャ座標をセット
VSOutput.TexCoords0.x = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 0 ] ) ;
VSOutput.TexCoords0.y = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 1 ] ) ;
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータを返す
return VSOutput ;
}
ピクセルシェーダーのプログラム
// ピクセルシェーダーの入力
struct PS_INPUT
{
float4 Diffuse : COLOR0 ;
float4 Specular : COLOR1 ;
float2 TexCoords0 : TEXCOORD0 ;
} ;
// ピクセルシェーダーの出力
struct PS_OUTPUT
{
float4 Color0 : COLOR0 ;
} ;
// C++ 側で設定するテクスチャの定義
sampler DiffuseMapTexture : register( s0 ) ; // ディフューズマップテクスチャ
float4 cfFactorColor : register( c5 ) ; // 不透明度等
// main関数
PS_OUTPUT main( PS_INPUT PSInput )
{
PS_OUTPUT PSOutput ;
float4 TextureDiffuseColor ;
// テクスチャカラーの読み込み
TextureDiffuseColor = tex2D( DiffuseMapTexture, PSInput.TexCoords0.xy ) ;
// 出力カラー = ディフューズカラー * テクスチャカラー + スペキュラカラー
PSOutput.Color0 = PSInput.Diffuse * TextureDiffuseColor + PSInput.Specular ;
// 出力アルファ = ディフューズアルファ * テクスチャアルファ * 不透明度
PSOutput.Color0.a = PSInput.Diffuse.a * TextureDiffuseColor.a * cfFactorColor.a ;
// 出力パラメータを返す
return PSOutput ;
}
1頂点へ影響を与えるフレームの数が1~4個のスキニングメッシュのディレクショナルライトあり描画
<実行に必要なファイル一式>
ライティング処理に剛体メッシュかスキニングメッシュかの違いは関係ないので、剛体メッシュのディレクショナルライトの
サンプルとの違いは最初に合成行列を作成しているかどうかだけです。
C++のプログラム
#include "DxLib.h"
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
int ModelHandle ;
int PixelShaderHandle ;
int VertexShaderHandle ;
int AnimIndex ;
float AnimCounter ;
// ウインドウモードで起動
ChangeWindowMode( TRUE ) ;
// Direct3D9Ex を使用する
SetUseDirect3DVersion( DX_DIRECT3D_9EX ) ;
// DXライブラリの初期化
if( DxLib_Init() < 0 )
{
// エラーが発生したら直ちに終了
return -1 ;
}
// プログラマブルシェーダーモデル2.0が使用できない場合はエラーを表示して終了
if( GetValidShaderVersion() < 200 )
{
// エラー表示
DrawString( 0, 0, "プログラマブルシェーダー2.0が使用できない環境のようです", GetColor( 255,255,255 ) ) ;
// キー入力待ち
WaitKey() ;
// DXライブラリの後始末
DxLib_End() ;
// ソフト終了
return 0 ;
}
// 頂点シェーダーを読み込む
VertexShaderHandle = LoadVertexShader( "SkinMesh4_DirLightVS.vso" ) ;
// ピクセルシェーダーを読み込む
PixelShaderHandle = LoadPixelShader( "SkinMesh4_DirLightPS.pso" ) ;
// スキニングメッシュモデルを読み込む
ModelHandle = MV1LoadModel( "DxChara.x" ) ;
// アニメーション0をアタッチ
AnimIndex = MV1AttachAnim( ModelHandle, 0 ) ;
// アニメーションカウンタをリセット
AnimCounter = 0.0f ;
// 描画先を裏画面にする
SetDrawScreen( DX_SCREEN_BACK ) ;
// モデルの描画にオリジナルシェーダーを使用する設定をONにする
MV1SetUseOrigShader( TRUE ) ;
// 使用する頂点シェーダーをセット
SetUseVertexShader( VertexShaderHandle ) ;
// 使用するピクセルシェーダーをセット
SetUsePixelShader( PixelShaderHandle ) ;
// モデルの見える位置にカメラを配置
SetCameraPositionAndTarget_UpVecY( VGet( 0.0f, 700.0f, -1100.0f ), VGet( 0.0f, 350.0f, 0.0f ) ) ;
// ESCキーが押されるまでループ
while( ProcessMessage() == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0 )
{
// 画面を初期化
ClearDrawScreen() ;
// アニメーション時間を進める
AnimCounter += 100.0f ;
if( AnimCounter > MV1GetAnimTotalTime( ModelHandle, 0 ) )
{
AnimCounter -= MV1GetAnimTotalTime( ModelHandle, 0 ) ;
}
MV1SetAttachAnimTime( ModelHandle, AnimIndex, AnimCounter ) ;
// モデルを描画
MV1DrawModel( ModelHandle ) ;
// 裏画面の内容を表画面に反映させる
ScreenFlip() ;
}
// 読み込んだ頂点シェーダーの削除
DeleteShader( VertexShaderHandle ) ;
// 読み込んだピクセルシェーダーの削除
DeleteShader( PixelShaderHandle ) ;
// 読み込んだモデルの削除
MV1DeleteModel( ModelHandle ) ;
// DXライブラリの後始末
DxLib_End() ;
// ソフトの終了
return 0 ;
}
頂点シェーダーのプログラム
// 頂点シェーダーの入力
struct VS_INPUT
{
float4 Position : POSITION ; // 座標( ローカル空間 )
int4 BlendIndices0 : BLENDINDICES0 ; // スキニング処理用 Float型定数配列インデックス
float4 BlendWeight0 : BLENDWEIGHT0 ; // スキニング処理用ウエイト値
float3 Normal : NORMAL0 ; // 法線( ローカル空間 )
float4 Diffuse : COLOR0 ; // ディフューズカラー
float4 Specular : COLOR1 ; // スペキュラカラー
float4 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
} ;
// 頂点シェーダーの出力
struct VS_OUTPUT
{
float4 Position : POSITION ;
float4 Diffuse : COLOR0 ;
float4 Specular : COLOR1 ;
float2 TexCoords0 : TEXCOORD0 ;
} ;
// マテリアルパラメータ
struct VS_CONST_MATERIAL
{
float4 Diffuse ; // マテリアルディフューズカラー
float4 Specular ; // マテリアルスペキュラカラー
float4 Power ; // マテリアルスペキュラハイライトの強さ
} ;
// ライトパラメータ
struct VS_CONST_LIGHT
{
float4 Position ; // 座標( ビュー空間 )
float3 Direction ; // 方向( ビュー空間 )
float4 Diffuse ; // ディフューズカラー
float4 Specular ; // スペキュラカラー
float4 Ambient ; // アンビエントカラーとマテリアルのアンビエントカラーを乗算したもの
float4 Range_FallOff_AT0_AT1 ; // x:有効距離の二乗 y:スポットライト用FallOff z:距離による減衰処理用パラメータ0 w:距離による減衰処理用パラメータ1
float4 AT2_SpotP0_SpotP1 ; // x:距離による減衰処理用パラメータ2 y:スポットライト用パラメータ0( cos( Phi / 2.0f ) ) z:スポットライト用パラメータ1( 1.0f / ( cos( Theta / 2.0f ) - cos( Phi / 2.0f ) ) )
} ;
// C++ 側で設定する定数の定義
float4 cfAmbient_Emissive : register( c1 ) ; // マテリアルエミッシブカラー + マテリアルアンビエントカラー * グローバルアンビエントカラー
float4 cfProjectionMatrix[ 4 ] : register( c2 ) ; // ビュー → 射影行列
float4 cfViewMatrix[ 3 ] : register( c6 ) ; // ワールド → ビュー行列
float4 cfTextureMatrix[ 3 ][ 2 ] : register( c88 ) ; // テクスチャ座標操作用行列
float4 cfLocalWorldMatrix[ 162 ] : register( c94 ) ; // ローカル → ワールド行列
VS_CONST_MATERIAL cfMaterial : register( c11 ) ; // マテリアルパラメータ
VS_CONST_LIGHT cfLight : register( c14 ) ; // 有効ライト0番のパラメータ
// main関数
VS_OUTPUT main( VS_INPUT VSInput )
{
VS_OUTPUT VSOutput ;
float4 lWorldPosition ;
float4 lViewPosition ;
float3 lWorldNrm ;
float3 lViewNrm ;
float3 lLightHalfVec ;
float4 lLightLitParam ;
float4 lLightLitDest ;
float4 lLocalWorldMatrix[ 3 ] ;
// 複数のフレームのブレンド行列の作成
lLocalWorldMatrix[ 0 ] = cfLocalWorldMatrix[ VSInput.BlendIndices0.x + 0 ] * VSInput.BlendWeight0.x;
lLocalWorldMatrix[ 1 ] = cfLocalWorldMatrix[ VSInput.BlendIndices0.x + 1 ] * VSInput.BlendWeight0.x;
lLocalWorldMatrix[ 2 ] = cfLocalWorldMatrix[ VSInput.BlendIndices0.x + 2 ] * VSInput.BlendWeight0.x;
lLocalWorldMatrix[ 0 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.y + 0 ] * VSInput.BlendWeight0.y;
lLocalWorldMatrix[ 1 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.y + 1 ] * VSInput.BlendWeight0.y;
lLocalWorldMatrix[ 2 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.y + 2 ] * VSInput.BlendWeight0.y;
lLocalWorldMatrix[ 0 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.z + 0 ] * VSInput.BlendWeight0.z;
lLocalWorldMatrix[ 1 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.z + 1 ] * VSInput.BlendWeight0.z;
lLocalWorldMatrix[ 2 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.z + 2 ] * VSInput.BlendWeight0.z;
lLocalWorldMatrix[ 0 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.w + 0 ] * VSInput.BlendWeight0.w;
lLocalWorldMatrix[ 1 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.w + 1 ] * VSInput.BlendWeight0.w;
lLocalWorldMatrix[ 2 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.w + 2 ] * VSInput.BlendWeight0.w;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ローカル座標をワールド座標に変換
lWorldPosition.x = dot( VSInput.Position, lLocalWorldMatrix[ 0 ] ) ;
lWorldPosition.y = dot( VSInput.Position, lLocalWorldMatrix[ 1 ] ) ;
lWorldPosition.z = dot( VSInput.Position, lLocalWorldMatrix[ 2 ] ) ;
lWorldPosition.w = 1.0f ;
// ワールド座標をビュー座標に変換
lViewPosition.x = dot( lWorldPosition, cfViewMatrix[ 0 ] ) ;
lViewPosition.y = dot( lWorldPosition, cfViewMatrix[ 1 ] ) ;
lViewPosition.z = dot( lWorldPosition, cfViewMatrix[ 2 ] ) ;
lViewPosition.w = 1.0f ;
// ビュー座標を射影座標に変換
VSOutput.Position.x = dot( lViewPosition, cfProjectionMatrix[ 0 ] ) ;
VSOutput.Position.y = dot( lViewPosition, cfProjectionMatrix[ 1 ] ) ;
VSOutput.Position.z = dot( lViewPosition, cfProjectionMatrix[ 2 ] ) ;
VSOutput.Position.w = dot( lViewPosition, cfProjectionMatrix[ 3 ] ) ;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// ライトの処理 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// 法線をビュー空間の角度に変換 =========================================( 開始 )
// ローカルベクトルをワールドベクトルに変換
lWorldNrm.x = dot( VSInput.Normal, lLocalWorldMatrix[ 0 ].xyz ) ;
lWorldNrm.y = dot( VSInput.Normal, lLocalWorldMatrix[ 1 ].xyz ) ;
lWorldNrm.z = dot( VSInput.Normal, lLocalWorldMatrix[ 2 ].xyz ) ;
// ワールドベクトルをビューベクトルに変換
lViewNrm.x = dot( lWorldNrm, cfViewMatrix[ 0 ].xyz ) ;
lViewNrm.y = dot( lWorldNrm, cfViewMatrix[ 1 ].xyz ) ;
lViewNrm.z = dot( lWorldNrm, cfViewMatrix[ 2 ].xyz ) ;
// 法線をビュー空間の角度に変換 =========================================( 終了 )
// ライトディフューズカラーとライトスペキュラカラーの角度減衰計算 =======( 開始 )
// 法線とライトの逆方向ベクトルとの内積を lLightLitParam.x にセット
lLightLitParam.x = dot( lViewNrm, -cfLight.Direction ) ;
// ハーフベクトルの計算 norm( ( norm( 頂点位置から視点へのベクトル ) + ライトの方向 ) )
lLightHalfVec = normalize( normalize( -lViewPosition.xyz ) - cfLight.Direction ) ;
// 法線とハーフベクトルの内積を lLightLitParam.y にセット
lLightLitParam.y = dot( lLightHalfVec, lViewNrm ) ;
// スペキュラ反射率を lLightLitParam.w にセット
lLightLitParam.w = cfMaterial.Power.x ;
// ライトパラメータ計算
lLightLitDest = lit( lLightLitParam.x, lLightLitParam.y, lLightLitParam.w ) ;
// ライトディフューズカラーとライトスペキュラカラーの角度減衰計算 =======( 終了 )
// ライトの処理 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ディフューズカラー =
// ディフューズ角度減衰計算結果 *
// ライトのディフューズカラー *
// マテリアルのディフューズカラー +
// ライトのアンビエントカラーとマテリアルのアンビエントカラーを乗算したもの +
// マテリアルのアンビエントカラーとグローバルアンビエントカラーを乗算したものとマテリアルエミッシブカラーを加算したもの
VSOutput.Diffuse = lLightLitDest.y * cfLight.Diffuse * cfMaterial.Diffuse + cfLight.Ambient + cfAmbient_Emissive ;
// ディフューズアルファはマテリアルのディフューズカラーのアルファをそのまま使う
VSOutput.Diffuse.w = cfMaterial.Diffuse.w ;
// スペキュラカラー = スペキュラ角度減衰計算結果 * ライトのスペキュラカラー * マテリアルのスペキュラカラー
VSOutput.Specular = lLightLitDest.z * cfLight.Specular * cfMaterial.Specular ;
// テクスチャ座標変換行列による変換を行った結果のテクスチャ座標をセット
VSOutput.TexCoords0.x = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 0 ] ) ;
VSOutput.TexCoords0.y = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 1 ] ) ;
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータを返す
return VSOutput ;
}
ピクセルシェーダーのプログラム
// ピクセルシェーダーの入力
struct PS_INPUT
{
float4 Diffuse : COLOR0 ;
float4 Specular : COLOR1 ;
float2 TexCoords0 : TEXCOORD0 ;
} ;
// ピクセルシェーダーの出力
struct PS_OUTPUT
{
float4 Color0 : COLOR0 ;
} ;
// C++ 側で設定するテクスチャの定義
sampler DiffuseMapTexture : register( s0 ) ; // ディフューズマップテクスチャ
float4 cfFactorColor : register( c5 ) ; // 不透明度等
// main関数
PS_OUTPUT main( PS_INPUT PSInput )
{
PS_OUTPUT PSOutput ;
float4 TextureDiffuseColor ;
// テクスチャカラーの読み込み
TextureDiffuseColor = tex2D( DiffuseMapTexture, PSInput.TexCoords0.xy ) ;
// 出力カラー = ディフューズカラー * テクスチャカラー + スペキュラカラー
PSOutput.Color0 = PSInput.Diffuse * TextureDiffuseColor + PSInput.Specular ;
// 出力アルファ = ディフューズアルファ * テクスチャアルファ * 不透明度
PSOutput.Color0.a = PSInput.Diffuse.a * TextureDiffuseColor.a * cfFactorColor.a ;
// 出力パラメータを返す
return PSOutput ;
}
固定機能パイプライン互換のポイントライトやスポットライトや複数の組み合わせなどの描画
剛体メッシュのポイントライトあり描画
<実行に必要なファイル一式>
人型モデルのように一つの頂点が複数のフレーム( ボーン )の影響を受けない背景のようなモデルに
ポイントライトを当ててライブラリ側で設定されているマテリアルも適用して描画するサンプルプログラムです。
ディレクショナルライトとの違いはライトの方向を頂点とライトの位置から算出しているところと、
距離が遠くなればなるほど影響が少なくなる距離減衰計算をしているところです。
C++のプログラム
#include "DxLib.h"
#include <math.h>
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
int ModelHandle ;
int PixelShaderHandle ;
int VertexShaderHandle ;
float LightRotateAngle ;
// ウインドウモードで起動
ChangeWindowMode( TRUE ) ;
// Direct3D9Ex を使用する
SetUseDirect3DVersion( DX_DIRECT3D_9EX ) ;
// DXライブラリの初期化
if( DxLib_Init() < 0 )
{
// エラーが発生したら直ちに終了
return -1 ;
}
// プログラマブルシェーダーモデル2.0が使用できない場合はエラーを表示して終了
if( GetValidShaderVersion() < 200 )
{
// エラー表示
DrawString( 0, 0, "プログラマブルシェーダー2.0が使用できない環境のようです", GetColor( 255,255,255 ) ) ;
// キー入力待ち
WaitKey() ;
// DXライブラリの後始末
DxLib_End() ;
// ソフト終了
return 0 ;
}
// 頂点シェーダーを読み込む
VertexShaderHandle = LoadVertexShader( "NormalMesh_PointLightVS.vso" ) ;
// ピクセルシェーダーを読み込む
PixelShaderHandle = LoadPixelShader( "NormalMesh_PointLightPS.pso" ) ;
// 剛体メッシュモデルを読み込む
ModelHandle = MV1LoadModel( "NormalBox.mqo" ) ;
// 描画先を裏画面にする
SetDrawScreen( DX_SCREEN_BACK ) ;
// モデルの描画にオリジナルシェーダーを使用する設定をONにする
MV1SetUseOrigShader( TRUE ) ;
// 使用する頂点シェーダーをセット
SetUseVertexShader( VertexShaderHandle ) ;
// 使用するピクセルシェーダーをセット
SetUsePixelShader( PixelShaderHandle ) ;
// 観察しやすい位置にカメラを移動
SetCameraPositionAndTarget_UpVecY( VGet( 400.0f, 400.0f, -400.0f ), VGet( 0.0f, 0.0f, 0.0f ) ) ;
// ライトの位置を回転する値を初期化
LightRotateAngle = 0.0f ;
// 標準ライトのタイプをポイントライトにする
ChangeLightTypePoint( VGet( 0.0f, 0.0f, 0.0f ), 700.0f, 0.391586f, 0.001662f, 0.0f ) ;
// ESCキーが押されるまでループ
while( ProcessMessage() == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0 )
{
// 画面を初期化
ClearDrawScreen() ;
// ライトの位置の回転値を加算
LightRotateAngle += 0.02f ;
// ライトの位置の更新
SetLightPosition( VGet( sin( LightRotateAngle ) * 400.0f, 200.0f, cos( LightRotateAngle ) * 400.0f ) ) ;
// モデルを描画
MV1DrawModel( ModelHandle ) ;
// 裏画面の内容を表画面に反映させる
ScreenFlip() ;
}
// 読み込んだ頂点シェーダーの削除
DeleteShader( VertexShaderHandle ) ;
// 読み込んだピクセルシェーダーの削除
DeleteShader( PixelShaderHandle ) ;
// 読み込んだモデルの削除
MV1DeleteModel( ModelHandle ) ;
// DXライブラリの後始末
DxLib_End() ;
// ソフトの終了
return 0 ;
}
頂点シェーダーのプログラム
// 頂点シェーダーの入力
struct VS_INPUT
{
float4 Position : POSITION ; // 座標( ローカル空間 )
float3 Normal : NORMAL0 ; // 法線( ローカル空間 )
float4 Diffuse : COLOR0 ; // ディフューズカラー
float4 Specular : COLOR1 ; // スペキュラカラー
float4 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
} ;
// 頂点シェーダーの出力
struct VS_OUTPUT
{
float4 Position : POSITION ;
float4 Diffuse : COLOR0 ;
float4 Specular : COLOR1 ;
float2 TexCoords0 : TEXCOORD0 ;
} ;
// マテリアルパラメータ
struct VS_CONST_MATERIAL
{
float4 Diffuse ; // マテリアルディフューズカラー
float4 Specular ; // マテリアルスペキュラカラー
float4 Power ; // マテリアルスペキュラハイライトの強さ
} ;
// ライトパラメータ
struct VS_CONST_LIGHT
{
float4 Position ; // 座標( ビュー空間 )
float3 Direction ; // 方向( ビュー空間 )
float4 Diffuse ; // ディフューズカラー
float4 Specular ; // スペキュラカラー
float4 Ambient ; // アンビエントカラーとマテリアルのアンビエントカラーを乗算したもの
float4 Range_FallOff_AT0_AT1 ; // x:有効距離の二乗 y:スポットライト用FallOff z:距離による減衰処理用パラメータ0 w:距離による減衰処理用パラメータ1
float4 AT2_SpotP0_SpotP1 ; // x:距離による減衰処理用パラメータ2 y:スポットライト用パラメータ0( cos( Phi / 2.0f ) ) z:スポットライト用パラメータ1( 1.0f / ( cos( Theta / 2.0f ) - cos( Phi / 2.0f ) ) )
} ;
// C++ 側で設定する定数の定義
float4 cfAmbient_Emissive : register( c1 ) ; // マテリアルエミッシブカラー + マテリアルアンビエントカラー * グローバルアンビエントカラー
float4 cfProjectionMatrix[ 4 ] : register( c2 ) ; // ビュー → 射影行列
float4 cfViewMatrix[ 3 ] : register( c6 ) ; // ワールド → ビュー行列
float4 cfTextureMatrix[ 3 ][ 2 ] : register( c88 ) ; // テクスチャ座標操作用行列
float4 cfLocalWorldMatrix[ 3 ] : register( c94 ) ; // ローカル → ワールド行列
VS_CONST_MATERIAL cfMaterial : register( c11 ) ; // マテリアルパラメータ
VS_CONST_LIGHT cfLight : register( c14 ) ; // 有効ライト0番のパラメータ
// main関数
VS_OUTPUT main( VS_INPUT VSInput )
{
VS_OUTPUT VSOutput ;
float4 lWorldPosition ;
float4 lViewPosition ;
float3 lWorldNrm ;
float3 lViewNrm ;
float3 lLightHalfVec ;
float4 lLightLitParam ;
float4 lLightLitDest ;
float3 lLightDir ;
float3 lLightTemp ;
float lLightDistancePow2 ;
float lLightGen ;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ローカル座標をワールド座標に変換
lWorldPosition.x = dot( VSInput.Position, cfLocalWorldMatrix[ 0 ] ) ;
lWorldPosition.y = dot( VSInput.Position, cfLocalWorldMatrix[ 1 ] ) ;
lWorldPosition.z = dot( VSInput.Position, cfLocalWorldMatrix[ 2 ] ) ;
lWorldPosition.w = 1.0f ;
// ワールド座標をビュー座標に変換
lViewPosition.x = dot( lWorldPosition, cfViewMatrix[ 0 ] ) ;
lViewPosition.y = dot( lWorldPosition, cfViewMatrix[ 1 ] ) ;
lViewPosition.z = dot( lWorldPosition, cfViewMatrix[ 2 ] ) ;
lViewPosition.w = 1.0f ;
// ビュー座標を射影座標に変換
VSOutput.Position.x = dot( lViewPosition, cfProjectionMatrix[ 0 ] ) ;
VSOutput.Position.y = dot( lViewPosition, cfProjectionMatrix[ 1 ] ) ;
VSOutput.Position.z = dot( lViewPosition, cfProjectionMatrix[ 2 ] ) ;
VSOutput.Position.w = dot( lViewPosition, cfProjectionMatrix[ 3 ] ) ;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// ライトの処理 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// 法線をビュー空間の角度に変換 =====================================================( 開始 )
// ローカルベクトルをワールドベクトルに変換
lWorldNrm.x = dot( VSInput.Normal, cfLocalWorldMatrix[ 0 ].xyz ) ;
lWorldNrm.y = dot( VSInput.Normal, cfLocalWorldMatrix[ 1 ].xyz ) ;
lWorldNrm.z = dot( VSInput.Normal, cfLocalWorldMatrix[ 2 ].xyz ) ;
// ワールドベクトルをビューベクトルに変換
lViewNrm.x = dot( lWorldNrm, cfViewMatrix[ 0 ].xyz ) ;
lViewNrm.y = dot( lWorldNrm, cfViewMatrix[ 1 ].xyz ) ;
lViewNrm.z = dot( lWorldNrm, cfViewMatrix[ 2 ].xyz ) ;
// 法線をビュー空間の角度に変換 =====================================================( 終了 )
// ライト方向ベクトルの計算
lLightDir = normalize( lViewPosition.xyz - cfLight.Position.xyz ) ;
// 距離減衰値計算 ===================================================================( 開始 )
// 頂点とライト位置との距離の二乗を求める
lLightTemp = lViewPosition.xyz - cfLight.Position.xyz ;
lLightDistancePow2 = dot( lLightTemp, lLightTemp ) ;
// 減衰率の計算 lLightGen = 1 / ( 減衰値0 + 減衰値1 * 距離 + 減衰値2 * ( 距離 * 距離 ) )
lLightGen = 1.0f / ( cfLight.Range_FallOff_AT0_AT1.z + cfLight.Range_FallOff_AT0_AT1.w * sqrt( lLightDistancePow2 ) + cfLight.AT2_SpotP0_SpotP1.x * lLightDistancePow2 ) ;
// 有効距離外だったら減衰率を最大にする処理
lLightGen *= step( lLightDistancePow2, cfLight.Range_FallOff_AT0_AT1.x ) ;
// 距離減衰値計算 ===================================================================( 終了 )
// ライトディフューズカラーとライトスペキュラカラーの角度減衰計算 ===================( 開始 )
// 法線とライトの逆方向ベクトルとの内積を lLightLitParam.x にセット
lLightLitParam.x = dot( lViewNrm, -lLightDir ) ;
// ハーフベクトルの計算 norm( ( norm( 頂点位置から視点へのベクトル ) + ライトの方向 ) )
lLightHalfVec = normalize( normalize( -lViewPosition.xyz ) - lLightDir ) ;
// 法線とハーフベクトルの内積を lLightLitParam.y にセット
lLightLitParam.y = dot( lLightHalfVec, lViewNrm ) ;
// スペキュラ反射率を lLightLitParam.w にセット
lLightLitParam.w = cfMaterial.Power.x ;
// ライトパラメータ計算
lLightLitDest = lit( lLightLitParam.x, lLightLitParam.y, lLightLitParam.w ) ;
// ライトディフューズカラーとライトスペキュラカラーの角度減衰計算 ===================( 終了 )
// ライトの処理 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ディフューズカラー =
// 距離減衰値 *
// ( ディフューズ角度減衰計算結果 *
// ライトのディフューズカラー *
// マテリアルのディフューズカラー +
// ライトのアンビエントカラーとマテリアルのアンビエントカラーを乗算したもの ) +
// マテリアルのアンビエントカラーとグローバルアンビエントカラーを乗算したものとマテリアルエミッシブカラーを加算したもの
VSOutput.Diffuse = lLightGen * ( lLightLitDest.y * cfLight.Diffuse * cfMaterial.Diffuse + cfLight.Ambient ) + cfAmbient_Emissive ;
// ディフューズアルファはマテリアルのディフューズカラーのアルファをそのまま使う
VSOutput.Diffuse.w = cfMaterial.Diffuse.w ;
// スペキュラカラー = 距離減衰値 * スペキュラ角度減衰計算結果 * ライトのスペキュラカラー * マテリアルのスペキュラカラー
VSOutput.Specular = lLightGen * lLightLitDest.z * cfLight.Specular * cfMaterial.Specular ;
// テクスチャ座標変換行列による変換を行った結果のテクスチャ座標をセット
VSOutput.TexCoords0.x = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 0 ] ) ;
VSOutput.TexCoords0.y = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 1 ] ) ;
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータを返す
return VSOutput ;
}
ピクセルシェーダーのプログラム
// ピクセルシェーダーの入力
struct PS_INPUT
{
float4 Diffuse : COLOR0 ;
float4 Specular : COLOR1 ;
float2 TexCoords0 : TEXCOORD0 ;
} ;
// ピクセルシェーダーの出力
struct PS_OUTPUT
{
float4 Color0 : COLOR0 ;
} ;
// C++ 側で設定するテクスチャの定義
sampler DiffuseMapTexture : register( s0 ) ; // ディフューズマップテクスチャ
float4 cfFactorColor : register( c5 ) ; // 不透明度等
// main関数
PS_OUTPUT main( PS_INPUT PSInput )
{
PS_OUTPUT PSOutput ;
float4 TextureDiffuseColor ;
// テクスチャカラーの読み込み
TextureDiffuseColor = tex2D( DiffuseMapTexture, PSInput.TexCoords0.xy ) ;
// 出力カラー = ディフューズカラー * テクスチャカラー + スペキュラカラー
PSOutput.Color0 = PSInput.Diffuse * TextureDiffuseColor + PSInput.Specular ;
// 出力アルファ = ディフューズアルファ * テクスチャアルファ * 不透明度
PSOutput.Color0.a = PSInput.Diffuse.a * TextureDiffuseColor.a * cfFactorColor.a ;
// 出力パラメータを返す
return PSOutput ;
}
剛体メッシュのスポットライトあり描画
<実行に必要なファイル一式>
人型モデルのように一つの頂点が複数のフレーム( ボーン )の影響を受けない背景のようなモデルに
スポットライトを当ててライブラリ側で設定されているマテリアルも適用して描画するサンプルプログラムです。
スポットライトはポイントライトに角度減衰の処理を数行加えて実現します。
C++のプログラム
#include "DxLib.h"
#include <math.h>
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
int ModelHandle ;
int PixelShaderHandle ;
int VertexShaderHandle ;
float LightRotateAngle ;
VECTOR LightPosition ;
// ウインドウモードで起動
ChangeWindowMode( TRUE ) ;
// Direct3D9Ex を使用する
SetUseDirect3DVersion( DX_DIRECT3D_9EX ) ;
// DXライブラリの初期化
if( DxLib_Init() < 0 )
{
// エラーが発生したら直ちに終了
return -1 ;
}
// プログラマブルシェーダーモデル2.0が使用できない場合はエラーを表示して終了
if( GetValidShaderVersion() < 200 )
{
// エラー表示
DrawString( 0, 0, "プログラマブルシェーダー2.0が使用できない環境のようです", GetColor( 255,255,255 ) ) ;
// キー入力待ち
WaitKey() ;
// DXライブラリの後始末
DxLib_End() ;
// ソフト終了
return 0 ;
}
// 頂点シェーダーを読み込む
VertexShaderHandle = LoadVertexShader( "NormalMesh_SpotLightVS.vso" ) ;
// ピクセルシェーダーを読み込む
PixelShaderHandle = LoadPixelShader( "NormalMesh_SpotLightPS.pso" ) ;
// 剛体メッシュモデルを読み込む
ModelHandle = MV1LoadModel( "NormalBox.mqo" ) ;
// 描画先を裏画面にする
SetDrawScreen( DX_SCREEN_BACK ) ;
// モデルの描画にオリジナルシェーダーを使用する設定をONにする
MV1SetUseOrigShader( TRUE ) ;
// 使用する頂点シェーダーをセット
SetUseVertexShader( VertexShaderHandle ) ;
// 使用するピクセルシェーダーをセット
SetUsePixelShader( PixelShaderHandle ) ;
// 観察しやすい位置にカメラを移動
SetCameraPositionAndTarget_UpVecY( VGet( 400.0f, 400.0f, -400.0f ), VGet( 0.0f, 0.0f, 0.0f ) ) ;
// ライトの位置を回転する値を初期化
LightRotateAngle = 0.0f ;
// 標準ライトのタイプをスポットライトにする
ChangeLightTypeSpot( VGet( 0.0f, 0.0f, 0.0f ), VGet( 1.0f, 0.0f, 0.0f ), 0.7f, 0.4f, 700.0f, 0.391586f, 0.001662f, 0.0f ) ;
// アンビエントライトをOFFにする
SetLightAmbColor( GetColorF( 0.0f, 0.0f, 0.0f, 0.0f ) ) ;
// グローバルアンビエントライト( 大域環境光 )を20%の明るさにする
SetGlobalAmbientLight( GetColorF( 0.2f, 0.2f, 0.2f, 0.0f ) ) ;
// ESCキーが押されるまでループ
while( ProcessMessage() == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0 )
{
// 画面を初期化
ClearDrawScreen() ;
// ライトの位置の回転値を加算
LightRotateAngle += 0.02f ;
// ライトの位置の更新
LightPosition.x = sin( LightRotateAngle ) * 400.0f ;
LightPosition.y = 0.0f ;
LightPosition.z = cos( LightRotateAngle ) * 400.0f ;
SetLightPosition( LightPosition ) ;
// ライトの向きを原点方向にする
SetLightDirection( VScale( LightPosition, -1.0f ) ) ;
// モデルを描画
MV1DrawModel( ModelHandle ) ;
// 裏画面の内容を表画面に反映させる
ScreenFlip() ;
}
// 読み込んだ頂点シェーダーの削除
DeleteShader( VertexShaderHandle ) ;
// 読み込んだピクセルシェーダーの削除
DeleteShader( PixelShaderHandle ) ;
// 読み込んだモデルの削除
MV1DeleteModel( ModelHandle ) ;
// DXライブラリの後始末
DxLib_End() ;
// ソフトの終了
return 0 ;
}
頂点シェーダーのプログラム
// 頂点シェーダーの入力
struct VS_INPUT
{
float4 Position : POSITION ; // 座標( ローカル空間 )
float3 Normal : NORMAL0 ; // 法線( ローカル空間 )
float4 Diffuse : COLOR0 ; // ディフューズカラー
float4 Specular : COLOR1 ; // スペキュラカラー
float4 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
} ;
// 頂点シェーダーの出力
struct VS_OUTPUT
{
float4 Position : POSITION ;
float4 Diffuse : COLOR0 ;
float4 Specular : COLOR1 ;
float2 TexCoords0 : TEXCOORD0 ;
} ;
// マテリアルパラメータ
struct VS_CONST_MATERIAL
{
float4 Diffuse ; // マテリアルディフューズカラー
float4 Specular ; // マテリアルスペキュラカラー
float4 Power ; // マテリアルスペキュラハイライトの強さ
} ;
// ライトパラメータ
struct VS_CONST_LIGHT
{
float4 Position ; // 座標( ビュー空間 )
float3 Direction ; // 方向( ビュー空間 )
float4 Diffuse ; // ディフューズカラー
float4 Specular ; // スペキュラカラー
float4 Ambient ; // アンビエントカラーとマテリアルのアンビエントカラーを乗算したもの
float4 Range_FallOff_AT0_AT1 ; // x:有効距離 y:スポットライト用FallOff z:距離による減衰処理用パラメータ0 w:距離による減衰処理用パラメータ1
float4 AT2_SpotP0_SpotP1 ; // x:距離による減衰処理用パラメータ2 y:スポットライト用パラメータ0( cos( Phi / 2.0f ) ) z:スポットライト用パラメータ1( 1.0f / ( cos( Theta / 2.0f ) - cos( Phi / 2.0f ) ) )
} ;
// C++ 側で設定する定数の定義
float4 cfAmbient_Emissive : register( c1 ) ; // マテリアルエミッシブカラー + マテリアルアンビエントカラー * グローバルアンビエントカラー
float4 cfProjectionMatrix[ 4 ] : register( c2 ) ; // ビュー → 射影行列
float4 cfViewMatrix[ 3 ] : register( c6 ) ; // ワールド → ビュー行列
float4 cfTextureMatrix[ 3 ][ 2 ] : register( c88 ) ; // テクスチャ座標操作用行列
float4 cfLocalWorldMatrix[ 3 ] : register( c94 ) ; // ローカル → ワールド行列
VS_CONST_MATERIAL cfMaterial : register( c11 ) ; // マテリアルパラメータ
VS_CONST_LIGHT cfLight : register( c14 ) ; // 有効ライト0番のパラメータ
// main関数
VS_OUTPUT main( VS_INPUT VSInput )
{
VS_OUTPUT VSOutput ;
float4 lWorldPosition ;
float4 lViewPosition ;
float3 lWorldNrm ;
float3 lViewNrm ;
float3 lLightHalfVec ;
float4 lLightLitParam ;
float4 lLightLitDest ;
float3 lLightDir ;
float3 lLightTemp ;
float lLightDistancePow2 ;
float lLightGen ;
float lLightDirectionCosA ;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ローカル座標をワールド座標に変換
lWorldPosition.x = dot( VSInput.Position, cfLocalWorldMatrix[ 0 ] ) ;
lWorldPosition.y = dot( VSInput.Position, cfLocalWorldMatrix[ 1 ] ) ;
lWorldPosition.z = dot( VSInput.Position, cfLocalWorldMatrix[ 2 ] ) ;
lWorldPosition.w = 1.0f ;
// ワールド座標をビュー座標に変換
lViewPosition.x = dot( lWorldPosition, cfViewMatrix[ 0 ] ) ;
lViewPosition.y = dot( lWorldPosition, cfViewMatrix[ 1 ] ) ;
lViewPosition.z = dot( lWorldPosition, cfViewMatrix[ 2 ] ) ;
lViewPosition.w = 1.0f ;
// ビュー座標を射影座標に変換
VSOutput.Position.x = dot( lViewPosition, cfProjectionMatrix[ 0 ] ) ;
VSOutput.Position.y = dot( lViewPosition, cfProjectionMatrix[ 1 ] ) ;
VSOutput.Position.z = dot( lViewPosition, cfProjectionMatrix[ 2 ] ) ;
VSOutput.Position.w = dot( lViewPosition, cfProjectionMatrix[ 3 ] ) ;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// ライトの処理 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// 法線をビュー空間の角度に変換 =====================================================( 開始 )
// ローカルベクトルをワールドベクトルに変換
lWorldNrm.x = dot( VSInput.Normal, cfLocalWorldMatrix[ 0 ].xyz ) ;
lWorldNrm.y = dot( VSInput.Normal, cfLocalWorldMatrix[ 1 ].xyz ) ;
lWorldNrm.z = dot( VSInput.Normal, cfLocalWorldMatrix[ 2 ].xyz ) ;
// ワールドベクトルをビューベクトルに変換
lViewNrm.x = dot( lWorldNrm, cfViewMatrix[ 0 ].xyz ) ;
lViewNrm.y = dot( lWorldNrm, cfViewMatrix[ 1 ].xyz ) ;
lViewNrm.z = dot( lWorldNrm, cfViewMatrix[ 2 ].xyz ) ;
// 法線をビュー空間の角度に変換 =====================================================( 終了 )
// ライト方向ベクトルの計算
lLightDir = normalize( lViewPosition.xyz - cfLight.Position.xyz ) ;
// 距離・スポットライト減衰値計算 ===================================================( 開始 )
// 距離減衰計算 ------------------
// 頂点とライト位置との距離の二乗を求める
lLightTemp = lViewPosition.xyz - cfLight.Position.xyz ;
lLightDistancePow2 = dot( lLightTemp, lLightTemp ) ;
// 減衰率の計算 lLightGen = 1 / ( 減衰値0 + 減衰値1 * 距離 + 減衰値2 * ( 距離 * 距離 ) )
lLightGen = 1.0f / ( cfLight.Range_FallOff_AT0_AT1.z + cfLight.Range_FallOff_AT0_AT1.w * sqrt( lLightDistancePow2 ) + cfLight.AT2_SpotP0_SpotP1.x * lLightDistancePow2 ) ;
// --------------------------------
// スポットライト減衰計算 --------
// ライト方向ベクトルとライト位置から頂点位置へのベクトルの内積( 即ち Cos a )を計算
lLightDirectionCosA = dot( lLightDir, cfLight.Direction ) ;
// スポットライト減衰計算 pow( falloff, ( ( Cos a - Cos f ) / ( Cos q - Cos f ) ) )
lLightGen *= saturate( pow( abs( max( lLightDirectionCosA - cfLight.AT2_SpotP0_SpotP1.y, 0.0f ) * cfLight.AT2_SpotP0_SpotP1.z ), cfLight.Range_FallOff_AT0_AT1.y ) ) ;
// --------------------------------
// 有効距離外だったら減衰率を最大にする処理
lLightGen *= step( lLightDistancePow2, cfLight.Range_FallOff_AT0_AT1.x ) ;
// 距離・スポットライト減衰値計算 ===================================================( 終了 )
// ライトディフューズカラーとライトスペキュラカラーの角度減衰計算 ===================( 開始 )
// 法線とライトの逆方向ベクトルとの内積を lLightLitParam.x にセット
lLightLitParam.x = dot( lViewNrm, -lLightDir ) ;
// ハーフベクトルの計算 norm( ( norm( 頂点位置から視点へのベクトル ) + ライトの方向 ) )
lLightHalfVec = normalize( normalize( -lViewPosition.xyz ) - lLightDir ) ;
// 法線とハーフベクトルの内積を lLightLitParam.y にセット
lLightLitParam.y = dot( lLightHalfVec, lViewNrm ) ;
// スペキュラ反射率を lLightLitParam.w にセット
lLightLitParam.w = cfMaterial.Power.x ;
// ライトパラメータ計算
lLightLitDest = lit( lLightLitParam.x, lLightLitParam.y, lLightLitParam.w ) ;
// ライトディフューズカラーとライトスペキュラカラーの角度減衰計算 ===================( 終了 )
// ライトの処理 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ディフューズカラー =
// 距離減衰値 *
// ( ディフューズ角度減衰計算結果 *
// ライトのディフューズカラー *
// マテリアルのディフューズカラー +
// ライトのアンビエントカラーとマテリアルのアンビエントカラーを乗算したもの ) +
// マテリアルのアンビエントカラーとグローバルアンビエントカラーを乗算したものとマテリアルエミッシブカラーを加算したもの
VSOutput.Diffuse = lLightGen * ( lLightLitDest.y * cfLight.Diffuse * cfMaterial.Diffuse + cfLight.Ambient ) + cfAmbient_Emissive ;
// ディフューズアルファはマテリアルのディフューズカラーのアルファをそのまま使う
VSOutput.Diffuse.w = cfMaterial.Diffuse.w ;
// スペキュラカラー = 距離減衰値 * スペキュラ角度減衰計算結果 * ライトのスペキュラカラー * マテリアルのスペキュラカラー
VSOutput.Specular = lLightGen * lLightLitDest.z * cfLight.Specular * cfMaterial.Specular ;
// テクスチャ座標変換行列による変換を行った結果のテクスチャ座標をセット
VSOutput.TexCoords0.x = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 0 ] ) ;
VSOutput.TexCoords0.y = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 1 ] ) ;
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータを返す
return VSOutput ;
}
ピクセルシェーダーのプログラム
// ピクセルシェーダーの入力
struct PS_INPUT
{
float4 Diffuse : COLOR0 ;
float4 Specular : COLOR1 ;
float2 TexCoords0 : TEXCOORD0 ;
} ;
// ピクセルシェーダーの出力
struct PS_OUTPUT
{
float4 Color0 : COLOR0 ;
} ;
// C++ 側で設定するテクスチャの定義
sampler DiffuseMapTexture : register( s0 ) ; // ディフューズマップテクスチャ
float4 cfFactorColor : register( c5 ) ; // 不透明度等
// main関数
PS_OUTPUT main( PS_INPUT PSInput )
{
PS_OUTPUT PSOutput ;
float4 TextureDiffuseColor ;
// テクスチャカラーの読み込み
TextureDiffuseColor = tex2D( DiffuseMapTexture, PSInput.TexCoords0.xy ) ;
// 出力カラー = ディフューズカラー * テクスチャカラー + スペキュラカラー
PSOutput.Color0 = PSInput.Diffuse * TextureDiffuseColor + PSInput.Specular ;
// 出力アルファ = ディフューズアルファ * テクスチャアルファ * 不透明度
PSOutput.Color0.a = PSInput.Diffuse.a * TextureDiffuseColor.a * cfFactorColor.a ;
// 出力パラメータを返す
return PSOutput ;
}
剛体メッシュのディレクショナルライトとポイントライトあり描画
<実行に必要なファイル一式>
人型モデルのように一つの頂点が複数のフレーム( ボーン )の影響を受けない背景のようなモデルに
ディレクショナルライトとポイントライトを当ててライブラリ側で設定されているマテリアルも適用して描画するサンプルプログラムです。
ライトが複数になった場合、ライト毎の計算結果を蓄積して最後にライト毎のパラメータの影響を受けないパラメータを加算したりして
最終結果にしています。これも固定機能パイプライン互換の為なので、オリジナルのライティング計算を作成する場合はもっとシンプルに
してしまっても問題ないと思います。
あとライトパラメータ0をディレクショナルライト、ライトパラメータ1をポイントライトと決め打ちしていますが、DXライブラリでは
有効になっているライトハンドルは「ディレクショナルライト」→「スポットライト」→「ポイントライト」の順番でパラメータが
格納されるので、今回はディレクショナルライト1個、ポイントライト1個なので、ライト0がディレクショナルライト、ライト1が
ポイントライトと決め打ちしても問題ない、というわけです。
C++のプログラム
#include "DxLib.h"
#include <math.h>
#define DRAW_NUM (3)
#define SPACE (512.0f)
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
int ModelHandle ;
int PixelShaderHandle ;
int VertexShaderHandle ;
float LightRotateAngle ;
int DirLightHandle ;
int PointLightHandle ;
int i, j ;
float DrawX, DrawZ ;
// ウインドウモードで起動
ChangeWindowMode( TRUE ) ;
// Direct3D9Ex を使用する
SetUseDirect3DVersion( DX_DIRECT3D_9EX ) ;
// DXライブラリの初期化
if( DxLib_Init() < 0 )
{
// エラーが発生したら直ちに終了
return -1 ;
}
// プログラマブルシェーダーモデル2.0が使用できない場合はエラーを表示して終了
if( GetValidShaderVersion() < 200 )
{
// エラー表示
DrawString( 0, 0, "プログラマブルシェーダー2.0が使用できない環境のようです", GetColor( 255,255,255 ) ) ;
// キー入力待ち
WaitKey() ;
// DXライブラリの後始末
DxLib_End() ;
// ソフト終了
return 0 ;
}
// 頂点シェーダーを読み込む
VertexShaderHandle = LoadVertexShader( "NormalMesh_DirPointLightVS.vso" ) ;
// ピクセルシェーダーを読み込む
PixelShaderHandle = LoadPixelShader( "NormalMesh_DirPointLightPS.pso" ) ;
// 剛体メッシュモデルを読み込む
ModelHandle = MV1LoadModel( "NormalBox.mqo" ) ;
// 描画先を裏画面にする
SetDrawScreen( DX_SCREEN_BACK ) ;
// モデルの描画にオリジナルシェーダーを使用する設定をONにする
MV1SetUseOrigShader( TRUE ) ;
// 使用する頂点シェーダーをセット
SetUseVertexShader( VertexShaderHandle ) ;
// 使用するピクセルシェーダーをセット
SetUsePixelShader( PixelShaderHandle ) ;
// 観察しやすい位置にカメラを移動
SetCameraPositionAndTarget_UpVecY( VGet( 800.0f, 400.0f, -800.0f ), VGet( 0.0f, 0.0f, 0.0f ) ) ;
// ライトの位置を回転する値を初期化
LightRotateAngle = 0.0f ;
// 標準ライトを無効にする
SetLightEnable( FALSE ) ;
// ディレクショナルライトを作成する
DirLightHandle = CreateDirLightHandle( VGet( -1.0f, 0.0f, 0.0f ) ) ;
// ディレクショナルライトのアンビエントカラーを抑える
SetLightAmbColorHandle( DirLightHandle, GetColorF( 0.0f, 0.1f, 0.0f, 0.0f ) ) ;
// ディレクショナルライトのディフューズカラーを緑にする
SetLightDifColorHandle( DirLightHandle, GetColorF( 0.0f, 1.0f, 0.0f, 0.0f ) ) ;
// ポイントライトを作成する
PointLightHandle = CreatePointLightHandle( VGet( 0.0f, 0.0f, 0.0f ), 7000.0f, 1.016523f, 0.000100f, 0.000010f ) ;
// ポイントライトのアンビエントカラーを無効にする
SetLightAmbColorHandle( PointLightHandle, GetColorF( 0.0f, 0.0f, 0.0f, 0.0f ) ) ;
// ポイントライトのディフューズカラーを強い赤色にする
SetLightDifColorHandle( PointLightHandle, GetColorF( 2.0f, 0.0f, 0.0f, 0.0f ) ) ;
// ESCキーが押されるまでループ
while( ProcessMessage() == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0 )
{
// 画面を初期化
ClearDrawScreen() ;
// ポイントライトの位置の回転値を加算
LightRotateAngle += 0.02f ;
// ポイントライトの位置の更新
SetLightPositionHandle( PointLightHandle, VGet( sin( LightRotateAngle ) * 400.0f, 400.0f, cos( LightRotateAngle ) * 400.0f ) ) ;
// モデルを描画
DrawZ = - ( DRAW_NUM - 1 ) * SPACE / 2.0f ;
for( i = 0 ; i < DRAW_NUM ; i ++ )
{
DrawX = - ( DRAW_NUM - 1 ) * SPACE / 2.0f ;
for( j = 0 ; j < DRAW_NUM ; j ++ )
{
// 位置を設定
MV1SetPosition( ModelHandle, VGet( DrawX, 0.0f, DrawZ ) ) ;
// 描画
MV1DrawModel( ModelHandle ) ;
DrawX += SPACE ;
}
DrawZ += SPACE ;
}
// 裏画面の内容を表画面に反映させる
ScreenFlip() ;
}
// ディレクショナルライトの削除
DeleteLightHandle( DirLightHandle ) ;
// ポイントライトの削除
DeleteLightHandle( PointLightHandle ) ;
// 読み込んだ頂点シェーダーの削除
DeleteShader( VertexShaderHandle ) ;
// 読み込んだピクセルシェーダーの削除
DeleteShader( PixelShaderHandle ) ;
// 読み込んだモデルの削除
MV1DeleteModel( ModelHandle ) ;
// DXライブラリの後始末
DxLib_End() ;
// ソフトの終了
return 0 ;
}
頂点シェーダーのプログラム
// 頂点シェーダーの入力
struct VS_INPUT
{
float4 Position : POSITION ; // 座標( ローカル空間 )
float3 Normal : NORMAL0 ; // 法線( ローカル空間 )
float4 Diffuse : COLOR0 ; // ディフューズカラー
float4 Specular : COLOR1 ; // スペキュラカラー
float4 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
} ;
// 頂点シェーダーの出力
struct VS_OUTPUT
{
float4 Position : POSITION ;
float4 Diffuse : COLOR0 ;
float4 Specular : COLOR1 ;
float2 TexCoords0 : TEXCOORD0 ;
} ;
// マテリアルパラメータ
struct VS_CONST_MATERIAL
{
float4 Diffuse ; // マテリアルディフューズカラー
float4 Specular ; // マテリアルスペキュラカラー
float4 Power ; // マテリアルスペキュラハイライトの強さ
} ;
// ライトパラメータ
struct VS_CONST_LIGHT
{
float4 Position ; // 座標( ビュー空間 )
float3 Direction ; // 方向( ビュー空間 )
float4 Diffuse ; // ディフューズカラー
float4 Specular ; // スペキュラカラー
float4 Ambient ; // アンビエントカラーとマテリアルのアンビエントカラーを乗算したもの
float4 Range_FallOff_AT0_AT1 ; // x:有効距離 y:スポットライト用FallOff z:距離による減衰処理用パラメータ0 w:距離による減衰処理用パラメータ1
float4 AT2_SpotP0_SpotP1 ; // x:距離による減衰処理用パラメータ2 y:スポットライト用パラメータ0( cos( Phi / 2.0f ) ) z:スポットライト用パラメータ1( 1.0f / ( cos( Theta / 2.0f ) - cos( Phi / 2.0f ) ) )
} ;
// C++ 側で設定する定数の定義
float4 cfAmbient_Emissive : register( c1 ) ; // マテリアルエミッシブカラー + マテリアルアンビエントカラー * グローバルアンビエントカラー
float4 cfProjectionMatrix[ 4 ] : register( c2 ) ; // ビュー → 射影行列
float4 cfViewMatrix[ 3 ] : register( c6 ) ; // ワールド → ビュー行列
float4 cfTextureMatrix[ 3 ][ 2 ] : register( c88 ) ; // テクスチャ座標操作用行列
float4 cfLocalWorldMatrix[ 3 ] : register( c94 ) ; // ローカル → ワールド行列
VS_CONST_MATERIAL cfMaterial : register( c11 ) ; // マテリアルパラメータ
VS_CONST_LIGHT cfLight[ 2 ] : register( c14 ) ; // ディレクショナルライトとポイントライトのパラメータ
// main関数
VS_OUTPUT main( VS_INPUT VSInput )
{
VS_OUTPUT VSOutput ;
float4 lWorldPosition ;
float4 lViewPosition ;
float3 lWorldNrm ;
float3 lViewNrm ;
float3 lLightHalfVec ;
float4 lLightLitParam ;
float4 lLightLitDest ;
float3 lLightDir ;
float3 lLightTemp ;
float lLightDistancePow2 ;
float lLightGen ;
float4 lTotalDiffuse ;
float4 lTotalSpecular ;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ローカル座標をワールド座標に変換
lWorldPosition.x = dot( VSInput.Position, cfLocalWorldMatrix[ 0 ] ) ;
lWorldPosition.y = dot( VSInput.Position, cfLocalWorldMatrix[ 1 ] ) ;
lWorldPosition.z = dot( VSInput.Position, cfLocalWorldMatrix[ 2 ] ) ;
lWorldPosition.w = 1.0f ;
// ワールド座標をビュー座標に変換
lViewPosition.x = dot( lWorldPosition, cfViewMatrix[ 0 ] ) ;
lViewPosition.y = dot( lWorldPosition, cfViewMatrix[ 1 ] ) ;
lViewPosition.z = dot( lWorldPosition, cfViewMatrix[ 2 ] ) ;
lViewPosition.w = 1.0f ;
// ビュー座標を射影座標に変換
VSOutput.Position.x = dot( lViewPosition, cfProjectionMatrix[ 0 ] ) ;
VSOutput.Position.y = dot( lViewPosition, cfProjectionMatrix[ 1 ] ) ;
VSOutput.Position.z = dot( lViewPosition, cfProjectionMatrix[ 2 ] ) ;
VSOutput.Position.w = dot( lViewPosition, cfProjectionMatrix[ 3 ] ) ;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// ライトの処理 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ディフューズカラーとスペキュラカラーの蓄積値の初期化
lTotalDiffuse = float4( 0, 0, 0, 0 ) ;
lTotalSpecular = float4( 0, 0, 0, 0 ) ;
// 法線をビュー空間の角度に変換 =====================================================( 開始 )
// ローカルベクトルをワールドベクトルに変換
lWorldNrm.x = dot( VSInput.Normal, cfLocalWorldMatrix[ 0 ].xyz ) ;
lWorldNrm.y = dot( VSInput.Normal, cfLocalWorldMatrix[ 1 ].xyz ) ;
lWorldNrm.z = dot( VSInput.Normal, cfLocalWorldMatrix[ 2 ].xyz ) ;
// ワールドベクトルをビューベクトルに変換
lViewNrm.x = dot( lWorldNrm, cfViewMatrix[ 0 ].xyz ) ;
lViewNrm.y = dot( lWorldNrm, cfViewMatrix[ 1 ].xyz ) ;
lViewNrm.z = dot( lWorldNrm, cfViewMatrix[ 2 ].xyz ) ;
// 法線をビュー空間の角度に変換 =====================================================( 終了 )
// ディレクショナルライトの処理 *****************************************************( 開始 )
// ライトの方向セット
lLightDir = cfLight[ 0 ].Direction ;
// ライトディフューズカラーとライトスペキュラカラーの角度減衰計算 =======( 開始 )
// 法線とライトの逆方向ベクトルとの内積を lLightLitParam.x にセット
lLightLitParam.x = dot( lViewNrm, -lLightDir ) ;
// ハーフベクトルの計算 norm( ( norm( 頂点位置から視点へのベクトル ) + ライトの方向 ) )
lLightHalfVec = normalize( normalize( -lViewPosition.xyz ) - lLightDir ) ;
// 法線とハーフベクトルの内積を lLightLitParam.y にセット
lLightLitParam.y = dot( lLightHalfVec, lViewNrm ) ;
// スペキュラ反射率を lLightLitParam.w にセット
lLightLitParam.w = cfMaterial.Power.x ;
// ライト計算
lLightLitDest = lit( lLightLitParam.x, lLightLitParam.y, lLightLitParam.w ) ;
// ライトディフューズカラーとライトスペキュラカラーの角度減衰計算 =======( 終了 )
// カラー計算 ===========================================================( 開始 )
// ディフューズライト蓄積値 += ディフューズ角度減衰計算結果 * マテリアルディフューズカラー * ライトのディフューズカラー + ライトのアンビエントカラーとマテリアルのアンビエントカラーを乗算したもの
lTotalDiffuse += lLightLitDest.y * cfLight[ 0 ].Diffuse * cfMaterial.Diffuse + cfLight[ 0 ].Ambient ;
// スペキュラライト蓄積値 += スペキュラ角度減衰計算結果 * ライトのスペキュラカラー
lTotalSpecular += lLightLitDest.z * cfLight[ 0 ].Specular ;
// カラー計算 ===========================================================( 終了 )
// ディレクショナルライトの処理 *****************************************************( 終了 )
// ポイントライトの処理 *************************************************************( 開始 )
// ライト方向ベクトルの計算
lLightDir = normalize( lViewPosition.xyz - cfLight[ 1 ].Position.xyz ) ;
// 距離減衰値計算 =======================================================( 開始 )
// 頂点とライト位置との距離の二乗を求める
lLightTemp = lViewPosition.xyz - cfLight[ 1 ].Position.xyz ;
lLightDistancePow2 = dot( lLightTemp, lLightTemp ) ;
// 減衰率の計算 lLightGen = 1 / ( 減衰値0 + 減衰値1 * 距離 + 減衰値2 * ( 距離 * 距離 ) )
lLightGen = 1.0f / ( cfLight[ 1 ].Range_FallOff_AT0_AT1.z + cfLight[ 1 ].Range_FallOff_AT0_AT1.w * sqrt( lLightDistancePow2 ) + cfLight[ 1 ].AT2_SpotP0_SpotP1.x * lLightDistancePow2 ) ;
// 有効距離外だったら減衰率を最大にする処理
lLightGen *= step( lLightDistancePow2, cfLight[ 1 ].Range_FallOff_AT0_AT1.x ) ;
// 距離減衰値計算 =======================================================( 終了 )
// ライトディフューズカラーとライトスペキュラカラーの角度減衰計算 =======( 開始 )
// 法線とライトの逆方向ベクトルとの内積を lLightLitParam.x にセット
lLightLitParam.x = dot( lViewNrm, -lLightDir ) ;
// ハーフベクトルの計算 norm( ( norm( 頂点位置から視点へのベクトル ) + ライトの方向 ) )
lLightHalfVec = normalize( normalize( -lViewPosition.xyz ) - lLightDir ) ;
// 法線とハーフベクトルの内積を lLightLitParam.y にセット
lLightLitParam.y = dot( lLightHalfVec, lViewNrm ) ;
// スペキュラ反射率を lLightLitParam.w にセット
lLightLitParam.w = cfMaterial.Power.x ;
// ライト計算
lLightLitDest = lit( lLightLitParam.x, lLightLitParam.y, lLightLitParam.w ) ;
// ライトディフューズカラーとライトスペキュラカラーの角度減衰計算 =======( 終了 )
// カラー計算 ===========================================================( 開始 )
// ディフーズライト蓄積値 += 距離・スポットライト角度減衰値 * ( ディフーズ角度減衰計算結果 * マテリアルディフューズカラー * ライトのディフーズカラー + ライトのアンビエントカラーとマテリアルのアンビエントカラーを乗算したもの )
lTotalDiffuse += lLightGen * ( lLightLitDest.y * cfLight[ 1 ].Diffuse * cfMaterial.Diffuse + cfLight[ 1 ].Ambient ) ;
// スペキュラライト蓄積値 += スペキュラ角度減衰計算結果 * 距離・スポットライト減衰 * ライトのスペキュラカラー
lTotalSpecular += lLightGen * lLightLitDest.z * cfLight[ 1 ].Specular ;
// カラー計算 ===========================================================( 終了 )
// ポイントライトの処理 *************************************************************( 終了 )
// ライトの処理 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ディフューズカラー = ディフューズライト蓄積値 + マテリアルのアンビエントカラーとグローバルアンビエントカラーを乗算したものとマテリアルエミッシブカラーを加算したもの
VSOutput.Diffuse = lTotalDiffuse + cfAmbient_Emissive ;
// ディフューズアルファはマテリアルのディフューズカラーのアルファをそのまま使う
VSOutput.Diffuse.w = cfMaterial.Diffuse.w ;
// スペキュラカラー = スペキュラライト蓄積値 * マテリアルのスペキュラカラー
VSOutput.Specular = lTotalSpecular * cfMaterial.Specular ;
// テクスチャ座標変換行列による変換を行った結果のテクスチャ座標をセット
VSOutput.TexCoords0.x = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 0 ] ) ;
VSOutput.TexCoords0.y = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 1 ] ) ;
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータを返す
return VSOutput ;
}
ピクセルシェーダーのプログラム
// ピクセルシェーダーの入力
struct PS_INPUT
{
float4 Diffuse : COLOR0 ;
float4 Specular : COLOR1 ;
float2 TexCoords0 : TEXCOORD0 ;
} ;
// ピクセルシェーダーの出力
struct PS_OUTPUT
{
float4 Color0 : COLOR0 ;
} ;
// C++ 側で設定するテクスチャの定義
sampler DiffuseMapTexture : register( s0 ) ; // ディフューズマップテクスチャ
float4 cfFactorColor : register( c5 ) ; // 不透明度等
// main関数
PS_OUTPUT main( PS_INPUT PSInput )
{
PS_OUTPUT PSOutput ;
float4 TextureDiffuseColor ;
// テクスチャカラーの読み込み
TextureDiffuseColor = tex2D( DiffuseMapTexture, PSInput.TexCoords0.xy ) ;
// 出力カラー = ディフューズカラー * テクスチャカラー + スペキュラカラー
PSOutput.Color0 = PSInput.Diffuse * TextureDiffuseColor + PSInput.Specular ;
// 出力アルファ = ディフューズアルファ * テクスチャアルファ * 不透明度
PSOutput.Color0.a = PSInput.Diffuse.a * TextureDiffuseColor.a * cfFactorColor.a ;
// 出力パラメータを返す
return PSOutput ;
}
剛体メッシュのスポットライトとポイントライトあり描画
<実行に必要なファイル一式>
人型モデルのように一つの頂点が複数のフレーム( ボーン )の影響を受けない背景のようなモデルに
スポットライトとポイントライトを当ててライブラリ側で設定されているマテリアルも適用して描画するサンプルプログラムです。
ライトが複数になった場合、ライト毎の計算結果を蓄積して最後にライト毎のパラメータの影響を受けないパラメータを加算したりして
最終結果にしています。これも固定機能パイプライン互換の為なので、オリジナルのライティング計算を作成する場合はもっとシンプルに
してしまっても問題ないと思います。
あとライトパラメータ0をスポットライト、ライトパラメータ1をポイントライトと決め打ちしていますが、DXライブラリでは
有効になっているライトハンドルは「ディレクショナルライト」→「スポットライト」→「ポイントライト」の順番でパラメータが
格納されるので、今回はスポットライト1個、ポイントライト1個なので、ライト0がスポットライト、ライト1がポイントライトと
決め打ちしても問題ない、というわけです。
C++のプログラム
#include "DxLib.h"
#include <math.h>
#define DRAW_NUM (3)
#define SPACE (512.0f)
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
int ModelHandle ;
int PixelShaderHandle ;
int VertexShaderHandle ;
float LightRotateAngle ;
int SpotLightHandle ;
int PointLightHandle ;
int i, j ;
float DrawX, DrawZ ;
// ウインドウモードで起動
ChangeWindowMode( TRUE ) ;
// Direct3D9Ex を使用する
SetUseDirect3DVersion( DX_DIRECT3D_9EX ) ;
// DXライブラリの初期化
if( DxLib_Init() < 0 )
{
// エラーが発生したら直ちに終了
return -1 ;
}
// プログラマブルシェーダーモデル2.0が使用できない場合はエラーを表示して終了
if( GetValidShaderVersion() < 200 )
{
// エラー表示
DrawString( 0, 0, "プログラマブルシェーダー2.0が使用できない環境のようです", GetColor( 255,255,255 ) ) ;
// キー入力待ち
WaitKey() ;
// DXライブラリの後始末
DxLib_End() ;
// ソフト終了
return 0 ;
}
// 頂点シェーダーを読み込む
VertexShaderHandle = LoadVertexShader( "NormalMesh_SpotPointLightVS.vso" ) ;
// ピクセルシェーダーを読み込む
PixelShaderHandle = LoadPixelShader( "NormalMesh_SpotPointLightPS.pso" ) ;
// 剛体メッシュモデルを読み込む
ModelHandle = MV1LoadModel( "NormalBox.mqo" ) ;
// 描画先を裏画面にする
SetDrawScreen( DX_SCREEN_BACK ) ;
// モデルの描画にオリジナルシェーダーを使用する設定をONにする
MV1SetUseOrigShader( TRUE ) ;
// 使用する頂点シェーダーをセット
SetUseVertexShader( VertexShaderHandle ) ;
// 使用するピクセルシェーダーをセット
SetUsePixelShader( PixelShaderHandle ) ;
// 観察しやすい位置にカメラを移動
SetCameraPositionAndTarget_UpVecY( VGet( 800.0f, 400.0f, -800.0f ), VGet( 0.0f, 0.0f, 0.0f ) ) ;
// ライトの位置を回転する値を初期化
LightRotateAngle = 0.0f ;
// 標準ライトを無効にする
SetLightEnable( FALSE ) ;
// スポットライトを作成する
SpotLightHandle = CreateSpotLightHandle( VGet( 0.0f, 0.0f, 0.0f ), VGet( 0.0f, -1.0f, 0.0f ), 0.7f, 0.6f, 1000.0f, 0.391586f, 0.001662f, 0.0f ) ;
// スポットライトのアンビエントカラーを無効にする
SetLightAmbColorHandle( SpotLightHandle, GetColorF( 0.0f, 0.0f, 0.0f, 0.0f ) ) ;
// スポットライトのディフューズカラーを緑にする
SetLightDifColorHandle( SpotLightHandle, GetColorF( 0.0f, 1.0f, 0.0f, 0.0f ) ) ;
// ポイントライトを作成する
PointLightHandle = CreatePointLightHandle( VGet( 0.0f, 0.0f, 0.0f ), 7000.0f, 1.016523f, 0.000100f, 0.000010f ) ;
// ポイントライトのアンビエントカラーを無効にする
SetLightAmbColorHandle( PointLightHandle, GetColorF( 0.0f, 0.0f, 0.0f, 0.0f ) ) ;
// ポイントライトのディフューズカラーを強い赤色にする
SetLightDifColorHandle( PointLightHandle, GetColorF( 2.0f, 0.0f, 0.0f, 0.0f ) ) ;
// グローバルアンビエントライト( 大域環境光 )を20%の明るさにする
SetGlobalAmbientLight( GetColorF( 0.2f, 0.2f, 0.2f, 0.0f ) ) ;
// ESCキーが押されるまでループ
while( ProcessMessage() == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0 )
{
// 画面を初期化
ClearDrawScreen() ;
// ライトの位置の回転値を加算
LightRotateAngle += 0.02f ;
// ポイントライトの位置の更新
SetLightPositionHandle( PointLightHandle, VGet( sin( LightRotateAngle ) * 500.0f, 400.0f, cos( LightRotateAngle ) * 500.0f ) ) ;
// スポットライトの位置の更新
SetLightPositionHandle( SpotLightHandle, VGet( sin( -LightRotateAngle ) * 500.0f, 800.0f, cos( -LightRotateAngle ) * 500.0f ) ) ;
// モデルを描画
DrawZ = - ( DRAW_NUM - 1 ) * SPACE / 2.0f ;
for( i = 0 ; i < DRAW_NUM ; i ++ )
{
DrawX = - ( DRAW_NUM - 1 ) * SPACE / 2.0f ;
for( j = 0 ; j < DRAW_NUM ; j ++ )
{
// 位置を設定
MV1SetPosition( ModelHandle, VGet( DrawX, 0.0f, DrawZ ) ) ;
// 描画
MV1DrawModel( ModelHandle ) ;
DrawX += SPACE ;
}
DrawZ += SPACE ;
}
// 裏画面の内容を表画面に反映させる
ScreenFlip() ;
}
// スポットライトの削除
DeleteLightHandle( SpotLightHandle ) ;
// ポイントライトの削除
DeleteLightHandle( PointLightHandle ) ;
// 読み込んだ頂点シェーダーの削除
DeleteShader( VertexShaderHandle ) ;
// 読み込んだピクセルシェーダーの削除
DeleteShader( PixelShaderHandle ) ;
// 読み込んだモデルの削除
MV1DeleteModel( ModelHandle ) ;
// DXライブラリの後始末
DxLib_End() ;
// ソフトの終了
return 0 ;
}
頂点シェーダーのプログラム
// 頂点シェーダーの入力
struct VS_INPUT
{
float4 Position : POSITION ; // 座標( ローカル空間 )
float3 Normal : NORMAL0 ; // 法線( ローカル空間 )
float4 Diffuse : COLOR0 ; // ディフューズカラー
float4 Specular : COLOR1 ; // スペキュラカラー
float4 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
} ;
// 頂点シェーダーの出力
struct VS_OUTPUT
{
float4 Position : POSITION ;
float4 Diffuse : COLOR0 ;
float4 Specular : COLOR1 ;
float2 TexCoords0 : TEXCOORD0 ;
} ;
// マテリアルパラメータ
struct VS_CONST_MATERIAL
{
float4 Diffuse ; // マテリアルディフューズカラー
float4 Specular ; // マテリアルスペキュラカラー
float4 Power ; // マテリアルスペキュラハイライトの強さ
} ;
// ライトパラメータ
struct VS_CONST_LIGHT
{
float4 Position ; // 座標( ビュー空間 )
float3 Direction ; // 方向( ビュー空間 )
float4 Diffuse ; // ディフューズカラー
float4 Specular ; // スペキュラカラー
float4 Ambient ; // アンビエントカラーとマテリアルのアンビエントカラーを乗算したもの
float4 Range_FallOff_AT0_AT1 ; // x:有効距離 y:スポットライト用FallOff z:距離による減衰処理用パラメータ0 w:距離による減衰処理用パラメータ1
float4 AT2_SpotP0_SpotP1 ; // x:距離による減衰処理用パラメータ2 y:スポットライト用パラメータ0( cos( Phi / 2.0f ) ) z:スポットライト用パラメータ1( 1.0f / ( cos( Theta / 2.0f ) - cos( Phi / 2.0f ) ) )
} ;
// C++ 側で設定する定数の定義
float4 cfAmbient_Emissive : register( c1 ) ; // マテリアルエミッシブカラー + マテリアルアンビエントカラー * グローバルアンビエントカラー
float4 cfProjectionMatrix[ 4 ] : register( c2 ) ; // ビュー → 射影行列
float4 cfViewMatrix[ 3 ] : register( c6 ) ; // ワールド → ビュー行列
float4 cfTextureMatrix[ 3 ][ 2 ] : register( c88 ) ; // テクスチャ座標操作用行列
float4 cfLocalWorldMatrix[ 3 ] : register( c94 ) ; // ローカル → ワールド行列
VS_CONST_MATERIAL cfMaterial : register( c11 ) ; // マテリアルパラメータ
VS_CONST_LIGHT cfLight[ 2 ] : register( c14 ) ; // スポットライトとポイントライトのパラメータ
// main関数
VS_OUTPUT main( VS_INPUT VSInput )
{
VS_OUTPUT VSOutput ;
float4 lWorldPosition ;
float4 lViewPosition ;
float3 lWorldNrm ;
float3 lViewNrm ;
float3 lLightHalfVec ;
float4 lLightLitParam ;
float4 lLightLitDest ;
float3 lLightDir ;
float3 lLightTemp ;
float lLightDistancePow2 ;
float lLightGen ;
float lLightDirectionCosA ;
float4 lTotalDiffuse ;
float4 lTotalSpecular ;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ローカル座標をワールド座標に変換
lWorldPosition.x = dot( VSInput.Position, cfLocalWorldMatrix[ 0 ] ) ;
lWorldPosition.y = dot( VSInput.Position, cfLocalWorldMatrix[ 1 ] ) ;
lWorldPosition.z = dot( VSInput.Position, cfLocalWorldMatrix[ 2 ] ) ;
lWorldPosition.w = 1.0f ;
// ワールド座標をビュー座標に変換
lViewPosition.x = dot( lWorldPosition, cfViewMatrix[ 0 ] ) ;
lViewPosition.y = dot( lWorldPosition, cfViewMatrix[ 1 ] ) ;
lViewPosition.z = dot( lWorldPosition, cfViewMatrix[ 2 ] ) ;
lViewPosition.w = 1.0f ;
// ビュー座標を射影座標に変換
VSOutput.Position.x = dot( lViewPosition, cfProjectionMatrix[ 0 ] ) ;
VSOutput.Position.y = dot( lViewPosition, cfProjectionMatrix[ 1 ] ) ;
VSOutput.Position.z = dot( lViewPosition, cfProjectionMatrix[ 2 ] ) ;
VSOutput.Position.w = dot( lViewPosition, cfProjectionMatrix[ 3 ] ) ;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// ライトの処理 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ディフューズカラーとスペキュラカラーの蓄積値の初期化
lTotalDiffuse = float4( 0, 0, 0, 0 ) ;
lTotalSpecular = float4( 0, 0, 0, 0 ) ;
// 法線をビュー空間の角度に変換 =====================================================( 開始 )
// ローカルベクトルをワールドベクトルに変換
lWorldNrm.x = dot( VSInput.Normal, cfLocalWorldMatrix[ 0 ].xyz ) ;
lWorldNrm.y = dot( VSInput.Normal, cfLocalWorldMatrix[ 1 ].xyz ) ;
lWorldNrm.z = dot( VSInput.Normal, cfLocalWorldMatrix[ 2 ].xyz ) ;
// ワールドベクトルをビューベクトルに変換
lViewNrm.x = dot( lWorldNrm, cfViewMatrix[ 0 ].xyz ) ;
lViewNrm.y = dot( lWorldNrm, cfViewMatrix[ 1 ].xyz ) ;
lViewNrm.z = dot( lWorldNrm, cfViewMatrix[ 2 ].xyz ) ;
// 法線をビュー空間の角度に変換 =====================================================( 終了 )
// スポットライトの処理 *************************************************************( 開始 )
// ライト方向ベクトルの計算
lLightDir = normalize( lViewPosition.xyz - cfLight[ 0 ].Position.xyz ) ;
// 距離・スポットライト減衰値計算 =======================================( 開始 )
// 距離減衰計算 ------------------
// 頂点とライト位置との距離の二乗を求める
lLightTemp = lViewPosition.xyz - cfLight[ 0 ].Position.xyz ;
lLightDistancePow2 = dot( lLightTemp, lLightTemp ) ;
// 減衰率の計算 lLightGen = 1 / ( 減衰値0 + 減衰値1 * 距離 + 減衰値2 * ( 距離 * 距離 ) )
lLightGen = 1.0f / ( cfLight[ 0 ].Range_FallOff_AT0_AT1.z + cfLight[ 0 ].Range_FallOff_AT0_AT1.w * sqrt( lLightDistancePow2 ) + cfLight[ 0 ].AT2_SpotP0_SpotP1.x * lLightDistancePow2 ) ;
// スポットライト減衰計算 --------
// ライト方向ベクトルとライト位置から頂点位置へのベクトルの内積( 即ち Cos a )を計算
lLightDirectionCosA = dot( lLightDir, cfLight[ 0 ].Direction ) ;
// スポットライト減衰計算 pow( falloff, ( ( Cos a - Cos f ) / ( Cos q - Cos f ) ) )
lLightGen *= saturate( pow( abs( max( lLightDirectionCosA - cfLight[ 0 ].AT2_SpotP0_SpotP1.y, 0.0f ) * cfLight[ 0 ].AT2_SpotP0_SpotP1.z ), cfLight[ 0 ].Range_FallOff_AT0_AT1.y ) ) ;
// 有効距離外だったら減衰率を最大にする処理
lLightGen *= step( lLightDistancePow2, cfLight[ 0 ].Range_FallOff_AT0_AT1.x ) ;
// 距離・スポットライト減衰値計算 =======================================( 終了 )
// ライトディフューズカラーとライトスペキュラカラーの角度減衰計算 =======( 開始 )
// 法線とライトの逆方向ベクトルとの内積を lLightLitParam.x にセット
lLightLitParam.x = dot( lViewNrm, -lLightDir ) ;
// ハーフベクトルの計算 norm( ( norm( 頂点位置から視点へのベクトル ) + ライトの方向 ) )
lLightHalfVec = normalize( normalize( -lViewPosition.xyz ) - lLightDir ) ;
// 法線とハーフベクトルの内積を lLightLitParam.y にセット
lLightLitParam.y = dot( lLightHalfVec, lViewNrm ) ;
// スペキュラ反射率を lLightLitParam.w にセット
lLightLitParam.w = cfMaterial.Power.x ;
// ライト計算
lLightLitDest = lit( lLightLitParam.x, lLightLitParam.y, lLightLitParam.w ) ;
// ライトディフューズカラーとライトスペキュラカラーの角度減衰計算 =======( 終了 )
// カラー計算 ===========================================================( 開始 )
// ディフーズライト蓄積値 += 距離・スポットライト角度減衰値 * ( ディフーズ角度減衰計算結果 * マテリアルディフューズカラー * ライトのディフーズカラー + ライトのアンビエントカラーとマテリアルのアンビエントカラーを乗算したもの )
lTotalDiffuse += lLightGen * ( lLightLitDest.y * cfLight[ 0 ].Diffuse * cfMaterial.Diffuse + cfLight[ 0 ].Ambient ) ;
// スペキュラライト蓄積値 += スペキュラ角度減衰計算結果 * 距離・スポットライト減衰 * ライトのスペキュラカラー
lTotalSpecular += lLightGen * lLightLitDest.z * cfLight[ 0 ].Specular ;
// カラー計算 ===========================================================( 終了 )
// スポットライトの処理 *************************************************************( 終了 )
// ポイントライトの処理 *************************************************************( 開始 )
// ライト方向ベクトルの計算
lLightDir = normalize( lViewPosition.xyz - cfLight[ 1 ].Position.xyz ) ;
// 距離減衰値計算 =======================================================( 開始 )
// 頂点とライト位置との距離の二乗を求める
lLightTemp = lViewPosition.xyz - cfLight[ 1 ].Position.xyz ;
lLightDistancePow2 = dot( lLightTemp, lLightTemp ) ;
// 減衰率の計算 lLightGen = 1 / ( 減衰値0 + 減衰値1 * 距離 + 減衰値2 * ( 距離 * 距離 ) )
lLightGen = 1.0f / ( cfLight[ 1 ].Range_FallOff_AT0_AT1.z + cfLight[ 1 ].Range_FallOff_AT0_AT1.w * sqrt( lLightDistancePow2 ) + cfLight[ 1 ].AT2_SpotP0_SpotP1.x * lLightDistancePow2 ) ;
// 有効距離外だったら減衰率を最大にする処理
lLightGen *= step( lLightDistancePow2, cfLight[ 1 ].Range_FallOff_AT0_AT1.x ) ;
// 距離減衰値計算 =======================================================( 終了 )
// ライトディフューズカラーとライトスペキュラカラーの角度減衰計算 =======( 開始 )
// 法線とライトの逆方向ベクトルとの内積を lLightLitParam.x にセット
lLightLitParam.x = dot( lViewNrm, -lLightDir ) ;
// ハーフベクトルの計算 norm( ( norm( 頂点位置から視点へのベクトル ) + ライトの方向 ) )
lLightHalfVec = normalize( normalize( -lViewPosition.xyz ) - lLightDir ) ;
// 法線とハーフベクトルの内積を lLightLitParam.y にセット
lLightLitParam.y = dot( lLightHalfVec, lViewNrm ) ;
// スペキュラ反射率を lLightLitParam.w にセット
lLightLitParam.w = cfMaterial.Power.x ;
// ライト計算
lLightLitDest = lit( lLightLitParam.x, lLightLitParam.y, lLightLitParam.w ) ;
// ライトディフューズカラーとライトスペキュラカラーの角度減衰計算 =======( 終了 )
// カラー計算 ===========================================================( 開始 )
// ディフーズライト蓄積値 += 距離・スポットライト角度減衰値 * ( ディフーズ角度減衰計算結果 * マテリアルディフューズカラー * ライトのディフーズカラー + ライトのアンビエントカラーとマテリアルのアンビエントカラーを乗算したもの )
lTotalDiffuse += lLightGen * ( lLightLitDest.y * cfLight[ 1 ].Diffuse * cfMaterial.Diffuse + cfLight[ 1 ].Ambient ) ;
// スペキュラライト蓄積値 += スペキュラ角度減衰計算結果 * 距離・スポットライト減衰 * ライトのスペキュラカラー
lTotalSpecular += lLightGen * lLightLitDest.z * cfLight[ 1 ].Specular ;
// カラー計算 ===========================================================( 終了 )
// ポイントライトの処理 *************************************************************( 終了 )
// ライトの処理 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ディフューズカラー = ディフューズライト蓄積値 + マテリアルのアンビエントカラーとグローバルアンビエントカラーを乗算したものとマテリアルエミッシブカラーを加算したもの
VSOutput.Diffuse = lTotalDiffuse + cfAmbient_Emissive ;
// ディフューズアルファはマテリアルのディフューズカラーのアルファをそのまま使う
VSOutput.Diffuse.w = cfMaterial.Diffuse.w ;
// スペキュラカラー = スペキュラライト蓄積値 * マテリアルのスペキュラカラー
VSOutput.Specular = lTotalSpecular * cfMaterial.Specular ;
// テクスチャ座標変換行列による変換を行った結果のテクスチャ座標をセット
VSOutput.TexCoords0.x = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 0 ] ) ;
VSOutput.TexCoords0.y = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 1 ] ) ;
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータを返す
return VSOutput ;
}
ピクセルシェーダーのプログラム
// ピクセルシェーダーの入力
struct PS_INPUT
{
float4 Diffuse : COLOR0 ;
float4 Specular : COLOR1 ;
float2 TexCoords0 : TEXCOORD0 ;
} ;
// ピクセルシェーダーの出力
struct PS_OUTPUT
{
float4 Color0 : COLOR0 ;
} ;
// C++ 側で設定するテクスチャの定義
sampler DiffuseMapTexture : register( s0 ) ; // ディフューズマップテクスチャ
float4 cfFactorColor : register( c5 ) ; // 不透明度等
// main関数
PS_OUTPUT main( PS_INPUT PSInput )
{
PS_OUTPUT PSOutput ;
float4 TextureDiffuseColor ;
// テクスチャカラーの読み込み
TextureDiffuseColor = tex2D( DiffuseMapTexture, PSInput.TexCoords0.xy ) ;
// 出力カラー = ディフューズカラー * テクスチャカラー + スペキュラカラー
PSOutput.Color0 = PSInput.Diffuse * TextureDiffuseColor + PSInput.Specular ;
// 出力アルファ = ディフューズアルファ * テクスチャアルファ * 不透明度
PSOutput.Color0.a = PSInput.Diffuse.a * TextureDiffuseColor.a * cfFactorColor.a ;
// 出力パラメータを返す
return PSOutput ;
}
ピクセルシェーダーでライティングを行うフォンシェーディング描画
剛体メッシュのディレクショナルライトありフォンシェーディング描画
<実行に必要なファイル一式>
人型モデルのように一つの頂点が複数のフレーム( ボーン )の影響を受けない背景のようなモデルに
ディレクショナルライトを当ててライブラリ側で設定されているマテリアルも適用して描画するサンプルプログラムです。
ただ、こちらのサンプルと違ってライティング処理を頂点シェーダーではなくピクセルシェーダーで行っています。
ピクセルシェーダーでライティングを行う利点はライティングの精度がモデルの頂点の細かさに依存しないことです。
というのも頂点シェーダーでライティングを行う場合は頂点毎のライティング後の色が決まるとピクセルシェーダーでは
頂点で決定した色を単純に線形補間するだけなのでモデルの頂点と頂点の間が広いとそれだけ適当なライティングになります。
( スポットライトはライトが当たっているところと当たっていないところがシャープに区切られているので頂点が少ないと
あからさまに不自然な描画結果になります )
というわけで、ピクセルシェーダーでライティングを行うことで頂点が幾ら荒くても綺麗にライティングすることができます。
ただ、欠点は基本的に頂点よりピクセルの方が数が多いので( モデルがカメラから遠くて豆粒くらいの大きさで表示されている
などの場合は除く )頂点シェーダーでライティングを行うより処理負荷が高くなります。
また、ピクセルシェーダー2.0ではあまり長いプログラムを書けず、使用できる定数も少ないのでピクセルシェーダーで
ライティングを行う場合はピクセルシェーダー3.0を使わないと結構大変です。というわけでこのサンプルでも
ピクセルシェーダー3.0を使用しています。
下記のサンプルについてですが、C++のプログラムは頂点シェーダー版のサンプルと変わりません、
ただ頂点シェーダーのプログラムからライティング処理がごっそり無くなって、代わりにピクセルシェーダーのプログラムに
ライティング処理が丸々移動しています。
C++のプログラム
#include "DxLib.h"
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
int ModelHandle ;
int PixelShaderHandle ;
int VertexShaderHandle ;
float RotateAngle ;
// ウインドウモードで起動
ChangeWindowMode( TRUE ) ;
// Direct3D9Ex を使用する
SetUseDirect3DVersion( DX_DIRECT3D_9EX ) ;
// DXライブラリの初期化
if( DxLib_Init() < 0 )
{
// エラーが発生したら直ちに終了
return -1 ;
}
// プログラマブルシェーダーモデル3.0が使用できない場合はエラーを表示して終了
if( GetValidShaderVersion() < 300 )
{
// エラー表示
DrawString( 0, 0, "プログラマブルシェーダー3.0が使用できない環境のようです", GetColor( 255,255,255 ) ) ;
// キー入力待ち
WaitKey() ;
// DXライブラリの後始末
DxLib_End() ;
// ソフト終了
return 0 ;
}
// 頂点シェーダーを読み込む
VertexShaderHandle = LoadVertexShader( "NormalMesh_DirLight_PhongVS.vso" ) ;
// ピクセルシェーダーを読み込む
PixelShaderHandle = LoadPixelShader( "NormalMesh_DirLight_PhongPS.pso" ) ;
// 剛体メッシュモデルを読み込む
ModelHandle = MV1LoadModel( "NormalBox.mqo" ) ;
// 描画先を裏画面にする
SetDrawScreen( DX_SCREEN_BACK ) ;
// モデルを回転される値を初期化
RotateAngle = 0.0f ;
// モデルの描画にオリジナルシェーダーを使用する設定をONにする
MV1SetUseOrigShader( TRUE ) ;
// 使用する頂点シェーダーをセット
SetUseVertexShader( VertexShaderHandle ) ;
// 使用するピクセルシェーダーをセット
SetUsePixelShader( PixelShaderHandle ) ;
// モデルを画面の中心にセット
MV1SetPosition( ModelHandle, VGet( 320.0f, 240.0f, 0.0f ) ) ;
// ESCキーが押されるまでループ
while( ProcessMessage() == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0 )
{
// 画面を初期化
ClearDrawScreen() ;
// モデルを回転値を加算
RotateAngle += 0.02f ;
// モデルの回転値をモデルにセット
MV1SetRotationXYZ( ModelHandle, VGet( 0.0f, RotateAngle, 0.0f ) ) ;
// モデルを描画
MV1DrawModel( ModelHandle ) ;
// 裏画面の内容を表画面に反映させる
ScreenFlip() ;
}
// 読み込んだ頂点シェーダーの削除
DeleteShader( VertexShaderHandle ) ;
// 読み込んだピクセルシェーダーの削除
DeleteShader( PixelShaderHandle ) ;
// 読み込んだモデルの削除
MV1DeleteModel( ModelHandle ) ;
// DXライブラリの後始末
DxLib_End() ;
// ソフトの終了
return 0 ;
}
頂点シェーダーのプログラム
// 頂点シェーダーの入力
struct VS_INPUT
{
float4 Position : POSITION ; // 座標( ローカル空間 )
float3 Normal : NORMAL0 ; // 法線( ローカル空間 )
float4 Diffuse : COLOR0 ; // ディフューズカラー
float4 Specular : COLOR1 ; // スペキュラカラー
float4 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
} ;
// 頂点シェーダーの出力
struct VS_OUTPUT
{
float4 Position : POSITION ; // 座標( プロジェクション空間 )
float2 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
float3 VPosition : TEXCOORD1 ; // 座標( ビュー空間 )
float3 VNormal : TEXCOORD2 ; // 法線( ビュー空間 )
} ;
// C++ 側で設定する定数の定義
float4 cfAmbient_Emissive : register( c1 ) ; // マテリアルエミッシブカラー + マテリアルアンビエントカラー * グローバルアンビエントカラー
float4 cfProjectionMatrix[ 4 ] : register( c2 ) ; // ビュー → 射影行列
float4 cfViewMatrix[ 3 ] : register( c6 ) ; // ワールド → ビュー行列
float4 cfTextureMatrix[ 3 ][ 2 ] : register( c88 ) ; // テクスチャ座標操作用行列
float4 cfLocalWorldMatrix[ 3 ] : register( c94 ) ; // ローカル → ワールド行列
// main関数
VS_OUTPUT main( VS_INPUT VSInput )
{
VS_OUTPUT VSOutput ;
float4 lWorldPosition ;
float4 lViewPosition ;
float3 lWorldNrm ;
float3 lViewNrm ;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ローカル座標をワールド座標に変換
lWorldPosition.x = dot( VSInput.Position, cfLocalWorldMatrix[ 0 ] ) ;
lWorldPosition.y = dot( VSInput.Position, cfLocalWorldMatrix[ 1 ] ) ;
lWorldPosition.z = dot( VSInput.Position, cfLocalWorldMatrix[ 2 ] ) ;
lWorldPosition.w = 1.0f ;
// ワールド座標をビュー座標に変換
lViewPosition.x = dot( lWorldPosition, cfViewMatrix[ 0 ] ) ;
lViewPosition.y = dot( lWorldPosition, cfViewMatrix[ 1 ] ) ;
lViewPosition.z = dot( lWorldPosition, cfViewMatrix[ 2 ] ) ;
lViewPosition.w = 1.0f ;
// ビュー座標を射影座標に変換
VSOutput.Position.x = dot( lViewPosition, cfProjectionMatrix[ 0 ] ) ;
VSOutput.Position.y = dot( lViewPosition, cfProjectionMatrix[ 1 ] ) ;
VSOutput.Position.z = dot( lViewPosition, cfProjectionMatrix[ 2 ] ) ;
VSOutput.Position.w = dot( lViewPosition, cfProjectionMatrix[ 3 ] ) ;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 法線をビュー空間の角度に変換 =========================================( 開始 )
// ローカルベクトルをワールドベクトルに変換
lWorldNrm.x = dot( VSInput.Normal, cfLocalWorldMatrix[ 0 ].xyz ) ;
lWorldNrm.y = dot( VSInput.Normal, cfLocalWorldMatrix[ 1 ].xyz ) ;
lWorldNrm.z = dot( VSInput.Normal, cfLocalWorldMatrix[ 2 ].xyz ) ;
// ワールドベクトルをビューベクトルに変換
lViewNrm.x = dot( lWorldNrm, cfViewMatrix[ 0 ].xyz ) ;
lViewNrm.y = dot( lWorldNrm, cfViewMatrix[ 1 ].xyz ) ;
lViewNrm.z = dot( lWorldNrm, cfViewMatrix[ 2 ].xyz ) ;
// 法線をビュー空間の角度に変換 =========================================( 終了 )
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// 頂点座標を保存
VSOutput.VPosition = lViewPosition.xyz ;
// 法線を保存
VSOutput.VNormal = lViewNrm ;
// テクスチャ座標変換行列による変換を行った結果のテクスチャ座標をセット
VSOutput.TexCoords0.x = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 0 ] ) ;
VSOutput.TexCoords0.y = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 1 ] ) ;
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータを返す
return VSOutput ;
}
ピクセルシェーダーのプログラム
// ピクセルシェーダーの入力
struct PS_INPUT
{
float2 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
float3 VPosition : TEXCOORD1 ; // 座標( ビュー空間 )
float3 VNormal : TEXCOORD2 ; // 法線( ビュー空間 )
} ;
// ピクセルシェーダーの出力
struct PS_OUTPUT
{
float4 Color0 : COLOR0 ;
} ;
// マテリアルパラメータ
struct MATERIAL
{
float4 Diffuse ; // ディフューズカラー
float4 Specular ; // スペキュラカラー
float4 Power ; // スペキュラの強さ
} ;
// ライトパラメータ
struct LIGHT
{
float4 Position ; // 座標( ビュー空間 )
float3 Direction ; // 方向( ビュー空間 )
float4 Diffuse ; // ディフューズカラー
float4 Specular ; // スペキュラカラー
float4 Ambient ; // アンビエントカラーとマテリアルのアンビエントカラーを乗算したもの
float4 Range_FallOff_AT0_AT1 ; // x:有効距離 y:スポットライト用FallOff z:距離による減衰処理用パラメータ0 w:距離による減衰処理用パラメータ1
float4 AT2_SpotP0_SpotP1 ; // x:距離による減衰処理用パラメータ2 y:スポットライト用パラメータ0( cos( Phi / 2.0f ) ) z:スポットライト用パラメータ1( 1.0f / ( cos( Theta / 2.0f ) - cos( Phi / 2.0f ) ) )
} ;
// C++ 側で設定するテクスチャや定数の定義
sampler DiffuseMapTexture : register( s0 ) ; // ディフューズマップテクスチャ
float4 cfAmbient_Emissive : register( c1 ) ; // エミッシブカラー + マテリアルアンビエントカラー * グローバルアンビエントカラー
MATERIAL cfMaterial : register( c2 ) ; // マテリアルパラメータ
float4 cfFactorColor : register( c5 ) ; // 不透明度等
LIGHT cfLight[ 1 ] : register( c32 ) ; // ライトパラメータ
// main関数
PS_OUTPUT main( PS_INPUT PSInput )
{
PS_OUTPUT PSOutput ;
float4 TextureDiffuseColor ;
float4 SpecularColor ;
float3 Normal ;
float DiffuseAngleGen ;
float4 TotalDiffuse ;
float4 TotalSpecular ;
float3 TempF3 ;
float Temp ;
float3 V_to_Eye ;
float3 lLightDir ;
// 法線の準備
Normal = normalize( PSInput.VNormal ) ;
// 頂点座標から視点へのベクトルを正規化
V_to_Eye = normalize( -PSInput.VPosition ) ;
// ディフューズカラーとスペキュラカラーの蓄積値を初期化
TotalDiffuse = float4( 0.0f, 0.0f, 0.0f, 0.0f ) ;
TotalSpecular = float4( 0.0f, 0.0f, 0.0f, 0.0f ) ;
// ディレクショナルライトの処理 +++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ライト方向ベクトルのセット
lLightDir = cfLight[ 0 ].Direction ;
// ディフューズ色計算
// DiffuseAngleGen = ディフューズ角度減衰率計算
DiffuseAngleGen = saturate( dot( Normal, -lLightDir ) ) ;
// ディフューズカラー蓄積値 += ライトのディフューズカラー * マテリアルのディフューズカラー * ディフューズカラー角度減衰率 + ライトのアンビエントカラーとマテリアルのアンビエントカラーを乗算したもの
TotalDiffuse += cfLight[ 0 ].Diffuse * cfMaterial.Diffuse * DiffuseAngleGen + cfLight[ 0 ].Ambient ;
// スペキュラカラー計算
// ハーフベクトルの計算
TempF3 = normalize( V_to_Eye - lLightDir ) ;
// Temp = pow( max( 0.0f, N * H ), cfMaterial.Power.x )
Temp = pow( max( 0.0f, dot( Normal, TempF3 ) ), cfMaterial.Power.x ) ;
// スペキュラカラー蓄積値 += Temp * ライトのスペキュラカラー
TotalSpecular += Temp * cfLight[ 0 ].Specular ;
// ディレクショナルライトの処理 +++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力カラー計算 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// TotalDiffuse = ライトディフューズカラー蓄積値 + ( マテリアルのアンビエントカラーとグローバルアンビエントカラーを乗算したものとマテリアルエミッシブカラーを加算したもの )
TotalDiffuse += cfAmbient_Emissive ;
// SpecularColor = ライトのスペキュラカラー蓄積値 * マテリアルのスペキュラカラー
SpecularColor = TotalSpecular * cfMaterial.Specular ;
// 出力カラー = TotalDiffuse * テクスチャカラー + SpecularColor
TextureDiffuseColor = tex2D( DiffuseMapTexture, PSInput.TexCoords0.xy ) ;
PSOutput.Color0.rgb = TextureDiffuseColor.rgb * TotalDiffuse.rgb + SpecularColor.rgb ;
// アルファ値 = テクスチャアルファ * マテリアルのディフューズアルファ * 不透明度
PSOutput.Color0.a = TextureDiffuseColor.a * cfMaterial.Diffuse.a * cfFactorColor.a ;
// 出力カラー計算 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータを返す
return PSOutput ;
}
剛体メッシュのポイントライトありフォンシェーディング描画
<実行に必要なファイル一式>
人型モデルのように一つの頂点が複数のフレーム( ボーン )の影響を受けない背景のようなモデルに
ポイントライトを当ててライブラリ側で設定されているマテリアルも適用して描画するサンプルプログラムです。
フォンシェーディングの解説についてはディレクショナルライトのフォンシェーディングの解説を参照してください。
このサンプルはポイントライトのフォンシェーディング描画のプログラムです。やはり頂点シェーダーではライティング処理を何もしません。
C++のプログラム
#include "DxLib.h"
#include <math.h>
#define DRAW_NUM (3)
#define SPACE (512.0f)
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
int ModelHandle ;
int PixelShaderHandle ;
int VertexShaderHandle ;
float LightRotateAngle ;
int i, j ;
float DrawX, DrawZ ;
// ウインドウモードで起動
ChangeWindowMode( TRUE ) ;
// Direct3D9Ex を使用する
SetUseDirect3DVersion( DX_DIRECT3D_9EX ) ;
// DXライブラリの初期化
if( DxLib_Init() < 0 )
{
// エラーが発生したら直ちに終了
return -1 ;
}
// プログラマブルシェーダーモデル3.0が使用できない場合はエラーを表示して終了
if( GetValidShaderVersion() < 300 )
{
// エラー表示
DrawString( 0, 0, "プログラマブルシェーダー3.0が使用できない環境のようです", GetColor( 255,255,255 ) ) ;
// キー入力待ち
WaitKey() ;
// DXライブラリの後始末
DxLib_End() ;
// ソフト終了
return 0 ;
}
// 頂点シェーダーを読み込む
VertexShaderHandle = LoadVertexShader( "NormalMesh_PointLight_PhongVS.vso" ) ;
// ピクセルシェーダーを読み込む
PixelShaderHandle = LoadPixelShader( "NormalMesh_PointLight_PhongPS.pso" ) ;
// 剛体メッシュモデルを読み込む
ModelHandle = MV1LoadModel( "NormalBox.mqo" ) ;
// 描画先を裏画面にする
SetDrawScreen( DX_SCREEN_BACK ) ;
// モデルの描画にオリジナルシェーダーを使用する設定をONにする
MV1SetUseOrigShader( TRUE ) ;
// 使用する頂点シェーダーをセット
SetUseVertexShader( VertexShaderHandle ) ;
// 使用するピクセルシェーダーをセット
SetUsePixelShader( PixelShaderHandle ) ;
// 観察しやすい位置にカメラを移動
SetCameraPositionAndTarget_UpVecY( VGet( 800.0f, 400.0f, -800.0f ), VGet( 0.0f, 0.0f, 0.0f ) ) ;
// ライトの位置を回転する値を初期化
LightRotateAngle = 0.0f ;
// 標準ライトをポイントライトにする
ChangeLightTypePoint( VGet( 0.0f, 0.0f, 0.0f ), 1000.0f, 1.016523f, 0.010100f, 0.0020f ) ;
// ライトの光を強くする
SetLightDifColor( GetColorF( 200.0f, 200.0f, 200.0f, 0.0f ) ) ;
// ライトのアンビエントカラーを無効にする
SetLightAmbColor( GetColorF( 0.0f, 0.0f, 0.0f, 0.0f ) ) ;
// ESCキーが押されるまでループ
while( ProcessMessage() == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0 )
{
// 画面を初期化
ClearDrawScreen() ;
// ポイントライトの位置の回転値を加算
LightRotateAngle += 0.02f ;
// ポイントライトの位置の更新
SetLightPosition( VGet( sin( LightRotateAngle ) * 400.0f, 400.0f, cos( LightRotateAngle ) * 400.0f ) ) ;
// モデルを描画
DrawZ = - ( DRAW_NUM - 1 ) * SPACE / 2.0f ;
for( i = 0 ; i < DRAW_NUM ; i ++ )
{
DrawX = - ( DRAW_NUM - 1 ) * SPACE / 2.0f ;
for( j = 0 ; j < DRAW_NUM ; j ++ )
{
// 位置を設定
MV1SetPosition( ModelHandle, VGet( DrawX, 0.0f, DrawZ ) ) ;
// 描画
MV1DrawModel( ModelHandle ) ;
DrawX += SPACE ;
}
DrawZ += SPACE ;
}
// 裏画面の内容を表画面に反映させる
ScreenFlip() ;
}
// 読み込んだ頂点シェーダーの削除
DeleteShader( VertexShaderHandle ) ;
// 読み込んだピクセルシェーダーの削除
DeleteShader( PixelShaderHandle ) ;
// 読み込んだモデルの削除
MV1DeleteModel( ModelHandle ) ;
// DXライブラリの後始末
DxLib_End() ;
// ソフトの終了
return 0 ;
}
頂点シェーダーのプログラム
// 頂点シェーダーの入力
struct VS_INPUT
{
float4 Position : POSITION ; // 座標( ローカル空間 )
float3 Normal : NORMAL0 ; // 法線( ローカル空間 )
float4 Diffuse : COLOR0 ; // ディフューズカラー
float4 Specular : COLOR1 ; // スペキュラカラー
float4 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
} ;
// 頂点シェーダーの出力
struct VS_OUTPUT
{
float4 Position : POSITION ; // 座標( プロジェクション空間 )
float2 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
float3 VPosition : TEXCOORD1 ; // 座標( ビュー空間 )
float3 VNormal : TEXCOORD2 ; // 法線( ビュー空間 )
} ;
// C++ 側で設定する定数の定義
float4 cfAmbient_Emissive : register( c1 ) ; // マテリアルエミッシブカラー + マテリアルアンビエントカラー * グローバルアンビエントカラー
float4 cfProjectionMatrix[ 4 ] : register( c2 ) ; // ビュー → 射影行列
float4 cfViewMatrix[ 3 ] : register( c6 ) ; // ワールド → ビュー行列
float4 cfTextureMatrix[ 3 ][ 2 ] : register( c88 ) ; // テクスチャ座標操作用行列
float4 cfLocalWorldMatrix[ 3 ] : register( c94 ) ; // ローカル → ワールド行列
// main関数
VS_OUTPUT main( VS_INPUT VSInput )
{
VS_OUTPUT VSOutput ;
float4 lWorldPosition ;
float4 lViewPosition ;
float3 lWorldNrm ;
float3 lViewNrm ;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ローカル座標をワールド座標に変換
lWorldPosition.x = dot( VSInput.Position, cfLocalWorldMatrix[ 0 ] ) ;
lWorldPosition.y = dot( VSInput.Position, cfLocalWorldMatrix[ 1 ] ) ;
lWorldPosition.z = dot( VSInput.Position, cfLocalWorldMatrix[ 2 ] ) ;
lWorldPosition.w = 1.0f ;
// ワールド座標をビュー座標に変換
lViewPosition.x = dot( lWorldPosition, cfViewMatrix[ 0 ] ) ;
lViewPosition.y = dot( lWorldPosition, cfViewMatrix[ 1 ] ) ;
lViewPosition.z = dot( lWorldPosition, cfViewMatrix[ 2 ] ) ;
lViewPosition.w = 1.0f ;
// ビュー座標を射影座標に変換
VSOutput.Position.x = dot( lViewPosition, cfProjectionMatrix[ 0 ] ) ;
VSOutput.Position.y = dot( lViewPosition, cfProjectionMatrix[ 1 ] ) ;
VSOutput.Position.z = dot( lViewPosition, cfProjectionMatrix[ 2 ] ) ;
VSOutput.Position.w = dot( lViewPosition, cfProjectionMatrix[ 3 ] ) ;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 法線をビュー空間の角度に変換 =========================================( 開始 )
// ローカルベクトルをワールドベクトルに変換
lWorldNrm.x = dot( VSInput.Normal, cfLocalWorldMatrix[ 0 ].xyz ) ;
lWorldNrm.y = dot( VSInput.Normal, cfLocalWorldMatrix[ 1 ].xyz ) ;
lWorldNrm.z = dot( VSInput.Normal, cfLocalWorldMatrix[ 2 ].xyz ) ;
// ワールドベクトルをビューベクトルに変換
lViewNrm.x = dot( lWorldNrm, cfViewMatrix[ 0 ].xyz ) ;
lViewNrm.y = dot( lWorldNrm, cfViewMatrix[ 1 ].xyz ) ;
lViewNrm.z = dot( lWorldNrm, cfViewMatrix[ 2 ].xyz ) ;
// 法線をビュー空間の角度に変換 =========================================( 終了 )
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// 頂点座標を保存
VSOutput.VPosition = lViewPosition.xyz ;
// 法線を保存
VSOutput.VNormal = lViewNrm ;
// テクスチャ座標変換行列による変換を行った結果のテクスチャ座標をセット
VSOutput.TexCoords0.x = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 0 ] ) ;
VSOutput.TexCoords0.y = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 1 ] ) ;
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータを返す
return VSOutput ;
}
ピクセルシェーダーのプログラム
// ピクセルシェーダーの入力
struct PS_INPUT
{
float2 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
float3 VPosition : TEXCOORD1 ; // 座標( ビュー空間 )
float3 VNormal : TEXCOORD2 ; // 法線( ビュー空間 )
} ;
// ピクセルシェーダーの出力
struct PS_OUTPUT
{
float4 Color0 : COLOR0 ;
} ;
// マテリアルパラメータ
struct MATERIAL
{
float4 Diffuse ; // ディフューズカラー
float4 Specular ; // スペキュラカラー
float4 Power ; // スペキュラの強さ
} ;
// ライトパラメータ
struct LIGHT
{
float4 Position ; // 座標( ビュー空間 )
float3 Direction ; // 方向( ビュー空間 )
float4 Diffuse ; // ディフューズカラー
float4 Specular ; // スペキュラカラー
float4 Ambient ; // アンビエントカラーとマテリアルのアンビエントカラーを乗算したもの
float4 Range_FallOff_AT0_AT1 ; // x:有効距離 y:スポットライト用FallOff z:距離による減衰処理用パラメータ0 w:距離による減衰処理用パラメータ1
float4 AT2_SpotP0_SpotP1 ; // x:距離による減衰処理用パラメータ2 y:スポットライト用パラメータ0( cos( Phi / 2.0f ) ) z:スポットライト用パラメータ1( 1.0f / ( cos( Theta / 2.0f ) - cos( Phi / 2.0f ) ) )
} ;
// C++ 側で設定するテクスチャや定数の定義
sampler DiffuseMapTexture : register( s0 ) ; // ディフューズマップテクスチャ
float4 cfAmbient_Emissive : register( c1 ) ; // エミッシブカラー + マテリアルアンビエントカラー * グローバルアンビエントカラー
MATERIAL cfMaterial : register( c2 ) ; // マテリアルパラメータ
float4 cfFactorColor : register( c5 ) ; // 不透明度等
LIGHT cfLight[ 1 ] : register( c32 ) ; // ライトパラメータ
// main関数
PS_OUTPUT main( PS_INPUT PSInput )
{
PS_OUTPUT PSOutput ;
float4 TextureDiffuseColor ;
float4 SpecularColor ;
float3 Normal ;
float DiffuseAngleGen ;
float4 TotalDiffuse ;
float4 TotalSpecular ;
float3 V_to_Eye ;
float3 TempF3 ;
float Temp ;
float3 lLightTemp ;
float lLightDistancePow2 ;
float lLightGen ;
float3 lLightDir ;
// 法線の準備
Normal = normalize( PSInput.VNormal ) ;
// 頂点座標から視点へのベクトルを正規化
V_to_Eye = normalize( -PSInput.VPosition ) ;
// ディフューズカラーとスペキュラカラーの蓄積値を初期化
TotalDiffuse = float4( 0.0f, 0.0f, 0.0f, 0.0f ) ;
TotalSpecular = float4( 0.0f, 0.0f, 0.0f, 0.0f ) ;
// ポイントライトの処理 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ライト方向ベクトルの計算
lLightDir = normalize( PSInput.VPosition.xyz - cfLight[ 0 ].Position.xyz ) ;
// 距離減衰値計算 =======================================================( 開始 )
// 頂点とライト位置との距離の二乗を求める
lLightTemp = PSInput.VPosition.xyz - cfLight[ 0 ].Position.xyz ;
lLightDistancePow2 = dot( lLightTemp, lLightTemp ) ;
// 減衰率の計算 lLightGen = 1 / ( 減衰値0 + 減衰値1 * 距離 + 減衰値2 * ( 距離 * 距離 ) )
lLightGen = 1.0f / ( cfLight[ 0 ].Range_FallOff_AT0_AT1.z + cfLight[ 0 ].Range_FallOff_AT0_AT1.w * sqrt( lLightDistancePow2 ) + cfLight[ 0 ].AT2_SpotP0_SpotP1.x * lLightDistancePow2 ) ;
// 有効距離外だったら減衰率を最大にする処理
lLightGen *= step( lLightDistancePow2, cfLight[ 0 ].Range_FallOff_AT0_AT1.x ) ;
// 距離減衰値計算 =======================================================( 終了 )
// ディフューズ色計算
// ディフューズ角度減衰率計算
DiffuseAngleGen = saturate( dot( Normal, -lLightDir ) ) ;
// ディフューズカラー蓄積値 += ( ライトのディフューズカラー * マテリアルディフューズカラー * ディフューズカラー角度減衰率 + ライトのアンビエントカラーとマテリアルのアンビエントカラーを乗算したもの ) * 距離・スポットライトの角度減衰率
TotalDiffuse += ( cfLight[ 0 ].Diffuse * cfMaterial.Diffuse * DiffuseAngleGen + cfLight[ 0 ].Ambient ) * lLightGen ;
// スペキュラカラー計算
// ハーフベクトルの計算
TempF3 = normalize( V_to_Eye - lLightDir ) ;
// Temp = pow( max( 0.0f, N * H ), cfMaterial.Power.x )
Temp = pow( max( 0.0f, dot( Normal, TempF3 ) ), cfMaterial.Power.x ) ;
// スペキュラカラー蓄積値 += Temp * 距離・スポットライトの角度減衰率 * ライトのスペキュラカラー
TotalSpecular += Temp * lLightGen.x * cfLight[ 0 ].Specular ;
// ポイントライトの処理 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力カラー計算 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// TotalDiffuse = ライトディフューズカラー蓄積値 + ( マテリアルのアンビエントカラーとグローバルアンビエントカラーを乗算したものとマテリアルエミッシブカラーを加算したもの )
TotalDiffuse += cfAmbient_Emissive ;
// SpecularColor = ライトのスペキュラカラー蓄積値 * マテリアルのスペキュラカラー
SpecularColor = TotalSpecular * cfMaterial.Specular ;
// 出力カラー = TotalDiffuse * テクスチャカラー + SpecularColor
TextureDiffuseColor = tex2D( DiffuseMapTexture, PSInput.TexCoords0.xy ) ;
PSOutput.Color0.rgb = TextureDiffuseColor.rgb * TotalDiffuse.rgb + SpecularColor.rgb ;
// アルファ値 = テクスチャアルファ * マテリアルのディフューズアルファ * 不透明度
PSOutput.Color0.a = TextureDiffuseColor.a * cfMaterial.Diffuse.a * cfFactorColor.a ;
// 出力カラー計算 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータを返す
return PSOutput ;
}
剛体メッシュのポイントライトありフォンシェーディング描画(ポイントライト6つ版)
<実行に必要なファイル一式>
人型モデルのように一つの頂点が複数のフレーム( ボーン )の影響を受けない背景のようなモデルに
ポイントライトを6つ当ててライブラリ側で設定されているマテリアルも適用して描画するサンプルプログラムです。
フォンシェーディングの解説についてはディレクショナルライトのフォンシェーディングの解説を参照してください。
このサンプルはポイントライト6つのフォンシェーディング描画のプログラムです。やはり頂点シェーダーではライティング処理を何もしません。
尚、DXライブラリでピクセルシェーダーに設定できるポイントライトの最大数は6つなので、もしそれ以上の数のライトを扱いたい場合は
自前でライトの情報を管理して、ピクセルシェーダーの定数として設定する必要があります。
C++のプログラム
#include "DxLib.h"
#include <math.h>
#define LIGHT_NUM (6)
#define DRAW_NUM (3)
#define SPACE (512.0f)
// ライトの色
float LightDifColor[ LIGHT_NUM ][ 3 ] =
{
{ 150.0f, 0.0f, 150.0f },
{ 150.0f, 0.0f, 0.0f },
{ 0.0f, 150.0f, 0.0f },
{ 0.0f, 0.0f, 150.0f },
{ 150.0f, 150.0f, 0.0f },
{ 0.0f, 150.0f, 150.0f },
} ;
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
int ModelHandle ;
int PixelShaderHandle ;
int VertexShaderHandle ;
float LightRotateAngle ;
int i, j ;
float DrawX, DrawZ ;
int PointLightHandle[ LIGHT_NUM ] ;
// ウインドウモードで起動
ChangeWindowMode( TRUE ) ;
// Direct3D9Ex を使用する
SetUseDirect3DVersion( DX_DIRECT3D_9EX ) ;
// DXライブラリの初期化
if( DxLib_Init() < 0 )
{
// エラーが発生したら直ちに終了
return -1 ;
}
// プログラマブルシェーダーモデル3.0が使用できない場合はエラーを表示して終了
if( GetValidShaderVersion() < 300 )
{
// エラー表示
DrawString( 0, 0, "プログラマブルシェーダー3.0が使用できない環境のようです", GetColor( 255,255,255 ) ) ;
// キー入力待ち
WaitKey() ;
// DXライブラリの後始末
DxLib_End() ;
// ソフト終了
return 0 ;
}
// 頂点シェーダーを読み込む
VertexShaderHandle = LoadVertexShader( "NormalMesh_PointLight6_PhongVS.vso" ) ;
// ピクセルシェーダーを読み込む
PixelShaderHandle = LoadPixelShader( "NormalMesh_PointLight6_PhongPS.pso" ) ;
// 剛体メッシュモデルを読み込む
ModelHandle = MV1LoadModel( "NormalBox.mqo" ) ;
// 描画先を裏画面にする
SetDrawScreen( DX_SCREEN_BACK ) ;
// モデルの描画にオリジナルシェーダーを使用する設定をONにする
MV1SetUseOrigShader( TRUE ) ;
// 使用する頂点シェーダーをセット
SetUseVertexShader( VertexShaderHandle ) ;
// 使用するピクセルシェーダーをセット
SetUsePixelShader( PixelShaderHandle ) ;
// 観察しやすい位置にカメラを移動
SetCameraPositionAndTarget_UpVecY( VGet( 800.0f, 400.0f, -800.0f ), VGet( 0.0f, 0.0f, 0.0f ) ) ;
// ライトの位置を回転する値を初期化
LightRotateAngle = 0.0f ;
// 標準ライトを無効にする
SetLightEnable( FALSE ) ;
// ポイントライトを作成する
for( i = 0 ; i < LIGHT_NUM ; i ++ )
{
// ライトハンドルの作成
PointLightHandle[ i ] = CreatePointLightHandle( VGet( 0.0f, 0.0f, 0.0f ), 1000.0f, 1.016523f, 0.010100f, 0.0020f ) ;
// ライトの色を設定する
SetLightDifColorHandle(
PointLightHandle[ i ],
GetColorF(
LightDifColor[ i ][ 0 ],
LightDifColor[ i ][ 1 ],
LightDifColor[ i ][ 2 ],
0.0f
)
) ;
// ライトのアンビエントカラーを無効にする
SetLightAmbColorHandle( PointLightHandle[ i ], GetColorF( 0.0f, 0.0f, 0.0f, 0.0f ) ) ;
}
// ESCキーが押されるまでループ
while( ProcessMessage() == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0 )
{
// 画面を初期化
ClearDrawScreen() ;
// ポイントライトの位置の回転値を加算
LightRotateAngle += 0.02f ;
// ポイントライトの位置の更新
for( i = 0; i < LIGHT_NUM ; i ++ )
{
SetLightPositionHandle(
PointLightHandle[ i ],
VGet(
sin( LightRotateAngle + DX_TWO_PI_F / LIGHT_NUM * i ) * 700.0f,
400.0f,
cos( LightRotateAngle + DX_TWO_PI_F / LIGHT_NUM * i ) * 700.0f
)
) ;
}
// モデルを描画
DrawZ = - ( DRAW_NUM - 1 ) * SPACE / 2.0f ;
for( i = 0 ; i < DRAW_NUM ; i ++ )
{
DrawX = - ( DRAW_NUM - 1 ) * SPACE / 2.0f ;
for( j = 0 ; j < DRAW_NUM ; j ++ )
{
// 位置を設定
MV1SetPosition( ModelHandle, VGet( DrawX, 0.0f, DrawZ ) ) ;
// 描画
MV1DrawModel( ModelHandle ) ;
DrawX += SPACE ;
}
DrawZ += SPACE ;
}
// 裏画面の内容を表画面に反映させる
ScreenFlip() ;
}
// 読み込んだ頂点シェーダーの削除
DeleteShader( VertexShaderHandle ) ;
// 読み込んだピクセルシェーダーの削除
DeleteShader( PixelShaderHandle ) ;
// 読み込んだモデルの削除
MV1DeleteModel( ModelHandle ) ;
// DXライブラリの後始末
DxLib_End() ;
// ソフトの終了
return 0 ;
}
頂点シェーダーのプログラム
// 頂点シェーダーの入力
struct VS_INPUT
{
float4 Position : POSITION ; // 座標( ローカル空間 )
float3 Normal : NORMAL0 ; // 法線( ローカル空間 )
float4 Diffuse : COLOR0 ; // ディフューズカラー
float4 Specular : COLOR1 ; // スペキュラカラー
float4 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
} ;
// 頂点シェーダーの出力
struct VS_OUTPUT
{
float4 Position : POSITION ; // 座標( プロジェクション空間 )
float2 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
float3 VPosition : TEXCOORD1 ; // 座標( ビュー空間 )
float3 VNormal : TEXCOORD2 ; // 法線( ビュー空間 )
} ;
// C++ 側で設定する定数の定義
float4 cfAmbient_Emissive : register( c1 ) ; // マテリアルエミッシブカラー + マテリアルアンビエントカラー * グローバルアンビエントカラー
float4 cfProjectionMatrix[ 4 ] : register( c2 ) ; // ビュー → 射影行列
float4 cfViewMatrix[ 3 ] : register( c6 ) ; // ワールド → ビュー行列
float4 cfTextureMatrix[ 3 ][ 2 ] : register( c88 ) ; // テクスチャ座標操作用行列
float4 cfLocalWorldMatrix[ 3 ] : register( c94 ) ; // ローカル → ワールド行列
// main関数
VS_OUTPUT main( VS_INPUT VSInput )
{
VS_OUTPUT VSOutput ;
float4 lWorldPosition ;
float4 lViewPosition ;
float3 lWorldNrm ;
float3 lViewNrm ;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ローカル座標をワールド座標に変換
lWorldPosition.x = dot( VSInput.Position, cfLocalWorldMatrix[ 0 ] ) ;
lWorldPosition.y = dot( VSInput.Position, cfLocalWorldMatrix[ 1 ] ) ;
lWorldPosition.z = dot( VSInput.Position, cfLocalWorldMatrix[ 2 ] ) ;
lWorldPosition.w = 1.0f ;
// ワールド座標をビュー座標に変換
lViewPosition.x = dot( lWorldPosition, cfViewMatrix[ 0 ] ) ;
lViewPosition.y = dot( lWorldPosition, cfViewMatrix[ 1 ] ) ;
lViewPosition.z = dot( lWorldPosition, cfViewMatrix[ 2 ] ) ;
lViewPosition.w = 1.0f ;
// ビュー座標を射影座標に変換
VSOutput.Position.x = dot( lViewPosition, cfProjectionMatrix[ 0 ] ) ;
VSOutput.Position.y = dot( lViewPosition, cfProjectionMatrix[ 1 ] ) ;
VSOutput.Position.z = dot( lViewPosition, cfProjectionMatrix[ 2 ] ) ;
VSOutput.Position.w = dot( lViewPosition, cfProjectionMatrix[ 3 ] ) ;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 法線をビュー空間の角度に変換 =========================================( 開始 )
// ローカルベクトルをワールドベクトルに変換
lWorldNrm.x = dot( VSInput.Normal, cfLocalWorldMatrix[ 0 ].xyz ) ;
lWorldNrm.y = dot( VSInput.Normal, cfLocalWorldMatrix[ 1 ].xyz ) ;
lWorldNrm.z = dot( VSInput.Normal, cfLocalWorldMatrix[ 2 ].xyz ) ;
// ワールドベクトルをビューベクトルに変換
lViewNrm.x = dot( lWorldNrm, cfViewMatrix[ 0 ].xyz ) ;
lViewNrm.y = dot( lWorldNrm, cfViewMatrix[ 1 ].xyz ) ;
lViewNrm.z = dot( lWorldNrm, cfViewMatrix[ 2 ].xyz ) ;
// 法線をビュー空間の角度に変換 =========================================( 終了 )
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// 頂点座標を保存
VSOutput.VPosition = lViewPosition.xyz ;
// 法線を保存
VSOutput.VNormal = lViewNrm ;
// テクスチャ座標変換行列による変換を行った結果のテクスチャ座標をセット
VSOutput.TexCoords0.x = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 0 ] ) ;
VSOutput.TexCoords0.y = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 1 ] ) ;
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータを返す
return VSOutput ;
}
ピクセルシェーダーのプログラム
// ピクセルシェーダーの入力
struct PS_INPUT
{
float2 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
float3 VPosition : TEXCOORD1 ; // 座標( ビュー空間 )
float3 VNormal : TEXCOORD2 ; // 法線( ビュー空間 )
} ;
// ピクセルシェーダーの出力
struct PS_OUTPUT
{
float4 Color0 : COLOR0 ;
} ;
// マテリアルパラメータ
struct MATERIAL
{
float4 Diffuse ; // ディフューズカラー
float4 Specular ; // スペキュラカラー
float4 Power ; // スペキュラの強さ
} ;
// ライトパラメータ
struct LIGHT
{
float4 Position ; // 座標( ビュー空間 )
float3 Direction ; // 方向( ビュー空間 )
float4 Diffuse ; // ディフューズカラー
float4 Specular ; // スペキュラカラー
float4 Ambient ; // アンビエントカラーとマテリアルのアンビエントカラーを乗算したもの
float4 Range_FallOff_AT0_AT1 ; // x:有効距離 y:スポットライト用FallOff z:距離による減衰処理用パラメータ0 w:距離による減衰処理用パラメータ1
float4 AT2_SpotP0_SpotP1 ; // x:距離による減衰処理用パラメータ2 y:スポットライト用パラメータ0( cos( Phi / 2.0f ) ) z:スポットライト用パラメータ1( 1.0f / ( cos( Theta / 2.0f ) - cos( Phi / 2.0f ) ) )
} ;
// C++ 側で設定するテクスチャや定数の定義
sampler DiffuseMapTexture : register( s0 ) ; // ディフューズマップテクスチャ
float4 cfAmbient_Emissive : register( c1 ) ; // エミッシブカラー + マテリアルアンビエントカラー * グローバルアンビエントカラー
MATERIAL cfMaterial : register( c2 ) ; // マテリアルパラメータ
float4 cfFactorColor : register( c5 ) ; // 不透明度等
LIGHT cfLight[ 6 ] : register( c32 ) ; // ライトパラメータ
// ポイントライトの計算をする関数
inline void CalcPointLight(
const in PS_INPUT PSInput,
const in int LightNo,
const in float3 Normal,
const in float3 V_to_Eye,
inout float4 TotalDiffuse,
inout float4 TotalSpecular
)
{
float DiffuseAngleGen ;
float3 TempF3 ;
float Temp ;
float3 lLightTemp ;
float lLightDistancePow2 ;
float lLightGen ;
float3 lLightDir ;
// ライト方向ベクトルの計算
lLightDir = normalize( PSInput.VPosition.xyz - cfLight[ LightNo ].Position.xyz ) ;
// 距離減衰値計算 =======================================================( 開始 )
// 頂点とライト位置との距離の二乗を求める
lLightTemp = PSInput.VPosition.xyz - cfLight[ LightNo ].Position.xyz ;
lLightDistancePow2 = dot( lLightTemp, lLightTemp ) ;
// 減衰率の計算 lLightGen = 1 / ( 減衰値0 + 減衰値1 * 距離 + 減衰値2 * ( 距離 * 距離 ) )
lLightGen = 1.0f / ( cfLight[ LightNo ].Range_FallOff_AT0_AT1.z + cfLight[ LightNo ].Range_FallOff_AT0_AT1.w * sqrt( lLightDistancePow2 ) + cfLight[ LightNo ].AT2_SpotP0_SpotP1.x * lLightDistancePow2 ) ;
// 有効距離外だったら減衰率を最大にする処理
lLightGen *= step( lLightDistancePow2, cfLight[ LightNo ].Range_FallOff_AT0_AT1.x ) ;
// 距離減衰値計算 =======================================================( 終了 )
// ディフューズ色計算
// ディフューズ角度減衰率計算
DiffuseAngleGen = saturate( dot( Normal, -lLightDir ) ) ;
// ディフューズカラー蓄積値 += ( ライトのディフューズカラー * マテリアルディフューズカラー * ディフューズカラー角度減衰率 + ライトのアンビエントカラーとマテリアルのアンビエントカラーを乗算したもの ) * 距離・スポットライトの角度減衰率
TotalDiffuse += ( cfLight[ LightNo ].Diffuse * cfMaterial.Diffuse * DiffuseAngleGen + cfLight[ LightNo ].Ambient ) * lLightGen ;
// スペキュラカラー計算
// ハーフベクトルの計算
TempF3 = normalize( V_to_Eye - lLightDir ) ;
// Temp = pow( max( 0.0f, N * H ), cfMaterial.Power.x )
Temp = pow( max( 0.0f, dot( Normal, TempF3 ) ), cfMaterial.Power.x ) ;
// スペキュラカラー蓄積値 += Temp * 距離・スポットライトの角度減衰率 * ライトのスペキュラカラー
TotalSpecular += Temp * lLightGen.x * cfLight[ LightNo ].Specular ;
}
// main関数
PS_OUTPUT main( PS_INPUT PSInput )
{
PS_OUTPUT PSOutput ;
float4 TextureDiffuseColor ;
float4 SpecularColor ;
float3 Normal ;
float4 TotalDiffuse ;
float4 TotalSpecular ;
float3 V_to_Eye ;
// 法線の準備
Normal = normalize( PSInput.VNormal ) ;
// 頂点座標から視点へのベクトルを正規化
V_to_Eye = normalize( -PSInput.VPosition ) ;
// ディフューズカラーとスペキュラカラーの蓄積値を初期化
TotalDiffuse = float4( 0.0f, 0.0f, 0.0f, 0.0f ) ;
TotalSpecular = float4( 0.0f, 0.0f, 0.0f, 0.0f ) ;
// ポイントライト0~5の処理 +++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
CalcPointLight( PSInput, 0, Normal, V_to_Eye, TotalDiffuse, TotalSpecular ) ;
CalcPointLight( PSInput, 1, Normal, V_to_Eye, TotalDiffuse, TotalSpecular ) ;
CalcPointLight( PSInput, 2, Normal, V_to_Eye, TotalDiffuse, TotalSpecular ) ;
CalcPointLight( PSInput, 3, Normal, V_to_Eye, TotalDiffuse, TotalSpecular ) ;
CalcPointLight( PSInput, 4, Normal, V_to_Eye, TotalDiffuse, TotalSpecular ) ;
CalcPointLight( PSInput, 5, Normal, V_to_Eye, TotalDiffuse, TotalSpecular ) ;
// ポイントライト0~5の処理 +++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力カラー計算 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// TotalDiffuse = ライトディフューズカラー蓄積値 + ( マテリアルのアンビエントカラーとグローバルアンビエントカラーを乗算したものとマテリアルエミッシブカラーを加算したもの )
TotalDiffuse += cfAmbient_Emissive ;
// SpecularColor = ライトのスペキュラカラー蓄積値 * マテリアルのスペキュラカラー
SpecularColor = TotalSpecular * cfMaterial.Specular ;
// 出力カラー = TotalDiffuse * テクスチャカラー + SpecularColor
TextureDiffuseColor = tex2D( DiffuseMapTexture, PSInput.TexCoords0.xy ) ;
PSOutput.Color0.rgb = TextureDiffuseColor.rgb * TotalDiffuse.rgb + SpecularColor.rgb ;
// アルファ値 = テクスチャアルファ * マテリアルのディフューズアルファ * 不透明度
PSOutput.Color0.a = TextureDiffuseColor.a * cfMaterial.Diffuse.a * cfFactorColor.a ;
// 出力カラー計算 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータを返す
return PSOutput ;
}
剛体メッシュのスポットライトありフォンシェーディング描画
<実行に必要なファイル一式>
人型モデルのように一つの頂点が複数のフレーム( ボーン )の影響を受けない背景のようなモデルに
スポットライトを当ててライブラリ側で設定されているマテリアルも適用して描画するサンプルプログラムです。
フォンシェーディングの解説についてはディレクショナルライトのフォンシェーディングの解説を参照してください。
このサンプルはスポットライトのフォンシェーディング描画のプログラムです。やはり頂点シェーダーではライティング処理を何もしません。
そしてフォンシェーディングの3サンプルの中では一番フォンシェーディングの効果が発揮されているサンプルです。
上記のスクリーンショットも、頂点シェーダーで行うスポットライトのスクリーンショットも
箱の側面にライトが当たっていますが、実は頂点シェーダーのスポットライトのサンプルパッケージに入っている箱モデルは他のパッケージに
入っているモデルの約70倍の頂点数( 1734頂点 )で構成されています。つまりスポットライトを頂点シェーダーで行っても
スポットライトらしく見えるように物凄くきめ細かく頂点が配置されているということです。
対してフォンシェーディングでスポットライト処理を行っている本サンプルで使用している箱モデルの頂点数は24頂点で構成されています。
見た目も良くなり、モデルの頂点数も少なくて済むので一石二鳥です。( ただ頂点シェーダー版より重いですが・・・ )
C++のプログラム
#include "DxLib.h"
#include <math.h>
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
int ModelHandle ;
int PixelShaderHandle ;
int VertexShaderHandle ;
float LightRotateAngle ;
VECTOR LightPosition ;
// ウインドウモードで起動
ChangeWindowMode( TRUE ) ;
// Direct3D9Ex を使用する
SetUseDirect3DVersion( DX_DIRECT3D_9EX ) ;
// DXライブラリの初期化
if( DxLib_Init() < 0 )
{
// エラーが発生したら直ちに終了
return -1 ;
}
// プログラマブルシェーダーモデル2.0が使用できない場合はエラーを表示して終了
if( GetValidShaderVersion() < 200 )
{
// エラー表示
DrawString( 0, 0, "プログラマブルシェーダー2.0が使用できない環境のようです", GetColor( 255,255,255 ) ) ;
// キー入力待ち
WaitKey() ;
// DXライブラリの後始末
DxLib_End() ;
// ソフト終了
return 0 ;
}
// 頂点シェーダーを読み込む
VertexShaderHandle = LoadVertexShader( "NormalMesh_SpotLight_PhongVS.vso" ) ;
// ピクセルシェーダーを読み込む
PixelShaderHandle = LoadPixelShader( "NormalMesh_SpotLight_PhongPS.pso" ) ;
// 剛体メッシュモデルを読み込む
ModelHandle = MV1LoadModel( "NormalBox.mqo" ) ;
// 描画先を裏画面にする
SetDrawScreen( DX_SCREEN_BACK ) ;
// モデルの描画にオリジナルシェーダーを使用する設定をONにする
MV1SetUseOrigShader( TRUE ) ;
// 使用する頂点シェーダーをセット
SetUseVertexShader( VertexShaderHandle ) ;
// 使用するピクセルシェーダーをセット
SetUsePixelShader( PixelShaderHandle ) ;
// 観察しやすい位置にカメラを移動
SetCameraPositionAndTarget_UpVecY( VGet( 400.0f, 400.0f, -400.0f ), VGet( 0.0f, 0.0f, 0.0f ) ) ;
// ライトの位置を回転する値を初期化
LightRotateAngle = 0.0f ;
// 標準ライトのタイプをスポットライトにする
ChangeLightTypeSpot( VGet( 0.0f, 0.0f, 0.0f ), VGet( 1.0f, 0.0f, 0.0f ), 0.7f, 0.6f, 700.0f, 0.391586f, 0.001662f, 0.0f ) ;
// アンビエントライトをOFFにする
SetLightAmbColor( GetColorF( 0.0f, 0.0f, 0.0f, 0.0f ) ) ;
// グローバルアンビエントライト( 大域環境光 )を20%の明るさにする
SetGlobalAmbientLight( GetColorF( 0.2f, 0.2f, 0.2f, 0.0f ) ) ;
// ESCキーが押されるまでループ
while( ProcessMessage() == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0 )
{
// 画面を初期化
ClearDrawScreen() ;
// ライトの位置の回転値を加算
LightRotateAngle += 0.02f ;
// ライトの位置の更新
LightPosition.x = sin( LightRotateAngle ) * 400.0f ;
LightPosition.y = 0.0f ;
LightPosition.z = cos( LightRotateAngle ) * 400.0f ;
SetLightPosition( LightPosition ) ;
// ライトの向きを原点方向にする
SetLightDirection( VScale( LightPosition, -1.0f ) ) ;
// モデルを描画
MV1DrawModel( ModelHandle ) ;
// 裏画面の内容を表画面に反映させる
ScreenFlip() ;
}
// 読み込んだ頂点シェーダーの削除
DeleteShader( VertexShaderHandle ) ;
// 読み込んだピクセルシェーダーの削除
DeleteShader( PixelShaderHandle ) ;
// 読み込んだモデルの削除
MV1DeleteModel( ModelHandle ) ;
// DXライブラリの後始末
DxLib_End() ;
// ソフトの終了
return 0 ;
}
頂点シェーダーのプログラム
// 頂点シェーダーの入力
struct VS_INPUT
{
float4 Position : POSITION ; // 座標( ローカル空間 )
float3 Normal : NORMAL0 ; // 法線( ローカル空間 )
float4 Diffuse : COLOR0 ; // ディフューズカラー
float4 Specular : COLOR1 ; // スペキュラカラー
float4 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
} ;
// 頂点シェーダーの出力
struct VS_OUTPUT
{
float4 Position : POSITION ; // 座標( プロジェクション空間 )
float2 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
float3 VPosition : TEXCOORD1 ; // 座標( ビュー空間 )
float3 VNormal : TEXCOORD2 ; // 法線( ビュー空間 )
} ;
// C++ 側で設定する定数の定義
float4 cfAmbient_Emissive : register( c1 ) ; // マテリアルエミッシブカラー + マテリアルアンビエントカラー * グローバルアンビエントカラー
float4 cfProjectionMatrix[ 4 ] : register( c2 ) ; // ビュー → 射影行列
float4 cfViewMatrix[ 3 ] : register( c6 ) ; // ワールド → ビュー行列
float4 cfTextureMatrix[ 3 ][ 2 ] : register( c88 ) ; // テクスチャ座標操作用行列
float4 cfLocalWorldMatrix[ 3 ] : register( c94 ) ; // ローカル → ワールド行列
// main関数
VS_OUTPUT main( VS_INPUT VSInput )
{
VS_OUTPUT VSOutput ;
float4 lWorldPosition ;
float4 lViewPosition ;
float3 lWorldNrm ;
float3 lViewNrm ;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ローカル座標をワールド座標に変換
lWorldPosition.x = dot( VSInput.Position, cfLocalWorldMatrix[ 0 ] ) ;
lWorldPosition.y = dot( VSInput.Position, cfLocalWorldMatrix[ 1 ] ) ;
lWorldPosition.z = dot( VSInput.Position, cfLocalWorldMatrix[ 2 ] ) ;
lWorldPosition.w = 1.0f ;
// ワールド座標をビュー座標に変換
lViewPosition.x = dot( lWorldPosition, cfViewMatrix[ 0 ] ) ;
lViewPosition.y = dot( lWorldPosition, cfViewMatrix[ 1 ] ) ;
lViewPosition.z = dot( lWorldPosition, cfViewMatrix[ 2 ] ) ;
lViewPosition.w = 1.0f ;
// ビュー座標を射影座標に変換
VSOutput.Position.x = dot( lViewPosition, cfProjectionMatrix[ 0 ] ) ;
VSOutput.Position.y = dot( lViewPosition, cfProjectionMatrix[ 1 ] ) ;
VSOutput.Position.z = dot( lViewPosition, cfProjectionMatrix[ 2 ] ) ;
VSOutput.Position.w = dot( lViewPosition, cfProjectionMatrix[ 3 ] ) ;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 法線をビュー空間の角度に変換 =========================================( 開始 )
// ローカルベクトルをワールドベクトルに変換
lWorldNrm.x = dot( VSInput.Normal, cfLocalWorldMatrix[ 0 ].xyz ) ;
lWorldNrm.y = dot( VSInput.Normal, cfLocalWorldMatrix[ 1 ].xyz ) ;
lWorldNrm.z = dot( VSInput.Normal, cfLocalWorldMatrix[ 2 ].xyz ) ;
// ワールドベクトルをビューベクトルに変換
lViewNrm.x = dot( lWorldNrm, cfViewMatrix[ 0 ].xyz ) ;
lViewNrm.y = dot( lWorldNrm, cfViewMatrix[ 1 ].xyz ) ;
lViewNrm.z = dot( lWorldNrm, cfViewMatrix[ 2 ].xyz ) ;
// 法線をビュー空間の角度に変換 =========================================( 終了 )
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// 頂点座標を保存
VSOutput.VPosition = lViewPosition.xyz ;
// 法線を保存
VSOutput.VNormal = lViewNrm ;
// テクスチャ座標変換行列による変換を行った結果のテクスチャ座標をセット
VSOutput.TexCoords0.x = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 0 ] ) ;
VSOutput.TexCoords0.y = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 1 ] ) ;
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータを返す
return VSOutput ;
}
ピクセルシェーダーのプログラム
// ピクセルシェーダーの入力
struct PS_INPUT
{
float2 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
float3 VPosition : TEXCOORD1 ; // 座標( ビュー空間 )
float3 VNormal : TEXCOORD2 ; // 法線( ビュー空間 )
} ;
// ピクセルシェーダーの出力
struct PS_OUTPUT
{
float4 Color0 : COLOR0 ;
} ;
// マテリアルパラメータ
struct MATERIAL
{
float4 Diffuse ; // ディフューズカラー
float4 Specular ; // スペキュラカラー
float4 Power ; // スペキュラの強さ
} ;
// ライトパラメータ
struct LIGHT
{
float4 Position ; // 座標( ビュー空間 )
float3 Direction ; // 方向( ビュー空間 )
float4 Diffuse ; // ディフューズカラー
float4 Specular ; // スペキュラカラー
float4 Ambient ; // アンビエントカラーとマテリアルのアンビエントカラーを乗算したもの
float4 Range_FallOff_AT0_AT1 ; // x:有効距離 y:スポットライト用FallOff z:距離による減衰処理用パラメータ0 w:距離による減衰処理用パラメータ1
float4 AT2_SpotP0_SpotP1 ; // x:距離による減衰処理用パラメータ2 y:スポットライト用パラメータ0( cos( Phi / 2.0f ) ) z:スポットライト用パラメータ1( 1.0f / ( cos( Theta / 2.0f ) - cos( Phi / 2.0f ) ) )
} ;
// C++ 側で設定するテクスチャや定数の定義
sampler DiffuseMapTexture : register( s0 ) ; // ディフューズマップテクスチャ
float4 cfAmbient_Emissive : register( c1 ) ; // エミッシブカラー + マテリアルアンビエントカラー * グローバルアンビエントカラー
MATERIAL cfMaterial : register( c2 ) ; // マテリアルパラメータ
float4 cfFactorColor : register( c5 ) ; // 不透明度等
LIGHT cfLight[ 1 ] : register( c32 ) ; // ライトパラメータ
// main関数
PS_OUTPUT main( PS_INPUT PSInput )
{
PS_OUTPUT PSOutput ;
float4 TextureDiffuseColor ;
float4 SpecularColor ;
float3 Normal ;
float DiffuseAngleGen ;
float4 TotalDiffuse ;
float4 TotalSpecular ;
float3 V_to_Eye ;
float3 TempF3 ;
float Temp ;
float3 lLightTemp ;
float lLightDistancePow2 ;
float lLightGen ;
float3 lLightDir ;
float lLightDirectionCosA ;
// 法線の準備
Normal = normalize( PSInput.VNormal ) ;
// 頂点座標から視点へのベクトルを正規化
V_to_Eye = normalize( -PSInput.VPosition ) ;
// ディフューズカラーとスペキュラカラーの蓄積値を初期化
TotalDiffuse = float4( 0.0f, 0.0f, 0.0f, 0.0f ) ;
TotalSpecular = float4( 0.0f, 0.0f, 0.0f, 0.0f ) ;
// スポットライトの処理 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ライト方向ベクトルの計算
lLightDir = normalize( PSInput.VPosition.xyz - cfLight[ 0 ].Position.xyz ) ;
// 距離・スポットライト減衰値計算 =======================================( 開始 )
// 距離減衰計算 ------------------
// 頂点とライト位置との距離の二乗を求める
lLightTemp = PSInput.VPosition.xyz - cfLight[ 0 ].Position.xyz ;
lLightDistancePow2 = dot( lLightTemp, lLightTemp ) ;
// 減衰率の計算 lLightGen = 1 / ( 減衰値0 + 減衰値1 * 距離 + 減衰値2 * ( 距離 * 距離 ) )
lLightGen = 1.0f / ( cfLight[ 0 ].Range_FallOff_AT0_AT1.z + cfLight[ 0 ].Range_FallOff_AT0_AT1.w * sqrt( lLightDistancePow2 ) + cfLight[ 0 ].AT2_SpotP0_SpotP1.x * lLightDistancePow2 ) ;
// --------------------------------
// スポットライト減衰計算 --------
// ライト方向ベクトルとライト位置から頂点位置へのベクトルの内積( 即ち Cos a )を計算
lLightDirectionCosA = dot( lLightDir, cfLight[ 0 ].Direction ) ;
// スポットライト減衰計算 pow( falloff, ( ( Cos a - Cos f ) / ( Cos q - Cos f ) ) )
lLightGen *= saturate( pow( abs( max( lLightDirectionCosA - cfLight[ 0 ].AT2_SpotP0_SpotP1.y, 0.0f ) * cfLight[ 0 ].AT2_SpotP0_SpotP1.z ), cfLight[ 0 ].Range_FallOff_AT0_AT1.y ) ) ;
// --------------------------------
// 有効距離外だったら減衰率を最大にする処理
lLightGen *= step( lLightDistancePow2, cfLight[ 0 ].Range_FallOff_AT0_AT1.x ) ;
// 距離・スポットライト減衰値計算 =======================================( 終了 )
// ディフューズ色計算
// ディフューズ角度減衰率計算
DiffuseAngleGen = saturate( dot( Normal, -lLightDir ) ) ;
// ディフューズカラー蓄積値 += ( ライトのディフューズカラー * マテリアルディフューズカラー * ディフューズカラー角度減衰率 + ライトのアンビエントカラーとマテリアルのアンビエントカラーを乗算したもの ) * 距離・スポットライトの角度減衰率
TotalDiffuse += ( cfLight[ 0 ].Diffuse * cfMaterial.Diffuse * DiffuseAngleGen + cfLight[ 0 ].Ambient ) * lLightGen ;
// スペキュラカラー計算
// ハーフベクトルの計算
TempF3 = normalize( V_to_Eye - lLightDir ) ;
// Temp = pow( max( 0.0f, N * H ), cfMaterial.Power.x )
Temp = pow( max( 0.0f, dot( Normal, TempF3 ) ), cfMaterial.Power.x ) ;
// スペキュラカラー蓄積値 += Temp * 距離・スポットライトの角度減衰率 * ライトのスペキュラカラー
TotalSpecular += Temp * lLightGen.x * cfLight[ 0 ].Specular ;
// スポットライトの処理 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力カラー計算 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// TotalDiffuse = ライトディフューズカラー蓄積値 + ( マテリアルのアンビエントカラーとグローバルアンビエントカラーを乗算したものとマテリアルエミッシブカラーを加算したもの )
TotalDiffuse += cfAmbient_Emissive ;
// SpecularColor = ライトのスペキュラカラー蓄積値 * マテリアルのスペキュラカラー
SpecularColor = TotalSpecular * cfMaterial.Specular ;
// 出力カラー = TotalDiffuse * テクスチャカラー + SpecularColor
TextureDiffuseColor = tex2D( DiffuseMapTexture, PSInput.TexCoords0.xy ) ;
PSOutput.Color0.rgb = TextureDiffuseColor.rgb * TotalDiffuse.rgb + SpecularColor.rgb ;
// アルファ値 = テクスチャアルファ * マテリアルのディフューズアルファ * 不透明度
PSOutput.Color0.a = TextureDiffuseColor.a * cfMaterial.Diffuse.a * cfFactorColor.a ;
// 出力カラー計算 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータを返す
return PSOutput ;
}
トゥーンレンダリング描画
MMD互換のトゥーンレンダリング スキニングメッシュでディレクショナルライト一つ
<実行に必要なファイル一式>
人型モデルのような一つの頂点が複数のフレーム( ボーン )の影響を受けるモデルのMMD互換のトゥーンレンダリングを
行うサンプルプログラムです。( ただ、輪郭線はありません )
MMDのトゥーン処理ではディフューズカラーとアンビエントカラーはそのままライトのカラーとマテリアルのカラーを乗算して
トゥーンのグラデーションはライトの減衰率から導き出しています。
このサンプルではライトの計算は全て頂点シェーダーで行い、減衰率をピクセルシェーダーに渡してピクセルシェーダー側で
減衰率からトゥーンのグラデーションテクスチャから色を取得してディフューズカラーと乗算しています。
因みにMMDのスペキュラライトの計算は Direct3D の固定機能パイプラインと全く同じで、こちらはトゥーンの処理は一切行いません。
C++のプログラム
#include "DxLib.h"
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
int ModelHandle ;
int GradTexHandle ;
int PixelShaderHandle ;
int VertexShaderHandle ;
int AnimIndex ;
float AnimCounter ;
// ウインドウモードで起動
ChangeWindowMode( TRUE ) ;
// Direct3D9Ex を使用する
SetUseDirect3DVersion( DX_DIRECT3D_9EX ) ;
// DXライブラリの初期化
if( DxLib_Init() < 0 )
{
// エラーが発生したら直ちに終了
return -1 ;
}
// プログラマブルシェーダーモデル2.0が使用できない場合はエラーを表示して終了
if( GetValidShaderVersion() < 200 )
{
// エラー表示
DrawString( 0, 0, "プログラマブルシェーダー2.0が使用できない環境のようです", GetColor( 255,255,255 ) ) ;
// キー入力待ち
WaitKey() ;
// DXライブラリの後始末
DxLib_End() ;
// ソフト終了
return 0 ;
}
// 頂点シェーダーを読み込む
VertexShaderHandle = LoadVertexShader( "SkinMesh4_DirLight_ToonVS.vso" ) ;
// ピクセルシェーダーを読み込む
PixelShaderHandle = LoadPixelShader( "SkinMesh4_DirLight_ToonPS.pso" ) ;
// グラデーションテクスチャを読み込む
GradTexHandle = LoadGraph( "GradTex.bmp" ) ;
// スキニングメッシュモデルを読み込む
ModelHandle = MV1LoadModel( "DxChara.x" ) ;
// 分かりやすいように服のマテリアルを緑色にする
MV1SetMaterialDifColor( ModelHandle, 1, GetColorF( 0.0f, 0.5f, 0.0f, 1.0f ) ) ;
// アニメーション0をアタッチ
AnimIndex = MV1AttachAnim( ModelHandle, 0 ) ;
// アニメーションカウンタをリセット
AnimCounter = 0.0f ;
// モデルの描画にオリジナルシェーダーを使用する設定をONにする
MV1SetUseOrigShader( TRUE ) ;
// 使用するテクスチャ1にグラデーションテクスチャをセットする
SetUseTextureToShader( 1, GradTexHandle ) ;
// 使用する頂点シェーダーをセット
SetUseVertexShader( VertexShaderHandle ) ;
// 使用するピクセルシェーダーをセット
SetUsePixelShader( PixelShaderHandle ) ;
// 描画先を裏画面にする
SetDrawScreen( DX_SCREEN_BACK ) ;
// モデルの見える位置にカメラを配置
SetCameraPositionAndTarget_UpVecY( VGet( 0.0f, 700.0f, -1100.0f ), VGet( 0.0f, 350.0f, 0.0f ) ) ;
// ESCキーが押されるまでループ
while( ProcessMessage() == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0 )
{
// 画面を初期化
ClearDrawScreen() ;
// アニメーション時間を進める
AnimCounter += 100.0f ;
if( AnimCounter > MV1GetAnimTotalTime( ModelHandle, 0 ) )
{
AnimCounter -= MV1GetAnimTotalTime( ModelHandle, 0 ) ;
}
MV1SetAttachAnimTime( ModelHandle, AnimIndex, AnimCounter ) ;
// モデルを描画
MV1DrawModel( ModelHandle ) ;
// 裏画面の内容を表画面に反映させる
ScreenFlip() ;
}
// 使用するテクスチャからグラデーションテクスチャを外す
SetUseTextureToShader( 1, -1 ) ;
// グラデーションテクスチャを削除
DeleteGraph( GradTexHandle ) ;
// 読み込んだ頂点シェーダーの削除
DeleteShader( VertexShaderHandle ) ;
// 読み込んだピクセルシェーダーの削除
DeleteShader( PixelShaderHandle ) ;
// 読み込んだモデルの削除
MV1DeleteModel( ModelHandle ) ;
// DXライブラリの後始末
DxLib_End() ;
// ソフトの終了
return 0 ;
}
頂点シェーダーのプログラム
// 頂点シェーダーの入力
struct VS_INPUT
{
float4 Position : POSITION ; // 座標( ローカル空間 )
int4 BlendIndices0 : BLENDINDICES0 ; // スキニング処理用 Float型定数配列インデックス
float4 BlendWeight0 : BLENDWEIGHT0 ; // スキニング処理用ウエイト値
float3 Normal : NORMAL0 ; // 法線( ローカル空間 )
float4 Diffuse : COLOR0 ; // ディフューズカラー
float4 Specular : COLOR1 ; // スペキュラカラー
float4 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
} ;
// 頂点シェーダーの出力
struct VS_OUTPUT
{
float4 Position : POSITION ; // 座標( プロジェクション空間 )
float4 Diffuse : COLOR0 ; // ディフューズカラー
float4 Specular : COLOR1 ; // スペキュラカラー
float2 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
float2 ToonCoords0 : TEXCOORD1 ; // トゥーンテクスチャ座標
} ;
// マテリアルパラメータ
struct VS_CONST_MATERIAL
{
float4 Diffuse ; // マテリアルディフューズカラー
float4 Specular ; // マテリアルスペキュラカラー
float4 Power ; // マテリアルスペキュラハイライトの強さ
} ;
// ライトパラメータ
struct VS_CONST_LIGHT
{
float4 Position ; // 座標( ビュー空間 )
float3 Direction ; // 方向( ビュー空間 )
float4 Diffuse ; // ディフューズカラー
float4 Specular ; // スペキュラカラー
float4 Ambient ; // アンビエントカラーとマテリアルのアンビエントカラーを乗算したもの
float4 Range_FallOff_AT0_AT1 ; // x:有効距離 y:スポットライト用FallOff z:距離による減衰処理用パラメータ0 w:距離による減衰処理用パラメータ1
float4 AT2_SpotP0_SpotP1 ; // x:距離による減衰処理用パラメータ2 y:スポットライト用パラメータ0( cos( Phi / 2.0f ) ) z:スポットライト用パラメータ1( 1.0f / ( cos( Theta / 2.0f ) - cos( Phi / 2.0f ) ) )
} ;
// C++ 側で設定する定数の定義
float4 cfAmbient_Emissive : register( c1 ) ; // マテリアルエミッシブカラー + マテリアルアンビエントカラー * グローバルアンビエントカラー
float4 cfProjectionMatrix[ 4 ] : register( c2 ) ; // ビュー → 射影行列
float4 cfViewMatrix[ 3 ] : register( c6 ) ; // ワールド → ビュー行列
float4 cfTextureMatrix[ 3 ][ 2 ] : register( c88 ) ; // テクスチャ座標操作用行列
float4 cfLocalWorldMatrix[ 162 ] : register( c94 ) ; // ローカル → ワールド行列
VS_CONST_MATERIAL cfMaterial : register( c11 ) ; // マテリアルパラメータ
VS_CONST_LIGHT cfLight[ 1 ] : register( c14 ) ; // 有効ライトのパラメータ
// main関数
VS_OUTPUT main( VS_INPUT VSInput )
{
VS_OUTPUT VSOutput ;
float4 lLocalWorldMatrix[ 3 ] ;
float4 lWorldPosition ;
float4 lViewPosition ;
float3 lWorldNrm ;
float3 lViewNrm ;
float4 lTotalDiffuse ;
float4 lTotalSpecular ;
float3 lLightDir ;
float3 lLightTemp ;
float3 lLightHalfVec ;
float4 lLightLitParam ;
float4 lLightLitDest ;
float4 lTotalAmbient ;
float lTotalLightGen ;
// 複数のフレームのブレンド行列の作成
lLocalWorldMatrix[ 0 ] = cfLocalWorldMatrix[ VSInput.BlendIndices0.x + 0 ] * VSInput.BlendWeight0.x;
lLocalWorldMatrix[ 1 ] = cfLocalWorldMatrix[ VSInput.BlendIndices0.x + 1 ] * VSInput.BlendWeight0.x;
lLocalWorldMatrix[ 2 ] = cfLocalWorldMatrix[ VSInput.BlendIndices0.x + 2 ] * VSInput.BlendWeight0.x;
lLocalWorldMatrix[ 0 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.y + 0 ] * VSInput.BlendWeight0.y;
lLocalWorldMatrix[ 1 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.y + 1 ] * VSInput.BlendWeight0.y;
lLocalWorldMatrix[ 2 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.y + 2 ] * VSInput.BlendWeight0.y;
lLocalWorldMatrix[ 0 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.z + 0 ] * VSInput.BlendWeight0.z;
lLocalWorldMatrix[ 1 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.z + 1 ] * VSInput.BlendWeight0.z;
lLocalWorldMatrix[ 2 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.z + 2 ] * VSInput.BlendWeight0.z;
lLocalWorldMatrix[ 0 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.w + 0 ] * VSInput.BlendWeight0.w;
lLocalWorldMatrix[ 1 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.w + 1 ] * VSInput.BlendWeight0.w;
lLocalWorldMatrix[ 2 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.w + 2 ] * VSInput.BlendWeight0.w;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ローカル座標をワールド座標に変換
lWorldPosition.x = dot( VSInput.Position, lLocalWorldMatrix[ 0 ] ) ;
lWorldPosition.y = dot( VSInput.Position, lLocalWorldMatrix[ 1 ] ) ;
lWorldPosition.z = dot( VSInput.Position, lLocalWorldMatrix[ 2 ] ) ;
lWorldPosition.w = 1.0f ;
// ワールド座標をビュー座標に変換
lViewPosition.x = dot( lWorldPosition, cfViewMatrix[ 0 ] ) ;
lViewPosition.y = dot( lWorldPosition, cfViewMatrix[ 1 ] ) ;
lViewPosition.z = dot( lWorldPosition, cfViewMatrix[ 2 ] ) ;
lViewPosition.w = 1.0f ;
// ビュー座標を射影座標に変換
VSOutput.Position.x = dot( lViewPosition, cfProjectionMatrix[ 0 ] ) ;
VSOutput.Position.y = dot( lViewPosition, cfProjectionMatrix[ 1 ] ) ;
VSOutput.Position.z = dot( lViewPosition, cfProjectionMatrix[ 2 ] ) ;
VSOutput.Position.w = dot( lViewPosition, cfProjectionMatrix[ 3 ] ) ;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// ライトの処理 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// 法線をビュー空間の角度に変換 =====================================================( 開始 )
// ローカルベクトルをワールドベクトルに変換
lWorldNrm.x = dot( VSInput.Normal, lLocalWorldMatrix[ 0 ].xyz ) ;
lWorldNrm.y = dot( VSInput.Normal, lLocalWorldMatrix[ 1 ].xyz ) ;
lWorldNrm.z = dot( VSInput.Normal, lLocalWorldMatrix[ 2 ].xyz ) ;
// ワールドベクトルをビューベクトルに変換
lViewNrm.x = dot( lWorldNrm, cfViewMatrix[ 0 ].xyz ) ;
lViewNrm.y = dot( lWorldNrm, cfViewMatrix[ 1 ].xyz ) ;
lViewNrm.z = dot( lWorldNrm, cfViewMatrix[ 2 ].xyz ) ;
// 法線をビュー空間の角度に変換 =====================================================( 終了 )
// ディフューズカラーとスペキュラカラーとアンビエントカラーの合計値を初期化
lTotalDiffuse = float4( 0, 0, 0, 0 ) ;
lTotalSpecular = float4( 0, 0, 0, 0 ) ;
lTotalAmbient = float4( 0, 0, 0, 0 ) ;
// ライトの減衰率合計値の初期化
lTotalLightGen = 0.0f ;
// ディレクショナルライトの処理 *****************************************************( 開始 )
// ライトの方向セット
lLightDir = cfLight[ 0 ].Direction ;
// ディフューズライトとスペキュラライトの角度減衰計算 ===================( 開始 )
// 法線とライトの逆方向ベクトルとの内積を lLightLitParam.x にセット
lLightLitParam.x = dot( lViewNrm, -lLightDir ) ;
// ハーフベクトルの計算 norm( ( norm( 頂点位置から視点へのベクトル ) + ライトの方向 ) )
lLightHalfVec = normalize( normalize( -lViewPosition.xyz ) - lLightDir ) ;
// 法線とハーフベクトルの内積を lLightLitParam.y にセット
lLightLitParam.y = dot( lLightHalfVec, lViewNrm ) ;
// スペキュラ反射率を lLightLitParam.w にセット
lLightLitParam.w = cfMaterial.Power.x ;
// ライト計算
lLightLitDest = lit( lLightLitParam.x, lLightLitParam.y, lLightLitParam.w ) ;
// ディフューズライトとスペキュラライトの角度減衰計算 ===================( 終了 )
// カラー計算 ===========================================================( 開始 )
// ライトのディフューズカラーを合計値に加算
lTotalDiffuse += cfLight[ 0 ].Diffuse ;
// ライトのアンビエントカラーを合計値に加算
lTotalAmbient += cfLight[ 0 ].Ambient ;
// ディフューズカラー角度減衰率を合計値に加算
lTotalLightGen += lLightLitDest.y ;
// スペキュラライト合計値 += スペキュラ角度減衰計算結果 * ライトのスペキュラカラー
lTotalSpecular += lLightLitDest.z * cfLight[ 0 ].Specular ;
// スペキュラカラー計算 =================================================( 終了 )
// ディレクショナルライトの処理 *****************************************************( 終了 )
// ライトの処理 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// 出力ディフューズカラー = ライトディフューズカラー蓄積値 * マテリアルディフューズカラー + ( マテリアルのアンビエントカラーとグローバルアンビエントカラーを乗算したものとマテリアルエミッシブカラーを加算したもの )
VSOutput.Diffuse = lTotalDiffuse * cfMaterial.Diffuse + cfAmbient_Emissive ;
// アルファはディフューズカラーのアルファをそのまま使う
VSOutput.Diffuse.w = cfMaterial.Diffuse.w ;
// 出力スペキュラカラー = ライトスペキュラカラー蓄積値 * スペキュラカラー
VSOutput.Specular = lTotalSpecular * cfMaterial.Specular ;
// テクスチャ座標変換行列による変換を行った結果のテクスチャ座標をセット
VSOutput.TexCoords0.x = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 0 ] ) ;
VSOutput.TexCoords0.y = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 1 ] ) ;
// トゥーンテクスチャ座標はライトの減衰率
VSOutput.ToonCoords0 = lTotalLightGen ;
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータを返す
return VSOutput ;
}
ピクセルシェーダーのプログラム
// ピクセルシェーダーの入力
struct PS_INPUT
{
float4 Diffuse : COLOR0 ; // ディフューズカラー
float4 Specular : COLOR1 ; // スペキュラカラー
float2 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
float2 ToonCoords0 : TEXCOORD1 ; // トゥーンテクスチャ座標
} ;
// ピクセルシェーダーの出力
struct PS_OUTPUT
{
float4 Color0 : COLOR0 ;
} ;
// C++ 側で設定するテクスチャの定義
sampler DiffuseMapTexture : register( s0 ) ; // ディフューズマップテクスチャ
sampler ToonTexture : register( s1 ) ; // トゥーンテクスチャ
float4 cfFactorColor : register( c5 ) ; // 不透明度等
// main関数
PS_OUTPUT main( PS_INPUT PSInput )
{
PS_OUTPUT PSOutput ;
float4 TextureDiffuseColor ;
float4 ToonColor ;
// テクスチャカラーの取得
TextureDiffuseColor = tex2D( DiffuseMapTexture, PSInput.TexCoords0 ) ;
// トゥーンテクスチャカラーを取得
ToonColor = tex2D( ToonTexture, PSInput.ToonCoords0 ) ;
// 出力 = saturate( ディフューズカラー * トゥーンテクスチャカラー + スペキュラカラー ) * テクスチャカラー
PSOutput.Color0.rgb = saturate( PSInput.Diffuse.rgb * ToonColor.rgb + PSInput.Specular.rgb ) * TextureDiffuseColor.rgb ;
// アルファ値 = ディフューズアルファ * マテリアルのディフューズアルファ * 不透明度
PSOutput.Color0.a = TextureDiffuseColor.a * PSInput.Diffuse.a * cfFactorColor.a ;
// 出力パラメータを返す
return PSOutput ;
}
MMD互換のトゥーンレンダリング スキニングメッシュでディレクショナルライトとスポットライトとポイントライト一つづつ
<実行に必要なファイル一式>
一つ前のサンプルのライトの数を増やしたものです。
一度に3つなので分かりにくいですが、ちゃんと3つの光が当たっています。
ライトの処理は全て頂点シェーダーで行っているので、ピクセルシェーダーの処理はディレクショナルライト一つのときと変わりません。
C++のプログラム
#include "DxLib.h"
#include <math.h>
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
int ModelHandle ;
int GradTexHandle ;
int PixelShaderHandle ;
int VertexShaderHandle ;
float LightRotateAngle ;
int AnimIndex ;
float AnimCounter ;
int DirLightHandle ;
int PointLightHandle ;
int SpotLightHandle ;
VECTOR LightPosition ;
// ウインドウモードで起動
ChangeWindowMode( TRUE ) ;
// Direct3D9Ex を使用する
SetUseDirect3DVersion( DX_DIRECT3D_9EX ) ;
// DXライブラリの初期化
if( DxLib_Init() < 0 )
{
// エラーが発生したら直ちに終了
return -1 ;
}
// プログラマブルシェーダーモデル2.0が使用できない場合はエラーを表示して終了
if( GetValidShaderVersion() < 200 )
{
// エラー表示
DrawString( 0, 0, "プログラマブルシェーダー2.0が使用できない環境のようです", GetColor( 255,255,255 ) ) ;
// キー入力待ち
WaitKey() ;
// DXライブラリの後始末
DxLib_End() ;
// ソフト終了
return 0 ;
}
// 頂点シェーダーを読み込む
VertexShaderHandle = LoadVertexShader( "SkinMesh4_DirSpotPointLight_ToonVS.vso" ) ;
// ピクセルシェーダーを読み込む
PixelShaderHandle = LoadPixelShader( "SkinMesh4_DirSpotPointLight_ToonPS.pso" ) ;
// グラデーションテクスチャを読み込む
GradTexHandle = LoadGraph( "GradTex.bmp" ) ;
// スキニングメッシュモデルを読み込む
ModelHandle = MV1LoadModel( "DxChara.x" ) ;
// 分かりやすいように服のマテリアルを緑色にする
MV1SetMaterialDifColor( ModelHandle, 1, GetColorF( 0.0f, 0.5f, 0.0f, 1.0f ) ) ;
// アニメーション0をアタッチ
AnimIndex = MV1AttachAnim( ModelHandle, 0 ) ;
// アニメーションカウンタをリセット
AnimCounter = 0.0f ;
// モデルの描画にオリジナルシェーダーを使用する設定をONにする
MV1SetUseOrigShader( TRUE ) ;
// 使用するテクスチャ1にグラデーションテクスチャをセットする
SetUseTextureToShader( 1, GradTexHandle ) ;
// 使用する頂点シェーダーをセット
SetUseVertexShader( VertexShaderHandle ) ;
// 使用するピクセルシェーダーをセット
SetUsePixelShader( PixelShaderHandle ) ;
// 標準ライトをオフにする
SetLightEnable( FALSE ) ;
// ディレクショナルライトを作成する
DirLightHandle = CreateDirLightHandle( VGet( 1.0f, 0.0f, 0.0f ) ) ;
// ポイントライトハンドルを作成する
PointLightHandle = CreatePointLightHandle( VGet( 0.0f, 0.0f, 0.0f ), 1000.0f, 0.86f, 0.0000f, 0.0000f ) ;
// スポットライトを作成する
SpotLightHandle = CreateSpotLightHandle( VGet( 0.0f, 0.0f, 0.0f ), VGet( 0.0f, -1.0f, 0.0f ), 0.7f, 0.6f, 1000.0f, 0.391586f, 0.001662f, 0.0f ) ;
// スポットライトのアンビエントカラーを無効にする
SetLightAmbColorHandle( SpotLightHandle, GetColorF( 0.0f, 0.0f, 0.0f, 0.0f ) ) ;
// スポットライトのディフューズカラーを緑にする
SetLightDifColorHandle( SpotLightHandle, GetColorF( 0.0f, 1.0f, 0.0f, 0.0f ) ) ;
// ライトの位置を回転する値を初期化
LightRotateAngle = 0.0f ;
// 描画先を裏画面にする
SetDrawScreen( DX_SCREEN_BACK ) ;
// モデルの見える位置にカメラを配置
SetCameraPositionAndTarget_UpVecY( VGet( 0.0f, 700.0f, -1100.0f ), VGet( 0.0f, 350.0f, 0.0f ) ) ;
// ESCキーが押されるまでループ
while( ProcessMessage() == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0 )
{
// 画面を初期化
ClearDrawScreen() ;
// ポイントライトの位置の回転値を加算
LightRotateAngle += 0.02f ;
// ポイントライトの位置の更新
LightPosition.x = sin( LightRotateAngle ) * 600.0f ;
LightPosition.y = 400.0f ;
LightPosition.z = cos( LightRotateAngle ) * 600.0f - 250.0f ;
SetLightPositionHandle( PointLightHandle, LightPosition ) ;
// スポットライトの位置の更新
LightPosition.x = 0.0f ;
LightPosition.y = cos( LightRotateAngle ) * 700.0f + 400.0f ;
LightPosition.z = sin( LightRotateAngle ) * 700.0f - 250.0f ;
SetLightPositionHandle( SpotLightHandle, LightPosition ) ;
// ライトの向きを回転中心方向にする
SetLightDirectionHandle( SpotLightHandle, VSub( VGet( 0.0f, 400.0f, -250.0f ), LightPosition ) ) ;
// アニメーション時間を進める
AnimCounter += 100.0f ;
if( AnimCounter > MV1GetAnimTotalTime( ModelHandle, 0 ) )
{
AnimCounter -= MV1GetAnimTotalTime( ModelHandle, 0 ) ;
}
MV1SetAttachAnimTime( ModelHandle, AnimIndex, AnimCounter ) ;
// モデルを描画
MV1DrawModel( ModelHandle ) ;
// 裏画面の内容を表画面に反映させる
ScreenFlip() ;
}
// ディレクショナルライトの削除
DeleteLightHandle( DirLightHandle ) ;
// スポットライトの削除
DeleteLightHandle( SpotLightHandle ) ;
// ポイントライトの削除
DeleteLightHandle( PointLightHandle ) ;
// 使用するテクスチャからグラデーションテクスチャを外す
SetUseTextureToShader( 1, -1 ) ;
// グラデーションテクスチャを削除
DeleteGraph( GradTexHandle ) ;
// 読み込んだ頂点シェーダーの削除
DeleteShader( VertexShaderHandle ) ;
// 読み込んだピクセルシェーダーの削除
DeleteShader( PixelShaderHandle ) ;
// 読み込んだモデルの削除
MV1DeleteModel( ModelHandle ) ;
// DXライブラリの後始末
DxLib_End() ;
// ソフトの終了
return 0 ;
}
頂点シェーダーのプログラム
// 頂点シェーダーの入力
struct VS_INPUT
{
float4 Position : POSITION ; // 座標( ローカル空間 )
int4 BlendIndices0 : BLENDINDICES0 ; // スキニング処理用 Float型定数配列インデックス
float4 BlendWeight0 : BLENDWEIGHT0 ; // スキニング処理用ウエイト値
float3 Normal : NORMAL0 ; // 法線( ローカル空間 )
float4 Diffuse : COLOR0 ; // ディフューズカラー
float4 Specular : COLOR1 ; // スペキュラカラー
float4 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
} ;
// 頂点シェーダーの出力
struct VS_OUTPUT
{
float4 Position : POSITION ; // 座標( プロジェクション空間 )
float4 Diffuse : COLOR0 ; // ディフューズカラー
float4 Specular : COLOR1 ; // スペキュラカラー
float2 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
float2 ToonCoords0 : TEXCOORD1 ; // トゥーンテクスチャ座標
} ;
// マテリアルパラメータ
struct VS_CONST_MATERIAL
{
float4 Diffuse ; // マテリアルディフューズカラー
float4 Specular ; // マテリアルスペキュラカラー
float4 Power ; // マテリアルスペキュラハイライトの強さ
} ;
// ライトパラメータ
struct VS_CONST_LIGHT
{
float4 Position ; // 座標( ビュー空間 )
float3 Direction ; // 方向( ビュー空間 )
float4 Diffuse ; // ディフューズカラー
float4 Specular ; // スペキュラカラー
float4 Ambient ; // アンビエントカラーとマテリアルのアンビエントカラーを乗算したもの
float4 Range_FallOff_AT0_AT1 ; // x:有効距離 y:スポットライト用FallOff z:距離による減衰処理用パラメータ0 w:距離による減衰処理用パラメータ1
float4 AT2_SpotP0_SpotP1 ; // x:距離による減衰処理用パラメータ2 y:スポットライト用パラメータ0( cos( Phi / 2.0f ) ) z:スポットライト用パラメータ1( 1.0f / ( cos( Theta / 2.0f ) - cos( Phi / 2.0f ) ) )
} ;
// C++ 側で設定する定数の定義
float4 cfAmbient_Emissive : register( c1 ) ; // マテリアルエミッシブカラー + マテリアルアンビエントカラー * グローバルアンビエントカラー
float4 cfProjectionMatrix[ 4 ] : register( c2 ) ; // ビュー → 射影行列
float4 cfViewMatrix[ 3 ] : register( c6 ) ; // ワールド → ビュー行列
float4 cfTextureMatrix[ 3 ][ 2 ] : register( c88 ) ; // テクスチャ座標操作用行列
float4 cfLocalWorldMatrix[ 162 ] : register( c94 ) ; // ローカル → ワールド行列
VS_CONST_MATERIAL cfMaterial : register( c11 ) ; // マテリアルパラメータ
VS_CONST_LIGHT cfLight[ 3 ] : register( c14 ) ; // 有効ライトのパラメータ
// main関数
VS_OUTPUT main( VS_INPUT VSInput )
{
VS_OUTPUT VSOutput ;
float4 lLocalWorldMatrix[ 3 ] ;
float4 lWorldPosition ;
float4 lViewPosition ;
float3 lWorldNrm ;
float3 lViewNrm ;
float4 lTotalDiffuse ;
float4 lTotalSpecular ;
float3 lLightDir ;
float3 lLightTemp ;
float lLightDistancePow2 ;
float lLightGen ;
float lLightDirectionCosA ;
float3 lLightHalfVec ;
float4 lLightLitParam ;
float4 lLightLitDest ;
float4 lTotalAmbient ;
float lTotalLightGen ;
// 複数のフレームのブレンド行列の作成
lLocalWorldMatrix[ 0 ] = cfLocalWorldMatrix[ VSInput.BlendIndices0.x + 0 ] * VSInput.BlendWeight0.x;
lLocalWorldMatrix[ 1 ] = cfLocalWorldMatrix[ VSInput.BlendIndices0.x + 1 ] * VSInput.BlendWeight0.x;
lLocalWorldMatrix[ 2 ] = cfLocalWorldMatrix[ VSInput.BlendIndices0.x + 2 ] * VSInput.BlendWeight0.x;
lLocalWorldMatrix[ 0 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.y + 0 ] * VSInput.BlendWeight0.y;
lLocalWorldMatrix[ 1 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.y + 1 ] * VSInput.BlendWeight0.y;
lLocalWorldMatrix[ 2 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.y + 2 ] * VSInput.BlendWeight0.y;
lLocalWorldMatrix[ 0 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.z + 0 ] * VSInput.BlendWeight0.z;
lLocalWorldMatrix[ 1 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.z + 1 ] * VSInput.BlendWeight0.z;
lLocalWorldMatrix[ 2 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.z + 2 ] * VSInput.BlendWeight0.z;
lLocalWorldMatrix[ 0 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.w + 0 ] * VSInput.BlendWeight0.w;
lLocalWorldMatrix[ 1 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.w + 1 ] * VSInput.BlendWeight0.w;
lLocalWorldMatrix[ 2 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.w + 2 ] * VSInput.BlendWeight0.w;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ローカル座標をワールド座標に変換
lWorldPosition.x = dot( VSInput.Position, lLocalWorldMatrix[ 0 ] ) ;
lWorldPosition.y = dot( VSInput.Position, lLocalWorldMatrix[ 1 ] ) ;
lWorldPosition.z = dot( VSInput.Position, lLocalWorldMatrix[ 2 ] ) ;
lWorldPosition.w = 1.0f ;
// ワールド座標をビュー座標に変換
lViewPosition.x = dot( lWorldPosition, cfViewMatrix[ 0 ] ) ;
lViewPosition.y = dot( lWorldPosition, cfViewMatrix[ 1 ] ) ;
lViewPosition.z = dot( lWorldPosition, cfViewMatrix[ 2 ] ) ;
lViewPosition.w = 1.0f ;
// ビュー座標を射影座標に変換
VSOutput.Position.x = dot( lViewPosition, cfProjectionMatrix[ 0 ] ) ;
VSOutput.Position.y = dot( lViewPosition, cfProjectionMatrix[ 1 ] ) ;
VSOutput.Position.z = dot( lViewPosition, cfProjectionMatrix[ 2 ] ) ;
VSOutput.Position.w = dot( lViewPosition, cfProjectionMatrix[ 3 ] ) ;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// ライトの処理 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// 法線をビュー空間の角度に変換 =====================================================( 開始 )
// ローカルベクトルをワールドベクトルに変換
lWorldNrm.x = dot( VSInput.Normal, lLocalWorldMatrix[ 0 ].xyz ) ;
lWorldNrm.y = dot( VSInput.Normal, lLocalWorldMatrix[ 1 ].xyz ) ;
lWorldNrm.z = dot( VSInput.Normal, lLocalWorldMatrix[ 2 ].xyz ) ;
// ワールドベクトルをビューベクトルに変換
lViewNrm.x = dot( lWorldNrm, cfViewMatrix[ 0 ].xyz ) ;
lViewNrm.y = dot( lWorldNrm, cfViewMatrix[ 1 ].xyz ) ;
lViewNrm.z = dot( lWorldNrm, cfViewMatrix[ 2 ].xyz ) ;
// 法線をビュー空間の角度に変換 =====================================================( 終了 )
// ディフューズカラーとスペキュラカラーとアンビエントカラーの合計値を初期化
lTotalDiffuse = float4( 0, 0, 0, 0 ) ;
lTotalSpecular = float4( 0, 0, 0, 0 ) ;
lTotalAmbient = float4( 0, 0, 0, 0 ) ;
// ライトの減衰率合計値の初期化
lTotalLightGen = 0.0f ;
// ディレクショナルライトの処理 *****************************************************( 開始 )
// ライトの方向セット
lLightDir = cfLight[ 0 ].Direction ;
// ディフューズライトとスペキュラライトの角度減衰計算 ===================( 開始 )
// 法線とライトの逆方向ベクトルとの内積を lLightLitParam.x にセット
lLightLitParam.x = dot( lViewNrm, -lLightDir ) ;
// ハーフベクトルの計算 norm( ( norm( 頂点位置から視点へのベクトル ) + ライトの方向 ) )
lLightHalfVec = normalize( normalize( -lViewPosition.xyz ) - lLightDir ) ;
// 法線とハーフベクトルの内積を lLightLitParam.y にセット
lLightLitParam.y = dot( lLightHalfVec, lViewNrm ) ;
// スペキュラ反射率を lLightLitParam.w にセット
lLightLitParam.w = cfMaterial.Power.x ;
// ライト計算
lLightLitDest = lit( lLightLitParam.x, lLightLitParam.y, lLightLitParam.w ) ;
// ディフューズライトとスペキュラライトの角度減衰計算 ===================( 終了 )
// カラー計算 ===========================================================( 開始 )
// ライトのディフューズカラーを合計値に加算
lTotalDiffuse += cfLight[ 0 ].Diffuse ;
// ライトのアンビエントカラーを合計値に加算
lTotalAmbient += cfLight[ 0 ].Ambient ;
// ディフューズカラー角度減衰率を合計値に加算
lTotalLightGen += lLightLitDest.y ;
// スペキュラライト合計値 += スペキュラ角度減衰計算結果 * ライトのスペキュラカラー
lTotalSpecular += lLightLitDest.z * cfLight[ 0 ].Specular ;
// スペキュラカラー計算 =================================================( 終了 )
// ディレクショナルライトの処理 *****************************************************( 終了 )
// スポットライトの処理 *************************************************************( 開始 )
// ライト方向ベクトルの計算
lLightDir = normalize( lViewPosition.xyz - cfLight[ 1 ].Position.xyz ) ;
// 距離・スポットライト減衰値計算 =======================================( 開始 )
// 距離減衰計算 ------------------
// 頂点とライト位置との距離の二乗を求める
lLightTemp = lViewPosition.xyz - cfLight[ 1 ].Position.xyz ;
lLightDistancePow2 = dot( lLightTemp, lLightTemp ) ;
// 減衰率の計算 lLightGen = 1 / ( 減衰値0 + 減衰値1 * 距離 + 減衰値2 * ( 距離 * 距離 ) )
lLightGen = 1.0f / ( cfLight[ 1 ].Range_FallOff_AT0_AT1.z + cfLight[ 1 ].Range_FallOff_AT0_AT1.w * sqrt( lLightDistancePow2 ) + cfLight[ 1 ].AT2_SpotP0_SpotP1.x * lLightDistancePow2 ) ;
// スポットライト減衰計算 --------
// ライト方向ベクトルとライト位置から頂点位置へのベクトルの内積( 即ち Cos a )を計算
lLightDirectionCosA = dot( lLightDir, cfLight[ 1 ].Direction ) ;
// スポットライト減衰計算 pow( falloff, ( ( Cos a - Cos f ) / ( Cos q - Cos f ) ) )
lLightGen *= saturate( pow( abs( max( lLightDirectionCosA - cfLight[ 1 ].AT2_SpotP0_SpotP1.y, 0.0f ) * cfLight[ 1 ].AT2_SpotP0_SpotP1.z ), cfLight[ 1 ].Range_FallOff_AT0_AT1.y ) ) ;
// 有効距離外だったら減衰率を最大にする処理
lLightGen *= step( lLightDistancePow2, cfLight[ 1 ].Range_FallOff_AT0_AT1.x ) ;
// 距離・スポットライト減衰値計算 =======================================( 終了 )
// ディフューズライトとスペキュラライトの角度減衰計算 ===================( 開始 )
// 法線とライトの逆方向ベクトルとの内積を lLightLitParam.x にセット
lLightLitParam.x = dot( lViewNrm, -lLightDir ) ;
// ハーフベクトルの計算 norm( ( norm( 頂点位置から視点へのベクトル ) + ライトの方向 ) )
lLightHalfVec = normalize( normalize( -lViewPosition.xyz ) - lLightDir ) ;
// 法線とハーフベクトルの内積を lLightLitParam.y にセット
lLightLitParam.y = dot( lLightHalfVec, lViewNrm ) ;
// スペキュラ反射率を lLightLitParam.w にセット
lLightLitParam.w = cfMaterial.Power.x ;
// ライト計算
lLightLitDest = lit( lLightLitParam.x, lLightLitParam.y, lLightLitParam.w ) ;
// ディフューズライトとスペキュラライトの角度減衰計算 ===================( 終了 )
// カラー計算 ===========================================================( 開始 )
// ライトのディフューズカラーを合計値に加算
lTotalDiffuse += cfLight[ 1 ].Diffuse ;
// ライトのアンビエントカラーを合計値に加算
lTotalAmbient += cfLight[ 1 ].Ambient ;
// ディフューズカラー角度減衰率と距離減衰率を乗算したものを合計値に加算
lTotalLightGen += lLightLitDest.y * lLightGen ;
// スペキュラライト合計値 += スペキュラ角度減衰計算結果 * 距離減衰率 * ライトのスペキュラカラー
lTotalSpecular += lLightLitDest.z * lLightGen * cfLight[ 1 ].Specular ;
// カラー計算 ===========================================================( 終了 )
// スポットライトの処理 *************************************************************( 終了 )
// ポイントライトの処理 *************************************************************( 開始 )
// ライト方向ベクトルの計算
lLightDir = normalize( lViewPosition.xyz - cfLight[ 2 ].Position.xyz ) ;
// 距離・スポットライト減衰値計算 =======================================( 開始 )
// 距離減衰計算 ------------------
// 頂点とライト位置との距離の二乗を求める
lLightTemp = lViewPosition.xyz - cfLight[ 1 ].Position.xyz ;
lLightDistancePow2 = dot( lLightTemp, lLightTemp ) ;
// 減衰率の計算 lLightGen = 1 / ( 減衰値0 + 減衰値1 * 距離 + 減衰値2 * ( 距離 * 距離 ) )
lLightGen = 1.0f / ( cfLight[ 2 ].Range_FallOff_AT0_AT1.z + cfLight[ 2 ].Range_FallOff_AT0_AT1.w * sqrt( lLightDistancePow2 ) + cfLight[ 2 ].AT2_SpotP0_SpotP1.x * lLightDistancePow2 ) ;
// 有効距離外だったら減衰率を最大にする処理
lLightGen *= step( lLightDistancePow2, cfLight[ 2 ].Range_FallOff_AT0_AT1.x ) ;
// 距離・スポットライト減衰値計算 =======================================( 終了 )
// ディフューズライトとスペキュラライトの角度減衰計算 ===================( 開始 )
// 法線とライトの逆方向ベクトルとの内積を lLightLitParam.x にセット
lLightLitParam.x = dot( lViewNrm, -lLightDir ) ;
// ハーフベクトルの計算 norm( ( norm( 頂点位置から視点へのベクトル ) + ライトの方向 ) )
lLightHalfVec = normalize( normalize( -lViewPosition.xyz ) - lLightDir ) ;
// 法線とハーフベクトルの内積を lLightLitParam.y にセット
lLightLitParam.y = dot( lLightHalfVec, lViewNrm ) ;
// スペキュラ反射率を lLightLitParam.w にセット
lLightLitParam.w = cfMaterial.Power.x ;
// ライト計算
lLightLitDest = lit( lLightLitParam.x, lLightLitParam.y, lLightLitParam.w ) ;
// ディフューズライトとスペキュラライトの角度減衰計算 ===================( 終了 )
// カラー計算 ===========================================================( 開始 )
// ライトのディフューズカラーを合計値に加算
lTotalDiffuse += cfLight[ 2 ].Diffuse ;
// ライトのアンビエントカラーを合計値に加算
lTotalAmbient += cfLight[ 2 ].Ambient ;
// ディフューズカラー角度減衰率と距離減衰率を乗算したものを合計値に加算
lTotalLightGen += lLightLitDest.y * lLightGen ;
// スペキュラライト合計値 += スペキュラ角度減衰計算結果 * 距離減衰率 * ライトのスペキュラカラー
lTotalSpecular += lLightLitDest.z * lLightGen * cfLight[ 2 ].Specular ;
// カラー計算 ===========================================================( 終了 )
// ポイントライトの処理 *************************************************************( 終了 )
// ライトの処理 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// 出力ディフューズカラー = ライトディフューズカラー蓄積値 * マテリアルディフューズカラー + ( マテリアルのアンビエントカラーとグローバルアンビエントカラーを乗算したものとマテリアルエミッシブカラーを加算したもの )
VSOutput.Diffuse = lTotalDiffuse * cfMaterial.Diffuse + cfAmbient_Emissive ;
// アルファはディフューズカラーのアルファをそのまま使う
VSOutput.Diffuse.w = cfMaterial.Diffuse.w ;
// 出力スペキュラカラー = ライトスペキュラカラー蓄積値 * スペキュラカラー
VSOutput.Specular = lTotalSpecular * cfMaterial.Specular ;
// テクスチャ座標変換行列による変換を行った結果のテクスチャ座標をセット
VSOutput.TexCoords0.x = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 0 ] ) ;
VSOutput.TexCoords0.y = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 1 ] ) ;
// トゥーンテクスチャ座標はライトの減衰率
VSOutput.ToonCoords0 = lTotalLightGen ;
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータを返す
return VSOutput ;
}
ピクセルシェーダーのプログラム
// ピクセルシェーダーの入力
struct PS_INPUT
{
float4 Diffuse : COLOR0 ; // ディフューズカラー
float4 Specular : COLOR1 ; // スペキュラカラー
float2 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
float2 ToonCoords0 : TEXCOORD1 ; // トゥーンテクスチャ座標
} ;
// ピクセルシェーダーの出力
struct PS_OUTPUT
{
float4 Color0 : COLOR0 ;
} ;
// C++ 側で設定するテクスチャの定義
sampler DiffuseMapTexture : register( s0 ) ; // ディフューズマップテクスチャ
sampler ToonTexture : register( s1 ) ; // トゥーンテクスチャ
float4 cfFactorColor : register( c5 ) ; // 不透明度等
// main関数
PS_OUTPUT main( PS_INPUT PSInput )
{
PS_OUTPUT PSOutput ;
float4 TextureDiffuseColor ;
float4 ToonColor ;
// テクスチャカラーの取得
TextureDiffuseColor = tex2D( DiffuseMapTexture, PSInput.TexCoords0 ) ;
// トゥーンテクスチャカラーを取得
ToonColor = tex2D( ToonTexture, PSInput.ToonCoords0 ) ;
// 出力 = saturate( ディフューズカラー * トゥーンテクスチャカラー + スペキュラカラー ) * テクスチャカラー
PSOutput.Color0.rgb = saturate( PSInput.Diffuse.rgb * ToonColor.rgb + PSInput.Specular.rgb ) * TextureDiffuseColor.rgb ;
// アルファ値 = ディフューズアルファ * マテリアルのディフューズアルファ * 不透明度
PSOutput.Color0.a = TextureDiffuseColor.a * PSInput.Diffuse.a * cfFactorColor.a ;
// 出力パラメータを返す
return PSOutput ;
}
MMD互換のトゥーンレンダリング スキニングメッシュでディレクショナルライト一つのフォンシェーディング
<実行に必要なファイル一式>
頂点シェーダーでライティング処理を行わず、ピクセルシェーダーでライティング処理を行うフォンシェーディングでMMD互換の
トゥーンレンダリングを行うサンプルです。( フォンシェーディングの解説はこちらの解説を参照してください )
このサンプルではディレクショナルライト一つだけで、次のサンプルではディレクショナルライト、スポットライト、ポイントライトの
3つを一度に有効にしているサンプルです。
例によってプログラマブルシェーダー3.0を使える環境ではない場合は動作しませんので注意してください。
C++のプログラム
#include "DxLib.h"
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
int ModelHandle ;
int GradTexHandle ;
int PixelShaderHandle ;
int VertexShaderHandle ;
int AnimIndex ;
float AnimCounter ;
// ウインドウモードで起動
ChangeWindowMode( TRUE ) ;
// Direct3D9Ex を使用する
SetUseDirect3DVersion( DX_DIRECT3D_9EX ) ;
// DXライブラリの初期化
if( DxLib_Init() < 0 )
{
// エラーが発生したら直ちに終了
return -1 ;
}
// プログラマブルシェーダーモデル3.0が使用できない場合はエラーを表示して終了
if( GetValidShaderVersion() < 300 )
{
// エラー表示
DrawString( 0, 0, "プログラマブルシェーダー3.0が使用できない環境のようです", GetColor( 255,255,255 ) ) ;
// キー入力待ち
WaitKey() ;
// DXライブラリの後始末
DxLib_End() ;
// ソフト終了
return 0 ;
}
// 頂点シェーダーを読み込む
VertexShaderHandle = LoadVertexShader( "SkinMesh4_DirLight_Toon_PhongVS.vso" ) ;
// ピクセルシェーダーを読み込む
PixelShaderHandle = LoadPixelShader( "SkinMesh4_DirLight_Toon_PhongPS.pso" ) ;
// グラデーションテクスチャを読み込む
GradTexHandle = LoadGraph( "GradTex.bmp" ) ;
// スキニングメッシュモデルを読み込む
ModelHandle = MV1LoadModel( "DxChara.x" ) ;
// 分かりやすいように服のマテリアルを緑色にする
MV1SetMaterialDifColor( ModelHandle, 1, GetColorF( 0.0f, 0.5f, 0.0f, 1.0f ) ) ;
// アニメーション0をアタッチ
AnimIndex = MV1AttachAnim( ModelHandle, 0 ) ;
// アニメーションカウンタをリセット
AnimCounter = 0.0f ;
// モデルの描画にオリジナルシェーダーを使用する設定をONにする
MV1SetUseOrigShader( TRUE ) ;
// 使用するテクスチャ1にグラデーションテクスチャをセットする
SetUseTextureToShader( 1, GradTexHandle ) ;
// 使用する頂点シェーダーをセット
SetUseVertexShader( VertexShaderHandle ) ;
// 使用するピクセルシェーダーをセット
SetUsePixelShader( PixelShaderHandle ) ;
// 描画先を裏画面にする
SetDrawScreen( DX_SCREEN_BACK ) ;
// モデルの見える位置にカメラを配置
SetCameraPositionAndTarget_UpVecY( VGet( 0.0f, 700.0f, -1100.0f ), VGet( 0.0f, 350.0f, 0.0f ) ) ;
// ESCキーが押されるまでループ
while( ProcessMessage() == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0 )
{
// 画面を初期化
ClearDrawScreen() ;
// アニメーション時間を進める
AnimCounter += 100.0f ;
if( AnimCounter > MV1GetAnimTotalTime( ModelHandle, 0 ) )
{
AnimCounter -= MV1GetAnimTotalTime( ModelHandle, 0 ) ;
}
MV1SetAttachAnimTime( ModelHandle, AnimIndex, AnimCounter ) ;
// モデルを描画
MV1DrawModel( ModelHandle ) ;
// 裏画面の内容を表画面に反映させる
ScreenFlip() ;
}
// 使用するテクスチャからグラデーションテクスチャを外す
SetUseTextureToShader( 1, -1 ) ;
// グラデーションテクスチャを削除
DeleteGraph( GradTexHandle ) ;
// 読み込んだ頂点シェーダーの削除
DeleteShader( VertexShaderHandle ) ;
// 読み込んだピクセルシェーダーの削除
DeleteShader( PixelShaderHandle ) ;
// 読み込んだモデルの削除
MV1DeleteModel( ModelHandle ) ;
// DXライブラリの後始末
DxLib_End() ;
// ソフトの終了
return 0 ;
}
頂点シェーダーのプログラム
// 頂点シェーダーの入力
struct VS_INPUT
{
float4 Position : POSITION ; // 座標( ローカル空間 )
int4 BlendIndices0 : BLENDINDICES0 ; // スキニング処理用 Float型定数配列インデックス
float4 BlendWeight0 : BLENDWEIGHT0 ; // スキニング処理用ウエイト値
float3 Normal : NORMAL0 ; // 法線( ローカル空間 )
float4 Diffuse : COLOR0 ; // ディフューズカラー
float4 Specular : COLOR1 ; // スペキュラカラー
float4 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
} ;
// 頂点シェーダーの出力
struct VS_OUTPUT
{
float4 Position : POSITION ; // 座標( プロジェクション空間 )
float2 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
float3 VPosition : TEXCOORD1 ; // 座標( ビュー空間 )
float3 VNormal : TEXCOORD2 ; // 法線( ビュー空間 )
} ;
// C++ 側で設定する定数の定義
float4 cfAmbient_Emissive : register( c1 ) ; // マテリアルエミッシブカラー + マテリアルアンビエントカラー * グローバルアンビエントカラー
float4 cfProjectionMatrix[ 4 ] : register( c2 ) ; // ビュー → 射影行列
float4 cfViewMatrix[ 3 ] : register( c6 ) ; // ワールド → ビュー行列
float4 cfTextureMatrix[ 3 ][ 2 ] : register( c88 ) ; // テクスチャ座標操作用行列
float4 cfLocalWorldMatrix[ 162 ] : register( c94 ) ; // ローカル → ワールド行列
// main関数
VS_OUTPUT main( VS_INPUT VSInput )
{
VS_OUTPUT VSOutput ;
float4 lLocalWorldMatrix[ 3 ] ;
float4 lWorldPosition ;
float4 lViewPosition ;
float3 lWorldNrm ;
float3 lViewNrm ;
// 複数のフレームのブレンド行列の作成
lLocalWorldMatrix[ 0 ] = cfLocalWorldMatrix[ VSInput.BlendIndices0.x + 0 ] * VSInput.BlendWeight0.x;
lLocalWorldMatrix[ 1 ] = cfLocalWorldMatrix[ VSInput.BlendIndices0.x + 1 ] * VSInput.BlendWeight0.x;
lLocalWorldMatrix[ 2 ] = cfLocalWorldMatrix[ VSInput.BlendIndices0.x + 2 ] * VSInput.BlendWeight0.x;
lLocalWorldMatrix[ 0 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.y + 0 ] * VSInput.BlendWeight0.y;
lLocalWorldMatrix[ 1 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.y + 1 ] * VSInput.BlendWeight0.y;
lLocalWorldMatrix[ 2 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.y + 2 ] * VSInput.BlendWeight0.y;
lLocalWorldMatrix[ 0 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.z + 0 ] * VSInput.BlendWeight0.z;
lLocalWorldMatrix[ 1 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.z + 1 ] * VSInput.BlendWeight0.z;
lLocalWorldMatrix[ 2 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.z + 2 ] * VSInput.BlendWeight0.z;
lLocalWorldMatrix[ 0 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.w + 0 ] * VSInput.BlendWeight0.w;
lLocalWorldMatrix[ 1 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.w + 1 ] * VSInput.BlendWeight0.w;
lLocalWorldMatrix[ 2 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.w + 2 ] * VSInput.BlendWeight0.w;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ローカル座標をワールド座標に変換
lWorldPosition.x = dot( VSInput.Position, lLocalWorldMatrix[ 0 ] ) ;
lWorldPosition.y = dot( VSInput.Position, lLocalWorldMatrix[ 1 ] ) ;
lWorldPosition.z = dot( VSInput.Position, lLocalWorldMatrix[ 2 ] ) ;
lWorldPosition.w = 1.0f ;
// ワールド座標をビュー座標に変換
lViewPosition.x = dot( lWorldPosition, cfViewMatrix[ 0 ] ) ;
lViewPosition.y = dot( lWorldPosition, cfViewMatrix[ 1 ] ) ;
lViewPosition.z = dot( lWorldPosition, cfViewMatrix[ 2 ] ) ;
lViewPosition.w = 1.0f ;
// ビュー座標を射影座標に変換
VSOutput.Position.x = dot( lViewPosition, cfProjectionMatrix[ 0 ] ) ;
VSOutput.Position.y = dot( lViewPosition, cfProjectionMatrix[ 1 ] ) ;
VSOutput.Position.z = dot( lViewPosition, cfProjectionMatrix[ 2 ] ) ;
VSOutput.Position.w = dot( lViewPosition, cfProjectionMatrix[ 3 ] ) ;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 法線をビュー空間の角度に変換 =====================================================( 開始 )
// ローカルベクトルをワールドベクトルに変換
lWorldNrm.x = dot( VSInput.Normal, lLocalWorldMatrix[ 0 ].xyz ) ;
lWorldNrm.y = dot( VSInput.Normal, lLocalWorldMatrix[ 1 ].xyz ) ;
lWorldNrm.z = dot( VSInput.Normal, lLocalWorldMatrix[ 2 ].xyz ) ;
// ワールドベクトルをビューベクトルに変換
lViewNrm.x = dot( lWorldNrm, cfViewMatrix[ 0 ].xyz ) ;
lViewNrm.y = dot( lWorldNrm, cfViewMatrix[ 1 ].xyz ) ;
lViewNrm.z = dot( lWorldNrm, cfViewMatrix[ 2 ].xyz ) ;
// 法線をビュー空間の角度に変換 =====================================================( 終了 )
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// 頂点座標を保存
VSOutput.VPosition = lViewPosition.xyz ;
// 法線を保存
VSOutput.VNormal = lViewNrm ;
// テクスチャ座標変換行列による変換を行った結果のテクスチャ座標をセット
VSOutput.TexCoords0.x = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 0 ] ) ;
VSOutput.TexCoords0.y = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 1 ] ) ;
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータを返す
return VSOutput ;
}
ピクセルシェーダーのプログラム
// ピクセルシェーダーの入力
struct PS_INPUT
{
float2 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
float3 VPosition : TEXCOORD1 ; // 座標( ビュー空間 )
float3 VNormal : TEXCOORD2 ; // 法線( ビュー空間 )
} ;
// ピクセルシェーダーの出力
struct PS_OUTPUT
{
float4 Color0 : COLOR0 ;
} ;
// マテリアルパラメータ
struct MATERIAL
{
float4 Diffuse ; // ディフューズカラー
float4 Specular ; // スペキュラカラー
float4 Power ; // スペキュラの強さ
} ;
// ライトパラメータ
struct LIGHT
{
float4 Position ; // 座標( ビュー空間 )
float3 Direction ; // 方向( ビュー空間 )
float4 Diffuse ; // ディフューズカラー
float4 Specular ; // スペキュラカラー
float4 Ambient ; // アンビエントカラーとマテリアルのアンビエントカラーを乗算したもの
float4 Range_FallOff_AT0_AT1 ; // x:有効距離 y:スポットライト用FallOff z:距離による減衰処理用パラメータ0 w:距離による減衰処理用パラメータ1
float4 AT2_SpotP0_SpotP1 ; // x:距離による減衰処理用パラメータ2 y:スポットライト用パラメータ0( cos( Phi / 2.0f ) ) z:スポットライト用パラメータ1( 1.0f / ( cos( Theta / 2.0f ) - cos( Phi / 2.0f ) ) )
} ;
// C++ 側で設定するテクスチャや定数の定義
sampler DiffuseMapTexture : register( s0 ) ; // ディフューズマップテクスチャ
sampler ToonTexture : register( s1 ) ; // トゥーンテクスチャ
float4 cfAmbient_Emissive : register( c1 ) ; // エミッシブカラー + マテリアルアンビエントカラー * グローバルアンビエントカラー
MATERIAL cfMaterial : register( c2 ) ; // マテリアルパラメータ
float4 cfFactorColor : register( c5 ) ; // 不透明度等
LIGHT cfLight[ 1 ] : register( c32 ) ; // ライトパラメータ
// main関数
PS_OUTPUT main( PS_INPUT PSInput )
{
PS_OUTPUT PSOutput ;
float4 lTextureDiffuseColor ;
float4 lSpecularColor ;
float4 lDiffuseColor ;
float3 Normal ;
float lDiffuseAngleGen ;
float4 lTotalDiffuse ;
float4 lTotalSpecular ;
float4 lTotalAmbient ;
float4 lToonColor ;
float lTotalLightGen ;
float3 V_to_Eye ;
float3 TempF3 ;
float Temp ;
float3 lLightTemp ;
float3 lLightDir ;
// 法線の準備
Normal = normalize( PSInput.VNormal ) ;
// 頂点座標から視点へのベクトルを正規化
V_to_Eye = normalize( -PSInput.VPosition ) ;
// ディフューズカラーとスペキュラカラーとアンビエントカラーの合計値を初期化
lTotalDiffuse = float4( 0.0f, 0.0f, 0.0f, 0.0f ) ;
lTotalSpecular = float4( 0.0f, 0.0f, 0.0f, 0.0f ) ;
lTotalAmbient = float4( 0.0f, 0.0f, 0.0f, 0.0f ) ;
// ライトの減衰率合計値の初期化
lTotalLightGen = 0.0f ;
// ディレクショナルライトの処理 +++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ライト方向ベクトルのセット
lLightDir = cfLight[ 0 ].Direction ;
// ディフューズ角度減衰率計算
lDiffuseAngleGen = saturate( dot( Normal, -lLightDir ) ) ;
// ディフューズ減衰率を合計値に加算
lTotalLightGen += lDiffuseAngleGen ;
// スペキュラカラー計算
// ハーフベクトルの計算
TempF3 = normalize( V_to_Eye - lLightDir ) ;
// Temp = pow( max( 0.0f, N * H ), cfMaterial.Power.x )
Temp = pow( max( 0.0f, dot( Normal, TempF3 ) ), cfMaterial.Power.x ) ;
// スペキュラライト合計値 += スペキュラ角度減衰計算結果 * ライトのスペキュラカラー
lTotalSpecular += Temp * cfLight[ 0 ].Specular ;
// ライトのディフューズカラーを合計値に加算
lTotalDiffuse += cfLight[ 0 ].Diffuse ;
// ライトのアンビエントカラーを合計値に加算
lTotalAmbient += cfLight[ 0 ].Ambient ;
// ディレクショナルライトの処理 +++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// アンビエントカラーの蓄積値 += マテリアルのアンビエントカラーとグローバルアンビエントカラーを乗算したものとマテリアルエミッシブカラーを加算したもの
lTotalAmbient += cfAmbient_Emissive ;
// 出力カラー計算 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// テクスチャカラーの取得
lTextureDiffuseColor = tex2D( DiffuseMapTexture, PSInput.TexCoords0 ) ;
// トゥーンテクスチャカラーをライトのディフューズ減衰率から取得
lToonColor = tex2D( ToonTexture, lTotalLightGen ) ;
// ディフューズカラー = ライトのディフューズカラー蓄積値 * マテリアルのディフューズカラー
lDiffuseColor = lTotalDiffuse * cfMaterial.Diffuse ;
// スペキュラカラー = ライトのスペキュラカラー蓄積値 * マテリアルのスペキュラカラー
lSpecularColor = lTotalSpecular * cfMaterial.Specular ;
// 出力 = saturate( saturate( ディフューズカラー * アンビエントカラーの蓄積値 ) * トゥーンテクスチャカラー + スペキュラカラー ) * テクスチャカラー
PSOutput.Color0.rgb = saturate( saturate( lDiffuseColor.rgb + lTotalAmbient.rgb ) * lToonColor.rgb + lSpecularColor.rgb ) * lTextureDiffuseColor.rgb ;
// アルファ値 = ディフューズアルファ * マテリアルのディフューズアルファ * 不透明度
PSOutput.Color0.a = lTextureDiffuseColor.a * cfMaterial.Diffuse.a * cfFactorColor.a ;
// 出力カラー計算 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータを返す
return PSOutput ;
}
MMD互換のトゥーンレンダリング スキニングメッシュでディレクショナルライトとスポットライトとポイントライト一つづつのフォンシェーディング
<実行に必要なファイル一式>
頂点シェーダーでライティング処理を行わず、ピクセルシェーダーでライティング処理を行うフォンシェーディングでMMD互換の
トゥーンレンダリングを行うサンプルです。( フォンシェーディングの解説はこちらの解説を参照してください )
ピクセルシェーダーのプログラムについては一つ前のディレクショナルライトだけのサンプルは、今回の3つのライトを使用する
サンプルからスポットライトとポイントライトの処理をしている部分を単純に削除しただけだったりします。
例によってプログラマブルシェーダー3.0を使える環境ではない場合は動作しませんので注意してください。
C++のプログラム
#include "DxLib.h"
#include <math.h>
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
int ModelHandle ;
int GradTexHandle ;
int PixelShaderHandle ;
int VertexShaderHandle ;
float LightRotateAngle ;
int AnimIndex ;
float AnimCounter ;
int DirLightHandle ;
int PointLightHandle ;
int SpotLightHandle ;
VECTOR LightPosition ;
// ウインドウモードで起動
ChangeWindowMode( TRUE ) ;
// Direct3D9Ex を使用する
SetUseDirect3DVersion( DX_DIRECT3D_9EX ) ;
// DXライブラリの初期化
if( DxLib_Init() < 0 )
{
// エラーが発生したら直ちに終了
return -1 ;
}
// プログラマブルシェーダーモデル3.0が使用できない場合はエラーを表示して終了
if( GetValidShaderVersion() < 300 )
{
// エラー表示
DrawString( 0, 0, "プログラマブルシェーダー3.0が使用できない環境のようです", GetColor( 255,255,255 ) ) ;
// キー入力待ち
WaitKey() ;
// DXライブラリの後始末
DxLib_End() ;
// ソフト終了
return 0 ;
}
// 頂点シェーダーを読み込む
VertexShaderHandle = LoadVertexShader( "SkinMesh4_DirSpotPointLight_Toon_PhongVS.vso" ) ;
// ピクセルシェーダーを読み込む
PixelShaderHandle = LoadPixelShader( "SkinMesh4_DirSpotPointLight_Toon_PhongPS.pso" ) ;
// グラデーションテクスチャを読み込む
GradTexHandle = LoadGraph( "GradTex.bmp" ) ;
// スキニングメッシュモデルを読み込む
ModelHandle = MV1LoadModel( "DxChara.x" ) ;
// 分かりやすいように服のマテリアルを緑色にする
MV1SetMaterialDifColor( ModelHandle, 1, GetColorF( 0.0f, 0.5f, 0.0f, 1.0f ) ) ;
// アニメーション0をアタッチ
AnimIndex = MV1AttachAnim( ModelHandle, 0 ) ;
// アニメーションカウンタをリセット
AnimCounter = 0.0f ;
// モデルの描画にオリジナルシェーダーを使用する設定をONにする
MV1SetUseOrigShader( TRUE ) ;
// 使用するテクスチャ1にグラデーションテクスチャをセットする
SetUseTextureToShader( 1, GradTexHandle ) ;
// 使用する頂点シェーダーをセット
SetUseVertexShader( VertexShaderHandle ) ;
// 使用するピクセルシェーダーをセット
SetUsePixelShader( PixelShaderHandle ) ;
// 標準ライトをオフにする
SetLightEnable( FALSE ) ;
// ディレクショナルライトを作成する
DirLightHandle = CreateDirLightHandle( VGet( 1.0f, 0.0f, 0.0f ) ) ;
// ポイントライトハンドルを作成する
PointLightHandle = CreatePointLightHandle( VGet( 0.0f, 0.0f, 0.0f ), 1000.0f, 0.86f, 0.0000f, 0.0000f ) ;
// スポットライトを作成する
SpotLightHandle = CreateSpotLightHandle( VGet( 0.0f, 0.0f, 0.0f ), VGet( 0.0f, -1.0f, 0.0f ), 0.7f, 0.6f, 1000.0f, 0.391586f, 0.001662f, 0.0f ) ;
// スポットライトのアンビエントカラーを無効にする
SetLightAmbColorHandle( SpotLightHandle, GetColorF( 0.0f, 0.0f, 0.0f, 0.0f ) ) ;
// スポットライトのディフューズカラーを緑にする
SetLightDifColorHandle( SpotLightHandle, GetColorF( 0.0f, 1.0f, 0.0f, 0.0f ) ) ;
// ライトの位置を回転する値を初期化
LightRotateAngle = 0.0f ;
// 描画先を裏画面にする
SetDrawScreen( DX_SCREEN_BACK ) ;
// モデルの見える位置にカメラを配置
SetCameraPositionAndTarget_UpVecY( VGet( 0.0f, 700.0f, -1100.0f ), VGet( 0.0f, 350.0f, 0.0f ) ) ;
// ESCキーが押されるまでループ
while( ProcessMessage() == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0 )
{
// 画面を初期化
ClearDrawScreen() ;
// ポイントライトの位置の回転値を加算
LightRotateAngle += 0.02f ;
// ポイントライトの位置の更新
LightPosition.x = sin( LightRotateAngle ) * 600.0f ;
LightPosition.y = 400.0f ;
LightPosition.z = cos( LightRotateAngle ) * 600.0f - 250.0f ;
SetLightPositionHandle( PointLightHandle, LightPosition ) ;
// スポットライトの位置の更新
LightPosition.x = 0.0f ;
LightPosition.y = cos( LightRotateAngle ) * 700.0f + 400.0f ;
LightPosition.z = sin( LightRotateAngle ) * 700.0f - 250.0f ;
SetLightPositionHandle( SpotLightHandle, LightPosition ) ;
// ライトの向きを回転中心方向にする
SetLightDirectionHandle( SpotLightHandle, VSub( VGet( 0.0f, 400.0f, -250.0f ), LightPosition ) ) ;
// アニメーション時間を進める
AnimCounter += 100.0f ;
if( AnimCounter > MV1GetAnimTotalTime( ModelHandle, 0 ) )
{
AnimCounter -= MV1GetAnimTotalTime( ModelHandle, 0 ) ;
}
MV1SetAttachAnimTime( ModelHandle, AnimIndex, AnimCounter ) ;
// モデルを描画
MV1DrawModel( ModelHandle ) ;
// 裏画面の内容を表画面に反映させる
ScreenFlip() ;
}
// ディレクショナルライトの削除
DeleteLightHandle( DirLightHandle ) ;
// スポットライトの削除
DeleteLightHandle( SpotLightHandle ) ;
// ポイントライトの削除
DeleteLightHandle( PointLightHandle ) ;
// 使用するテクスチャからグラデーションテクスチャを外す
SetUseTextureToShader( 1, -1 ) ;
// グラデーションテクスチャを削除
DeleteGraph( GradTexHandle ) ;
// 読み込んだ頂点シェーダーの削除
DeleteShader( VertexShaderHandle ) ;
// 読み込んだピクセルシェーダーの削除
DeleteShader( PixelShaderHandle ) ;
// 読み込んだモデルの削除
MV1DeleteModel( ModelHandle ) ;
// DXライブラリの後始末
DxLib_End() ;
// ソフトの終了
return 0 ;
}
頂点シェーダーのプログラム
// 頂点シェーダーの入力
struct VS_INPUT
{
float4 Position : POSITION ; // 座標( ローカル空間 )
int4 BlendIndices0 : BLENDINDICES0 ; // スキニング処理用 Float型定数配列インデックス
float4 BlendWeight0 : BLENDWEIGHT0 ; // スキニング処理用ウエイト値
float3 Normal : NORMAL0 ; // 法線( ローカル空間 )
float4 Diffuse : COLOR0 ; // ディフューズカラー
float4 Specular : COLOR1 ; // スペキュラカラー
float4 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
} ;
// 頂点シェーダーの出力
struct VS_OUTPUT
{
float4 Position : POSITION ; // 座標( プロジェクション空間 )
float2 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
float3 VPosition : TEXCOORD1 ; // 座標( ビュー空間 )
float3 VNormal : TEXCOORD2 ; // 法線( ビュー空間 )
} ;
// C++ 側で設定する定数の定義
float4 cfAmbient_Emissive : register( c1 ) ; // マテリアルエミッシブカラー + マテリアルアンビエントカラー * グローバルアンビエントカラー
float4 cfProjectionMatrix[ 4 ] : register( c2 ) ; // ビュー → 射影行列
float4 cfViewMatrix[ 3 ] : register( c6 ) ; // ワールド → ビュー行列
float4 cfTextureMatrix[ 3 ][ 2 ] : register( c88 ) ; // テクスチャ座標操作用行列
float4 cfLocalWorldMatrix[ 162 ] : register( c94 ) ; // ローカル → ワールド行列
// main関数
VS_OUTPUT main( VS_INPUT VSInput )
{
VS_OUTPUT VSOutput ;
float4 lLocalWorldMatrix[ 3 ] ;
float4 lWorldPosition ;
float4 lViewPosition ;
float3 lWorldNrm ;
float3 lViewNrm ;
// 複数のフレームのブレンド行列の作成
lLocalWorldMatrix[ 0 ] = cfLocalWorldMatrix[ VSInput.BlendIndices0.x + 0 ] * VSInput.BlendWeight0.x;
lLocalWorldMatrix[ 1 ] = cfLocalWorldMatrix[ VSInput.BlendIndices0.x + 1 ] * VSInput.BlendWeight0.x;
lLocalWorldMatrix[ 2 ] = cfLocalWorldMatrix[ VSInput.BlendIndices0.x + 2 ] * VSInput.BlendWeight0.x;
lLocalWorldMatrix[ 0 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.y + 0 ] * VSInput.BlendWeight0.y;
lLocalWorldMatrix[ 1 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.y + 1 ] * VSInput.BlendWeight0.y;
lLocalWorldMatrix[ 2 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.y + 2 ] * VSInput.BlendWeight0.y;
lLocalWorldMatrix[ 0 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.z + 0 ] * VSInput.BlendWeight0.z;
lLocalWorldMatrix[ 1 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.z + 1 ] * VSInput.BlendWeight0.z;
lLocalWorldMatrix[ 2 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.z + 2 ] * VSInput.BlendWeight0.z;
lLocalWorldMatrix[ 0 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.w + 0 ] * VSInput.BlendWeight0.w;
lLocalWorldMatrix[ 1 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.w + 1 ] * VSInput.BlendWeight0.w;
lLocalWorldMatrix[ 2 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.w + 2 ] * VSInput.BlendWeight0.w;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ローカル座標をワールド座標に変換
lWorldPosition.x = dot( VSInput.Position, lLocalWorldMatrix[ 0 ] ) ;
lWorldPosition.y = dot( VSInput.Position, lLocalWorldMatrix[ 1 ] ) ;
lWorldPosition.z = dot( VSInput.Position, lLocalWorldMatrix[ 2 ] ) ;
lWorldPosition.w = 1.0f ;
// ワールド座標をビュー座標に変換
lViewPosition.x = dot( lWorldPosition, cfViewMatrix[ 0 ] ) ;
lViewPosition.y = dot( lWorldPosition, cfViewMatrix[ 1 ] ) ;
lViewPosition.z = dot( lWorldPosition, cfViewMatrix[ 2 ] ) ;
lViewPosition.w = 1.0f ;
// ビュー座標を射影座標に変換
VSOutput.Position.x = dot( lViewPosition, cfProjectionMatrix[ 0 ] ) ;
VSOutput.Position.y = dot( lViewPosition, cfProjectionMatrix[ 1 ] ) ;
VSOutput.Position.z = dot( lViewPosition, cfProjectionMatrix[ 2 ] ) ;
VSOutput.Position.w = dot( lViewPosition, cfProjectionMatrix[ 3 ] ) ;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 法線をビュー空間の角度に変換 =====================================================( 開始 )
// ローカルベクトルをワールドベクトルに変換
lWorldNrm.x = dot( VSInput.Normal, lLocalWorldMatrix[ 0 ].xyz ) ;
lWorldNrm.y = dot( VSInput.Normal, lLocalWorldMatrix[ 1 ].xyz ) ;
lWorldNrm.z = dot( VSInput.Normal, lLocalWorldMatrix[ 2 ].xyz ) ;
// ワールドベクトルをビューベクトルに変換
lViewNrm.x = dot( lWorldNrm, cfViewMatrix[ 0 ].xyz ) ;
lViewNrm.y = dot( lWorldNrm, cfViewMatrix[ 1 ].xyz ) ;
lViewNrm.z = dot( lWorldNrm, cfViewMatrix[ 2 ].xyz ) ;
// 法線をビュー空間の角度に変換 =====================================================( 終了 )
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// 頂点座標を保存
VSOutput.VPosition = lViewPosition.xyz ;
// 法線を保存
VSOutput.VNormal = lViewNrm ;
// テクスチャ座標変換行列による変換を行った結果のテクスチャ座標をセット
VSOutput.TexCoords0.x = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 0 ] ) ;
VSOutput.TexCoords0.y = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 1 ] ) ;
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータを返す
return VSOutput ;
}
ピクセルシェーダーのプログラム
// ピクセルシェーダーの入力
struct PS_INPUT
{
float2 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
float3 VPosition : TEXCOORD1 ; // 座標( ビュー空間 )
float3 VNormal : TEXCOORD2 ; // 法線( ビュー空間 )
} ;
// ピクセルシェーダーの出力
struct PS_OUTPUT
{
float4 Color0 : COLOR0 ;
} ;
// マテリアルパラメータ
struct MATERIAL
{
float4 Diffuse ; // ディフューズカラー
float4 Specular ; // スペキュラカラー
float4 Power ; // スペキュラの強さ
} ;
// ライトパラメータ
struct LIGHT
{
float4 Position ; // 座標( ビュー空間 )
float3 Direction ; // 方向( ビュー空間 )
float4 Diffuse ; // ディフューズカラー
float4 Specular ; // スペキュラカラー
float4 Ambient ; // アンビエントカラーとマテリアルのアンビエントカラーを乗算したもの
float4 Range_FallOff_AT0_AT1 ; // x:有効距離 y:スポットライト用FallOff z:距離による減衰処理用パラメータ0 w:距離による減衰処理用パラメータ1
float4 AT2_SpotP0_SpotP1 ; // x:距離による減衰処理用パラメータ2 y:スポットライト用パラメータ0( cos( Phi / 2.0f ) ) z:スポットライト用パラメータ1( 1.0f / ( cos( Theta / 2.0f ) - cos( Phi / 2.0f ) ) )
} ;
// C++ 側で設定するテクスチャや定数の定義
sampler DiffuseMapTexture : register( s0 ) ; // ディフューズマップテクスチャ
sampler ToonTexture : register( s1 ) ; // トゥーンテクスチャ
float4 cfAmbient_Emissive : register( c1 ) ; // エミッシブカラー + マテリアルアンビエントカラー * グローバルアンビエントカラー
MATERIAL cfMaterial : register( c2 ) ; // マテリアルパラメータ
float4 cfFactorColor : register( c5 ) ; // 不透明度等
LIGHT cfLight[ 3 ] : register( c32 ) ; // ライトパラメータ
// main関数
PS_OUTPUT main( PS_INPUT PSInput )
{
PS_OUTPUT PSOutput ;
float4 lTextureDiffuseColor ;
float4 lSpecularColor ;
float4 lDiffuseColor ;
float3 Normal ;
float lDiffuseAngleGen ;
float4 lTotalDiffuse ;
float4 lTotalSpecular ;
float4 lTotalAmbient ;
float4 lToonColor ;
float lTotalLightGen ;
float3 V_to_Eye ;
float3 TempF3 ;
float Temp ;
float3 lLightTemp ;
float lLightDistancePow2 ;
float lLightGen ;
float3 lLightDir ;
float lLightDirectionCosA ;
// 法線の準備
Normal = normalize( PSInput.VNormal ) ;
// 頂点座標から視点へのベクトルを正規化
V_to_Eye = normalize( -PSInput.VPosition ) ;
// ディフューズカラーとスペキュラカラーとアンビエントカラーの合計値を初期化
lTotalDiffuse = float4( 0.0f, 0.0f, 0.0f, 0.0f ) ;
lTotalSpecular = float4( 0.0f, 0.0f, 0.0f, 0.0f ) ;
lTotalAmbient = float4( 0.0f, 0.0f, 0.0f, 0.0f ) ;
// ライトの減衰率合計値の初期化
lTotalLightGen = 0.0f ;
// ディレクショナルライトの処理 +++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ライト方向ベクトルのセット
lLightDir = cfLight[ 0 ].Direction ;
// ディフューズ角度減衰率計算
lDiffuseAngleGen = saturate( dot( Normal, -lLightDir ) ) ;
// ディフューズ減衰率を合計値に加算
lTotalLightGen += lDiffuseAngleGen ;
// スペキュラカラー計算
// ハーフベクトルの計算
TempF3 = normalize( V_to_Eye - lLightDir ) ;
// Temp = pow( max( 0.0f, N * H ), cfMaterial.Power.x )
Temp = pow( max( 0.0f, dot( Normal, TempF3 ) ), cfMaterial.Power.x ) ;
// スペキュラライト合計値 += スペキュラ角度減衰計算結果 * ライトのスペキュラカラー
lTotalSpecular += Temp * cfLight[ 0 ].Specular ;
// ライトのディフューズカラーを合計値に加算
lTotalDiffuse += cfLight[ 0 ].Diffuse ;
// ライトのアンビエントカラーを合計値に加算
lTotalAmbient += cfLight[ 0 ].Ambient ;
// ディレクショナルライトの処理 +++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// スポットライトの処理 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ライト方向ベクトルの計算
lLightDir = normalize( PSInput.VPosition.xyz - cfLight[ 1 ].Position.xyz ) ;
// 距離・スポットライト減衰値計算 =======================================( 開始 )
// 距離減衰計算 ------------------
// 頂点とライト位置との距離の二乗を求める
lLightTemp = PSInput.VPosition.xyz - cfLight[ 1 ].Position.xyz ;
lLightDistancePow2 = dot( lLightTemp, lLightTemp ) ;
// 減衰率の計算 lLightGen = 1 / ( 減衰値0 + 減衰値1 * 距離 + 減衰値2 * ( 距離 * 距離 ) )
lLightGen = 1.0f / ( cfLight[ 1 ].Range_FallOff_AT0_AT1.z + cfLight[ 1 ].Range_FallOff_AT0_AT1.w * sqrt( lLightDistancePow2 ) + cfLight[ 1 ].AT2_SpotP0_SpotP1.x * lLightDistancePow2 ) ;
// --------------------------------
// スポットライト減衰計算 --------
// ライト方向ベクトルとライト位置から頂点位置へのベクトルの内積( 即ち Cos a )を計算
lLightDirectionCosA = dot( lLightDir, cfLight[ 1 ].Direction ) ;
// スポットライト減衰計算 pow( falloff, ( ( Cos a - Cos f ) / ( Cos q - Cos f ) ) )
lLightGen *= saturate( pow( abs( max( lLightDirectionCosA - cfLight[ 1 ].AT2_SpotP0_SpotP1.y, 0.0f ) * cfLight[ 1 ].AT2_SpotP0_SpotP1.z ), cfLight[ 1 ].Range_FallOff_AT0_AT1.y ) ) ;
// --------------------------------
// 有効距離外だったら減衰率を最大にする処理
lLightGen *= step( lLightDistancePow2, cfLight[ 1 ].Range_FallOff_AT0_AT1.x ) ;
// 距離・スポットライト減衰値計算 =======================================( 終了 )
// ディフューズ角度減衰率計算
lDiffuseAngleGen = saturate( dot( Normal, -lLightDir ) ) ;
// ディフューズ減衰率を合計値に加算
lTotalLightGen += lDiffuseAngleGen * lLightGen ;
// スペキュラカラー計算
// ハーフベクトルの計算
TempF3 = normalize( V_to_Eye - lLightDir ) ;
// Temp = pow( max( 0.0f, N * H ), cfMaterial.Power.x )
Temp = pow( max( 0.0f, dot( Normal, TempF3 ) ), cfMaterial.Power.x ) ;
// スペキュラライト合計値 += スペキュラ角度減衰計算結果 * 距離・スポットライトの角度減衰率 * ライトのスペキュラカラー
lTotalSpecular += Temp * lLightGen * cfLight[ 1 ].Specular ;
// ライトのディフューズカラーを合計値に加算
lTotalDiffuse += cfLight[ 1 ].Diffuse ;
// ライトのアンビエントカラーを合計値に加算
lTotalAmbient += cfLight[ 1 ].Ambient ;
// スポットライトの処理 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// ポイントライトの処理 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ライト方向ベクトルの計算
lLightDir = normalize( PSInput.VPosition.xyz - cfLight[ 2 ].Position.xyz ) ;
// 距離減衰値計算 =======================================================( 開始 )
// 頂点とライト位置との距離の二乗を求める
lLightTemp = PSInput.VPosition.xyz - cfLight[ 2 ].Position.xyz ;
lLightDistancePow2 = dot( lLightTemp, lLightTemp ) ;
// 減衰率の計算 lLightGen = 1 / ( 減衰値0 + 減衰値1 * 距離 + 減衰値2 * ( 距離 * 距離 ) )
lLightGen = 1.0f / ( cfLight[ 2 ].Range_FallOff_AT0_AT1.z + cfLight[ 2 ].Range_FallOff_AT0_AT1.w * sqrt( lLightDistancePow2 ) + cfLight[ 2 ].AT2_SpotP0_SpotP1.x * lLightDistancePow2 ) ;
// 有効距離外だったら減衰率を最大にする処理
lLightGen *= step( lLightDistancePow2, cfLight[ 2 ].Range_FallOff_AT0_AT1.x ) ;
// 距離減衰値計算 =======================================================( 終了 )
// ディフューズ色計算
// ディフューズ角度減衰率計算
lDiffuseAngleGen = saturate( dot( Normal, -lLightDir ) ) ;
// ディフューズ減衰率を合計値に加算
lTotalLightGen += lDiffuseAngleGen ;
// スペキュラカラー計算
// ハーフベクトルの計算
TempF3 = normalize( V_to_Eye - lLightDir ) ;
// Temp = pow( max( 0.0f, N * H ), cfMaterial.Power.x )
Temp = pow( max( 0.0f, dot( Normal, TempF3 ) ), cfMaterial.Power.x ) ;
// スペキュラライト合計値 += スペキュラ角度減衰計算結果 * 距離・スポットライトの角度減衰率 * ライトのスペキュラカラー
lTotalSpecular += Temp * lLightGen * cfLight[ 2 ].Specular ;
// ライトのディフューズカラーを合計値に加算
lTotalDiffuse += cfLight[ 2 ].Diffuse ;
// ライトのアンビエントカラーを合計値に加算
lTotalAmbient += cfLight[ 2 ].Ambient ;
// ポイントライトの処理 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// アンビエントカラーの蓄積値 += マテリアルのアンビエントカラーとグローバルアンビエントカラーを乗算したものとマテリアルエミッシブカラーを加算したもの
lTotalAmbient += cfAmbient_Emissive ;
// 出力カラー計算 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// テクスチャカラーの取得
lTextureDiffuseColor = tex2D( DiffuseMapTexture, PSInput.TexCoords0 ) ;
// トゥーンテクスチャカラーをライトのディフューズ減衰率から取得
lToonColor = tex2D( ToonTexture, lTotalLightGen ) ;
// ディフューズカラー = ライトのディフューズカラー蓄積値 * マテリアルのディフューズカラー
lDiffuseColor = lTotalDiffuse * cfMaterial.Diffuse ;
// スペキュラカラー = ライトのスペキュラカラー蓄積値 * マテリアルのスペキュラカラー
lSpecularColor = lTotalSpecular * cfMaterial.Specular ;
// 出力 = saturate( saturate( ディフューズカラー * アンビエントカラーの蓄積値 ) * トゥーンテクスチャカラー + スペキュラカラー ) * テクスチャカラー
PSOutput.Color0.rgb = saturate( saturate( lDiffuseColor.rgb + lTotalAmbient.rgb ) * lToonColor.rgb + lSpecularColor.rgb ) * lTextureDiffuseColor.rgb ;
// アルファ値 = ディフューズアルファ * マテリアルのディフューズアルファ * 不透明度
PSOutput.Color0.a = lTextureDiffuseColor.a * cfMaterial.Diffuse.a * cfFactorColor.a ;
// 出力カラー計算 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータを返す
return PSOutput ;
}
MMD互換のトゥーンレンダリング スキニングメッシュでディレクショナルライト一つとスフィアマップのフォンシェーディング
<実行に必要なファイル一式>
「スキニングメッシュでディレクショナルライト一つのフォンシェーディング」にスフィアマップの機能を追加したものです。
頂点シェーダーからピクセルシェーダーに渡されるパラメータ Normal からスフィアマップのテクスチャ座標は簡単に得ることができます。
例によってプログラマブルシェーダー3.0を使える環境ではない場合は動作しませんので注意してください。
C++のプログラム
#include "DxLib.h"
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
int ModelHandle ;
int GradTexHandle ;
int PixelShaderHandle ;
int VertexShaderHandle ;
// ウインドウモードで起動
ChangeWindowMode( TRUE ) ;
// Direct3D9Ex を使用する
SetUseDirect3DVersion( DX_DIRECT3D_9EX ) ;
// DXライブラリの初期化
if( DxLib_Init() < 0 )
{
// エラーが発生したら直ちに終了
return -1 ;
}
// プログラマブルシェーダーモデル3.0が使用できない場合はエラーを表示して終了
if( GetValidShaderVersion() < 300 )
{
// エラー表示
DrawString( 0, 0, "プログラマブルシェーダー3.0が使用できない環境のようです", GetColor( 255,255,255 ) ) ;
// キー入力待ち
WaitKey() ;
// DXライブラリの後始末
DxLib_End() ;
// ソフト終了
return 0 ;
}
// 頂点シェーダーを読み込む
VertexShaderHandle = LoadVertexShader( "SkinMesh4_DirLight_SphereMap_Toon_PhongVS.vso" ) ;
// ピクセルシェーダーを読み込む
PixelShaderHandle = LoadPixelShader( "SkinMesh4_DirLight_SphereMap_Toon_PhongPS.pso" ) ;
// グラデーションテクスチャを読み込む
GradTexHandle = LoadGraph( "GradTex.bmp" ) ;
// スキニングメッシュモデルを読み込む
ModelHandle = MV1LoadModel( "DxChara.pmx" ) ;
// モデルの描画にオリジナルシェーダーを使用する設定をONにする
MV1SetUseOrigShader( TRUE ) ;
// 使用するテクスチャ1にグラデーションテクスチャをセットする
SetUseTextureToShader( 1, GradTexHandle ) ;
// 使用する頂点シェーダーをセット
SetUseVertexShader( VertexShaderHandle ) ;
// 使用するピクセルシェーダーをセット
SetUsePixelShader( PixelShaderHandle ) ;
// 描画先を裏画面にする
SetDrawScreen( DX_SCREEN_BACK ) ;
// モデルの見える位置にカメラを配置
SetCameraPositionAndTarget_UpVecY( VGet( 0.0f, 700.0f, -800.0f ), VGet( 0.0f, 350.0f, 0.0f ) ) ;
// ESCキーが押されるまでループ
while( ProcessMessage() == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0 )
{
// 画面を初期化
ClearDrawScreen() ;
// モデルを描画
MV1DrawModel( ModelHandle ) ;
// 裏画面の内容を表画面に反映させる
ScreenFlip() ;
}
// 使用するテクスチャからグラデーションテクスチャを外す
SetUseTextureToShader( 1, -1 ) ;
// グラデーションテクスチャを削除
DeleteGraph( GradTexHandle ) ;
// 読み込んだ頂点シェーダーの削除
DeleteShader( VertexShaderHandle ) ;
// 読み込んだピクセルシェーダーの削除
DeleteShader( PixelShaderHandle ) ;
// 読み込んだモデルの削除
MV1DeleteModel( ModelHandle ) ;
// DXライブラリの後始末
DxLib_End() ;
// ソフトの終了
return 0 ;
}
頂点シェーダーのプログラム
// 頂点シェーダーの入力
struct VS_INPUT
{
float4 Position : POSITION ; // 座標( ローカル空間 )
int4 BlendIndices0 : BLENDINDICES0 ; // スキニング処理用 Float型定数配列インデックス
float4 BlendWeight0 : BLENDWEIGHT0 ; // スキニング処理用ウエイト値
float3 Normal : NORMAL0 ; // 法線( ローカル空間 )
float4 Diffuse : COLOR0 ; // ディフューズカラー
float4 Specular : COLOR1 ; // スペキュラカラー
float4 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
} ;
// 頂点シェーダーの出力
struct VS_OUTPUT
{
float4 Position : POSITION ; // 座標( プロジェクション空間 )
float2 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
float3 VPosition : TEXCOORD1 ; // 座標( ビュー空間 )
float3 VNormal : TEXCOORD2 ; // 法線( ビュー空間 )
} ;
// C++ 側で設定する定数の定義
float4 cfAmbient_Emissive : register( c1 ) ; // マテリアルエミッシブカラー + マテリアルアンビエントカラー * グローバルアンビエントカラー
float4 cfProjectionMatrix[ 4 ] : register( c2 ) ; // ビュー → 射影行列
float4 cfViewMatrix[ 3 ] : register( c6 ) ; // ワールド → ビュー行列
float4 cfTextureMatrix[ 3 ][ 2 ] : register( c88 ) ; // テクスチャ座標操作用行列
float4 cfLocalWorldMatrix[ 162 ] : register( c94 ) ; // ローカル → ワールド行列
// main関数
VS_OUTPUT main( VS_INPUT VSInput )
{
VS_OUTPUT VSOutput ;
float4 lLocalWorldMatrix[ 3 ] ;
float4 lWorldPosition ;
float4 lViewPosition ;
float3 lWorldNrm ;
float3 lViewNrm ;
// 複数のフレームのブレンド行列の作成
lLocalWorldMatrix[ 0 ] = cfLocalWorldMatrix[ VSInput.BlendIndices0.x + 0 ] * VSInput.BlendWeight0.x;
lLocalWorldMatrix[ 1 ] = cfLocalWorldMatrix[ VSInput.BlendIndices0.x + 1 ] * VSInput.BlendWeight0.x;
lLocalWorldMatrix[ 2 ] = cfLocalWorldMatrix[ VSInput.BlendIndices0.x + 2 ] * VSInput.BlendWeight0.x;
lLocalWorldMatrix[ 0 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.y + 0 ] * VSInput.BlendWeight0.y;
lLocalWorldMatrix[ 1 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.y + 1 ] * VSInput.BlendWeight0.y;
lLocalWorldMatrix[ 2 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.y + 2 ] * VSInput.BlendWeight0.y;
lLocalWorldMatrix[ 0 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.z + 0 ] * VSInput.BlendWeight0.z;
lLocalWorldMatrix[ 1 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.z + 1 ] * VSInput.BlendWeight0.z;
lLocalWorldMatrix[ 2 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.z + 2 ] * VSInput.BlendWeight0.z;
lLocalWorldMatrix[ 0 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.w + 0 ] * VSInput.BlendWeight0.w;
lLocalWorldMatrix[ 1 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.w + 1 ] * VSInput.BlendWeight0.w;
lLocalWorldMatrix[ 2 ] += cfLocalWorldMatrix[ VSInput.BlendIndices0.w + 2 ] * VSInput.BlendWeight0.w;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ローカル座標をワールド座標に変換
lWorldPosition.x = dot( VSInput.Position, lLocalWorldMatrix[ 0 ] ) ;
lWorldPosition.y = dot( VSInput.Position, lLocalWorldMatrix[ 1 ] ) ;
lWorldPosition.z = dot( VSInput.Position, lLocalWorldMatrix[ 2 ] ) ;
lWorldPosition.w = 1.0f ;
// ワールド座標をビュー座標に変換
lViewPosition.x = dot( lWorldPosition, cfViewMatrix[ 0 ] ) ;
lViewPosition.y = dot( lWorldPosition, cfViewMatrix[ 1 ] ) ;
lViewPosition.z = dot( lWorldPosition, cfViewMatrix[ 2 ] ) ;
lViewPosition.w = 1.0f ;
// ビュー座標を射影座標に変換
VSOutput.Position.x = dot( lViewPosition, cfProjectionMatrix[ 0 ] ) ;
VSOutput.Position.y = dot( lViewPosition, cfProjectionMatrix[ 1 ] ) ;
VSOutput.Position.z = dot( lViewPosition, cfProjectionMatrix[ 2 ] ) ;
VSOutput.Position.w = dot( lViewPosition, cfProjectionMatrix[ 3 ] ) ;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 法線をビュー空間の角度に変換 =====================================================( 開始 )
// ローカルベクトルをワールドベクトルに変換
lWorldNrm.x = dot( VSInput.Normal, lLocalWorldMatrix[ 0 ].xyz ) ;
lWorldNrm.y = dot( VSInput.Normal, lLocalWorldMatrix[ 1 ].xyz ) ;
lWorldNrm.z = dot( VSInput.Normal, lLocalWorldMatrix[ 2 ].xyz ) ;
// ワールドベクトルをビューベクトルに変換
lViewNrm.x = dot( lWorldNrm, cfViewMatrix[ 0 ].xyz ) ;
lViewNrm.y = dot( lWorldNrm, cfViewMatrix[ 1 ].xyz ) ;
lViewNrm.z = dot( lWorldNrm, cfViewMatrix[ 2 ].xyz ) ;
// 法線をビュー空間の角度に変換 =====================================================( 終了 )
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// 頂点座標を保存
VSOutput.VPosition = lViewPosition.xyz ;
// 法線を保存
VSOutput.VNormal = lViewNrm ;
// テクスチャ座標変換行列による変換を行った結果のテクスチャ座標をセット
VSOutput.TexCoords0.x = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 0 ] ) ;
VSOutput.TexCoords0.y = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 1 ] ) ;
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータを返す
return VSOutput ;
}
ピクセルシェーダーのプログラム
// ピクセルシェーダーの入力
struct PS_INPUT
{
float2 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
float3 VPosition : TEXCOORD1 ; // 座標( ビュー空間 )
float3 VNormal : TEXCOORD2 ; // 法線( ビュー空間 )
} ;
// ピクセルシェーダーの出力
struct PS_OUTPUT
{
float4 Color0 : COLOR0 ;
} ;
// マテリアルパラメータ
struct MATERIAL
{
float4 Diffuse ; // ディフューズカラー
float4 Specular ; // スペキュラカラー
float4 Power ; // スペキュラの強さ
} ;
// ライトパラメータ
struct LIGHT
{
float4 Position ; // 座標( ビュー空間 )
float3 Direction ; // 方向( ビュー空間 )
float4 Diffuse ; // ディフューズカラー
float4 Specular ; // スペキュラカラー
float4 Ambient ; // アンビエントカラーとマテリアルのアンビエントカラーを乗算したもの
float4 Range_FallOff_AT0_AT1 ; // x:有効距離 y:スポットライト用FallOff z:距離による減衰処理用パラメータ0 w:距離による減衰処理用パラメータ1
float4 AT2_SpotP0_SpotP1 ; // x:距離による減衰処理用パラメータ2 y:スポットライト用パラメータ0( cos( Phi / 2.0f ) ) z:スポットライト用パラメータ1( 1.0f / ( cos( Theta / 2.0f ) - cos( Phi / 2.0f ) ) )
} ;
// C++ 側で設定するテクスチャや定数の定義
sampler DiffuseMapTexture : register( s0 ) ; // ディフューズマップテクスチャ
sampler ToonTexture : register( s1 ) ; // トゥーンテクスチャ
sampler ToonSphereMapTexture : register( s5 ) ; // トゥーンレンダリング用スフィアマップテクスチャ
float4 cfAmbient_Emissive : register( c1 ) ; // エミッシブカラー + マテリアルアンビエントカラー * グローバルアンビエントカラー
MATERIAL cfMaterial : register( c2 ) ; // マテリアルパラメータ
float4 cfFactorColor : register( c5 ) ; // 不透明度等
LIGHT cfLight[ 1 ] : register( c32 ) ; // ライトパラメータ
// main関数
PS_OUTPUT main( PS_INPUT PSInput )
{
PS_OUTPUT PSOutput ;
float4 lTextureDiffuseColor ;
float4 lToonSphereMapColor ;
float4 lSpecularColor ;
float4 lDiffuseColor ;
float3 Normal ;
float lDiffuseAngleGen ;
float4 lTotalDiffuse ;
float4 lTotalSpecular ;
float4 lTotalAmbient ;
float4 lToonColor ;
float lTotalLightGen ;
float3 V_to_Eye ;
float3 TempF3 ;
float Temp ;
float3 lLightTemp ;
float3 lLightDir ;
// 法線の準備
Normal = normalize( PSInput.VNormal ) ;
// 頂点座標から視点へのベクトルを正規化
V_to_Eye = normalize( -PSInput.VPosition ) ;
// ディフューズカラーとスペキュラカラーとアンビエントカラーの合計値を初期化
lTotalDiffuse = float4( 0.0f, 0.0f, 0.0f, 0.0f ) ;
lTotalSpecular = float4( 0.0f, 0.0f, 0.0f, 0.0f ) ;
lTotalAmbient = float4( 0.0f, 0.0f, 0.0f, 0.0f ) ;
// ライトの減衰率合計値の初期化
lTotalLightGen = 0.0f ;
// ディレクショナルライトの処理 +++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ライト方向ベクトルのセット
lLightDir = cfLight[ 0 ].Direction ;
// ディフューズ角度減衰率計算
lDiffuseAngleGen = saturate( dot( Normal, -lLightDir ) ) ;
// ディフューズ減衰率を合計値に加算
lTotalLightGen += lDiffuseAngleGen ;
// スペキュラカラー計算
// ハーフベクトルの計算
TempF3 = normalize( V_to_Eye - lLightDir ) ;
// Temp = pow( max( 0.0f, N * H ), cfMaterial.Power.x )
Temp = pow( max( 0.0f, dot( Normal, TempF3 ) ), cfMaterial.Power.x ) ;
// スペキュラライト合計値 += スペキュラ角度減衰計算結果 * ライトのスペキュラカラー
lTotalSpecular += Temp * cfLight[ 0 ].Specular ;
// ライトのディフューズカラーを合計値に加算
lTotalDiffuse += cfLight[ 0 ].Diffuse ;
// ライトのアンビエントカラーを合計値に加算
lTotalAmbient += cfLight[ 0 ].Ambient ;
// ディレクショナルライトの処理 +++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// アンビエントカラーの蓄積値 += マテリアルのアンビエントカラーとグローバルアンビエントカラーを乗算したものとマテリアルエミッシブカラーを加算したもの
lTotalAmbient += cfAmbient_Emissive ;
// 出力カラー計算 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// テクスチャカラーの取得
lTextureDiffuseColor = tex2D( DiffuseMapTexture, PSInput.TexCoords0 ) ;
// トゥーンテクスチャカラーをライトのディフューズ減衰率から取得
lToonColor = tex2D( ToonTexture, lTotalLightGen ) ;
// ディフューズカラー = ライトのディフューズカラー蓄積値 * マテリアルのディフューズカラー
lDiffuseColor = lTotalDiffuse * cfMaterial.Diffuse ;
// スペキュラカラー = ライトのスペキュラカラー蓄積値 * マテリアルのスペキュラカラー
lSpecularColor = lTotalSpecular * cfMaterial.Specular ;
// 出力 = saturate( saturate( ディフューズカラー * アンビエントカラーの蓄積値 ) * トゥーンテクスチャカラー + スペキュラカラー ) * テクスチャカラー
PSOutput.Color0.rgb = saturate( saturate( lDiffuseColor.rgb + lTotalAmbient.rgb ) * lToonColor.rgb + lSpecularColor.rgb ) * lTextureDiffuseColor.rgb ;
// スフィアマップの色を取得
TempF3 = Normal * float3( 0.5f, -0.5f, 0.0f ) + float3( 0.5f, 0.5f, 0.5f ) ;
lToonSphereMapColor = tex2D( ToonSphereMapTexture, TempF3.xy ) ;
/* スフィアマップの効果が乗算の場合はこちらを加算の2行の代わりに使います
// スフィアマップの色を出力に乗算
PSOutput.Color0.rgb *= lToonSphereMapColor.rgb ;
*/
// スフィアマップの色を出力に加算
PSOutput.Color0.rgb += lToonSphereMapColor.rgb ;
// アルファ値 = ディフューズアルファ * マテリアルのディフューズアルファ * 不透明度
PSOutput.Color0.a = lTextureDiffuseColor.a * cfMaterial.Diffuse.a * cfFactorColor.a ;
// 出力カラー計算 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータを返す
return PSOutput ;
}
法線マップ付きモデルの描画
法線マップ付き剛体メッシュのディレクショナルライトあり描画
<実行に必要なファイル一式>
メタセコイアで材質の「凸凹」の項目にバンプマップ用画像を指定して保存したモデルを、DXライブラリ付属の DxLibModelViewer
の読み込みオプション「法線再計算」にチェックを入れた状態で mqoファイルを読み込むと、「凸凹」画像から法線マップ画像が作成され、
法線マップが適用されて頂点的には凸凹が無いのにあたかも凸凹しているかのように見える処理が行われます。
このサンプルはその法線マップ画像があるモデルをディレクショナルライト一つだけを適用して描画する処理を自前のシェーダーで処理しているものです。
「フォンシェーディング」と同じく頂点シェーダーではライティングの処理を行わず、ピクセルシェーダーでライティングの処理を
行います、その際の計算に使用する法線を頂点から出力された法線に、法線マップから得られる法線情報を加味して演算を行います。
そうすることで実際に凹凸はないのに、あたかも凹凸があるかのようなライティング結果になるというわけです。
因みに「従法線」や「接線」は法線マップ処理に法線と共に使用する三軸でUV座標から計算で割り出すことができるのですが、
私自身ちゃんと理解できていません・・・
C++のプログラム
#include "DxLib.h"
#include <math.h>
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
int ModelHandle ;
int PixelShaderHandle ;
int VertexShaderHandle ;
float ModelRotateAngle ;
// ウインドウモードで起動
ChangeWindowMode( TRUE ) ;
// Direct3D9Ex を使用する
SetUseDirect3DVersion( DX_DIRECT3D_9EX ) ;
// DXライブラリの初期化
if( DxLib_Init() < 0 )
{
// エラーが発生したら直ちに終了
return -1 ;
}
// プログラマブルシェーダーモデル3.0が使用できない場合はエラーを表示して終了
if( GetValidShaderVersion() < 300 )
{
// エラー表示
DrawString( 0, 0, "プログラマブルシェーダー3.0が使用できない環境のようです", GetColor( 255,255,255 ) ) ;
// キー入力待ち
WaitKey() ;
// DXライブラリの後始末
DxLib_End() ;
// ソフト終了
return 0 ;
}
// 頂点シェーダーを読み込む
VertexShaderHandle = LoadVertexShader( "NormalMesh_DirLight_NrmMapVS.vso" ) ;
// ピクセルシェーダーを読み込む
PixelShaderHandle = LoadPixelShader( "NormalMesh_DirLight_NrmMapPS.pso" ) ;
// 剛体メッシュモデルを読み込む
ModelHandle = MV1LoadModel( "NormalBox_NrmMap.mv1" ) ;
// モデルの回転角度を初期化
ModelRotateAngle = 0.0f ;
// モデルの描画にオリジナルシェーダーを使用する設定をONにする
MV1SetUseOrigShader( TRUE ) ;
// 使用する頂点シェーダーをセット
SetUseVertexShader( VertexShaderHandle ) ;
// 使用するピクセルシェーダーをセット
SetUsePixelShader( PixelShaderHandle ) ;
// 描画先を裏画面にする
SetDrawScreen( DX_SCREEN_BACK ) ;
// モデルの見える位置にカメラを配置
SetCameraPositionAndTarget_UpVecY( VGet( -400.0f, 200.0f, -400.0f ), VGet( 0.0f, 0.0f, 0.0f ) ) ;
// ESCキーが押されるまでループ
while( ProcessMessage() == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0 )
{
// 画面を初期化
ClearDrawScreen() ;
// モデルの回転値を加算
ModelRotateAngle += 0.002f ;
// モデルの回転角度を変更
MV1SetRotationXYZ( ModelHandle, VGet( 0.0f, ModelRotateAngle, 0.0f ) ) ;
// モデルを描画
MV1DrawModel( ModelHandle ) ;
// 裏画面の内容を表画面に反映させる
ScreenFlip() ;
}
// 読み込んだ頂点シェーダーの削除
DeleteShader( VertexShaderHandle ) ;
// 読み込んだピクセルシェーダーの削除
DeleteShader( PixelShaderHandle ) ;
// 読み込んだモデルの削除
MV1DeleteModel( ModelHandle ) ;
// DXライブラリの後始末
DxLib_End() ;
// ソフトの終了
return 0 ;
}
頂点シェーダーのプログラム
// 頂点シェーダーの入力
struct VS_INPUT
{
float4 Position : POSITION ; // 座標( ローカル空間 )
float3 Tan : TANGENT0 ; // 接線( ローカル空間 )
float3 Bin : BINORMAL0 ; // 従法線( ローカル空間 )
float3 Normal : NORMAL0 ; // 法線( ローカル空間 )
float4 Diffuse : COLOR0 ; // ディフューズカラー
float4 Specular : COLOR1 ; // スペキュラカラー
float4 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
} ;
// 頂点シェーダーの出力
struct VS_OUTPUT
{
float4 Position : POSITION ; // 座標( プロジェクション空間 )
float2 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
float3 VPosition : TEXCOORD1 ; // 座標( ビュー空間 )
float3 VTan : TEXCOORD2 ; // 接線( ビュー空間 )
float3 VBin : TEXCOORD3 ; // 従法線( ビュー空間 )
float3 VNormal : TEXCOORD4 ; // 法線( ビュー空間 )
} ;
// C++ 側で設定する定数の定義
float4 cfProjectionMatrix[ 4 ] : register( c2 ) ; // ビュー → 射影行列
float4 cfViewMatrix[ 3 ] : register( c6 ) ; // ワールド → ビュー行列
float4 cfTextureMatrix[ 3 ][ 2 ] : register( c88 ) ; // テクスチャ座標操作用行列
float4 cfLocalWorldMatrix[ 3 ] : register( c94 ) ; // ローカル → ワールド行列
// main関数
VS_OUTPUT main( VS_INPUT VSInput )
{
VS_OUTPUT VSOutput ;
float4 lWorldPosition ;
float4 lViewPosition ;
float3 lWorldNrm ;
float3 lWorldTan ;
float3 lWorldBin ;
float3 lViewNrm ;
float3 lViewTan ;
float3 lViewBin ;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ローカル座標をワールド座標に変換
lWorldPosition.x = dot( VSInput.Position, cfLocalWorldMatrix[ 0 ] ) ;
lWorldPosition.y = dot( VSInput.Position, cfLocalWorldMatrix[ 1 ] ) ;
lWorldPosition.z = dot( VSInput.Position, cfLocalWorldMatrix[ 2 ] ) ;
lWorldPosition.w = 1.0f ;
// ワールド座標をビュー座標に変換
lViewPosition.x = dot( lWorldPosition, cfViewMatrix[ 0 ] ) ;
lViewPosition.y = dot( lWorldPosition, cfViewMatrix[ 1 ] ) ;
lViewPosition.z = dot( lWorldPosition, cfViewMatrix[ 2 ] ) ;
lViewPosition.w = 1.0f ;
// ビュー座標を射影座標に変換
VSOutput.Position.x = dot( lViewPosition, cfProjectionMatrix[ 0 ] ) ;
VSOutput.Position.y = dot( lViewPosition, cfProjectionMatrix[ 1 ] ) ;
VSOutput.Position.z = dot( lViewPosition, cfProjectionMatrix[ 2 ] ) ;
VSOutput.Position.w = dot( lViewPosition, cfProjectionMatrix[ 3 ] ) ;
// 頂点座標変換 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 法線をビュー空間の角度に変換 =====================================================( 開始 )
// 従法線、接線、法線をビューベクトルに変換
lWorldTan.x = dot( VSInput.Tan, cfLocalWorldMatrix[ 0 ].xyz ) ;
lWorldTan.y = dot( VSInput.Tan, cfLocalWorldMatrix[ 1 ].xyz ) ;
lWorldTan.z = dot( VSInput.Tan, cfLocalWorldMatrix[ 2 ].xyz ) ;
lWorldBin.x = dot( VSInput.Bin, cfLocalWorldMatrix[ 0 ].xyz ) ;
lWorldBin.y = dot( VSInput.Bin, cfLocalWorldMatrix[ 1 ].xyz ) ;
lWorldBin.z = dot( VSInput.Bin, cfLocalWorldMatrix[ 2 ].xyz ) ;
lWorldNrm.x = dot( VSInput.Normal, cfLocalWorldMatrix[ 0 ].xyz ) ;
lWorldNrm.y = dot( VSInput.Normal, cfLocalWorldMatrix[ 1 ].xyz ) ;
lWorldNrm.z = dot( VSInput.Normal, cfLocalWorldMatrix[ 2 ].xyz ) ;
lViewTan.x = dot( lWorldTan, cfViewMatrix[ 0 ].xyz ) ;
lViewTan.y = dot( lWorldTan, cfViewMatrix[ 1 ].xyz ) ;
lViewTan.z = dot( lWorldTan, cfViewMatrix[ 2 ].xyz ) ;
lViewBin.x = dot( lWorldBin, cfViewMatrix[ 0 ].xyz ) ;
lViewBin.y = dot( lWorldBin, cfViewMatrix[ 1 ].xyz ) ;
lViewBin.z = dot( lWorldBin, cfViewMatrix[ 2 ].xyz ) ;
lViewNrm.x = dot( lWorldNrm, cfViewMatrix[ 0 ].xyz ) ;
lViewNrm.y = dot( lWorldNrm, cfViewMatrix[ 1 ].xyz ) ;
lViewNrm.z = dot( lWorldNrm, cfViewMatrix[ 2 ].xyz ) ;
// 法線をビュー空間の角度に変換 =====================================================( 終了 )
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// テクスチャ座標変換行列による変換を行った結果のテクスチャ座標をセット
VSOutput.TexCoords0.x = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 0 ] ) ;
VSOutput.TexCoords0.y = dot( VSInput.TexCoords0, cfTextureMatrix[ 0 ][ 1 ] ) ;
// 頂点座標を保存
VSOutput.VPosition = lViewPosition.xyz ;
// 接線を保存
VSOutput.VTan = lViewTan ;
// 従法線を保存
VSOutput.VBin = lViewBin ;
// 法線を保存
VSOutput.VNormal = lViewNrm ;
// 出力パラメータセット ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータを返す
return VSOutput ;
}
ピクセルシェーダーのプログラム
// ピクセルシェーダーの入力
struct PS_INPUT
{
float2 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
float3 VPosition : TEXCOORD1 ; // 座標( ビュー空間 )
float3 VTan : TEXCOORD2 ; // 接線( ビュー空間 )
float3 VBin : TEXCOORD3 ; // 従法線( ビュー空間 )
float3 VNormal : TEXCOORD4 ; // 法線( ビュー空間 )
} ;
// ピクセルシェーダーの出力
struct PS_OUTPUT
{
float4 Color0 : COLOR0 ;
} ;
// マテリアルパラメータ
struct MATERIAL
{
float4 Diffuse ; // ディフューズカラー
float4 Specular ; // スペキュラカラー
float4 Power ; // スペキュラの強さ
} ;
// ライトパラメータ
struct LIGHT
{
float4 Position ; // 座標( ビュー空間 )
float3 Direction ; // 方向( ビュー空間 )
float4 Diffuse ; // ディフューズカラー
float4 Specular ; // スペキュラカラー
float4 Ambient ; // アンビエントカラーとマテリアルのアンビエントカラーを乗算したもの
float4 Range_FallOff_AT0_AT1 ; // x:有効距離 y:スポットライト用FallOff z:距離による減衰処理用パラメータ0 w:距離による減衰処理用パラメータ1
float4 AT2_SpotP0_SpotP1 ; // x:距離による減衰処理用パラメータ2 y:スポットライト用パラメータ0( cos( Phi / 2.0f ) ) z:スポットライト用パラメータ1( 1.0f / ( cos( Theta / 2.0f ) - cos( Phi / 2.0f ) ) )
} ;
// C++ 側で設定するテクスチャや定数の定義
sampler DiffuseMapTexture : register( s0 ) ; // ディフューズマップテクスチャ
sampler NormalMapTexture : register( s1 ) ; // 法線マップテクスチャ
float4 cfAmbient_Emissive : register( c1 ) ; // エミッシブカラー + マテリアルアンビエントカラー * グローバルアンビエントカラー
MATERIAL cfMaterial : register( c2 ) ; // マテリアルパラメータ
float4 cfFactorColor : register( c5 ) ; // 不透明度等
LIGHT cfLight[ 3 ] : register( c32 ) ; // ライトパラメータ
// main関数
PS_OUTPUT main( PS_INPUT PSInput )
{
PS_OUTPUT PSOutput ;
float4 TextureDiffuseColor ;
float3 V_to_Eye ;
float3 Normal ;
float3 VNrm ;
float3 VTan ;
float3 VBin ;
float4 TotalDiffuse ;
float4 TotalSpecular ;
float4 SpecularColor ;
float3 TempF3 ;
float Temp ;
float3 lLightTemp ;
float3 lLightDir ;
float DiffuseAngleGen ;
// 接線・従法線・法線を正規化
VNrm = normalize( PSInput.VNormal );
VTan = normalize( PSInput.VTan );
VBin = normalize( PSInput.VBin );
// 頂点座標から視点へのベクトルを接底空間に投影した後正規化して保存
TempF3.x = dot( VTan, -PSInput.VPosition.xyz ) ;
TempF3.y = dot( VBin, -PSInput.VPosition.xyz ) ;
TempF3.z = dot( VNrm, -PSInput.VPosition.xyz ) ;
V_to_Eye = normalize( TempF3 ) ;
// 法線の 0~1 の値を -1.0~1.0 に変換する
Normal = ( tex2D( NormalMapTexture, PSInput.TexCoords0.xy ).rgb - float3( 0.5f, 0.5f, 0.5f ) ) * 2.0f ;
// ディフューズカラーとスペキュラカラーの蓄積値を初期化
TotalDiffuse = float4( 0.0f, 0.0f, 0.0f, 0.0f ) ;
TotalSpecular = float4( 0.0f, 0.0f, 0.0f, 0.0f ) ;
// ディレクショナルライトの処理 +++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ライト方向ベクトルの計算
TempF3 = cfLight[ 0 ].Direction ;
// ライトのベクトルを接地空間に変換
lLightDir.x = dot( VTan, TempF3 ) ;
lLightDir.y = dot( VBin, TempF3 ) ;
lLightDir.z = dot( VNrm, TempF3 ) ;
// ディフューズ色計算
// DiffuseAngleGen = ディフューズ角度減衰率計算
DiffuseAngleGen = saturate( dot( Normal, -lLightDir ) ) ;
// ディフューズカラー蓄積値 += ライトのディフューズカラー * マテリアルのディフューズカラー * ディフューズカラー角度減衰率 + ライトのアンビエントカラーとマテリアルのアンビエントカラーを乗算したもの
TotalDiffuse += cfLight[ 0 ].Diffuse * cfMaterial.Diffuse * DiffuseAngleGen + cfLight[ 0 ].Ambient ;
// スペキュラカラー計算
// ハーフベクトルの計算
TempF3 = normalize( V_to_Eye - lLightDir ) ;
// Temp = pow( max( 0.0f, N * H ), cfMaterial.Power.x )
Temp = pow( max( 0.0f, dot( Normal, TempF3 ) ), cfMaterial.Power.x ) ;
// スペキュラカラー蓄積値 += Temp * ライトのスペキュラカラー
TotalSpecular += Temp * cfLight[ 0 ].Specular ;
// ディレクショナルライトの処理 +++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力カラー計算 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// TotalDiffuse = ライトディフューズカラー蓄積値 + ( マテリアルのアンビエントカラーとグローバルアンビエントカラーを乗算したものとマテリアルエミッシブカラーを加算したもの )
TotalDiffuse += cfAmbient_Emissive ;
// SpecularColor = ライトのスペキュラカラー蓄積値 * マテリアルのスペキュラカラー
SpecularColor = TotalSpecular * cfMaterial.Specular ;
// 出力カラー = TotalDiffuse * テクスチャカラー + SpecularColor
TextureDiffuseColor = tex2D( DiffuseMapTexture, PSInput.TexCoords0.xy ) ;
PSOutput.Color0.rgb = TextureDiffuseColor.rgb * TotalDiffuse.rgb + SpecularColor.rgb ;
// アルファ値 = テクスチャアルファ * マテリアルのディフューズアルファ * 不透明度
PSOutput.Color0.a = TextureDiffuseColor.a * cfMaterial.Diffuse.a * cfFactorColor.a ;
// 出力カラー計算 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータを返す
return PSOutput ;
}
法線マップ付き剛体メッシュのディレクショナルライトとスポットライトとポイントライト一つづつ描画
<実行に必要なファイル一式>
一つ前のサンプルのライトの数を増やしただけのサンプルです。
一応ライト一つ一つの処理が綺麗に区切られているので、増やす場合も減らす場合も区切られたまとまりをごっそり抜いたり足したりするだけで
済むようになっています。
C++のプログラム
#include "DxLib.h"
#include <math.h>
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
int ModelHandle ;
int PixelShaderHandle ;
int VertexShaderHandle ;
float LightRotateAngle ;
float ModelRotateAngle ;
int DirLightHandle ;
int PointLightHandle ;
int SpotLightHandle ;
VECTOR LightPosition ;
// ウインドウモードで起動
ChangeWindowMode( TRUE ) ;
// Direct3D9Ex を使用する
SetUseDirect3DVersion( DX_DIRECT3D_9EX ) ;
// DXライブラリの初期化
if( DxLib_Init() < 0 )
{
// エラーが発生したら直ちに終了
return -1 ;
}
// プログラマブルシェーダーモデル3.0が使用できない場合はエラーを表示して終了
if( GetValidShaderVersion() < 300 )
{
// エラー表示
DrawString( 0, 0, "プログラマブルシェーダー3.0が使用できない環境のようです", GetColor( 255,255,255 ) ) ;
// キー入力待ち
WaitKey() ;
// DXライブラリの後始末
DxLib_End() ;
// ソフト終了
return 0 ;
}
// 頂点シェーダーを読み込む
VertexShaderHandle = LoadVertexShader( "NormalMesh_DirSpotPointLight_NrmMapVS.vso" ) ;
// ピクセルシェーダーを読み込む
PixelShaderHandle = LoadPixelShader( "NormalMesh_DirSpotPointLight_NrmMapPS.pso" ) ;
// 剛体メッシュモデルを読み込む
ModelHandle = MV1LoadModel( "NormalBox_NrmMap.mv1" ) ;
// モデルの回転角度を初期化
ModelRotateAngle = 0.0f ;
// モデルの描画にオリジナルシェーダーを使用する設定をONにする
MV1SetUseOrigShader( TRUE ) ;
// 使用する頂点シェーダーをセット
SetUseVertexShader( VertexShaderHandle ) ;
// 使用するピクセルシェーダーをセット
SetUsePixelShader( PixelShaderHandle ) ;
// 標準ライトをオフにする
SetLightEnable( FALSE ) ;
// ディレクショナルライトを作成する
DirLightHandle = CreateDirLightHandle( VGet( -1.0f, -1.0f, 0.0f ) ) ;
// ポイントライトハンドルを作成する
PointLightHandle = CreatePointLightHandle( VGet( 0.0f, 0.0f, 0.0f ), 1000.0f, 0.86f, 0.0000f, 0.0000f ) ;
// スポットライトを作成する
SpotLightHandle = CreateSpotLightHandle( VGet( 0.0f, 0.0f, 0.0f ), VGet( 0.0f, -1.0f, 0.0f ), 0.7f, 0.6f, 1000.0f, 0.391586f, 0.001662f, 0.0f ) ;
// スポットライトのアンビエントカラーを無効にする
SetLightAmbColorHandle( SpotLightHandle, GetColorF( 0.0f, 0.0f, 0.0f, 0.0f ) ) ;
// スポットライトのディフューズカラーを緑にする
SetLightDifColorHandle( SpotLightHandle, GetColorF( 0.0f, 1.0f, 0.0f, 0.0f ) ) ;
// ライトの位置を回転する値を初期化
LightRotateAngle = 0.0f ;
// 描画先を裏画面にする
SetDrawScreen( DX_SCREEN_BACK ) ;
// モデルの見える位置にカメラを配置
SetCameraPositionAndTarget_UpVecY( VGet( 400.0f, 200.0f, -400.0f ), VGet( 0.0f, 0.0f, 0.0f ) ) ;
// ESCキーが押されるまでループ
while( ProcessMessage() == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0 )
{
// 画面を初期化
ClearDrawScreen() ;
// ポイントライトの位置の回転値を加算
LightRotateAngle += 0.02f ;
// ポイントライトの位置の更新
LightPosition.x = sin( LightRotateAngle ) * 600.0f ;
LightPosition.y = 0.0f ;
LightPosition.z = cos( LightRotateAngle ) * 600.0f ;
SetLightPositionHandle( PointLightHandle, LightPosition ) ;
// スポットライトの位置の更新
LightPosition.x = 0.0f ;
LightPosition.y = cos( LightRotateAngle ) * 700.0f ;
LightPosition.z = sin( LightRotateAngle ) * 700.0f ;
SetLightPositionHandle( SpotLightHandle, LightPosition ) ;
// ライトの向きを回転中心方向にする
SetLightDirectionHandle( SpotLightHandle, VScale( LightPosition, -1.0f ) ) ;
// モデルの回転値を加算
ModelRotateAngle += 0.002f ;
// モデルの回転角度を変更
MV1SetRotationXYZ( ModelHandle, VGet( 0.0f, ModelRotateAngle, 0.0f ) ) ;
// モデルを描画
MV1DrawModel( ModelHandle ) ;
// 裏画面の内容を表画面に反映させる
ScreenFlip() ;
}
// ディレクショナルライトの削除
DeleteLightHandle( DirLightHandle ) ;
// スポットライトの削除
DeleteLightHandle( SpotLightHandle ) ;
// ポイントライトの削除
DeleteLightHandle( PointLightHandle ) ;
// 読み込んだ頂点シェーダーの削除
DeleteShader( VertexShaderHandle ) ;
// 読み込んだピクセルシェーダーの削除
DeleteShader( PixelShaderHandle ) ;
// 読み込んだモデルの削除
MV1DeleteModel( ModelHandle ) ;
// DXライブラリの後始末
DxLib_End() ;
// ソフトの終了
return 0 ;
}
頂点シェーダーのプログラム
// ピクセルシェーダーの入力
struct PS_INPUT
{
float2 TexCoords0 : TEXCOORD0 ; // テクスチャ座標
float3 VPosition : TEXCOORD1 ; // 座標( ビュー空間 )
float3 VTan : TEXCOORD2 ; // 接線( ビュー空間 )
float3 VBin : TEXCOORD3 ; // 従法線( ビュー空間 )
float3 VNormal : TEXCOORD4 ; // 法線( ビュー空間 )
} ;
// ピクセルシェーダーの出力
struct PS_OUTPUT
{
float4 Color0 : COLOR0 ;
} ;
// マテリアルパラメータ
struct MATERIAL
{
float4 Diffuse ; // ディフューズカラー
float4 Specular ; // スペキュラカラー
float4 Power ; // スペキュラの強さ
} ;
// ライトパラメータ
struct LIGHT
{
float4 Position ; // 座標( ビュー空間 )
float3 Direction ; // 方向( ビュー空間 )
float4 Diffuse ; // ディフューズカラー
float4 Specular ; // スペキュラカラー
float4 Ambient ; // アンビエントカラーとマテリアルのアンビエントカラーを乗算したもの
float4 Range_FallOff_AT0_AT1 ; // x:有効距離 y:スポットライト用FallOff z:距離による減衰処理用パラメータ0 w:距離による減衰処理用パラメータ1
float4 AT2_SpotP0_SpotP1 ; // x:距離による減衰処理用パラメータ2 y:スポットライト用パラメータ0( cos( Phi / 2.0f ) ) z:スポットライト用パラメータ1( 1.0f / ( cos( Theta / 2.0f ) - cos( Phi / 2.0f ) ) )
} ;
// C++ 側で設定するテクスチャや定数の定義
sampler DiffuseMapTexture : register( s0 ) ; // ディフューズマップテクスチャ
sampler NormalMapTexture : register( s1 ) ; // 法線マップテクスチャ
float4 cfAmbient_Emissive : register( c1 ) ; // エミッシブカラー + マテリアルアンビエントカラー * グローバルアンビエントカラー
MATERIAL cfMaterial : register( c2 ) ; // マテリアルパラメータ
float4 cfFactorColor : register( c5 ) ; // 不透明度等
LIGHT cfLight[ 3 ] : register( c32 ) ; // ライトパラメータ
// main関数
PS_OUTPUT main( PS_INPUT PSInput )
{
PS_OUTPUT PSOutput ;
float4 TextureDiffuseColor ;
float3 V_to_Eye ;
float3 Normal ;
float3 VNrm ;
float3 VTan ;
float3 VBin ;
float4 TotalDiffuse ;
float4 TotalSpecular ;
float4 SpecularColor ;
float3 TempF3 ;
float Temp ;
float3 lLightTemp ;
float lLightDistancePow2 ;
float lLightDirectionCosA ;
float lLightGen ;
float3 lLightDir ;
float DiffuseAngleGen ;
// 接線・従法線・法線を正規化
VNrm = normalize( PSInput.VNormal );
VTan = normalize( PSInput.VTan );
VBin = normalize( PSInput.VBin );
// 頂点座標から視点へのベクトルを接底空間に投影した後正規化して保存
TempF3.x = dot( VTan, -PSInput.VPosition.xyz ) ;
TempF3.y = dot( VBin, -PSInput.VPosition.xyz ) ;
TempF3.z = dot( VNrm, -PSInput.VPosition.xyz ) ;
V_to_Eye = normalize( TempF3 ) ;
// 法線の 0~1 の値を -1.0~1.0 に変換する
Normal = ( tex2D( NormalMapTexture, PSInput.TexCoords0.xy ).rgb - float3( 0.5f, 0.5f, 0.5f ) ) * 2.0f ;
// ディフューズカラーとスペキュラカラーの蓄積値を初期化
TotalDiffuse = float4( 0.0f, 0.0f, 0.0f, 0.0f ) ;
TotalSpecular = float4( 0.0f, 0.0f, 0.0f, 0.0f ) ;
// ディレクショナルライトの処理 +++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ライト方向ベクトルの計算
TempF3 = cfLight[ 0 ].Direction ;
// ライトのベクトルを接地空間に変換
lLightDir.x = dot( VTan, TempF3 ) ;
lLightDir.y = dot( VBin, TempF3 ) ;
lLightDir.z = dot( VNrm, TempF3 ) ;
// ディフューズ色計算
// DiffuseAngleGen = ディフューズ角度減衰率計算
DiffuseAngleGen = saturate( dot( Normal, -lLightDir ) ) ;
// ディフューズカラー蓄積値 += ライトのディフューズカラー * マテリアルのディフューズカラー * ディフューズカラー角度減衰率 + ライトのアンビエントカラーとマテリアルのアンビエントカラーを乗算したもの
TotalDiffuse += cfLight[ 0 ].Diffuse * cfMaterial.Diffuse * DiffuseAngleGen + cfLight[ 0 ].Ambient ;
// スペキュラカラー計算
// ハーフベクトルの計算
TempF3 = normalize( V_to_Eye - lLightDir ) ;
// Temp = pow( max( 0.0f, N * H ), cfMaterial.Power.x )
Temp = pow( max( 0.0f, dot( Normal, TempF3 ) ), cfMaterial.Power.x ) ;
// スペキュラカラー蓄積値 += Temp * ライトのスペキュラカラー
TotalSpecular += Temp * cfLight[ 0 ].Specular ;
// ディレクショナルライトの処理 +++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// スポットライトの処理 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ライト方向ベクトルの計算
TempF3 = normalize( PSInput.VPosition.xyz - cfLight[ 1 ].Position.xyz ) ;
// ライトのベクトルを接地空間に変換
lLightDir.x = dot( VTan, TempF3 ) ;
lLightDir.y = dot( VBin, TempF3 ) ;
lLightDir.z = dot( VNrm, TempF3 ) ;
// 距離・スポットライト減衰値計算 =======================================( 開始 )
// 距離減衰計算 ------------------
// 頂点とライト位置との距離の二乗を求める
lLightTemp = PSInput.VPosition.xyz - cfLight[ 1 ].Position.xyz ;
lLightDistancePow2 = dot( lLightTemp, lLightTemp ) ;
// 減衰率の計算 lLightGen = 1 / ( 減衰値0 + 減衰値1 * 距離 + 減衰値2 * ( 距離 * 距離 ) )
lLightGen = 1.0f / ( cfLight[ 1 ].Range_FallOff_AT0_AT1.z + cfLight[ 1 ].Range_FallOff_AT0_AT1.w * sqrt( lLightDistancePow2 ) + cfLight[ 1 ].AT2_SpotP0_SpotP1.x * lLightDistancePow2 ) ;
// --------------------------------
// スポットライト減衰計算 --------
// ライト方向ベクトルとライト位置から頂点位置へのベクトルの内積( 即ち Cos a )を計算
TempF3.x = dot( VTan, cfLight[ 1 ].Direction ) ;
TempF3.y = dot( VBin, cfLight[ 1 ].Direction ) ;
TempF3.z = dot( VNrm, cfLight[ 1 ].Direction ) ;
lLightDirectionCosA = dot( lLightDir, TempF3 ) ;
// スポットライト減衰計算 pow( falloff, ( ( Cos a - Cos f ) / ( Cos q - Cos f ) ) )
lLightGen *= saturate( pow( abs( max( lLightDirectionCosA - cfLight[ 1 ].AT2_SpotP0_SpotP1.y, 0.0f ) * cfLight[ 1 ].AT2_SpotP0_SpotP1.z ), cfLight[ 1 ].Range_FallOff_AT0_AT1.y ) ) ;
// --------------------------------
// 有効距離外だったら減衰率を最大にする処理
lLightGen *= step( lLightDistancePow2, cfLight[ 1 ].Range_FallOff_AT0_AT1.x ) ;
// 距離・スポットライト減衰値計算 =======================================( 終了 )
// ディフューズ色計算
// ディフューズ角度減衰率計算
DiffuseAngleGen = saturate( dot( Normal, -lLightDir ) ) ;
// ディフューズカラー蓄積値 += ( ライトのディフューズカラー * マテリアルディフューズカラー * ディフューズカラー角度減衰率 + ライトのアンビエントカラーとマテリアルのアンビエントカラーを乗算したもの ) * 距離・スポットライトの角度減衰率
TotalDiffuse += ( cfLight[ 1 ].Diffuse * cfMaterial.Diffuse * DiffuseAngleGen + cfLight[ 1 ].Ambient ) * lLightGen ;
// スペキュラカラー計算
// ハーフベクトルの計算
TempF3 = normalize( V_to_Eye - lLightDir ) ;
// Temp = pow( max( 0.0f, N * H ), cfMaterial.Power.x )
Temp = pow( max( 0.0f, dot( Normal, TempF3 ) ), cfMaterial.Power.x ) ;
// スペキュラカラー蓄積値 += Temp * 距離・スポットライトの角度減衰率 * ライトのスペキュラカラー
TotalSpecular += Temp * lLightGen.x * cfLight[ 1 ].Specular ;
// スポットライトの処理 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// ポイントライトの処理 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// ライト方向ベクトルの計算
TempF3 = normalize( PSInput.VPosition.xyz - cfLight[ 2 ].Position.xyz ) ;
// ライトのベクトルを接地空間に変換
lLightDir.x = dot( VTan, TempF3 ) ;
lLightDir.y = dot( VBin, TempF3 ) ;
lLightDir.z = dot( VNrm, TempF3 ) ;
// 距離減衰値計算 =======================================================( 開始 )
// 頂点とライト位置との距離の二乗を求める
lLightTemp = PSInput.VPosition.xyz - cfLight[ 2 ].Position.xyz ;
lLightDistancePow2 = dot( lLightTemp, lLightTemp ) ;
// 減衰率の計算 lLightGen = 1 / ( 減衰値0 + 減衰値1 * 距離 + 減衰値2 * ( 距離 * 距離 ) )
lLightGen = 1.0f / ( cfLight[ 2 ].Range_FallOff_AT0_AT1.z + cfLight[ 2 ].Range_FallOff_AT0_AT1.w * sqrt( lLightDistancePow2 ) + cfLight[ 2 ].AT2_SpotP0_SpotP1.x * lLightDistancePow2 ) ;
// 有効距離外だったら減衰率を最大にする処理
lLightGen *= step( lLightDistancePow2, cfLight[ 2 ].Range_FallOff_AT0_AT1.x ) ;
// 距離減衰値計算 =======================================================( 終了 )
// ディフューズ色計算
// ディフューズ角度減衰率計算
DiffuseAngleGen = saturate( dot( Normal, -lLightDir ) ) ;
// ディフューズカラー蓄積値 += ( ライトのディフューズカラー * マテリアルディフューズカラー * ディフューズカラー角度減衰率 + ライトのアンビエントカラーとマテリアルのアンビエントカラーを乗算したもの ) * 距離・スポットライトの角度減衰率
TotalDiffuse += ( cfLight[ 2 ].Diffuse * cfMaterial.Diffuse * DiffuseAngleGen + cfLight[ 2 ].Ambient ) * lLightGen ;
// スペキュラカラー計算
// ハーフベクトルの計算
TempF3 = normalize( V_to_Eye - lLightDir ) ;
// Temp = pow( max( 0.0f, N * H ), cfMaterial.Power.x )
Temp = pow( max( 0.0f, dot( Normal, TempF3 ) ), cfMaterial.Power.x ) ;
// スペキュラカラー蓄積値 += Temp * 距離・スポットライトの角度減衰率 * ライトのスペキュラカラー
TotalSpecular += Temp * lLightGen.x * cfLight[ 2 ].Specular ;
// ポイントライトの処理 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力カラー計算 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 開始 )
// TotalDiffuse = ライトディフューズカラー蓄積値 + ( マテリアルのアンビエントカラーとグローバルアンビエントカラーを乗算したものとマテリアルエミッシブカラーを加算したもの )
TotalDiffuse += cfAmbient_Emissive ;
// SpecularColor = ライトのスペキュラカラー蓄積値 * マテリアルのスペキュラカラー
SpecularColor = TotalSpecular * cfMaterial.Specular ;
// 出力カラー = TotalDiffuse * テクスチャカラー + SpecularColor
TextureDiffuseColor = tex2D( DiffuseMapTexture, PSInput.TexCoords0.xy ) ;
PSOutput.Color0.rgb = TextureDiffuseColor.rgb * TotalDiffuse.rgb + SpecularColor.rgb ;
// アルファ値 = テクスチャアルファ * マテリアルのディフューズアルファ * 不透明度
PSOutput.Color0.a = TextureDiffuseColor.a * cfMaterial.Diffuse.a * cfFactorColor.a ;
// 出力カラー計算 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++( 終了 )
// 出力パラメータを返す
return PSOutput ;
}
戻る