Panda3D Manual: Lighting
<<prev | top | next>> |
## Lighting Basics In the real world, if you put a light in a room, objects in that room are illuminated. For example, if you put a table lamp in your living room, that lamp automatically illuminates your sofa and your chair. In a 3D engine like Panda3D, lights don't illuminate things automatically. Instead, you must tell the chair and the sofa to be illuminated by the lamp. So to reiterate, lighting a scene in Panda3D consists of two steps: 1. Creating lights, and positioning them within the scene. 2. Telling the other objects to be illuminated by the lights. Panda3D defines four different kinds of light objects: point, directional, ambient, and spotlight. Each of these is a node that should be attached somewhere within the scene graph. Like anything you put into the scene, lights have a position and orientation, which is determined by the basic scene graph operations like `setPos()`, `setHpr()`, etc. The `lookAt()` method is particularly useful for pointing spotlights and directional lights at a particular object. The following code inserts a directional light into the scene:
    dlight = DirectionalLight('my dlight')
    dlnp = render.attachNewNode(dlight)
Note that, unlike a real, physical light bulb, the light objects are not themselves directly visible. Although you can't see a Panda light itself, you can see the effect it has on the geometry around it. If you want to make a light visible, one simple trick is to load a simple model (like a sphere) and parent it directly to the light itself. Creating the light and putting it in the scene graph doesn't, by itself, have any visible effect. Your next step is to tell some object to be illuminated by the light. To do this, use the `nodePath.setLight()` method, which turns on the light for the indicated NodePath and everything below it in the scene graph. In the simplest case, you want all of your lights to illuminate everything they can, so you turn them on at render, the top of the scene graph:
    render.setLight(plnp)
You can remove the light setting from render:
    render.clearLight(plnp)
You could also apply the `setLight()` call to a sub-node in the scene graph, so that a given light only affects a particular object or group of objects:
    sofa.setLight(plnp)
Note that there are two (or more) different NodePaths involved here: the NodePath of the light itself, which defines the position and/or orientation of the light, and the NodePath(s) on which you call `setLight()`, which determines what subset of the scene graph the light illuminates. There's no requirement for these two NodePaths to be related in any way. ## Lots of Lights: Performance Implications Each light slows down rendering a little. Using a half-dozen lights to illuminate an object is no problem at all. However, if you were to use a hundred lights to illuminate an object, that object would render slowly. Because of this, when you create a big virtual world, you need to pick and choose which lights affect which objects. For example, if you had a dungeon containing a hundred torches, it would not be practical to tell every object to be illuminated by every torch. Instead, for each object in the dungeon, you would want to search for the three or four nearest torches, and tell the object to be illuminated only by those three or four torches. When per-pixel lighting is enabled, lights are considerably more costly. ## Colored Lights All lights have a color, which is specified by `light.setColor(VBase4(r, g, b, a))`. The default color is full white: `setColor(VBase4(1, 1, 1, 1))`. The alpha component is largely irrelevant. Note: The R, G, B values can be larger than 1, if you want brighter lights! However, you can't use lighting to make a model brighter than its texture color. ## Point Lights Point lights are the easiest kind of light to understand: a point light simulates a light originating from a single point in space and shining in all directions, like a very tiny light bulb. A point light's position is important, but its orientation doesn't matter.
    plight = PointLight('plight')
    plight.setColor(VBase4(0.2, 0.2, 0.2, 1))
    plnp = render.attachNewNode(plight)
    plnp.setPos(10, 20, 0)
    render.setLight(plnp)
## Attenuation You can set the attenuation coefficients, which causes the light to drop off gradually with distance. There are three attenuation coefficients: A, B, C.
    plight.setAttenuation(Point3(A, B, C))
Setting coefficients A and B to 0 and C between 0 and 1 works well.
    plight.setAttenuation(Point3(0, 0, 0.5))
## Directional Lights A directional light is an infinite wave of light, always in the same direction, like sunlight. A directional light's position doesn't matter, but its orientation is important. The default directional light is shining down the forward (+Y) axis; you can use `nodePath.setHpr()` or `nodePath.lookAt()` to rotate it to face in a different direction.
    dlight = DirectionalLight('dlight')
    dlight.setColor(VBase4(0.8, 0.8, 0.5, 1))
    dlnp = render.attachNewNode(dlight)
    dlnp.setHpr(0, -60, 0)
    render.setLight(dlnp)
## Ambient Lights An ambient light is used to fill in the shadows on the dark side of an object, so it doesn't look completely black. The light from an ambient light is uniformly distributed everywhere in the world, so the ambient light's position and orientation are irrelevant. Usually you don't want to create an ambient light without also creating one of the other kinds of lights, since an object illuminated solely by ambient light will be completely flat shaded and you won't be able to see any of its details. Typically, ambient lights are given a fairly dark gray color, so they don't overpower the other lights in the scene.
    alight = AmbientLight('alight')
    alight.setColor(VBase4(0.2, 0.2, 0.2, 1))
    alnp = render.attachNewNode(alight)
    render.setLight(alnp)
## Spotlights Spotlights represent the most sophisticated kind of light. A spotlight has both a point and a direction, and a field-of-view. In fact, a spotlight contains a lens, just like a camera does; the lens should be a PerspectiveLens and is used to define the area of effect of the light (the light illuminates everything within the field of view of the lens). Note that the English word "spotlight" is one word, as opposed to the other kinds of lights, which are two words. Thus, the class name is correctly spelled "Spotlight", not "SpotLight".
    slight = Spotlight('slight')
    slight.setColor(VBase4(1, 1, 1, 1))
    lens = PerspectiveLens()
    slight.setLens(lens)
    slnp = render.attachNewNode(slight)
    slnp.setPos(10, 20, 0)
    slnp.lookAt(myObject)
    render.setLight(slnp)
## Putting it all Together Here is an example of lighting. There are an ambient light and two directional lights lighting the scene, and a green ambient light that only affects one of the pandas.
    import direct.directbase.DirectStart
    from pandac.PandaModules import *

    # Put two pandas in the scene, panda x and panda y.
    x= loader.loadModel("panda")
    x.reparentTo(render)
    x.setPos(10,0,-6)

    y= loader.loadModel("panda")
    y.reparentTo(render)
    y.setPos(-10,0,-6)

    # Position the camera to view the two pandas.
    base.trackball.node().setPos(0, 60, 0)

    # Now create some lights to apply to everything in the scene.

    # Create Ambient Light
    ambientLight = AmbientLight( 'ambientLight' )
    ambientLight.setColor( Vec4( 0.1, 0.1, 0.1, 1 ) )
    ambientLightNP = render.attachNewNode( ambientLight.upcastToPandaNode() )
    render.setLight(ambientLightNP)

    # Directional light 01
    directionalLight = DirectionalLight( "directionalLight" )
    directionalLight.setColor( Vec4( 0.8, 0.2, 0.2, 1 ) )
    directionalLightNP = render.attachNewNode( directionalLight.upcastToPandaNode() )
    # This light is facing backwards, towards the camera.
    directionalLightNP.setHpr(180, -20, 0)
    render.setLight(directionalLightNP)

    # Directional light 02
    directionalLight = DirectionalLight( "directionalLight" )
    directionalLight.setColor( Vec4( 0.2, 0.2, 0.8, 1 ) )
    directionalLightNP = render.attachNewNode( directionalLight.upcastToPandaNode() )
    # This light is facing forwards, away from the camera.
    directionalLightNP.setHpr(0, -20, 0)
    render.setLight(directionalLightNP)

    # Now attach a green light only to object x.
    ambient = AmbientLight('ambient')
    ambient.setColor(Vec4(.5,1,.5,1))
    ambientNP = x.attachNewNode(ambient.upcastToPandaNode())

    # If we did not call setLightOff() first, the green light would add to
    # the total set of lights on this object.  Since we do call
    # setLightOff(), we are turning off all the other lights on this
    # object first, and then turning on only the green light.
    x.setLightOff()
    x.setLight(ambientNP)

    #run the example
    run()
<<prev | top | next>> |