Click to See Complete Forum and Search --> : [HLSL] Diffuse lighting oddities...


Alluminitti
March 18th, 2009, 09:06 PM
Hello,

I've spent the last couple of days trying to get into HLSL and to eventually use fancy shaders in my future games. Currently, I'm following Frank D. Luna's excellent book entitled, "Introduction to 3D Game Programming w/ DirectX 9: A Shader Approach". So far, I'm at chapter 10--this chapter discusses various lighting techniques including: ambient, diffuse, specular, point, spotlight, phong shading, etc.

I've ran into an unusual problem with my 3D application, unfortunately. The solution is probably simple and perhaps I'm overlooking something small, or perhaps my math isn't what it's supposed to be. In my 3D application I have four rows of spheres laid out in a grid. This grid of spheres are all located int the positive xz plane. In my shader I set up a directional diffuse light that points along the positive z-axis (0, 0, 1}. Additionally, my movable camera is set up in the position {0, 0, -10} with the view target at {0, 0, 0} (looking towards the grid of spheres on the xz plane).

With the aforementioned details, the expected result is for all of the spheres in the grid to be lit on the {0, 0, -1} side. However, the result is quite opposite. The side of the spheres that aren't supposed to be lit are lit, and vice-versa for the opposite side.

Here is my diffuse lighting shader in its entirety:

struct InputVS
{

float4 position : POSITION ;
float4 normal : NORMAL ;
} ;

struct OutputVS
{

float4 position : POSITION ;
float4 diffuse : COLOR ;
} ;

uniform extern float4x4 mtrxWVP : WorldViewProjection ;
uniform extern float4x4 mtrxWorld_IT : WorldInverseTranspose ;

// General light attributes
const float4 vLightDir = { 0.0f, 0.0f, 1.0f, 0.0f } ;

// Ambient lighting attributes
uniform extern float4 ligAmbient ;
uniform extern float4 mtrlAmbient ;

// Diffuse lighting attributes
uniform extern float4 ligDiffuse ;
uniform extern float4 mtrlDiffuse ;

OutputVS MainVS( InputVS input )
{

OutputVS output = (OutputVS)0 ;
float3 ambient, diffuse ;
float diffuse_co = 0.0f ;

input.position.w = 1.0f ;
input.normal.w = 0.0f ;

output.position = mul( input.position, mtrxWVP ) ;

input.normal = mul( input.normal, mtrxWorld_IT ) ;
input.normal = normalize( input.normal ) ;

diffuse_co = max( dot( vLightDir, input.normal ), 0 ) ;

ambient = (ligAmbient * mtrlAmbient).rgb ;
diffuse = diffuse_co * (ligDiffuse * mtrlDiffuse).rgb ;

output.diffuse.rgb = ambient + diffuse ;
output.diffuse.a = 1.0f ;

return output ;
}

float4 MainPS( float4 diffuse : COLOR0 ) : COLOR
{

return diffuse ;
}

technique MainFX
{

pass p0
{

AlphaBlendEnable = TRUE ;
Ambient = float4( 1.0f, 1.0f, 1.0f, 1.0f ) ;
CullMode = CCW ;
DestBlend = INVSRCALPHA ;
FillMode = SOLID ;
Lighting = FALSE ;
NormalizeNormals = FALSE ;
ShadeMode = GOURAUD ;
SpecularEnable = FALSE ;
SrcBlend = SRCALPHA ;
StencilEnable = FALSE ;
ZEnable = TRUE ;
ZFunc = LESSEQUAL ;

VertexShader = compile vs_2_0 MainVS() ;
PixelShader = compile ps_2_0 MainPS() ;
}
}



Here is the c++ code snippet detailing how the spheres are rendered:

void CDemo::DrawSpheres( float fCount, float fSpacing )
{

float fRoot = sqrt( fCount ) ;

if( !isWholeNumber( fRoot ) )
return ;

D3DXVECTOR4 mtrlAmbient( 0.5f, 0.0f, 0.0f, 1.0f ) ;
_pEffect->SetVector( _hmtrlAmbient, &mtrlAmbient ) ;

D3DXVECTOR4 mtrlDiffuse( 0.9f, 0.0f, 0.0f, 1.0f ) ;
_pEffect->SetVector( _hmtrlDiffuse, &mtrlDiffuse ) ;

D3DXMATRIX mtrxWorldViewProj, mtrxWorld_IT ;
D3DXMATRIX mtrxWorld, mtrxView, mtrxProj ;

_pGDevice->GetTransform( D3DTS_VIEW, &mtrxView ) ;
_pGDevice->GetTransform( D3DTS_PROJECTION, &mtrxProj ) ;

for( unsigned int j = 0 ; j < (unsigned int)fRoot ; j++ )
{

for( unsigned int k = 0 ; k < (unsigned int)fRoot ; k++ )
{

D3DXMatrixTranslation( &mtrxWorld, k * (5.0f + fSpacing), 2.5f, j * (5.0f + fSpacing) ) ;

mtrxWorldViewProj = mtrxWorld * mtrxView * mtrxProj ;
_pEffect->SetMatrix( _hmtrxWVP, &mtrxWorldViewProj ) ;

D3DXMatrixInverse( &mtrxWorld_IT, 0, &mtrxWorld ) ;
D3DXMatrixTranspose( &mtrxWorld_IT, &mtrxWorld_IT ) ;
_pEffect->SetMatrix( _hmtrxWorld_IT, &mtrxWorld_IT ) ;

_pEffect->CommitChanges() ;
_mshBall->DrawSubset( 0 ) ;
}
}
}

//************************************************************




Now for the screenshots of my 3D application. The first screen shows the grid of spheres rendered when the shader variable, 'vLightDir', is set to {0, 0, 1}. Please note that the front of spheres are supposed to be lit, not the back (which is my problem):

http://celestialtree.org/demo/Demo_01.jpg

This 2nd screenshot shows the application at work while the shader variable, 'vLightDir', is set to {0, 0, -1}. Note how the spheres are lit, very unusual. Also note how the first sphere in the grid (lower left corner) is perfectly lit; very bizarre.

http://celestialtree.org/demo/Demo_02.jpg

Freeman0119
March 23rd, 2009, 11:19 AM
I'm learning HLSL too :)

To me, your problem seems to be the math. the following line in your vertex shader:

diffuse_co = max( dot( vLightDir, input.normal ), 0 ) ;

I think it should be the other way around, as

diffuse_co = max( dot( -vLightDir, input.normal ), 0 ) ;

Because the normals are pointing out of the surface of the sphere, and the surface will be lit most brightly when light direction is opposite of the normal.

Hope this will help ;)

Freeman0119
March 23rd, 2009, 11:22 AM
I still don't get why only the first ball in your second picture is lit correctly, while others still look bizzare... they should all look like the first ball... it seems like the normal vectors of the balls are incorrect...