前回の薄膜シェーダーで、輪郭抽出ができそうだったので、これをトゥーンシェーダーに使います。
全体の外側に黒い影が引かれるのが今回の特徴です。
以下のファイルをダウンロードしてください。
また、トラ君に登場してもらいましょう。
今回のものは、ポリゴン数が多いほうがきれいに出ますね。
今回使うエフェクト用のテクスチャーは、
です。 今回は、横軸方向も色が変化します。それぞれ、
| U 軸 | : | 段階的な色分け |
| V 軸 | : | 輪郭抽出 |
に、使用します。
横軸の下の方を、見ると、前のトゥーンの時と同じようなグラデーションです。
ということで、光源と法線の内積を取り、ライティングに使用します。
縦軸は、右のほうを見ると、上が黒で、下が白になっています。
こちらは輪郭抽出に使用します。
視線の方向と法線の内積を計算し、値が小さい、あさっての向きを向いているポリゴンを上の黒い色にします。
これで、輪郭抽出がなされます。
では、バーテックスシェーダープログラムです。
vs.1.0
; c0-3 -- world + ビュー + 透視変換行列
; c4-7 -- world 行列
; c8-11 -- world の逆転置行列
; c12 -- {0.0, 0.5, 1.0, -1.0}
; c13 -- ライトのベクトル
; c14 -- カメラの位置
; c15 -- メッシュの色
;
; v0 頂点の座標値
; v3 法線ベクトル
; v8 テクスチャ座標1
;座標変換
dp4 oPos.x, v0, c0
dp4 oPos.y, v0, c1
dp4 oPos.z, v0, c2
dp4 oPos.w, v0, c3
;法線の変換
dp3 r0.x, v3, c8
dp3 r0.y, v3, c9
dp3 r0.z, v3, c10
;法線の正規化
dp3 r0.w, r0, r0
rsq r0.w, r0.w
mul r0, r0, r0.w
;ワールド座標系での頂点の位置を計算する
dp4 r1.x, v0, c4
dp4 r1.y, v0, c5
dp4 r1.z, v0, c6
dp4 r1.w, v0, c7
;カメラへの向きeを計算する
add r2, c14, -r1
;e の正規化
dp3 r2.w, r2, r2
rsq r2.w, r2.w
mul r2, r2, r2.w
; l dot n (ライティング)
dp3 oT0.x, r0, c13
; e dot n (輪郭抽出)
dp3 oT0.y, r0, r2
; メッシュのテクスチャー
mov oT1, v8
; メッシュの色
mov oD0, c15
今回は、トゥーンのプログラムに、薄膜シェーダーで使った、e dot n の計算を行います。
それを oT0.y に入れて、テクスチャーの縦成分を利用します。
あと、oT0.x 及び oT0.y が 0 以下もしくは 1 以上の値になった場合に、
SetTextureStageState の方で処理するようにしたので、内積計算の部分が簡単になっています。
今回の draw.cpp は、描画部分しか前回のトゥーンからの変化がありません。
しかも、今回の draw.cpp を前回のプログラムで使用してもきちんと動きます
(テクスチャーの模様は変更しなければなりませんが)。
違いは、SetTextureStageState(0,D3DTSS_ADDRESSU(D3DTSS_ADDRESSV), D3DTADDRESS_CLAMP) で、
テクスチャーの繰り返しをクランプモードにします。
これは、U, V の値が 0 より小さかったら、0 のピクセルを引っ張ってきて、
1 より大きかったら、1 のピクセルを使用します。
つまり、はみ出した分を境界の色で塗りつぶします。
これで、バーテックスシェーダープログラムでのクランプ処理がいらなくなりました。
後は、前回使用した視線方向の位置を固定レジスタの 14 に入れました。
void Render(LPDIRECT3DDEVICE8 lpD3DDEV)
{
if(NULL == pMeshVB) return;
D3DXMATRIX mWorld, mView, mProj;
D3DXMatrixRotationY( &mWorld, timeGetTime()/1000.0f );
D3DXVECTOR3 eye, lookAt, up;
eye.x = 0.0f; eye.y = 1.5f; eye.z = 3.0f;
lookAt.x = 0.0f; lookAt.y = 0.0f; lookAt.z = 0.0f;
up.x = 0.0f; up.y = 1.0f; up.z = 0.0f;
D3DXMatrixLookAtLH(&mView, &eye, &lookAt, &up);
D3DXMatrixPerspectiveFovLH(&mProj
,60.0f*PI/180.0f // 視野角
,(float)WIDTH/(float)HEIGHT // アスペクト比
,0.01f // 最近接距離
,100.0f // 最遠方距離
);
D3DXMATRIX m = mWorld * mView * mProj;
D3DXMatrixTranspose( &m , &m);
lpD3DDEV->SetVertexShaderConstant(0,&m, 4);
D3DXMatrixTranspose( &m , &mWorld);
lpD3DDEV->SetVertexShaderConstant(4, &m, 4);
D3DXMatrixInverse( &m, NULL, &mWorld);
lpD3DDEV->SetVertexShaderConstant(8, &m, 4);
lpD3DDEV->SetVertexShaderConstant(12, D3DXVECTOR4(0.0f, 0.5f, 1.0f, -1.0f), 1);
D3DXVECTOR4 lightDir(0.5f, 1.0f, 1.0f, 0.0f);
D3DXVec4Normalize(&lightDir, &lightDir);
lpD3DDEV->SetVertexShaderConstant(13, &lightDir, 1);
lpD3DDEV->SetVertexShaderConstant(14, &eye, 1);
lpD3DDEV->SetVertexShader(hVertexShader);
lpD3DDEV->SetTextureStageState(0,D3DTSS_COLOROP, D3DTOP_MODULATE);
lpD3DDEV->SetTextureStageState(0,D3DTSS_COLORARG1, D3DTA_TEXTURE);
lpD3DDEV->SetTextureStageState(0,D3DTSS_COLORARG2, D3DTA_DIFFUSE);
lpD3DDEV->SetTextureStageState(0,D3DTSS_MAGFILTER, D3DTEXF_LINEAR);
lpD3DDEV->SetTextureStageState(0,D3DTSS_MINFILTER, D3DTEXF_LINEAR);
lpD3DDEV->SetTextureStageState(0,D3DTSS_ADDRESSU, D3DTADDRESS_CLAMP);
lpD3DDEV->SetTextureStageState(0,D3DTSS_ADDRESSV, D3DTADDRESS_CLAMP);
lpD3DDEV->SetTexture(0,pTexture);
//メッシュの描画
lpD3DDEV->SetStreamSource(0, pMeshVB, sizeof(D3D_CUSTOMVERTEX));
lpD3DDEV->SetIndices(pMeshIndex,0);
for(DWORD i=0;iSetVertexShaderConstant(15,&vl,1);
lpD3DDEV->SetTexture(1,pMeshTextures[i]);
lpD3DDEV->SetTextureStageState(1,D3DTSS_COLOROP,D3DTOP_MODULATE);
lpD3DDEV->SetTextureStageState(1,D3DTSS_COLORARG1,D3DTA_TEXTURE);
lpD3DDEV->SetTextureStageState(1,D3DTSS_COLORARG2,D3DTA_CURRENT);
lpD3DDEV->SetTextureStageState(1,D3DTSS_MAGFILTER,D3DTEXF_LINEAR);
lpD3DDEV->SetTextureStageState(1,D3DTSS_MINFILTER,D3DTEXF_LINEAR);
lpD3DDEV->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,
pSubsetTable[i].VertexStart,
pSubsetTable[i].VertexCount,
pSubsetTable[i].FaceStart * 3,
pSubsetTable[i].FaceCount);
}
lpD3DDEV->SetTexture(0, NULL);
}
ということで、輪郭抽出も終わって、トォーンシェーダーと呼ばれるものができました。
ただ、輪郭がポリゴン数や、モデルの形にシビアなので、もう少し調整しないと、実用と呼ばれるレベルにはならないかと思います。