トゥーンシェーダー2


~輪郭抽出してみました~




■はじめに

前回の薄膜シェーダーで、輪郭抽出ができそうだったので、これをトゥーンシェーダーに使います。

全体の外側に黒い影が引かれるのが今回の特徴です。

以下のファイルをダウンロードしてください。

また、トラ君に登場してもらいましょう。

今回のものは、ポリゴン数が多いほうがきれいに出ますね。

■解説

今回使うエフェクト用のテクスチャーは、

です。 今回は、横軸方向も色が変化します。それぞれ、

U 軸:段階的な色分け
V 軸:輪郭抽出

に、使用します。
横軸の下の方を、見ると、前のトゥーンの時と同じようなグラデーションです。 ということで、光源と法線の内積を取り、ライティングに使用します。
縦軸は、右のほうを見ると、上が黒で、下が白になっています。 こちらは輪郭抽出に使用します。 視線の方向と法線の内積を計算し、値が小さい、あさっての向きを向いているポリゴンを上の黒い色にします。 これで、輪郭抽出がなされます。

■バーテックスシェーダー(vs.vsh)

では、バーテックスシェーダープログラムです。

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);
}

■最後に

ということで、輪郭抽出も終わって、トォーンシェーダーと呼ばれるものができました。
ただ、輪郭がポリゴン数や、モデルの形にシビアなので、もう少し調整しないと、実用と呼ばれるレベルにはならないかと思います。





もどる

imagire@gmail.com