Lighting must not move

I’m new with Panda3D and try simple example - rotating box, looked and lighted by side. But the light spot must be always infront of the camera, why it is moves with the rotating box, as if the light is child to the box node (it’s NOT!):


from direct.showbase.ShowBase import ShowBase
from direct.task import Task
from panda3d.core import PointLight
from geomgen import MakeBox


class MyApp(ShowBase):
    """app"""

    def __init__(self):
        super().__init__()

        light_node = PointLight("point_light")
        light = self.cam.attach_new_node(light_node)
        self.render.set_light(light)

        self.holder = self.render.attach_new_node('holder')
        self.box = self.holder.attach_new_node(MakeBox().generate())
        self.box.setPos(-0.5, -0.5, -0.5)
        self.box.set_render_mode_filled_wireframe((1., 0., 0., 1.))

        self.cam.setPos(0, -5, 0)

        self.taskMgr.add(self.rot_box_task, 'rot_box_task')

    def rot_box_task(self, task):
        '''rot'''
        alfa = task.time * 50.0
        self.holder.setHpr(alfa, alfa*2, alfa*3)
        return Task.cont


app = MyApp()
app.run()

and the model is generated by this (geomgen.py file):

# -*- coding: utf-8 -*-
from panda3d.core import (GeomVertexData, GeomVertexFormat, GeomVertexWriter,
                          Geom, GeomTriangles, GeomNode)

class MakeBox:
    '''box'''

    def generate(self):
        vdata = GeomVertexData('name', GeomVertexFormat.getV3(), Geom.UHStatic)
        vdata.setNumRows(8)

        vertex = GeomVertexWriter(vdata, 'vertex')
        #   6 7
        #   4 5
        # 2 3
        # 0 1
        vertex.add_data3(0, 0, 0)
        vertex.add_data3(1, 0, 0)
        vertex.add_data3(0, 0, 1)
        vertex.add_data3(1, 0, 1)
        vertex.add_data3(0, 1, 0)
        vertex.add_data3(1, 1, 0)
        vertex.add_data3(0, 1, 1)
        vertex.add_data3(1, 1, 1)

        prim = GeomTriangles(Geom.UHStatic)
        prim.add_vertices(0, 1, 3)
        prim.add_vertices(0, 3, 2)

        prim.add_vertices(1, 5, 7)
        prim.add_vertices(1, 7, 3)

        prim.add_vertices(5, 4, 6)
        prim.add_vertices(5, 6, 7)

        prim.add_vertices(4, 0, 2)
        prim.add_vertices(4, 2, 6)

        prim.add_vertices(2, 3, 7)
        prim.add_vertices(2, 7, 6)

        prim.add_vertices(1, 0, 4)
        prim.add_vertices(1, 4, 5)

        geom = Geom(vdata)
        geom.addPrimitive(prim)

        node = GeomNode('gnode')
        node.addGeom(geom)

        return node

I think that the problem may be that lighting relies on normals, and your box-generation code doesn’t appear to include any.

Tinkering with your example code, I find that adding some basic normals directed away from the centre of the box produces results rather more as one might expect from lighting.

It’s not true, because the light on the sides must be always dark or something, but it changes accordingly. The problem is only that the most light spot must be always statically pointing to the camera (in front, the center of the screen), but it rotates and is fixed to the box, see:

But without normals, how would the engine “know” which side is facing the camera?

And, as I said, actually adding normals solves the problem, which does seem to support the idea that it’s a lack of normals that’s causing it.

[edit]
Specifically, try changing the first part of your “generate” method from this:

def generate(self):
        vdata = GeomVertexData('name', GeomVertexFormat.getV3(), Geom.UHStatic)
        vdata.setNumRows(8)

        vertex = GeomVertexWriter(vdata, 'vertex')
        #   6 7
        #   4 5
        # 2 3
        # 0 1
        vertex.add_data3(0, 0, 0)
        vertex.add_data3(1, 0, 0)
        vertex.add_data3(0, 0, 1)
        vertex.add_data3(1, 0, 1)
        vertex.add_data3(0, 1, 0)
        vertex.add_data3(1, 1, 0)
        vertex.add_data3(0, 1, 1)
        vertex.add_data3(1, 1, 1)

to this:

def generate(self):
        vdata = GeomVertexData('name', GeomVertexFormat.getV3n3(), Geom.UHStatic)
        vdata.setNumRows(8)

        vertex = GeomVertexWriter(vdata, 'vertex')
        normal = GeomVertexWriter(vdata, 'normal')
        #   6 7
        #   4 5
        # 2 3
        # 0 1
        vertex.add_data3(0, 0, 0)
        normal.add_data3(-1, -1, -1)
        vertex.add_data3(1, 0, 0)
        normal.add_data3(1, -1, -1)
        vertex.add_data3(0, 0, 1)
        normal.add_data3(-1, -1, 1)
        vertex.add_data3(1, 0, 1)
        normal.add_data3(1, -1, 1)
        vertex.add_data3(0, 1, 0)
        normal.add_data3(-1, 1, -1)
        vertex.add_data3(1, 1, 0)
        normal.add_data3(1, 1, -1)
        vertex.add_data3(0, 1, 1)
        normal.add_data3(-1, 1, 1)
        vertex.add_data3(1, 1, 1)
        normal.add_data3(1, 1, 1)

Note:

  • The change to the GeomVertexFormat
  • The addition of a new GeomVertexWriter for the normals
  • And the addition of normal-data along with the vertex-data

Thank, you! With normals, it’s seems more adequate:

I didn’t expected that such mathematical detail can cause so different, UNEXPECTED, visual effect! The computer graphics is far from just think as real world. But I didn’t succeed to find more simple library than Panda3D, so I continue experimenting.

P.S. I thought that the counter-clockwise vertex arrangement determines default normals. Also I see that the flat side must has more triangles, with more normals, because now its lighting is not proper. I will experiment.
Thank, you, again!

1 Like

I’m glad that you got it working! :slight_smile:

It doesn’t determine the normal, but it does determine which side of a polygon is considered to be “facing the camera” for the purposes of backface-culling.

Especially as there’s nothing preventing normals from facing some direction other than directly out from their surface. For example, one might want the normals for blades of grass to point upwards, even if the faces of the grass-geometry point along the horizontal.

Now, some maths could be applied to generate normals from the relative arrangements of vertices–but that’s seldom done, I think.

(I imagine that it’s both more efficient, and–as suggested just above–more flexible to have normals be specified separately.)

Indeed, this is a common problem with low-poly models under fixed-function lighting.

However! The solution isn’t necessarily more polygons!

Try activating the auto-shader to use modern, shader-based lighting, and you should find that it looks much improved–even with as few polygons as you have there!

See this manual page for more:
https://docs.panda3d.org/1.10/python/programming/shaders/shader-generator