How can I fix the normals in this instanced lighting example? (Monogame/XNA)

62 Views Asked by At

The models in my game are instanced to reduce draw calls, which basically means I'm passing in a 4x4 transform as part of a custom vertex definition. I am trying to get basic lighting working but can't seem to get the normals calculations working. It seems that no matter what I try, only the top of models are lit, regardless of the position of the sun.

Furthermore, I also can't get any of the lights other than the sun to even show up. I'm thinking I'm maybe missing a transform somewhere.

float4x4 World;
float4x4 View;
float4x4 Projection;


#define MAXLIGHT 20

float3 PointLightPosition[MAXLIGHT];
float4 PointLightColor[MAXLIGHT];
float PointLightIntensity[MAXLIGHT];
float PointLightRadius[MAXLIGHT];
int MaxLightsRendered = 0;
struct VS_MODEL_ShaderInput
{
    float4 Position : SV_Position;
    float4 Color : COLOR;
    float2 TexCoord : TEXCOORD0;
    float3 Normal : NORMAL;
    
};
struct VertexShaderOutput
{
    float4 Position : SV_Position;
    float4 WorldPosition : COLOR1;
    
    float3 Normal : TEXCOORD1;
    float2 TexCoord : TEXCOORD0;
    float4 Color : COLOR;
    
};
float4 CalcDiffuseLight(float3 normal, float3 lightDirection, float4 lightColor, float lightIntensity)
{

    return saturate(dot(normal, -lightDirection)) * lightIntensity * lightColor;
}

float4 CalcSpecularLight(float3 normal, float3 lightDirection, float3 cameraDirection, float4 lightColor, float lightIntensity)
{
    float3 halfVector = normalize(-lightDirection + -cameraDirection);
    float specular = saturate(dot(halfVector, normal));

    //I have all models be the same reflectance
    float specularPower = 20;

    return lightIntensity * lightColor * pow(abs(specular), specularPower);
}

// The squared length of a vector
float lengthSquared(float3 v1)
{
    return v1.x * v1.x + v1.y * v1.y + v1.z * v1.z;
}

float3 CameraPosition;
float3 SunLightDirection;
float4 SunLightColor;
float SunLightIntensity;





Texture2D Texture;
sampler textureSampler = sampler_state
{
    Texture = <Texture>;
    MinFilter = linear;
    MagFilter = Anisotropic;
    AddressU = Wrap;
    AddressV = Wrap;
};




VertexShaderOutput Model_InstancingVS(VS_MODEL_ShaderInput input, float4x4 instanceTransform : BLENDWEIGHT)
{
    instanceTransform = mul(World, transpose(instanceTransform));
    VertexShaderOutput output;

    // Apply the world and camera matrices to compute the output position.
    float4 worldPosition = mul(input.Position, instanceTransform);
    float4 viewPosition = mul(worldPosition, View);
    output.Position = mul(viewPosition, Projection);
    output.WorldPosition = (float4) worldPosition;
    output.Normal = normalize(mul(input.Normal, (float3x3) instanceTransform));
    // Compute lighting, using a simple Lambert model.
    
    output.Color = input.Color;

    // Copy across the input texture coordinate.
    output.TexCoord = input.TexCoord;

    return output;


}
float4 MainPS(VertexShaderOutput input) : COLOR0
{

    float4 outColor = tex2D(textureSampler, input.TexCoord); // * output.Color;
    float4 diffuseLight = float4(0, 0, 0, 0);
    float4 specularLight = float4(0, 0, 0, 0);
    float4 diffuseSpecular = float4(0, 0, 0, 0);
    
    float3 cameraDirection = normalize((float3) input.WorldPosition - CameraPosition);
    diffuseLight += CalcDiffuseLight(input.Normal, SunLightDirection, SunLightColor, SunLightIntensity);
    diffuseSpecular += CalcSpecularLight(input.Normal, SunLightDirection, cameraDirection, SunLightColor, SunLightIntensity);

    
    //return outColor;
    
    
////calculate our pointLights
    [loop]
    for (int i = 0; i < MaxLightsRendered; i++) 
    {
        float3 PointLightDirection = (float3) input.WorldPosition - PointLightPosition[i];
                           
        float DistanceSq = lengthSquared(PointLightDirection);

        float radius = PointLightRadius[i];
             
        [branch]
        if (DistanceSq < abs(radius * radius))
        {
            float Distance = sqrt(DistanceSq);

            PointLightDirection /= Distance;

            float du = Distance / (1.0 - DistanceSq / (radius * radius - 1));

            float denom = du / abs(radius) + 1.0;

            //The attenuation is the falloff of the light depending on distance basically
            float attenuation = 1.0 / (denom * denom);

            diffuseLight += CalcDiffuseLight(input.Normal, PointLightDirection, PointLightColor[i], PointLightIntensity[i]) * attenuation;
            
            specularLight += CalcSpecularLight(input.Normal, PointLightDirection, cameraDirection, PointLightColor[i], PointLightIntensity[i]) * attenuation;
        }
    }
    
    return outColor * diffuseLight * diffuseSpecular;

}



technique ModelInstancing
{
    pass P0
    {
        VertexShader = compile vs_3_0 Model_InstancingVS();
        PixelShader = compile PS_SHADERMODEL MainPS();
    }
};

If anyone can spot anything or give me a shove in the right direction I'd really appreciate it.

1

There are 1 best solutions below

0
propellerhat23 On

diffuseLight * diffuseSpecular needed to be addition:

diffuseLight + diffuseSpecular