Straightforward (Linear) Panorama View

Long time listener, first time caller says Hi!

I’ve been helped by this forum a lot over some time now, and before diving into my problem, would like to thank all those unsung heroes who’ve rescued me in the past; you know who you are :wink:

Now, my problem: what I basically would like to do is stretching my (horizontal) FOV angle to an arbitrarily wide value, say 320° for example. I do not wish for a fisheye effect (i.e., curvature of lines), but just a simple stretching of the viewing frustum.

Now as I understand it, it’s not possible to simply call glPerspective with a parameter set which would indicate such a FOV, since something like


glPerspective( 40.0, 320.0/40.0, <near>, <far> )

stretches my image, but cleary does not what I wand it to do, i.e., I don’t see the environment behind me.

So, question #1: Is there a way to just adapt basic viewing frustum setup steps to stretch the horizontal viewing frustum up to 320 degrees? As I don’t want a curvature effect, I would have assumed this to be possible…?

Next, my current plan of attack is to turn to framebuffer objects, similar to how cube maps are generated: render four different 80° views, and paste them together into a 320° image. Thus my second (possibly newbie-ish) question: how can I draw an FBO texture directly into the framebuffer? I don’t want to just ‘hang’ the FBO textures in front of the camera, but to directly paste the four FBO images into the framebuffer … which should be possible, given the right calls?

Cheers!

Now, my problem: what I basically would like to do is stretching my (horizontal) FOV angle to an arbitrarily wide value, say 320° for example. I do not wish for a fisheye effect (i.e., curvature of lines), but just a simple stretching of the viewing frustum.

Unfortunately, this is not possible.
A good visual explanation here :
http://strlen.com/gfxengine/fisheyequake/compare.html
Your case exactly, for a wide cylindrical projection :
http://strlen.com/gfxengine/panquake/

There may be ways to do it without apparent curvature of lines, but that will depend on the screen geometry and viewer position.

Thanks for the links! So just using basic calls like glPerspective wont work then.

However, rendering several views from the same position and gluing them together afterward should be a viable approach?
Is there an easy way to paste a framebuffer object’s texture directly into the main framebuffer? (And thus avoiding having to place the texture in front of the camera via educated guessing?)

*Edit: Ok, the several-views idea also doesn’t work, at least not as I would like it to. On to the next idea …

*edit 2: Alright, what I had in mind can actually be easily done with just using orthographic instead of a perspective projection matrix. Just fiddling all into the framework now…

Also:
Question #2, anyone? How to paste data from different FBO textures directly into the main framebuffer? Any advice would still be very much appreciated!

There is GL_EXT_framebuffer_blit or you use an ortho camera and draw a quad for each fbo texture. If you use glOrtho(0.f, w, 0.f, h, -1.f, 1.f) where w,h are width and height of your window you can basically use pixels as coordinates for the vertices of the quad.

But I recommend rendering to a cube map. Then you can custom sample it to the fullscreen quad, to allow whatever per pixel projection scheme you can imagine.

The rendering to a cube map can be done with a single pass, with geometry shader and high end hardware it may even be faster than 6 passes.

I agree. After some fiddling with orthogonal projections, cube mapping seems the most attractive way to go. However, first tests deliver somewhat unintuitive results.

When rendering simple 90° view cones and pasting them together, I get the following result:

And while every single image of the four seems correct, the overall expression is rather weird. First of all the perspective seems just wrong to mee, and second - and more obvious - the edges do not fit seamlessly together (see marker).

So I tried a more brute force approach, which basically renders one degree slices, and pastes all of them together - i.e., a horizontal FOV of 320° degrees requires 320 render passes. The result looks like this:

Here the angles seem much more pleasing, but there are still artefacts (marked).

Now, two questions:

#1: Isn’t cube mapping exactly what one does to AVOID having one render pass per FOV degree? But the resulting image composition looks just wrong. My current theory: I’m doing it wrong, and cube mapping needs a look-up table function instead of just pasting the perspectives together. Is this correct? (But even so, the edges still should merge seamlessly?)

#2: Any idea how to get rid of the artifacts of the brute force method…?

For more details on how the second picture is created, have some code:


p=np.array([150,140,10])
fov = 360
pan_view = img.new( 'RGBA', (def_WINDOW_WIDTH*fov,def_WINDOW_HEIGHT), 'White' )

for i in range(0,fov):

    gluLookAt( p[0],p[1],p[2], p[0]+math.cos(i*def_DEG2RAD)*100.0,p[1]+math.sin(i*def_DEG2RAD)*100.0,p[2]-10, 0,0,1 )

    ctrl.world.drawWorld()

    shot = glReadPixels( 0,0,def_WINDOW_WIDTH,def_WINDOW_HEIGHT,GL_RGBA,GL_UNSIGNED_BYTE )
    im   = img.frombuffer( 'RGBA',(def_WINDOW_WIDTH,def_WINDOW_HEIGHT),shot,'raw','RGBA',0,0 )
    pan_view.paste( im, (i*2,0) ) # every slice/degree is rendered two pixels wide

    glLoadIdentity()
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT )

pan_view.save( 'pan_view_fine.png' )

My guess is: simple rounding errors. Then again, when rendering a normal viewing cone, such artifacts do not occur.

I think you’ll need the GL_ARB_seamless_cube_map extension to get rid of your problems.

edit nevermind, I thought you were using a cube-map.

Regards
elFarto

1a: indeed, a ‘lookup’ formula should be used for continuous perspectives. cubeface.x = (tan(texcoord.x*2-1)+1)/2 or something like that.
1b: indeed, pasting the perspectives together should have matching edges. My guess is that your horizontal fov is wrong, slightly smaller than 90°. How do you set it ?
Or the rendered part is truncated somewhere.

#2: Any idea how to get rid of the artifacts of the brute force method…?

Try one slice per width pixel. Even bruter force.
Seriously though, it looks like the same problem you have in #1.

Ah, thanks for the clarification! It’s alright if the cube map looks weird, as long as I know why.

Regarding the problem: Agreed, that was my first thought as well. However, I can’t find anything wrong with the source:


# config - window
def_WINDOW_WIDTH  = 2
def_WINDOW_HEIGHT = 80

# config - viewport
def_FOV          =  40.0
def_CLIP_NEAR    =   2.0 
def_CLIP_FAR     = 512.0
def_ASPECT_RATIO = float(def_WINDOW_WIDTH) / float(def_WINDOW_HEIGHT)

(...)

# viewport
glViewport( 0, 0, def_WINDOW_WIDTH, def_WINDOW_HEIGHT )

# projection matrix setup
glMatrixMode( GL_PROJECTION )
glLoadIdentity()
gluPerspective( def_FOV,          
                def_ASPECT_RATIO, 
                def_CLIP_NEAR,    
                def_CLIP_FAR    )

# viewing matrix setup
glMatrixMode( GL_MODELVIEW )
glLoadIdentity()

Try one slice per width pixel. Even bruter force.

Yes! Brütal Force! This will be the next step - indeed the times-two version is only for human review purposes, the real deal only takes one pixel anyway. But it’s always nice to show what’s actually being done :slight_smile: