CylindricalLens

Hi all,
Can someone tell me how i can use a camera with a cylindricallens, i need it for a 360 Roundview.

lens = CylindricalLens()
base.camNode.setLens(lens)

doesn´t work i always get a render error, i tested it with Ortho-lens witch causes no problems.
Thanks all!

A CylindricalLens is a special lens that, because it is curved, cannot be used directly by your graphics card.

It is possible to use it to render with, but you will have to do a two-pass rendering algorithm, in which you ask the graphics card to render with a normal lens, and then use the CylindricalLens to distort the effects of what it rendered.

Panda does have an object that will manage this setup for you automatically. It’s called the NonlinearImager. It’s a bit complicated to use it, though. CMU has used this object to implement their ORB project, which you can see links to on this site.

David

Alas, neither the ORB nor the Interactive Dome project have much to offer for implementation issues. I noticed in the HTML version of the “BVW Reference” that there is a call to “import CubeCam”, which I assume handles the details of adding five cameras to the NonlinearImager and sets a nonlinear lens. However, I was unable to find this module anywhere within Panda. Is this in the standard build? If not, are there any more resources available for showing how to warp the rendered image to look like it’s being viewed through a fisheye lens?
Thanks!

Have a close look at the generated docs for NonlinearImager and related classes. You may also want to download the C++ source and read the nonlinearImager.h file, which may present the same information in a slightly more coherent form than the auto-generated docs.

The following code is some sample code I threw together a few years back; it demonstrates using the NonlinearImager to create a fisheye and cylindrical lens projection of a world in a side-by-side display.

David

# Make the camera into a fisheye camera using the NonlinearImager.

import direct.directbase.DirectStart
from direct.task.Task import Task
from pandac.PandaModules import *

def getSampleGeom():
    smiley = loader.loadModel('smiley')
    smiley.reparentTo(render)

    room = loader.loadModel('environment')
    room.setPos(0, 0, -10)
    room.reparentTo(render)

def showDarkRoom():
    w3 = base.openWindow(gsg = base.win.getGsg())
    wp = WindowProperties()
    wp.setTitle('Dark Room')
    w3.requestProperties(wp)
    
    c3 = NodePath(w3.getDisplayRegion(0).getCamera())
    c3.reparentTo(screens)
    light_np.instanceTo(c3)
    
    c3.node().setScene(screens)
    m = base.dataRoot.attachNewNode(MouseAndKeyboard(w3, 0, 'DarkRoomMouse'))
    t = m.attachNewNode(Trackball('t3'))
    m2c = t.attachNewNode(Transform2SG('m2c'))
    m2c.node().setNode(c3.node())

    return c3

# A node to attach all the screens together.  No real good reason to
# have this, but no good reason not to either.
screens = NodePath('dark_room')

# Let's put a light in the scene, strictly so we can make sense out of
# it if we call showDarkRoom().  (I guess the room's not so dark now.)
light = DirectionalLight('dark_room_light')
light_np = NodePath(light.upcastToPandaNode())
screens.node().setAttrib(LightAttrib.make(LightAttrib.OAdd, light))

# A node parented to the original camera node to hold all the new cube
# face cameras.
cubeCam = base.cam.attachNewNode('cubeCam')

# Define the forward vector for the cube.  We have this up to the
# upper right, so we can get away with using only the front, right,
# and up faces if we want.

cubeForward = (1, 1, 1)
#cubeForward = (0, 1, 0)


class CubeFace:
    def __init__(self, name, view, up, res):
        self.name = name

        # A camera, for viewing the world under render.
        self.camNode = Camera('cam' + self.name)
        self.camNode.setScene(render)
        self.cam = cubeCam.attachNewNode(self.camNode)

        # A projector, for projecting the generated image of the world
        # onto our screen.
        self.projNode = LensNode('proj' + self.name)
        self.proj = screens.attachNewNode(self.projNode)

        # A perspective lens, for both of the above.  The same lens is
        # used both to film the world and to project it onto the
        # screen.
        self.lens = PerspectiveLens()
        self.lens.setFov(92)
        self.lens.setNear(0.1)
        self.lens.setFar(10000)
        self.lens.setViewVector(view[0], view[1], view[2],
                                up[0], up[1], up[2])

        self.camNode.setLens(self.lens)
        self.projNode.setLens(self.lens)

        # Now the projection screen itself, which is tied to the
        # projector.
        self.psNode = ProjectionScreen('ps' + self.name)
        self.ps = self.proj.attachNewNode(self.psNode)
        self.psNode.setProjector(self.proj)

        # Generate a flat, rectilinear mesh to project the image onto.
        self.psNode.regenerateScreen(self.proj, "screen", res[0], res[1], 10, 0.97)

# Define the six faces.
cubeFaces = [
    CubeFace('Right', (1, 0, 0), (0, 0, 1), (10, 40)),
    CubeFace('Back', (0, -1, 0), (0, 0, 1), (40, 40)),
    CubeFace('Left', (-1, 0, 0), (0, 0, 1), (10, 40)),
    CubeFace('Front', (0, 1, 0), (0, 0, 1), (20, 20)),
    CubeFace('Up', (0, 0, 1), (0, -1, 0), (40, 10)),
    CubeFace('Down', (0, 0, -1), (0, 1, 0), (40, 10)),
    ]

# Indices into the above.
cri = 0
cbi = 1
cli = 2
cfi = 3
cui = 4
cdi = 5


# Rotate the cube to the forward axis.
cubeCam.lookAt(cubeForward[0], cubeForward[1], cubeForward[2])
m = Mat4()
m.invertFrom(cubeCam.getMat())
cubeCam.setMat(m)

# Get the base display region.
dr = base.camNode.getDisplayRegion(0)

# Now make a fisheye lens to view the whole thing.
fcamNode = Camera('fcam')
fcam = screens.attachNewNode(fcamNode)
flens = FisheyeLens()
flens.setViewVector(cubeForward[0], cubeForward[1], cubeForward[2],  0, 0, 1)
flens.setFov(180)
flens.setFilmSize(dr.getPixelWidth() / 2, dr.getPixelHeight())
fcamNode.setLens(flens)

# And a cylindrical lens for fun.
ccamNode = Camera('ccam')
ccam = screens.attachNewNode(ccamNode)
clens = CylindricalLens()
clens.setViewVector(cubeForward[0], cubeForward[1], cubeForward[2],  0, 0, 1)
clens.setFov(120)
clens.setFilmSize(dr.getPixelWidth() / 2, dr.getPixelHeight())
ccamNode.setLens(clens)

# Turn off the base display region and replace it with two
# side-by-side regions.
dr.setActive(0)
window = dr.getWindow()
dr1 = window.makeDisplayRegion(0, 0.5, 0, 1)
dr1.setSort(dr.getSort())
dr2 = window.makeDisplayRegion(0.5, 1, 0, 1)
dr2.setSort(dr.getSort())

# Set the fisheye lens on the left, and the cylindrical lens on the right.
dr1.setCamera(fcam)
dr2.setCamera(ccam)

# And create the NonlinearImager to do all the fancy stuff.
nli = NonlinearImager()
nli.addViewer(dr1)
nli.addViewer(dr2)

for face in cubeFaces:
    i = nli.addScreen(face.ps, face.name)
    nli.setSourceCamera(i, face.cam)
    nli.setTextureSize(i, 256, 256)

def hideAll():
    for i in range(6):
        nli.setScreenActive(i, 0)

def showAll():
    for i in range(6):
        nli.setScreenActive(i, 1)

hideAll()
nli.setScreenActive(cfi, 1)
nli.setScreenActive(cri, 1)
nli.setScreenActive(cui, 1)

getSampleGeom()
run()

Outstanding! Thanks so much!

Is it possible to parent a camera to another camera, or is there some way to set up a different view of what a particular camera is seeing? I"m trying to show a 160 degree view on a dome, but to get as many pixels to display as possible, I’d like to truncate the top (rear) quarter of the image, like this:

Moving the fisheye camera, of course, doesn’t affect the shape of the output. Can I have an Orthographic camera pointed at a point “above” the fisheye lens?

You can achieve this effect by playing with the film size and film offset of your fisheye lens–flens in the fisheye.py example.

David

Hello There,

I’m new to Panda3D and quite new to Python.

I’m trying to run the example above to get dome-correction going. I’ve replaced the models for models I actually have. When I run it all I get is white screens.

I notice in the code a reference to showDarkRoom() - but don’t see it run anywhere - anything else I should be doing?

This seems to just be a Linux issue - Running the code on WIndows works fine…We have to sit around and wait for the next release then…or compile from CVS?

Possibly. It may also help to try to update your graphics driver. But before you do that, try putting:

prefer-parasite-buffer 1

in your Config.prc file.

David

Thanks for the response - I tried both, in the order above, but to no avail - what version of the nVidia driver do you support?

Is it a card issue? This is an nVidia Quadro 3450

I’m not aware of any particular problems with that card.

It’s probably worth trying the latest version of Panda, either from CVS (if you’re impatient) or from the next release (if you can wait for it).

Here are some other options you can try in your Config.prc. Try them one at a time.

prefer-parasite-buffer 0
support-render-texture 0
prefer-single-buffer 0

David

OK…

I tried all of these options, separately, in order, setting them to 0 and 1 - no joy.

I then compiled from CVS, and again tried all of these options, still no joy.

I would like to use Panda3D for teaching dome correction at our University and for postgraduate projects, as C++ and OpenSceneGraph is a bit too much for people to take in a module. Should I hold out much hope? Or is Linux not considered a tier 1 platform in terms of support?

Thanks for the support thus far.

It should work on Linux. The fact that it doesn’t surprises me, but not too much–it’s probably broken just because we’ve recently been shaking up the low-level render-to-texture implementation, and we haven’t tested everything around the world yet.

As far as Linux support goes, well, I actually do all of my development on Linux first, and then make sure it works on Windows second. Other people have different preferences.

David

That gives me hope, so it could be a due to when I checked out from CVS? I’ll keep going with windows, and wait for the next release…it hurts though.