[OT] Stereo OpenGL Teapot demo for PsychToolbox

Posted on Monday 7 May 2007

The StereoDemo in PsychToolbox is pretty impressive. I figured it would be more practical for my own experimental setup though to render the stimulus in 3d through OpenGL using two cameras, and pipe the results to the special stereo buffers of PsychToolbox (rather, than, say, do the 3d manipulations on matlab matrices). I couldn't get any of the OpenWindow stereo modes (1-9) to work once I called BeginOpenGL. I sent a message on the PsychToolbox list, and Mario Kleiner kindly pointed out that there's a bug in OpenWindow that requires you to add the kPsychNeedFastBackingStore flag as the last argument if you want to use OpenGL and stereo modes together. Don't ask me what the parameter means, but it works! Using some sample C code from the Computer Graphics for Research course by Quoc Vuong and Franck Caniard, and mixing with the UtahTeapotDemo, I got a nice stereo 3d rotating teapot. I'm surprised how easy it is to fuse the images too, you can get away with a lot more disparity without breaking fusion than with static anaglyph. Here's the code, hope it's useful to someone:


function [] = StereoTeapotDemo(viewMode)

    % StereoTeapotDemo - Demonstrate the combo of OpenGL 3d and PsychToolbox
    % stereo pipeline (anaglyph, LCD shutter, etc.).
    %
    % This is a reprise of the UtahTeapotDemo with stereo vision support. Set
    % viewMode to 1 for LCD shutter glasses, 2-5 for split screen 6-9 for
    % anaglyph
    %
    % 07-May-2007 - Created by Patrick Mineault

    % Is the script running in OpenGL Psychtoolbox?
    AssertOpenGL;

    % Find the screen to use for display:
    screenid=max(Screen('Screens'));

    % Disable Synctests for this simple demo:
    Screen('Preference','SkipSyncTests',1);

    % Setup Psychtoolbox for OpenGL 3D rendering support and initialize the
    % mogl OpenGL for Matlab wrapper:
    global GL;
    InitializeMatlabOpenGL(1);

    if(nargin() < 1)
        viewMode = 9; %Default to red/blue anaglyph
    end

    % Open a double-buffered full-screen window on the main displays screen.
    [win , winRect] = Screen('OpenWindow', screenid, 0, [0 0 1280 1024], 32, 2, viewMode, 0, kPsychNeedFastBackingStore);

    % Setup the OpenGL rendering context of the onscreen window for use by
    % OpenGL wrapper. After this command, all following OpenGL commands will
    % draw into the onscreen window 'win':

    Screen('BeginOpenGL', win);

    % Get the aspect ratio of the screen:
    ar=winRect(4)/winRect(3);

    % Turn on OpenGL local lighting model: The lighting model supported by
    % OpenGL is a local Phong model with Gouraud shading.
    glEnable(GL.LIGHTING);

    % Enable the first local light source GL.LIGHT_0. Each OpenGL
    % implementation is guaranteed to support at least 8 light sources.
    glEnable(GL.LIGHT0);

    % Enable two-sided lighting - Back sides of polygons are lit as well.
    glLightModelfv(GL.LIGHT_MODEL_TWO_SIDE,GL.TRUE);

    % Enable proper occlusion handling via depth tests:
    glEnable(GL.DEPTH_TEST);

    % Define the cubes light reflection properties by setting up reflection
    % coefficients for ambient, diffuse and specular reflection:
    glMaterialfv(GL.FRONT_AND_BACK,GL.AMBIENT, [ 1 1 1 1 ]);
    glMaterialfv(GL.FRONT_AND_BACK,GL.DIFFUSE, [ .78 .57 .11 1 ]);
    glMaterialfv(GL.FRONT_AND_BACK,GL.SPECULAR, [ 1 1 1 1 ]);
    glMaterialfv(GL.FRONT_AND_BACK,GL.SHININESS,128);

    % Set projection matrix: This defines a perspective projection,
    % corresponding to the model of a pin-hole camera - which is a good
    % approximation of the human eye and of standard real world cameras --
    % well, the best aproximation one can do with 3 lines of code ;-)
    glMatrixMode(GL.PROJECTION);
    glLoadIdentity;

    % Setup modelview matrix: This defines the position, orientation and
    % looking direction of the virtual camera:
    glMatrixMode(GL.MODELVIEW);
    glLoadIdentity;

    % Setup position and emission properties of the light source:

    % Set background color to 'black':
    glClearColor(0,0,0,0);

    % Point lightsource at (1,2,3)...
    glLightfv(GL.LIGHT0,GL.POSITION,[ 0 0 -100 0 ]);

    % Emits white (1,1,1,1) diffuse light:
    glLightfv(GL.LIGHT0,GL.DIFFUSE, [ 1 1 1 1 ]);

    % Emits reddish (1,1,1,1) specular light:
    glLightfv(GL.LIGHT0,GL.SPECULAR, [ 1 0 0 1 ]);

    % There's also some blue, but weak (R,G,B) = (0.1, 0.1, 0.1)
    % ambient light present:
    glLightfv(GL.LIGHT0,GL.AMBIENT, [ .1 .1 .6 1 ]);

    %Screen('EndOpenGL', win);

    % Initialize amount and direction of rotation
    theta=0;
    rotatev=[ 0 0 1 ];

    numframes = 1;
    Screen('EndOpenGL', win);
   
    % Animation loop: Run until key press...
    while (1)

        % Calculate rotation angle for next frame:
        theta=mod(theta+0.3,360);
        rotatev=rotatev+0.1*[ sin((pi/180)*theta) sin((pi/180)*2*theta) sin((pi/180)*theta/5) ];
        rotatev=rotatev/sqrt(sum(rotatev.^2));

        RenderScene(0, theta, rotatev, win)
        RenderScene(1, theta, rotatev, win)


        Screen('Flip', win);
        % Check for keyboard press and exit, if so:
        if KbCheck
            break;
        end



        numframes = numframes + 1;
    end


    % Close onscreen window and release all other ressources:
    Screen('CloseAll');

    % Reenable Synctests after this simple demo:
    Screen('Preference','SkipSyncTests',1);

    % Well done!
    %return

%Render the Teapot
function [] = RenderScene(whichEye, theta, rotatev, win)

    global GL;
    %%Render an eye
    Screen('SelectStereoDrawBuffer', win, whichEye);
    Screen('BeginOpenGL', win);
    % Setup cubes rotation around axis:
   
    glMatrixMode(GL.PROJECTION);
    glPushMatrix;
   
    %Appropriate values for each parameter are listed
    %The last parameter is the important one, which defines eye position
    StereoProjection(-6.0, 4.8, 6.0, -4.8, 6.0, -6.0, 0, 10, -0.3+0.6*whichEye);
    glMatrixMode(GL.MODELVIEW);
   
    glPushMatrix;
   
    %rotate us some teapot
    glRotated(theta,rotatev(1),rotatev(2),rotatev(3));

    % Clear out the backbuffer: This also cleans the depth-buffer for
    % proper occlusion handling:
    glClear;
   
    glutSolidTeapot(3   );

    glPopMatrix;
   
    glMatrixMode(GL.PROJECTION);
   
    glPopMatrix;
   
    % Finish OpenGL rendering into PTB window and check for OpenGL errors.
    Screen('EndOpenGL', win);


%Set up the stereo projection matrices. Taken verbatim from Quoc Vuong's
%class Computer Graphics for Vision Research,
%http://www.kyb.mpg.de/bu/people/qvuong/CGV05.html
function [] = StereoProjection(left, top, right, bottom, near, far, zero_plane, dist, eye)

%{
        Perform the perspective projection for one eye's subfield.

    The projection is in the direction of the negative z-axis.

    [default: -6.0, 6.0, -4.8, 4.8]
    left, right, bottom, top = the coordinate range, in the plane of zero parallax setting,
         which will be displayed on the screen.

         The ratio between (right-left) and (top-bottom) should equal the aspect
    ratio of the display.

    [default: 6.0, -6.0]
    near, far = the z-coordinate values of the clipping planes.

    [default: 0.0]
    zero_plane = the z-coordinate of the plane of zero parallax setting.

    [default: 14.5]
    dist = the distance from the center of projection to the plane of zero parallax.

    [default: -0.3]
    eye = half the eye separation; positive for the right eye subfield,
    negative for the left eye subfield.
%}


    dx = right - left;
    dy = top - bottom;

    xmid = (right + left) / 2.0;
    ymid = (top + bottom) / 2.0;

    clip_near = dist + zero_plane - near;
    clip_far  = dist + zero_plane - far;

    n_over_d = clip_near / dist;

    topw = n_over_d * dy / 2.0;
    bottomw = -topw;
    rightw = n_over_d * (dx / 2.0 - eye);
    leftw  = n_over_d *(-dx / 2.0 - eye);

        %// Create a fustrum, and shift it off axis
        %// glTranslate() applies to PROJECTION matrix
    glLoadIdentity();
    glFrustum(leftw,  rightw,  bottomw,  topw, clip_near,  clip_far);
    glTranslatef(-xmid - eye,  -ymid,  -zero_plane -dist);
   
   
 


WordPress database error: [Can't open file: 'wp_comments.MYD'. (errno: 145)]
SELECT * FROM wp_comments WHERE comment_post_ID = '246' AND comment_approved = '1' ORDER BY comment_date

No comments have been added to this post yet.

Leave a comment




Your e-mail address is never displayed. If you run into issues with SpamKarma blocking you, email me at $patrick->5etdemi(com)


RSS feed for comments on this post | TrackBack URI