MMDモデルを使用したデスクトップユーティリティの開発 その7 色表現


前: その6
次: その8

ディフューズ・スペキュラ光など、色の表現を追加する。
トゥーンシェーダ、スフィアマッピングを導入する。

マテリアルから必要な情報を取り出し、適切なテクスチャ座標の計算を行うことで、質感の表現ができます。
今回のソースはこちら

effect.fx
matrix World;
matrix View;
matrix Projection;
float3 ambientLight;
float4 matDiffuse;
float4 matAmbient;
float4 matSpecular;
float matAlpha;
float matSpecularity;
float3 lightDir;
float3 eyePos;
Texture2D normalTexture;
SamplerState normalSampler {};
Texture2D toonTexture;
SamplerState toonSampler {};
Texture2D sphTexture;
SamplerState sphSampler {};
Texture2D spaTexture;
SamplerState spaSampler {};
bool tex;
bool ton;
bool sph;
bool spa;
bool edge;

struct Vertexes {
    float4 position : SV_Position;
    float4 normal : NORMAL;
    float2 uv : TEXCOORD;
};

struct OutVert {
    float4 position : SV_Position;
    float2 uv : TEXCOORD;
    float4 diffuse : COLOR0;
    float3 specular : COLOR1;
    float2 toonCoord : TEXCOORD1;
    float2 sphereCoord : TEXCOORD2;
};

struct Colors {
    float3 diffuse;
    float3 specular;
    float2 toonCoord;
};

Colors CalcLight(float3 E, float3 N) {
    Colors col = (Colors)0;

    col.diffuse = ambientLight;
    col.specular = 0;

    float3 L = normalize(-lightDir);
    float3 H = normalize(E + L);
    float2 prt = lit(dot(N, L), dot(N, H), matSpecularity).yz;
    col.specular += ambientLight * prt.y;

    col.diffuse *= matDiffuse.rgb;
    col.diffuse += matAmbient.rgb;
    col.diffuse = saturate(col.diffuse);
    col.specular *= matSpecular.rgb;

    col.toonCoord.x = clamp(0.5f - dot(normalize(N), normalize(E)) * 0.5f, 0, 1);
    col.toonCoord.y = clamp(0.5f - dot(normalize(N), normalize(L)) * 0.5f, 0, 1);

    return col;
}

OutVert myVertexShader(Vertexes input) {
    OutVert output = (OutVert)0;

    float4 posW = mul(input.position, World);
    float4 pos = mul(posW, View);
    pos = mul(pos, Projection);
    output.position = pos;
    output.uv = input.uv;

    float3 N = normalize(mul(input.normal, World)).xyz;
    float3 subEye = eyePos - posW.xyz;
    float3 E = normalize(subEye);
    Colors light = CalcLight(E, N);

    output.diffuse = float4(light.diffuse.rgb, matAlpha);
    output.specular = light.specular;

    output.toonCoord = light.toonCoord;
    output.sphereCoord = float2(input.normal.x / 2 + 0.5f, input.normal.y / 2 + 0.5f);

    return output;
}

float4 myPixelShader(OutVert input) : SV_Target {
    float4 color = normalTexture.Sample(normalSampler, input.uv) * input.diffuse + float4(input.specular.rgb, 0);
    if (sph) {
        color.rgb *= sphTexture.Sample(sphSampler, input.sphereCoord).rgb;
    }
    if (spa) {
        color.rgb += spaTexture.Sample(spaSampler, input.sphereCoord).rgb;
    }
    if (ton) {
        color.rgb *= toonTexture.Sample(toonSampler, input.toonCoord).rgb;
    }

    return color;
}

technique10 myTechnique {
    pass myPass {
        SetVertexShader(CompileShader(vs_5_0, myVertexShader()));
        SetPixelShader(CompileShader(ps_5_0, myPixelShader()));
    }
}

さいごに

調べながら色々ためしてみたのですが、なかなか思うようにはいかなかったため、シェーダ部分は色々なところからの寄せ集めです。

次回は、PMX読み込み・表示に取り掛かる予定です。