ピクセルシェーダの処理
後は表示されているラインにテクスチャを貼り付ければ完成です。
このエフェクトのピクセルシェーダは、テクスチャの色を取得、出力しているだけの極めてシンプルな構造です。
// ピクセルシェーダ
float4 Line_PS( VS_OUTPUT IN ) : COLOR0
{
//出力用カラー
float4 Color;
Color = tex2D(LineTexSampler,IN.Tex); //修正
return Color;
}
このようなテクスチャをモデルのUV値に合わせて張る処理だけを行っています。
エフェクトの改造
ラインエフェクトは以上で完成です。しかし、若干面白みにかけるのでここからさらに改造を加えていきます。
エルミート曲線の実装
エルミート曲線という計算式を用いて、ラインを曲げる処理を付け加えます。
//エルミート補完関数
float3 HermiteLerp(float3 s,float3 e,float3 svec,float3 evec,float t)
{
return (((t-1)*(t-1))*(2*t+1)*s) + ((t*t)*(3-2*t)*e) +((1-(t*t))*t*svec) + ((t-1)*(t*t)*evec);
}
このような関数がコード内48行目付近にあるはずです。
ゲームなどでも利用される比較的高速な計算で、内容としては、始点、終点に"勢い"を与え、それに合わせて曲線を描く計算式です。
これを利用したコードは以下のようになります。
// 頂点シェーダ
VS_OUTPUT Line_VS(float4 Pos : POSITION, float2 Tex : TEXCOORD0)
{
VS_OUTPUT Out = (VS_OUTPUT)0;
//ターゲット用座標
float3 TargetPos = TargetPos2;
//---追加---//
//エルミート曲線用始点ベクトル
float3 StartVec = float3(0,100,0);
//エルミート曲線用終点ベクトル
float3 EndVec = float3(0,300,0);
//----------//
//出力用座標
float4 LastPos = 1;
//---現在の座標取得---//
//ワールド行列の4行目からワールド座標を取得
float3 ZeroPos = WorldMatrix[3].xyz;
//原点と目標点の間を線形補間で座標を取得
//補間用係数はモデルのZ座標
//修正
//float3 NowPos = lerp(ZeroPos,TargetPos,Pos.z);
float3 NowPos = HermiteLerp(ZeroPos,TargetPos,StartVec,EndVec,Pos.z);
//出力用座標に保存
LastPos.xyz = NowPos;
//---ビルボード計算---//
//現在座標から少しだけ先に進めた座標を取得
//修正
//float3 NextPos = lerp(ZeroPos,TargetPos,Pos.z+0.01);
float3 NextPos = HermiteLerp(ZeroPos,TargetPos,StartVec,EndVec,Pos.z+0.01);
//カメラへの視線ベクトルを計算する
float3 EyeVec = normalize(NowPos - CameraPosition);
//現在の座標と先の座標から正面方向のベクトルを得る
float3 FrontVec = normalize(NextPos - NowPos);
//正面ベクトルと視線ベクトルの外積からサイドベクトルを得る
float3 SideVec = normalize(cross(EyeVec,FrontVec))*3;
//現在処理している頂点を左右に割り振る
//すべての頂点のX座標は0.5もしくは-0.5
if(Pos.x > 0)
{
LastPos.xyz += SideVec;
}else{
LastPos.xyz -= SideVec;
}
// カメラ視点ビュー射影変換
Out.Pos = mul( LastPos, ViewProjMatrix );
// テクスチャ座標
Out.Tex = Tex;
return Out;
}
座標を取得する部分を書き換えてあります。
基本的にlerp関数と同じような使い方で、引数に始点、終点の勢いベクトルが代入されています。
始点終点ベクトルのパラメータをいろいろ書き換えて挙動を確認してみてください。


