wierd flicker when using lighting

Started by
10 comments, last by godmodder 18 years, 1 month ago
I have a cube centered at the world origin. Using gouraud shading. Light direction is positive z. Using specular. (I have a camera I can move about the world using my keyboard and mouse.) When I'm facing the cube, the lighting works fine. As I rotate to the left or right of the cube (the camera moves, the cube does not rotate), the lighting becomes unpredictable. When I look left or right just a few degrees, the specular lighting becomes very eratic. One degree left it's shiny. One degree right it isn't. As I rotate about the face it flashes as the specular highlights appear in one frame and disappear in the next. The normals are all correct. Any ideas?
Advertisement
Ok, let me ask this another way.

If a face is parallel with the directional light source, how can you see specular reflection from it at any angle? Because that's what's happening.

...I've tinkered with it some more, and the problem seems to come when the face is perfectly parallel with the direction of the light source.

When using point lights, works fine.

Tested it out using the rasterizer, and it works the same.

So this suggests to me that I need to somehow avoid situations where the face normal is perpendicular to the directional light source. Yes/No?

And a link to a similar question asked about OpenGL:
http://www.gamedev.net/community/forums/topic.asp?topic_id=208357

[Edited by - SparkyFlooner on March 5, 2006 4:41:56 PM]
Anyone?
Ok, here's some sample code that demonstrates the problem. It ain't pretty, but it gets the job done...

2 sides of a cube are created, centered at the world origin.

The front face faces the -z axis.
The left face faces the -x axis.
The light source is infinite and shines towards the positive-z axis.

There is a camera - controls are:
CTRL + A: look left
CTRL + D: look right
CTRL + W: look up
CTRL + S: look down
W: move towards
S: move away
D: move right
A: move left
SHIFT + W: move up
SHIFT + S: move down

It's tied to the frame rate, but it's late and I'm tired so it's good enough.

You can rotate the cube using the left and right arrow keys.

When you run the app, you're position down the -x axis looking at the origin. The cube face you're looking at (the left face) is parallel to the direction of the light. Only specular reflections are returned, to emphasize the problem. Looking up, down, left, or right with the camera should cause the 'flicker' I'm talking about. For some reason the left face, which doesn't receive any light, will return a specular reflection intermittently as you look up, down, left, and right with the camera.

Don't rotate the cube until you've observed the problem as it's initially set up. Just look around with the camera.

=============================================================================


#include <windows.h>
#include <d3d9.h>
#include <d3dx9.h>
#include <dxut.h>

struct VERTEX {
float x, y, z;
float nx, ny, nz;
};

#define VERTEX_FVF (D3DFVF_XYZ | D3DFVF_NORMAL)

IDirect3DVertexBuffer9* g_pVB;
float g_rot = 0.0f;
D3DXVECTOR3 g_pos(-5.0f, 0.0f, 0.0f);
D3DXVECTOR3 g_right(0.0f, 0.0f, -1.0f);
D3DXVECTOR3 g_up(0.0f, 1.0f, 0.0f);
D3DXVECTOR3 g_look(1.0f, 0.0f, 0.0f);
float g_rx = D3DX_PI / 2;
float g_ry = 0.0f;

HRESULT CALLBACK DeviceCreated( IDirect3DDevice9* pDevice, const D3DSURFACE_DESC* surface, void* )
{
VERTEX vertices[] = {
// front
{ -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f },
{ -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f },
{ 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f },
{ -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f },
{ 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f },
{ 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f },
// left size
{ -1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 0.0f },
{ -1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f },
{ -1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f },
{ -1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 0.0f },
{ -1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f },
{ -1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f },
};

pDevice->CreateVertexBuffer(sizeof(vertices), D3DUSAGE_WRITEONLY, VERTEX_FVF, D3DPOOL_MANAGED, &g_pVB, NULL);
VERTEX* pBuffer;
g_pVB->Lock(0, 0, reinterpret_cast<void**>(&pBuffer), 0);
memcpy(pBuffer, vertices, sizeof(vertices));
g_pVB->Unlock();

return S_OK;
}

void RecalcLook(IDirect3DDevice9* pDevice)
{
D3DXVec3Normalize(&g_look, &g_look);
D3DXVec3Cross(&g_up, &g_look, &g_right);
D3DXVec3Normalize(&g_up, &g_up);
D3DXVec3Cross(&g_right, &g_up, &g_look);
D3DXVec3Normalize(&g_right, &g_right);

float x = -D3DXVec3Dot(&g_right, &g_pos);
float y = -D3DXVec3Dot(&g_up, &g_pos);
float z = -D3DXVec3Dot(&g_look, &g_pos);

D3DXMATRIX view;
view(0, 0) = g_right.x;
view(0, 1) = g_up.x;
view(0, 2) = g_look.x;
view(0, 3) = 0.0f;

view(1, 0) = g_right.y;
view(1, 1) = g_up.y;
view(1, 2) = g_look.y;
view(1, 3) = 0.0f;

view(2, 0) = g_right.z;
view(2, 1) = g_up.z;
view(2, 2) = g_look.z;
view(2, 3) = 0.0f;

view(3, 0) = x;
view(3, 1) = y;
view(3, 2) = z;
view(3, 3) = 1.0f;

pDevice->SetTransform(D3DTS_VIEW, &view);
}

HRESULT CALLBACK DeviceReset( IDirect3DDevice9* pDevice, const D3DSURFACE_DESC* surface, void* )
{
pDevice->SetRenderState(D3DRS_LIGHTING, TRUE);
pDevice->SetRenderState(D3DRS_NORMALIZENORMALS, TRUE);
pDevice->SetRenderState(D3DRS_SPECULARENABLE, TRUE);
pDevice->SetRenderState(D3DRS_SPECULARMATERIALSOURCE, D3DMCS_MATERIAL);

RecalcLook(pDevice);

D3DXMATRIX proj;
D3DXMatrixPerspectiveFovLH(
&proj,
D3DX_PI / 4,
(float)surface->Width / (float)surface->Height,
1.0f,
1000.0f);

pDevice->SetTransform(D3DTS_PROJECTION, &proj);

D3DLIGHT9 l;
::ZeroMemory(&l, sizeof(l));
l.Type = D3DLIGHT_DIRECTIONAL;
l.Ambient.a = l.Ambient.b = l.Ambient.g = l.Ambient.r = 1.0f;
l.Diffuse = l.Ambient;
l.Specular = l.Ambient;
l.Direction = D3DXVECTOR3(0.0f, 0.0f, 1.0f);

pDevice->SetLight(0, &l);
pDevice->LightEnable(0, TRUE);

D3DMATERIAL9 m;
::ZeroMemory(&m, sizeof(m));
m.Specular.a = m.Specular.b = m.Specular.g = m.Specular.r = 1.0f;
m.Power = 5.0f;

pDevice->SetMaterial(&m);

return S_OK;
}

void CALLBACK DeviceDestroyed(void*)
{
g_pVB->Release();
}

void CALLBACK Animate( IDirect3DDevice9* pDevice, double, float, void* )
{
float old = g_rot;

if ( GetAsyncKeyState(VK_LCONTROL) & 0x800f ) {
if ( GetAsyncKeyState('A') & 0x800f ) {
g_rx -= 0.001f;
}
if ( GetAsyncKeyState('D') & 0x800f ) {
g_rx += 0.001f;
}
if ( GetAsyncKeyState('W') & 0x800f ) {
g_ry += 0.001f;
}
if ( GetAsyncKeyState('S') & 0x800f ) {
g_ry -= 0.001f;
}

g_right = D3DXVECTOR3(1.0f, 0.0f, 0.0f);
g_up = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
g_look = D3DXVECTOR3(0.0f, 0.0f, 1.0f);

D3DXMATRIX m;
D3DXMatrixRotationAxis(&m, &g_up, g_rx);

D3DXVec3TransformCoord(&g_right, &g_right, &m);
D3DXVec3TransformCoord(&g_look, &g_look, &m);

D3DXMatrixRotationAxis(&m, &g_right, g_ry);

D3DXVec3TransformCoord(&g_up, &g_up, &m);
D3DXVec3TransformCoord(&g_look, &g_look, &m);

RecalcLook(pDevice);
}
else {
if ( GetAsyncKeyState('A') & 0x800f ) {
g_pos -= g_right * 0.01f;
RecalcLook(pDevice);
}
if ( GetAsyncKeyState('D') & 0x800f ) {
g_pos += g_right * 0.01f;
RecalcLook(pDevice);
}
if ( GetAsyncKeyState('W') & 0x800f) {
if (GetAsyncKeyState(VK_LSHIFT) & 0x800f) {
g_pos += g_up * 0.01f;
RecalcLook(pDevice);
}
else {
g_pos += g_look * 0.01f;
RecalcLook(pDevice);
}
}
if ( GetAsyncKeyState('S') & 0x800f) {
if (GetAsyncKeyState(VK_LSHIFT) & 0x800f) {
g_pos -= g_up * 0.01f;
RecalcLook(pDevice);
}
else {
g_pos -= g_look * 0.01f;
RecalcLook(pDevice);
}
}
}
if ( GetAsyncKeyState(VK_RIGHT) ) {
g_rot += 0.001f;
}
if ( GetAsyncKeyState(VK_LEFT) ) {
g_rot -= 0.001f;
}

if (g_rot != old) {
D3DXMATRIX m;
D3DXMatrixRotationY(&m, g_rot);

pDevice->SetTransform(D3DTS_WORLD, &m);
}
}

void CALLBACK Render( IDirect3DDevice9* pDevice, double, float, void* )
{
pDevice->Clear(0, NULL, D3DCLEAR_ZBUFFER | D3DCLEAR_TARGET, D3DCOLOR_ARGB(255, 0, 0, 0), 1.0f, 0);

pDevice->BeginScene();

pDevice->SetFVF(VERTEX_FVF);
pDevice->SetStreamSource(0, g_pVB, 0, sizeof(VERTEX));
pDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 4);

pDevice->EndScene();
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR cmdLine, INT cmdShow)
{
DXUTInit();

DXUTSetCallbackDeviceCreated(DeviceCreated);
DXUTSetCallbackDeviceDestroyed(DeviceDestroyed);
DXUTSetCallbackFrameRender(Render);
DXUTSetCallbackDeviceReset(DeviceReset);
DXUTSetCallbackFrameMove(Animate);

DXUTCreateWindow();
DXUTCreateDevice();

DXUTMainLoop();
}

[Edited by - SparkyFlooner on March 6, 2006 8:49:39 AM]
bump
come on gang. help me out here.

bump.
Guess what gang! It doesn't work when the point light is on the same plane as the face!

Lending more credibility to the idea of a glitch occuring when the direction of the light is perfectly perpendicular to the face.



Unfortunately...no one will confirm or deny this....
bump
Wow. Nobody looked into this thread. I'm pretty lazy about updating SDKs. I don't have one with DXUT installed, so I can't actually run this code, but...

Specular lighting depends on the eyevector, and moving the camera will alter the eyevector, so moving the camera causing the flicker is to be expected. Specular lighting is quite picky. It's a highlight around a very small set of angles. Per vertex, specular lighting is pretty crap, as you can have the highlight flicker away between vertices. I imagine it's the cause of the flickering you're seeing. If you set the power to 1.0 it should behave much like diffuse lighting, but at different angles to the light.

Another render state that affects the fixed pipe specular is D3DRS_LOCALVIEWER. You can try playing with it to see if it helps.

Specular with a low power can look okay per vertex, but quickly starts to look pretty bad as the power increases, as the highlight falls between vertices rather than directly on them.
I run your code, but couldn't see whats the problem.

I have similar weird problem, camera is looking down on a square with texture. (Square is made of two triangles, texture is jpg file, all as simple as can be).
Camera is looking at (0,0,0)
If it is positioned right above (0,y,0) square is gone.
if we move it only inch left or right (0,y,1) and in all other positions square is there.

never figured out the problem, just moved the camera.

This topic is closed to new replies.

Advertisement