Почему мой шейдер дает неправильные зеркальные результаты? [DX11]

В настоящее время я пытаюсь реализовать затенение и освещение Фонга на текстурированном кубе с использованием HLSL и DirectX 11. Я считаю, что мои расчеты рассеянного и рассеянного освещения верны и визуально дают ожидаемый результат. Однако, когда я применяю зеркальное освещение, я получаю странные результаты (см. Ссылки)

Diffuse, Ambient & Specular: https://i.gyazo.com/f7700d758e05227e27be91ab0cfdf64e.png

Specular Only: https://i.gyazo.com/27bbfa0efce5c60748f61f54365cc042.png

Мой файл.fx:

//Texture Variables
Texture2D txDiffuse[2] : register(t0);
SamplerState anisoSampler : register(s0);

//--------------------------------------------------------------------------------------
// Constant Buffer Variables
//--------------------------------------------------------------------------------------
cbuffer ConstantBuffer : register(b0)
{
    matrix World;
    matrix View;
    matrix Projection;

    float4 DiffuseMtrl;
    float4 DiffuseLight;

    float3 LightPosition;

    float4 AmbientMaterial;
    float4 AmbientLight;

    float4 specularMaterial;
    float4 specularLight;
    float  specularPower;

    float3 eyePosW;
}
//--------------------------------------------------------------------------------------
struct VS_INPUT
{
    float4 Pos : POSITION;
    float3 Normal : NORMAL;
    float2 Tex : TEXCOORD0;
};

//--------------------------------------------------------------------------------------
struct VS_OUTPUT
{
    float4 Pos : SV_POSITION;
    float3 Norm : NORMAL;
    float3 PosW : POSITION; //Eye Vector
    float3 LPos : LIGHTPOS; //Position of light
    float2 Tex : TEXCOORD0;
};

//--------------------------------------------------------------------------------------
// Vertex Shader
//--------------------------------------------------------------------------------------

VS_OUTPUT VS(VS_INPUT vIn)
{
    VS_OUTPUT output = (VS_OUTPUT)0;
    float4 worldPosition;

    output.Tex = vIn.Tex;
    vIn.Pos.w = 1.0f;
    output.Pos = mul(vIn.Pos, World);


    output.Pos = mul(output.Pos, View);
    output.Pos = mul(output.Pos, Projection);

    worldPosition = mul(vIn.Pos, World);

    output.LPos = normalize(worldPosition - LightPosition);
    output.PosW = normalize(eyePosW.xyz - worldPosition.xyz);
    float3 normalW = mul(float4(vIn.Normal, 0.0f), World).xyz;
    normalW = normalize(normalW);


    output.Norm = normalW;
    return output;
}


//--------------------------------------------------------------------------------------
// Pixel Shader
//--------------------------------------------------------------------------------------
float4 PS(VS_OUTPUT input) : SV_Target
{
    float4 textureColor = txDiffuse[0].Sample(anisoSampler, input.Tex);
    float4 bumpNormal = txDiffuse[1].Sample(anisoSampler, input.Tex);
    float4 output;
    float lightIntensity;
    float specularAmount;

    input.Norm = normalize(input.Norm);
    bumpNormal = normalize(bumpNormal);

    //Invert LDir for calculations
    float3 LDir = -input.LPos;

    lightIntensity = saturate(dot((input.Norm + bumpNormal.rgb), LDir));

    float3 r = reflect(LDir, (input.Norm + bumpNormal.rgb));
    specularAmount = pow(max(dot(r, input.PosW), 0.0f), specularPower);


    // Compute Colour using Diffuse ambient and texture
    float diffuseAmount = max(dot(LDir, (input.Norm + bumpNormal.rgb)), 0.0f);

    float3 diffuse = (diffuseAmount * (DiffuseMtrl * DiffuseLight).rgb) * lightIntensity;
    float3 ambient = AmbientLight * AmbientMaterial;
    float3 specular = specularAmount * (specularMaterial * specularLight).rgb * lightIntensity;

    output.rgb = ((ambient + diffuse) * textureColor) + specular;
    output.a = textureColor.a;

    return output;
}

Прошу прощения за любой грязный код или имена переменных, я пытался заставить это работать, используя различные учебники и учебники, каждый со своими соглашениями об именах, так что мой немного запутался в данный момент.

РЕДАКТИРОВАТЬ: Используя ответ и некоторые другие источники информации, я переработал мой шейдер и получил его работать. Одним из моих вопросов было неправильное заполнение константного буфера. Я также добавил расчеты касательного пространства и правильно преобразовал свои нормальные значения неровностей в диапазон от -1 до +1

//--------------------------------------------------------------------------------------
// Constant Buffer Variables
//--------------------------------------------------------------------------------------
cbuffer ConstantBuffer
{
    matrix World;
    matrix View;
    matrix Projection;

}

struct PointLight
{
    float4 ambient;
    float4 diffuse;
    float4 specular;

    float3 pos;
    float range;

    float3 att;
    float pad;

};

cbuffer CbPerFrame
{
    PointLight light;
    float3 eyePosW;
    float pad;
    float4 SpecularMaterial;
    float SpecularPower;
    float3 pad2;
};

Texture2D ObjTexture[2];
SamplerState ObjSamplerState;

//--------------------------------------------------------------------------------------
struct VS_OUTPUT
{
    float4 Pos : SV_POSITION;
    float4 worldPos : POSITION;
    float2 TexCoord : TEXCOORD;
    float3 normal : NORMAL;
    float3 tangent : TANGENT;
    float3 biTangent : BITANGENT;
};

void CalcTanBiTan(float3 norm, out float3 tan, out float3 biTan)
{

    float3 c1 = cross(norm, float3(0.0f, 0.0f, 1.0f));
    float3 c2 = cross(norm, float3(0.0f, 1.0f, 0.0f));

    if (length(c1) > length(c2))
    {
        tan = c1;
    }
    else
    {
        tan = c2;
    }
    tan = normalize(tan);

    biTan = cross(norm, tan);
    biTan = normalize(biTan);
}

//--------------------------------------------------------------------------------------
// Vertex Shader
//--------------------------------------------------------------------------------------

VS_OUTPUT VS(float4 inPos : POSITION, float2 inTexCoord : TEXCOORD, float3 normal : NORMAL)
{
    VS_OUTPUT output = (VS_OUTPUT)0;

    output.Pos = mul(inPos, World);
    output.worldPos = mul(inPos, World);
    output.Pos = mul(output.Pos, View);
    output.Pos = mul(output.Pos, Projection);
    output.normal = mul(normal, World);
    output.TexCoord = inTexCoord;

    float3 tangent, biTangent;
    CalcTanBiTan(normal, tangent, biTangent);

    output.tangent = mul(tangent, (float3x3)World);
    output.tangent = normalize(output.tangent);
    output.biTangent = mul(biTangent, (float3x3)World);
    output.biTangent = normalize(output.biTangent);

    return output;
}


//--------------------------------------------------------------------------------------
// Pixel Shader
//--------------------------------------------------------------------------------------
float4 PS(VS_OUTPUT input) : SV_Target
{
    input.normal = normalize(input.normal);
    float4 diffuse = ObjTexture[0].Sample(ObjSamplerState, input.TexCoord);
    float4 bumpMap = ObjTexture[1].Sample(ObjSamplerState, input.TexCoord);

    bumpMap = (bumpMap * 2.0f) - 1.0f;

    float3 bumpNormal = (bumpMap.x * input.tangent) + (bumpMap.y * input.biTangent) + (bumpMap.z * input.normal);
    bumpNormal = normalize(bumpNormal);

    float3 finalColor = float3(0.0f, 0.0f, 0.0f);

    //Create vector between light and pixel
    float3 lightToPixelVec = light.pos - input.worldPos;

    //find distance between light pos and pixel pos
    float d = length(lightToPixelVec);

    float3 finalAmbient = diffuse * light.ambient;
    if (d > light.range)
        return float4(finalAmbient, diffuse.a);

    //Turn lightToPixelVec into a unit vector describing pixel direction from the light position
    lightToPixelVec /= d;

    float howMuchLight = dot(lightToPixelVec, bumpNormal);

    float3 toEye = normalize(eyePosW - input.worldPos);
    float3 spec;
    [flatten]
    if (howMuchLight > 0.0f)
    {
        float3 v = reflect(-lightToPixelVec, bumpNormal);
        float specFactor = pow(max(dot(v, toEye), 0.0f), SpecularPower);
        spec = specFactor * SpecularMaterial * light.specular;
        finalColor += howMuchLight * diffuse * light.diffuse;

        finalColor /= light.att[0] + (light.att[1] * d) + light.att[2] * (d*d);
    }

    finalColor = saturate(finalColor + finalAmbient + spec);

    return float4(finalColor, diffuse.a);
}

1 ответ

Решение

Первый:

 float4 bumpNormal = txDiffuse[1].Sample(anisoSampler, input.Tex);
 bumpNormal = normalize(bumpNormal);

хранилище данных в текстуре обычно имеет формат RGBA с 8 битами для каждого канала. если вы не читаете текстуру с плавающей точкой. когда текстуры хранят каждый канал с 8 битами. вам нужно отобразить значение от 0~1 до -1~1 в шейдере.

второй:

 input.Norm = normalize(input.Norm);
 bumpNormal = normalize(bumpNormal);
 (input.Norm + bumpNormal.rgb)

добавить неверно, вы должны заменить нормальный на нормальный удар.

в третьих:

Я не уверен, в каком пространстве будет храниться ваше нормальное, обычно нормальное в касательном пространстве, это означает, что вы также должны передавать касательный или бинормальный из вершинного шейдера.

если вы хотите проверить только зеркальное отражение, вы можете использовать исходное нормальное изображение.

Другие вопросы по тегам