Per-Pixel Point Light

Started by
17 comments, last by Waaayoff 10 years, 9 months ago

I have been having some troubles to get point light to work, I looked into some code samples, but always getting a problem when trying to implement point light.

Can someone show me how do I modify my shader to implement point light?


matrix World;
matrix View;
matrix Projection;

// Point light variables
float4 lightPosition;
float lightRange;
float4 lightDiffuse;
float4 lightAmbient;
float4 lightSpecular;

// Texture
texture colorMapTexture;

sampler2D colorMap = sampler_state
{
    Texture = <colorMapTexture>;
    MagFilter = Linear;
    MinFilter = Anisotropic;
    MipFilter = Linear;
    MaxAnisotropy = 16;
};

struct PS_INPUT
{
   float4 Pos : SV_POSITION;
   float2 UV : TEXCOORD0;
   float3 Normal : TEXCOORD1;
};

struct VS_INPUT 
{
   float4 Pos : SV_POSITION;
   float2 UV : TEXCOORD0;
   float3 Normal : TEXCOORD1;
};

struct VS_OUTPUT 
{
   float4 Pos : SV_POSITION;
   float2 UV : TEXCOORD0;
   float3 Normal : TEXCOORD1;
};

//-----------------------------------------------------------------------
// Vertex shader function
//-----------------------------------------------------------------------
VS_OUTPUT VS( VS_INPUT IN )
{
    VS_OUTPUT Out;
    Out = (VS_OUTPUT)0;
    Out.Pos = mul (IN.Pos, World);
    Out.Pos = mul (Out.Pos, View);
    Out.Pos = mul (Out.Pos, Projection);
    Out.UV = IN.UV;
    Out.Normal = mul(IN.Normal, (float3x3)World);
    return Out;
}

//-----------------------------------------------------------------------
// Pixel shader function
//-----------------------------------------------------------------------
float4 PS( PS_INPUT IN ) : COLOR
{
     // *** I need to modify this function for point light ***
     return tex2D(colorMap, IN.UV);
}

//-----------------------------------------------------------------------------
// Techniques.
//-----------------------------------------------------------------------------
technique PointLighting
{
    pass
    {
        AlphaBlendEnable = false;
        ZEnable = true;
        VertexShader = compile vs_3_0 VS();
        PixelShader = compile ps_3_0 PS();
    }
}

Advertisement
Okay, I have been trying for a while to do basic lighting in shader and here is what I got so far (Point Light):

[attachment=16818:5948.png]

So here I have 2 problems:

1. The point light is weird (as shown in the screenshot)

2. When the mesh move away from the point light it get black

Shader code:


float4x4 World;
float4x4 View;
float4x4 Projection;

// Point light
float4 lightPosition;
float4 lightDiffuse;
float4 lightAmbient;
float4 lightSpecular;
float lightRange;

// Material
float4 materialDiffuse;
float4 materialAmbient;
float4 materialSpecular;
float materialPower;

float4 globalAmbient;

// Camera position
float eyePos;

// Texture
texture colorMapTexture;

sampler2D colorMap = sampler_state
{
    Texture = <colorMapTexture>;
    MagFilter = Linear;
    MinFilter = Anisotropic;
    MipFilter = Linear;
    MaxAnisotropy = 16;
};

struct PS_INPUT
{
   float3 Pos : POSITION;
   float2 UV : TEXCOORD0;
   float3 Normal : TEXCOORD1;
   float3 worldPos : TEXCOORD2;
};

struct VS_INPUT 
{
   float3 Pos : POSITION;
   float2 UV : TEXCOORD0;
   float3 Normal : TEXCOORD1;
};

struct VS_OUTPUT 
{
   float4 Pos : POSITION;
   float2 UV : TEXCOORD0;
   float3 Normal : TEXCOORD1;
   float3 worldPos : TEXCOORD2;
};

//-----------------------------------------------------------------------
// Vertex shader function
//-----------------------------------------------------------------------
VS_OUTPUT VS( VS_INPUT IN )
{
    VS_OUTPUT Out;
    Out = (VS_OUTPUT)0;
    float4x4 WVP = mul(World, mul(View, Projection));
    Out.Pos = mul(float4(IN.Pos, 1.0f), WVP);
    Out.UV = IN.UV;
    Out.Normal = mul(IN.Normal, (float3x3)World);
    Out.worldPos = mul(float4(IN.Pos, 1.0f), World).xyz;
    return Out;
}

//-----------------------------------------------------------------------
// Pixel shader function
//-----------------------------------------------------------------------
float4 PS( PS_INPUT IN ) : COLOR
{
     float3 viewDir = eyePos - IN.worldPos;
     float3 lightDir = (lightPosition - IN.worldPos) / lightRange;


    float atten = saturate(1.0f - dot(lightDir, lightDir));


    float3 n = normalize(tex2D(colorMap, IN.UV).rgb * 2.0f - 1.0f);
    float3 l = normalize(lightDir);
    float3 v = normalize(viewDir);
    float3 h = normalize(l + v);
    
    float nDotL = saturate(dot(n, l));
    float nDotH = saturate(dot(n, h));
    float power = (nDotL == 0.0f) ? 0.0f : pow(nDotH, materialPower);


    float4 FinalDiffuse = materialDiffuse * lightDiffuse;
    float4 FinalSpecular = materialSpecular * lightSpecular;


    float4 color = (materialAmbient * (globalAmbient + (atten * lightAmbient))) +
                   (FinalDiffuse * nDotL * atten) + (FinalSpecular * power * atten);
            
    return color * tex2D(colorMap, IN.UV);
}

//-----------------------------------------------------------------------------
// Techniques.
//-----------------------------------------------------------------------------
technique PointLighting
{
    pass
    {
        AlphaBlendEnable = false;
        ZEnable = true;
        VertexShader = compile vs_3_0 VS();
        PixelShader = compile ps_3_0 PS();
    }
}

The whiteness looks like your specularity is too high (if materialPower is too big) and the darkness is something you should expect from a point light. The brightness decreases with the square of the distance. So it may be the case that it is just falling off too quickly from lightRange being too small.

VS_OUTPUT and PS_INPUT are the same thing--use a typedef or just use one of them to avoid mistakes.

@menohack: I notice that the whiteness decrease when I give higher materialPower.

However, it still doesn't look like specular light, I see just white pixels on the mesh.

How do I avoid the darkness? For example, when there is a fire, there should be a point light, however the point light should not make everything black

lightRange is not too small, I notice in a direction the falloff decrease smoothly, while in the other direction it decrease not smoothly (like in the screenshot).

So, I guess that there is something wrong with the Shader code.

I am confused as you are trying to use diffuse map as normal, WTF?

Do following changes and report back if it works as expected:

//float3 n = normalize(tex2D(colorMap, IN.UV).rgb * 2.0f - 1.0f);
float3 n = normalize(IN.Normal);

@belfegor: Okay, the problem is partly resolved, the falloff transition is now smooth.

But I have two problems:

1. I don't see the speular light anymore

2. The mesh get black when it's far away from the light, how do I make it never black? the mesh should be AFFECTED by the light but never get black

By my logic, this:


float4 color = (materialAmbient * (globalAmbient + (atten * lightAmbient))) + ...

should be like this:


// ambients should be added not multiplied or modified by light attenaution
float4 color = (materialAmbient + globalAmbient) + ...

When you fix this above also set some reasonable values for specular color and "materialPower".

@belfegor: When I change the line to:


float4 color = (materialAmbient + globalAmbient) + (FinalDiffuse * nDotL * atten) + (FinalSpecular * power * atten);

I don't see the light anymore.


I don't see the light anymore.

This is not descriptive enough.

Give me all your color values that you are sending to shader.

@belfegor:


static D3DXHANDLE hTechnique;
hTechnique = pEffect->GetTechniqueByName("PointLighting");
if (FAILED(pEffect->SetTechnique(hTechnique)))
    return;

pEffect->SetMatrix("World", &world);
pEffect->SetMatrix("View", &view);
pEffect->SetMatrix("Projection", &proj);

// Light
float lightDiffuse[4] = {1.0f, 1.0f, 1.0f, 255.0f};
float lightAmbient[4] = {1.0f, 1.0f, 1.0f, 255.0f};
float lightSpecular[4] = {255.0f, 1.0f, 1.0f, 255.0f};
float lightRange = 100.0f;
pEffect->SetValue("lightDiffuse", &lightDiffuse, sizeof(lightDiffuse));
pEffect->SetValue("lightAmbient", &lightAmbient, sizeof(lightAmbient));
pEffect->SetValue("lightSpecular", &lightSpecular, sizeof(lightSpecular));
pEffect->SetValue("lightRange", &lightRange, sizeof(lightRange));


// Material
float materialDiffuse[4] = {1.0f, 1.0f, 1.0f, 255.0f};
float materialAmbient[4] = {1.0f, 1.0f, 1.0f, 255.0f};
float materialSpecular[4] = {1.0f, 1.0f, 1.0f, 1.0f};
float materialPower = 20;
pEffect->SetValue("materialDiffuse", &materialDiffuse, sizeof(materialDiffuse));
pEffect->SetValue("materialAmbient", &materialAmbient, sizeof(materialAmbient));
pEffect->SetValue("materialSpecular", &materialSpecular, sizeof(materialSpecular));
pEffect->SetValue("materialPower", &materialPower, sizeof(materialPower));

float LightPos[4] = {0.0f, 0.0f, -50.1f, 0.0f};
pEffect->SetValue("lightPosition", &LightPos, sizeof(LightPos));
pEffect->SetValue("eyePos", &camera->GetPosition(), sizeof(camera->GetPosition()));

// Texture
pEffect->SetTexture("colorMapTexture", texture);

UINT passes = 0;
HRESULT hr = pEffect->Begin(&passes, 0);
hr = pEffect->BeginPass(0);

model->Render();

pEffect->EndPass();
pEffect->End();

Here is the current shader code as well:


float4x4 World;
float4x4 View;
float4x4 Projection;


// Point light
float4 lightPosition;
float4 lightDiffuse;
float4 lightAmbient;
float4 lightSpecular;
float lightRange;

// Material
float4 materialDiffuse;
float4 materialAmbient;
float4 materialSpecular;
float materialPower;

float4 globalAmbient;

// Camera position
float eyePos;

// Texture
texture colorMapTexture;

sampler2D colorMap = sampler_state
{
    Texture = <colorMapTexture>;
    MagFilter = Linear;
    MinFilter = Anisotropic;
    MipFilter = Linear;
    MaxAnisotropy = 16;
};

struct PS_INPUT
{
   float3 Pos : POSITION;
   float2 UV : TEXCOORD0;
   float3 Normal : TEXCOORD1;
   float3 worldPos : TEXCOORD2;
};

struct VS_INPUT 
{
   float3 Pos : POSITION;
   float2 UV : TEXCOORD0;
   float3 Normal : TEXCOORD1;
};

struct VS_OUTPUT 
{
   float4 Pos : POSITION;
   float2 UV : TEXCOORD0;
   float3 Normal : TEXCOORD1;
   float3 worldPos : TEXCOORD2;
};

//-----------------------------------------------------------------------
// Vertex shader function
//-----------------------------------------------------------------------
VS_OUTPUT VS( VS_INPUT IN )
{
    VS_OUTPUT Out;
    Out = (VS_OUTPUT)0;
    float4x4 WVP = mul(World, mul(View, Projection));
    Out.Pos = mul(float4(IN.Pos, 1.0f), WVP);
    Out.UV = IN.UV;
    Out.Normal = mul(IN.Normal, (float3x3)World);
    Out.worldPos = mul(float4(IN.Pos, 1.0f), World).xyz;
    return Out;
}

//-----------------------------------------------------------------------
// Pixel shader function
//-----------------------------------------------------------------------
float4 PS( PS_INPUT IN ) : COLOR
{
     float3 viewDir = eyePos - IN.worldPos;
     float3 lightDir = (lightPosition - IN.worldPos) / lightRange;


    float atten = saturate(1.0f - dot(lightDir, lightDir));


    float3 n = normalize(IN.Normal);
    float3 l = normalize(lightDir);
    float3 v = normalize(viewDir);
    float3 h = normalize(l + v);
    
    float nDotL = saturate(dot(n, l));
    float nDotH = saturate(dot(n, h));
    float power = (nDotL == 0.0f) ? 0.0f : pow(nDotH, materialPower);


    float4 FinalDiffuse = materialDiffuse * lightDiffuse;
    float4 FinalSpecular = materialSpecular * lightSpecular;


    // float4 color = (materialAmbient * (globalAmbient + (atten * lightAmbient))) +
    //               (FinalDiffuse * nDotL * atten) + (FinalSpecular * power * atten);
    
    float4 color = (materialAmbient + globalAmbient) + (FinalDiffuse * nDotL * atten) + (FinalSpecular * power * atten);
    return color * tex2D(colorMap, IN.UV);
}

//-----------------------------------------------------------------------------
// Techniques.
//-----------------------------------------------------------------------------
technique PointLighting
{
    pass
    {
        AlphaBlendEnable = false;
        ZEnable = true;
        VertexShader = compile vs_3_0 VS();
        PixelShader = compile ps_3_0 PS();
    }
}

This topic is closed to new replies.

Advertisement