
RESEARCH
PEOPLE
INFORMATION
EVENTS
SPONSORS
|
Stickydots
| The following source code creates a scintillating random dot cinematogram
(SRDC) using OpenGL. It bases the shape of this SRDC on the depth map
in the current OpenGL depth buffer (Z-buffer). The main routine in
this code is the Draw() function. The other functions help adjust the
various parameters associated with SRDC displays, such as the number of
dots, frequency of update, and percent refreshed during each update. For the anaglyphic presentation mode, the blue color is a mix of 100%
blue and 50% green. We find this works best with the anaglyphics glasses we
have purchased at www.reel3d.com. The
red color is 100% red plus 10% green. For reasons unknown, the anti-aliasing
algorithm on our nVidia Quadro4 graphics card does not perform correctly if
pure red pixels are rendered (anti-aliasing becomes spatially asymmetric).
In our research studies, we have actually used dual channel graphics
hardware which provides independent displays for each eye. We've found
the anaglyphs useful for demonstrating the stimuli on normal computer
monitors.
More to come--please feel free to contact me in the meantime.
Andy Beall-
beall@psych.ucsb.edu
|
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <math.h>
#include <gl\gl.h> // OpenGL
#include <gl\glu.h> // GLU library
#define LEFT_EYE 0
#define RIGHT_EYE 1
#define MAX_DOTS 9000
float gPointSize = 3;
float gColorRed = 1.0;
float gColorGreen = 1.0;
float gColorBlue = 1.0;
int W = 640;
int H = 480;
int gNumDots = 1000;
int gSkipFrames = 30;
int gNumRefresh = 500;
int lastDotIndex = 0;
int gHTop = 0;
char gRandomize = 1;
FILE *fout;
char gSaveData = 0;
char gAnaglyph = 0;
char ok2clear = 1;
static int w[MAX_DOTS], h[MAX_DOTS];
static GLdouble objX[MAX_DOTS], objY[MAX_DOTS], objZ[MAX_DOTS];
/*********************************************************************
Find out the viewport size
*********************************************************************/
void
Initialize(void)
{
int data[4];
glGetIntegerv(GL_VIEWPORT, data);
W = data[2];
H = data[3];
}
/*********************************************************************
This function is the heart of the stickydot technique. It accesses
the OpenGL Z (depth) buffer and then in confunction with the PROJECTION
and the MODELVIEW matrices, it is straightforward to extract the
X, Y, Z coordinates of any surface point currently visible in the view
frustum. This function samples the 3D scene by randomly drawing from
a uniform distribution of points in the projection plane. Once found,
this routine re-draws these surface points using the MODELVIEW matrix
provided by the calling OpenGL draw routine (which has drawn the actual
scene) for stereo disparity.
*********************************************************************/
void
Draw(void)
{
static char whichEye;
int i, j, k, d, v, index;
static unsigned ctr;
float x, y;
// Used to read the OGL depth buffer
GLint viewport[4];
GLdouble modelview[16],projection[16];
GLdouble winX,winY,winZ;
GLfloat z;
/*------------------------------------------------------------------
This block of code is executed once perform graphics update for both
the left and right eye. As implemented here, it assumes that the
scene has already been rendered to the GL graphics buffer.
*/
if (whichEye == LEFT_EYE && (ctr % gSkipFrames == 0)) {
// Prepare to unproject the scene
glGetDoublev (GL_MODELVIEW_MATRIX, modelview);
glGetDoublev (GL_PROJECTION_MATRIX, projection);
glGetIntegerv(GL_VIEWPORT, viewport);
// If needed, create a random array of points in screen coordinates
for (i=0; i<gNumRefresh; i++) {
index = (i + lastDotIndex) % gNumDots;
if (gRandomize) {
if (gAnaglyph) {
// Points will be confined to a W by H viewport
w[index] = (int)(((float)rand() / 32767.0 * W));
h[index] = (int)((float)rand() / 32767.0 * (H-gHTop) + gHTop);
}
else {
// Points are shifted W to the right
w[index] = (int)(((float)rand() / 32767.0 * W)+W);
h[index] = (int)((float)rand() / 32767.0 * (H-gHTop) + gHTop);
}
}
// Unproject the scene for array of points
glReadPixels (w[index], H-h[index],1,1, GL_DEPTH_COMPONENT, GL_FLOAT, &z);
gluUnProject (w[index], H-h[index], z, modelview, projection, viewport, &objX[index], &objY[index], &objZ[index]);
}
// Keep track of which points were updated for case for partial refresh
lastDotIndex = index;
// Is this really necessary??
// Undo IPD shift
glPopMatrix();
if (ok2clear)
glClear(GL_COLOR_BUFFER_BIT);
}
if (gSaveData) {
fout = fopen("data3d.txt", "w");
}
if (gAnaglyph) {
if (whichEye == LEFT_EYE) {
// Left eye is blue
glColor3f(0,.5,1);
}
else {
// Right eye is red
glColor3f(1,.1,0);
// Since this is first pass, it's OK
if (ok2clear)
glClear(GL_COLOR_BUFFER_BIT);
}
}
// Store current state of the depth test
glPushAttrib(GL_DEPTH_BUFFER_BIT | GL_POINT_BIT | GL_CURRENT_BIT);
glDisable(GL_DEPTH_TEST);
glPointSize(gPointSize);
if (!gAnaglyph)
glColor3f(gColorRed, gColorGreen, gColorBlue);
glBegin(GL_POINTS);
for(i=0;i<gNumDots;i++){
glVertex3f(objX[i], objY[i], objZ[i]);
if (gSaveData)
fprintf(fout, "%10.4f\t%10.4f\t%10.4f\n", objX[i], objY[i], objZ[i]);
}
glEnd();
glPopAttrib();
if (gSaveData) {
gSaveData = 0;
fclose(fout);
}
// Everytime time the right eye is drawn, increment the graphics frame counter
if(whichEye == RIGHT_EYE)
ctr++;
whichEye ^= 1;
}
/*********************************************************************
Enable point anti-aliasing
*********************************************************************/
void
SetPointAntialias(void)
{
glEnable(GL_POINT_SMOOTH);
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
}
/*********************************************************************
Enable anaglyph rendering as opposed to frame parallel
*********************************************************************/
void
SetAnaglyphMode(void)
{
gAnaglyph = 1;
}
/*********************************************************************
Change the number of points to render
*********************************************************************/
void
SetNumDots(int num)
{
gNumDots = num;
if (gNumDots < 1) {
gNumDots = 1;
}
if (gNumDots > MAX_DOTS) {
printf("**ERROR: Max dots allowed is: %i\n", MAX_DOTS);
gNumDots = MAX_DOTS;
}
}
/*********************************************************************
Change the number of frames to skip between point updates
*********************************************************************/
void
SetSkipFrames(int skip)
{
gSkipFrames = skip;
if (skip < 1)
gSkipFrames = 1;
printf("skip frames set to %i\n", gSkipFrames);
}
/*********************************************************************
Change proportion of points to refresh during graphics updates
*********************************************************************/
void
SetRefreshPercent(float percent)
{
gNumRefresh = percent * gNumDots;
if (gNumRefresh < 1)
gNumRefresh = 1;
if (gNumRefresh > gNumDots)
gNumRefresh = gNumDots;
}
/*********************************************************************
Set top of horizontal point boundard (useful for concentrating points
on a ground surface for apps such as driving simulators.
*********************************************************************/
void
SetHTop(int top)
{
gHTop = top;
}
/*********************************************************************
Define the width of the graphics viewport
*********************************************************************/
void
SetViewportWidth(int width)
{
W = width;
}
/*********************************************************************
Define the height of the graphics viewport
*********************************************************************/
void
SetViewportHeight(int height)
{
H = height;
}
/*********************************************************************
Save a single "snapshot" of the 3D point coordinates
*********************************************************************/
void
SetSaveData(void)
{
gSaveData = 1;
}
/*********************************************************************
TRUE to resample point locations in the projection plane; FALSE
to stop resampling in projection plane.
*********************************************************************/
void
SetRandomize(int state)
{
gRandomize = state;
}
/*********************************************************************
Define size of points (can be fractional units with anti-aliasing)
*********************************************************************/
void
SetPointSize(int size)
{
gPointSize = size;
}
/*********************************************************************
Set the RGB color of points for non-anaglyph mode
*********************************************************************/
void
SetColorRGB(float red, float green, float blue)
{
gColorRed = red;
gColorGreen = green;
gColorBlue = blue;
}
/*********************************************************************
Toggle whether to actively clear color buffer (to erase the scene
drawn by the calling drawing function).
*********************************************************************/
void
ToggleOK2Clear(void)
{
ok2clear ^= 1;
}
|