Page 1 of 1

light problems with normal maps

Posted: Tue Sep 09, 2008 7:43 am
by HydroNom
Hello,

I have a problem with lighting of an object using normal maps. I try to create a planet which got it's light from a star. When I use EMT_SOLID as material type, then everything is fine. I can rotate the planet and it looks as it should be. The picture below shows some following frames captured:

Image

When I'm using normal or parallax mapping, the light seems to be fixed to a static point on the object surface. When I rotate the planet, the light is moving. When the planet rotates to 180°, the planet is black as I would look to the back side. Then it switches to completely enlighted at next frame. See the next picture:

Image

I only rotate the planet. Camera and light is not moving!

The code is not difficult:

Code: Select all

    vector3df vPPos = vector3df(-120000,102000,-100000);
    ISceneNode *pEarth = smgr->addSphereSceneNode(71500,64,NULL,10,vPPos);
    pEarth->setMaterialTexture(0, driver->getTexture("media/earthmap1k.jpg"));

    video::ITexture* normalMap = driver->getTexture("media/earthbump1k.jpg");
	driver->makeNormalMapTexture(normalMap, 20.0f);
    pEarth->setMaterialTexture(1, normalMap);

    pEarth->setMaterialType(video::EMT_NORMAL_MAP_SOLID);
    scene::ISceneNodeAnimator* anim =
        smgr->createRotationAnimator(core::vector3df(0,0.5f,0));
    pEarth->addAnimator(anim);
    anim->drop();

    ICameraSceneNode *pCam = smgr->addCameraSceneNode(0, core::vector3df(0,0,0), vPPos, 3);
    pCam->setFarValue(3000000);

    scene::ILightSceneNode* light1 = smgr->addLightSceneNode(0, core::vector3df(4000000,0,0),video::SColorf(1.0f, 1.0f, 0.9f, 1.0f), 20000000.0f, 2);
I tried this also with more smaller numbers (planet size of 200), but the effect is the same. What's wrong there?

Posted: Tue Sep 09, 2008 2:07 pm
by sudi
yeah i noticed the same problem thats why i had to write my own parallax shader.

Posted: Wed Sep 10, 2008 9:24 am
by HydroNom
Hm, do you know where the problem is located? In the PerPixelLighting example there is a similar scene, where this problem seems not to be present.

Posted: Wed Sep 10, 2008 11:05 am
by hybrid
The sphere scene node is not using tangent meshes, so you cannot apply the shaders directly. You have to convert the sphere into the proper mesh format first.

Posted: Sun Sep 14, 2008 3:08 pm
by HydroNom
But when I copy the earth sphere code from the example to my testing app, I got the same effect, an this uses tangent meshes???

Posted: Sun Sep 14, 2008 6:01 pm
by dlangdev

Code: Select all

IAnimatedMesh* mesh = smgr->getMesh("sampleMesh.b3d");
scene::IMesh* tangentMesh = smgr->getMeshManipulator()->createMeshWithTangents(mesh->getMesh(0));
node  = smgr->addMeshSceneNode(tangentMesh);


Posted: Mon Sep 15, 2008 1:13 pm
by HydroNom
Yes, this is what I have done. But I discovered another reason of this problem, because this behaviour occurs with the OpenGL driver also with tangent meshes (I had my example on OpenGL for a test). When I switch to DirectX, everything is working...

Posted: Wed Nov 19, 2008 3:07 am
by LoneKiltedNinja
Alright. This is exactly the issue I'm running into, and it's driving me nuts, and I could really use an OpenGL solution since for my particular project (NDA, sorry) DirectX isn't going to be an option.

Obviously OpenGL can handle things correctly, as evidenced by Per Pixel Lighting example 11. But like HydroNom, I've tried dropping the exact same earth model with the exact same normal map into my own project, running the mesh through the tangent generator as suggested here and performed in the example, and it still lights as if the light is attached to and/or counter-rotating about the model. Parallax distortion may also be going a little screwy.

I've tried playing with all the differences I can find between my code and the example code (fixed vs FPS camera, point vs directional light, sphere mesh vs loaded model with and without creating tangent mesh, 1 light vs 2 lights, scale & distance, fog vs no, event receiver vs no, order of instantiating camera/lights/models, even enabling EMF_NORMALIZE_NORMALS on the material) to no avail. Can anyone think of any other important factors in getting the stock shaders working correctly?

Edit: I even dropped my for-real model into the working example and it lit just fine even without running it through the tangent generator (high-end high-poly Maya obj, it probably has the tangents already). There's something I'm obviously not quite catching, but I could swear I've been through the example code, line by line, at least 5 times :P

I'll keep poking, and post the solution if I find it, but until then any help would be greatly appreciated.

Edit2: For the nonbelievers, here's a stripped down example demonstrating the problem. rainbow.png could really be anything, earthbump.bmp and earth.x are as provided in the examples, the whole thing is basically the Hello World example with parallax added. And the problem persists regardless of whether everything is parented to a root dummy node or to 0. If anyone bothers to run this on their own, thank you in advance, and if it actually works correctly on your machine under OpenGL, I'll be quite curious to know, since that means it does actually boil down to an OpenGL implementation issue that the stock parallax example somehow unintentionally circumvents...

Code: Select all

int main()
{
	video::E_DRIVER_TYPE driverType = video::EDT_DIRECT3D9;

	printf("Please select the driver you want for this example:\n"\
		" (a) Direct3D 9.0c\n (b) Direct3D 8.1\n (c) OpenGL 1.5\n"\
		" (d) Software Renderer\n (e) Burning's Software Renderer\n"\
		" (f) NullDevice\n (otherKey) exit\n\n");

	char i;
	std::cin >> i;

	switch(i)
	{
		case 'a': driverType = video::EDT_DIRECT3D9;break;
		case 'b': driverType = video::EDT_DIRECT3D8;break;
		case 'c': driverType = video::EDT_OPENGL;   break;
		case 'd': driverType = video::EDT_SOFTWARE; break;
		case 'e': driverType = video::EDT_BURNINGSVIDEO;break;
		case 'f': driverType = video::EDT_NULL;     break;
		default: return 0;
	}

	IrrlichtDevice *device = createDevice( driverType, core::dimension2d<s32>(640, 480), 16,
			false, false, false, 0);
	device->setWindowCaption(L"Hello World! - Irrlicht Engine Demo");

	video::IVideoDriver* driver = device->getVideoDriver();
	scene::ISceneManager* smgr = device->getSceneManager();
	gui::IGUIEnvironment* guienv = device->getGUIEnvironment();

	guienv->addStaticText(L"Hello World! This is the Irrlicht Software renderer!",
		core::rect<s32>(10,10,260,22), true);

	driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT, true);
	video::E_MATERIAL_TYPE type = video::EMT_PARALLAX_MAP_SOLID;
	// set up for parallax, etc.
     video::IMaterialRenderer* renderer = driver->getMaterialRenderer(type);
	if (renderer && renderer->getRenderCapability() != 0){
          cout<<"Can't do parallax mapping on this hardware."<<endl;
          type = video::EMT_NORMAL_MAP_SOLID;
          renderer = driver->getMaterialRenderer(type);
     }
     if (renderer && renderer->getRenderCapability() != 0){
          cout<<"Can't even do normal mapping."<<endl;
          type = video::EMT_SOLID;
          renderer = driver->getMaterialRenderer(type);
     }

     scene::ISceneNode *root = smgr->addEmptySceneNode();

     scene::ILightSceneNode *light1 =
		smgr->addLightSceneNode(root, core::vector3df(0,30,-40),
		video::SColorf(0.8f,0.8f,0.8f,0.0f), 500.0f);

     //scene::IAnimatedMesh *sphereMesh = smgr->addSphereMesh("",5,16,16);
     scene::IAnimatedMesh *sphereMesh = smgr->getMesh("earth.x");
     scene::IMeshManipulator *manipulator = smgr->getMeshManipulator();
	scene::IMesh *tangentMesh = manipulator->createMeshWithTangents(sphereMesh->getMesh(0));
	scene::IMeshSceneNode* node2 = smgr->addMeshSceneNode(
          tangentMesh,
          root,//parent
          -1,//id
          core::vector3df(0,25,-20),//position
          core::vector3df(0,180,0),//rotation
          core::vector3df(1,1,1),//scale
          false); // still add if mesh pointer NULL
     node2->setMaterialType(type); // set the type for all materials
     node2->getMaterial(0).setTexture(0, driver->getTexture("rainbow.png"));
     video::ITexture* normalMap = driver->getTexture("earthbump.bmp");
	driver->makeNormalMapTexture(normalMap, 1.0f);
     node2->getMaterial(0).setTexture(1, normalMap);
     node2->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, true);
     
     scene::ISceneNodeAnimator* anim =
         smgr->createRotationAnimator(core::vector3df(0,0.2f,0));
     node2->addAnimator(anim);
     anim->drop();


	smgr->addCameraSceneNode(root, core::vector3df(0,30,-40), core::vector3df(0,5,0));

	while(device->run())
	{
		driver->beginScene(true, true, video::SColor(255,100,101,140));

		smgr->drawAll();
		guienv->drawAll();

		driver->endScene();
	}

	device->drop();

	return 0;
}