Mike5050
Mike5050

Reputation: 605

DirectX 11 Diffuse Lighting Implementation

Following: https://www.gamasutra.com/view/feature/131275/implementing_lighting_models_with_.php?page=2

I am trying to implement Diffuse Lighting, but I think I am not understanding something..., and I dont know if I am calculating it correctly.

The vertex information for the cube is:

SimpleVertex vertices[] =
        {
            { DirectX::XMFLOAT3(-1.0f, 1.0f, -1.0f), DirectX::XMFLOAT3(0.0f, 1.0f, 0.0f), DirectX::XMFLOAT2(1.0f, 0.0f) },
            { DirectX::XMFLOAT3(1.0f, 1.0f, -1.0f), DirectX::XMFLOAT3(0.0f, 1.0f, 0.0f), DirectX::XMFLOAT2(0.0f, 0.0f) },
            { DirectX::XMFLOAT3(1.0f, 1.0f, 1.0f), DirectX::XMFLOAT3(0.0f, 1.0f, 0.0f), DirectX::XMFLOAT2(0.0f, 1.0f) },
            { DirectX::XMFLOAT3(-1.0f, 1.0f, 1.0f), DirectX::XMFLOAT3(0.0f, 1.0f, 0.0f), DirectX::XMFLOAT2(1.0f, 1.0f) },

            { DirectX::XMFLOAT3(-1.0f, -1.0f, -1.0f), DirectX::XMFLOAT3(0.0f, -1.0f, 0.0f), DirectX::XMFLOAT2(0.0f, 0.0f) },
            { DirectX::XMFLOAT3(1.0f, -1.0f, -1.0f), DirectX::XMFLOAT3(0.0f, -1.0f, 0.0f), DirectX::XMFLOAT2(1.0f, 0.0f) },
            { DirectX::XMFLOAT3(1.0f, -1.0f, 1.0f), DirectX::XMFLOAT3(0.0f, -1.0f, 0.0f), DirectX::XMFLOAT2(1.0f, 1.0f) },
            { DirectX::XMFLOAT3(-1.0f, -1.0f, 1.0f), DirectX::XMFLOAT3(0.0f, -1.0f, 0.0f), DirectX::XMFLOAT2(0.0f, 1.0f) },

            { DirectX::XMFLOAT3(-1.0f, -1.0f, 1.0f), DirectX::XMFLOAT3(-1.0f, 0.0f, 0.0f), DirectX::XMFLOAT2(0.0f, 1.0f) },
            { DirectX::XMFLOAT3(-1.0f, -1.0f, -1.0f), DirectX::XMFLOAT3(-1.0f, 0.0f, 0.0f), DirectX::XMFLOAT2(1.0f, 1.0f) },
            { DirectX::XMFLOAT3(-1.0f, 1.0f, -1.0f), DirectX::XMFLOAT3(-1.0f, 0.0f, 0.0f), DirectX::XMFLOAT2(1.0f, 0.0f) },
            { DirectX::XMFLOAT3(-1.0f, 1.0f, 1.0f), DirectX::XMFLOAT3(-1.0f, 0.0f, 0.0f), DirectX::XMFLOAT2(0.0f, 0.0f) },

            { DirectX::XMFLOAT3(1.0f, -1.0f, 1.0f), DirectX::XMFLOAT3(1.0f, 0.0f, 0.0f), DirectX::XMFLOAT2(1.0f, 1.0f) },
            { DirectX::XMFLOAT3(1.0f, -1.0f, -1.0f), DirectX::XMFLOAT3(1.0f, 0.0f, 0.0f), DirectX::XMFLOAT2(0.0f, 1.0f) },
            { DirectX::XMFLOAT3(1.0f, 1.0f, -1.0f), DirectX::XMFLOAT3(1.0f, 0.0f, 0.0f), DirectX::XMFLOAT2(0.0f, 0.0f) },
            { DirectX::XMFLOAT3(1.0f, 1.0f, 1.0f), DirectX::XMFLOAT3(1.0f, 0.0f, 0.0f), DirectX::XMFLOAT2(1.0f, 0.0f) },

            { DirectX::XMFLOAT3(-1.0f, -1.0f, -1.0f), DirectX::XMFLOAT3(0.0f, 0.0f, -1.0f), DirectX::XMFLOAT2(0.0f, 1.0f) },
            { DirectX::XMFLOAT3(1.0f, -1.0f, -1.0f), DirectX::XMFLOAT3(0.0f, 0.0f, -1.0f), DirectX::XMFLOAT2(1.0f, 1.0f) },
            { DirectX::XMFLOAT3(1.0f, 1.0f, -1.0f), DirectX::XMFLOAT3(0.0f, 0.0f, -1.0f), DirectX::XMFLOAT2(1.0f, 0.0f) },
            { DirectX::XMFLOAT3(-1.0f, 1.0f, -1.0f), DirectX::XMFLOAT3(0.0f, 0.0f, -1.0f), DirectX::XMFLOAT2(0.0f, 0.0f) },

            { DirectX::XMFLOAT3(-1.0f, -1.0f, 1.0f), DirectX::XMFLOAT3(0.0f, 0.0f, 1.0f), DirectX::XMFLOAT2(1.0f, 1.0f) },
            { DirectX::XMFLOAT3(1.0f, -1.0f, 1.0f), DirectX::XMFLOAT3(0.0f, 0.0f, 1.0f), DirectX::XMFLOAT2(0.0f, 1.0f) },
            { DirectX::XMFLOAT3(1.0f, 1.0f, 1.0f), DirectX::XMFLOAT3(0.0f, 0.0f, 1.0f), DirectX::XMFLOAT2(0.0f, 0.0f) },
            { DirectX::XMFLOAT3(-1.0f, 1.0f, 1.0f), DirectX::XMFLOAT3(0.0f, 0.0f, 1.0f), DirectX::XMFLOAT2(1.0f, 0.0f) },
        };

Vertex Shader Is:

cbuffer ConstantBuffer : register( b0 )
{
    matrix World;
    matrix View;
    matrix Projection;
    float4 vMeshColor;
};

struct VS_INPUT
{
    float4 Position : POSITION;
    float3 Normal : NORMAL;
    float2 Texture : TEXCOORD0;
};

struct PS_INPUT
{
    float4 Position : SV_POSITION;
    float3 Normal : TEXCOORD0;
    float2 Texture : TEXCOORD1;
};

PS_INPUT VS( VS_INPUT input )
{
    PS_INPUT output = (PS_INPUT)0;

    output.Position = mul( input.Position, World );
    output.Position = mul( output.Position, View );
    output.Position = mul( output.Position, Projection );

    output.Normal = mul( input.Normal, World );
    output.Normal = mul( output.Normal, View );
    output.Normal = mul( output.Normal, Projection );
    output.Normal = normalize( output.Normal );

    output.Texture = input.Texture;

    return output;
}

Pixel Is:

PS_OUTPUT PS( PS_INPUT input )
{
    PS_OUTPUT output;
    float4 ambient = {0.1, 0.0, 0.0, 1.0};
    float4 lightColor = { 1.0f, 1.0f, 1.0f, 1.0f};

    float3 lightPosition = ( 1.0f, 1.0f, 0.0f );
    float3 lightDirection = normalize(lightPosition - input.Position);

    float1 diffuse = saturate( dot( lightDirection, input.Normal )) * lightColor;

    output.color = diffuse;

    //float4 solidColor = float4( 1.0f, 1.0f, 0.0f, 1.0f );
    //output.color = solidColor;
    return output;
}

Result (when cube is rotated 50): enter image description here

CPP: https://github.com/walbourn/directx-sdk-samples/blob/master/Direct3D11Tutorials/Tutorial06/Tutorial06.cpp

HLSL: https://github.com/walbourn/directx-sdk-samples/blob/master/Direct3D11Tutorials/Tutorial06/Tutorial06.fx

Upvotes: 2

Views: 2425

Answers (1)

Zebrafish
Zebrafish

Reputation: 13878

There not much information here to go on, but the normal is a direction vector (a direction) pointing in a certain direction from a vertex(a point). When you calculate diffuse lighting what you do is you get the position of the light, get the position of the vertex (the point, in this case I assume from the cube), and you find what the angle is between the two vectors. Picture it as a triangle, you draw a line from the light position to the vertex on the cube, and calculate the angle between that line from the light and the normal vector (the line pointing perpendicular, or orthogonal, or straight out) from the cube surface. The higher the angle between these two vectors the darker it will be, because the you take the "dot product". When the angles are at right angles the dot product will be zero, meaning no light. When the "normal" direction points directly in the direction of the light the value of the dot product is 1, meaning full light. When you multiply this dot product (basically the light intensity) with another colour you're adjusting the brightness of the original colour that the vertex (or the cube) had. That regulates the colour that you see. Hope that explained something.

As for why you see what you see when you change the numbers, I don't know.

I'll just add, as to your question about why you see something different if you change the normal with the position, the normal vector is nearly always a unit vector (length of one). When you do the lighting calculation you only get the desired values of between 0 and 1 if both vectors are normalised (are both unit vectors, or are both vectors with length 1). Otherwise you can get massively big or small negative numbers. If you use a massive number like 45 as your lighting multiplication factor, then things aren't going to be right. Colours in shaders are values between 0 and 1.

enter image description here

Update: OK, this gets complicated to explain, and I don't know if I can draw a picture to show this. What's happening in this code is you're taking each corner vertex of the cube, and you're multiplying it by what's called the world/view/projection matrix. Basically it takes the vertex of the cube, it moves it to its location in the world, then moves it to the position that's the inverse of the camera's position, which usually means right near the origin (0, 0, 0) in front of the camera. And then the projection matrix is what transforms those points onto your screen. The important thing to remember here is that multiplying the incoming vertex in the vertex shader by this magical WVP matrix is that the end result is that the vertices end up in front of the camera and then projected onto your screen.

Now there's a second part that's happening, and that's the lighting calculation. It can be done in world space, OR after the world x view transformation has been done. In this case the code does the lighting calculation in world space, meaning that from wherever the vertex of your cube is in its local space, it's transformed to its world position. It could be anywhere in the world, it could be (150, 20, 60). You need to transform it to this world position because the light position is also somewhere in the world in this space. The normal vector will usually be a unit vector (vector of length 1). Picture the normal vector starting at (0, 0, 0) and pointing in a direction.

In the first instance where you multiply the normal vector by the world matrix, this is wrong. Ideally what should happen is that the normal vector should 'follow' the transformation of the position of the vertex, and then point out with a length of one from that transformed world position. I wish I could I draw something out to explain it better. The first one is definitely wrong, and the second one where you multiply the position by the world matrix is also wrong I think.

Edit: I went through the tutorial code and it works fine. You specify a light point of (1, 1, 0), and then you use it in your shader. On the other hand in the tutorial the two light points are:

XMFLOAT4( -0.577f, 0.577f, -0.577f, 1.0f ),
XMFLOAT4( 0.0f, 0.0f, -1.0f, 1.0f ),

The second one you'll see is a unit vector, only the Z is -1. The first one is also a unit vector (length of 1), because A squared + B squared + C squared = length. That's why both of these are unit vectors and the dot product will result correctly. On the other your (1, 1, 0) vector isn't a unit vector. I'm not sure if that's the difference, though in any case your code and the tutorial's one aren't the same, I'm not sure if these changes are why your code doesn't work. Also note that Chuck's light code works because the cube is in the centre, if it were moved anywhere else you'd get lighting bugs everywhere. I guess he's done it like this simply just for demonstration.

Upvotes: 1

Related Questions