odd lighting bug

I’ve encountered this bug many times in the past, but never taken the time to nail it down. The bug is this: flats created by CardMaker (and perhaps other flats as well?) are lit differently based on other objects in the world moving on and off screen. More recently I’ve seen this behavior simply from other objects moving around, and not actually going off screen.

This bug is only exhibited in OpenGL mode (and I’ve only tested on Windows), although DirectX seems to have its own problems with lighting.

Here’s some sample code:

import math

import direct.directbase.DirectStart
from direct.showbase.ShowBaseGlobal import *

class World( DirectObject.DirectObject ):
	def __init__( self ):
		base.mouseInterfaceNode.setPos( 0, 20, -2 )
		base.setBackgroundColor( Vec4( 0 ) )
		self.accept( "f9", base.screenshot )

		self.teapot = loader.loadModelCopy( "teapot" )
		self.teapot.reparentTo( render )

		cm = CardMaker( "cm" )
		self.card = NodePath( cm.generate() )
		self.card.setScale( 4, 1, 4 )
		self.card.setPos( -2, 0, 0 )
		self.card.reparentTo( render )

		self.ambientLight = AmbientLight( "ambientLight" )
		self.ambientLight.setColor( Vec4( 0.2, 0.2, 0.2, 1.0 ) )
		self.ambientLightNodePath = render.attachNewNode( self.ambientLight.upcastToPandaNode() )

		self.directionalLight = DirectionalLight( "directionalLight" )
		self.directionalLight.setColor( Vec4( 0.5, 0.5, 0.5, 1 ) )
		self.directionalLightNodePath = render.attachNewNode( self.directionalLight.upcastToPandaNode() )
		self.directionalLightNodePath.setHpr( -40, -50, 0 )

		self.pointLight = PointLight( "pointLight" )
		self.pointLight.setColor( Vec4( 0.5, 0.5, 0.5, 1 ) )
		self.pointLightNodePath = render.attachNewNode( self.pointLight.upcastToPandaNode() )
		self.pointLightNodePath.setPos( -5, -5, 3 )

		render.setLight( self.ambientLightNodePath )
		render.setLight( self.directionalLightNodePath )
		render.setLight( self.pointLightNodePath )

		taskMgr.add( self.circleTask, "circleTask" )

	def circleTask( self, task ):
		self.teapot.setPos( 10.0*math.sin( task.time ), 10.0*math.cos( task.time ), 0 )
		return Task.cont

world = World()


When running the code, the square is lit more brightly when the teapot moves past the right edge of the screen:

and is lit more darkly when it is not past the right edge of the screen:

I’m not sure why it only happens based on the right edge. I’ve seen it occur at other times when objects have gone off other sides of the screen.

This occurs for both the PointLight and the DirectionalLight (you can comment each out independently).

Anyone have any ideas?

Geometry generated by the CardMaker doesn’t have normals. When you enable lighting without normals, the result is undefined; in OpenGL it inherits the normals from some other object in the scene (which is why it behaves differently according to what else is onscreen).

The reason that CardMaker doesn’t generate normals is that it simply never occurred to me that anyone would want to apply lighting to a flat card. Do you really want to light the card, or is it just getting lit by accident because you’re lighting the whole scene? In any case, I will add an option to CardMaker to generate normals (similar to setHasUvs()).


Ahh, that makes a lot of sense.

In this case, we’re using cards to create walls and floors of arbitrary sizes and setting the uvs to use the appropriate portion of a larger texture. This way we can make modular levels that can be modified with an in-game level editor rather than requiring each level to be created and modified in Maya. We want those walls and floors to be lit.

Thanks very much for putting that in.