Difference between revisions of "Tutorial - Stereo rendering"

From Horde3D Wiki
Jump to: navigation, search
Line 88: Line 88:
 
int eye = 0;
 
int eye = 0;
 
void Application::mainLoop( int fps )
 
void Application::mainLoop( int fps )
 
 
{
 
{
 
 
_curFPS = fps;
 
_curFPS = fps;
 
 
 
keyHandler();
 
keyHandler();
 
 
 
if(! _freeze )
 
if(! _freeze )
 
 
{
 
{
 
 
// calculate eye-offsets for stereo rendering
 
// calculate eye-offsets for stereo rendering
 
 
float xEyeOffset = eyOffsetFactor*sinf( degToRad( _ry + 90 ) );
 
float xEyeOffset = eyOffsetFactor*sinf( degToRad( _ry + 90 ) );
 
 
float zEyeOffset = eyOffsetFactor*cosf( degToRad( _ry + 90 ) );
 
float zEyeOffset = eyOffsetFactor*cosf( degToRad( _ry + 90 ) );
 
 
  
 
// ........  
 
// ........  
  
 
// Render scene
 
// Render scene
 
 
// render stereo
 
// render stereo
 
 
int ga = 0;
 
int ga = 0;
 
 
if (SDL_GL_GetAttribute(SDL_GL_STEREO, &ga) == 0 && ga == 1) {
 
if (SDL_GL_GetAttribute(SDL_GL_STEREO, &ga) == 0 && ga == 1) {
 
 
Horde3D::setNodeParami(_cam, CameraNodeParams::OutputBufferIndex, 0);
 
Horde3D::setNodeParami(_cam, CameraNodeParams::OutputBufferIndex, 0);
 
 
Horde3D::setNodeTransform( _cam, _x-xEyeOffset, _y, _z-zEyeOffset, _rx ,_ry-strabismus, 0, 1, 1, 1 );
 
Horde3D::setNodeTransform( _cam, _x-xEyeOffset, _y, _z-zEyeOffset, _rx ,_ry-strabismus, 0, 1, 1, 1 );
 
Horde3D::render( _cam );
 
Horde3D::render( _cam );
 
+
Horde3D::finalizeFrame();  // comment this line if you are using Horde3D 1.0.0 beta 2
Horde3D::finalizeFrame();
 
  // comment this line if you are using Horde3D 1.0.0 beta 2
 
 
Horde3D::setNodeParami(_cam, CameraNodeParams::OutputBufferIndex, 1);
 
Horde3D::setNodeParami(_cam, CameraNodeParams::OutputBufferIndex, 1);
 
 
Horde3D::setNodeTransform( _cam, _x+xEyeOffset, _y, _z+zEyeOffset, _rx ,_ry+strabismus, 0, 1, 1, 1 );
 
Horde3D::setNodeTransform( _cam, _x+xEyeOffset, _y, _z+zEyeOffset, _rx ,_ry+strabismus, 0, 1, 1, 1 );
 
Horde3D::render( _cam );
 
Horde3D::render( _cam );
 
+
Horde3D::finalizeFrame(); // comment this line if you are using Horde3D 1.0.0 beta 2
Horde3D::finalizeFrame();
 
// comment this line if you are using Horde3D 1.0.0 beta 2
 
 
}
 
}
 
+
else { // non stereo
else {
 
 
 
 
// Set camera parameters
 
// Set camera parameters
 
+
if (eye == 1)  // left eye
if (eye == 1)
 
  // left eye
 
 
Horde3D::setNodeTransform( _cam, _x-xEyeOffset, _y, _z-zEyeOffset, _rx ,_ry-strabismus, 0, 1, 1, 1 );
 
Horde3D::setNodeTransform( _cam, _x-xEyeOffset, _y, _z-zEyeOffset, _rx ,_ry-strabismus, 0, 1, 1, 1 );
 
+
else if (eye == 2)  // right eye
else if (eye == 2)
 
  // right eye
 
 
Horde3D::setNodeTransform( _cam, _x+xEyeOffset, _y, _z+zEyeOffset, _rx ,_ry+strabismus, 0, 1, 1, 1 );
 
Horde3D::setNodeTransform( _cam, _x+xEyeOffset, _y, _z+zEyeOffset, _rx ,_ry+strabismus, 0, 1, 1, 1 );
 
+
else if(eye == 0)  // normal camera (centered)
else if(eye == 0)
 
  // normal camera (centered)
 
 
Horde3D::setNodeTransform( _cam, _x, _y, _z, _rx ,_ry, 0, 1, 1, 1 );
 
Horde3D::setNodeTransform( _cam, _x, _y, _z, _rx ,_ry, 0, 1, 1, 1 );
 
 
Horde3D::render( _cam );
 
Horde3D::render( _cam );
 
+
Horde3D::finalizeFrame();  // comment this line if you are using Horde3D 1.0.0 beta 2
Horde3D::finalizeFrame();
 
  // comment this line if you are using Horde3D 1.0.0 beta 2
 
 
}
 
}
 
 
// Remove all overlays
 
// Remove all overlays
 
 
Horde3D::clearOverlays();
 
Horde3D::clearOverlays();
 
 
 
 
 
// Write all mesages to log file
 
// Write all mesages to log file
 
 
Horde3DUtils::dumpMessages();
 
Horde3DUtils::dumpMessages();
  
 
SDL_GL_SwapBuffers();
 
SDL_GL_SwapBuffers();
 
 
}
 
}
 
 
}
 
}
  
 
void Application::keyPressEvent( SDLKey key )
 
void Application::keyPressEvent( SDLKey key )
 
 
{
 
{
 
 
// add more key events here...
 
// add more key events here...
  
 
// switch between eyes (0: normal/center, 1: left eye, 2: right eye)
 
// switch between eyes (0: normal/center, 1: left eye, 2: right eye)
 
 
if( key == SDLK_F5 ) {// F5
 
if( key == SDLK_F5 ) {// F5
 
 
++eye;
 
++eye;
 
 
if (eye >= 3)
 
if (eye >= 3)
 
 
eye = 0;
 
eye = 0;
 
 
std::cout<<"eye: "<<eye<<std::endl;
 
std::cout<<"eye: "<<eye<<std::endl;
 
 
}
 
}
 
}
 
}
  
 
void Application::keyHandler()
 
void Application::keyHandler()
 
 
{
 
{
 
// add more keys if desired...
 
// add more keys if desired...
Line 201: Line 152:
 
// adjust strabismus (cross eyedness)
 
// adjust strabismus (cross eyedness)
 
if(_keys[SDLK_0])
 
if(_keys[SDLK_0])
 
 
{
 
{
 
 
if (strabismus < 45)
 
if (strabismus < 45)
 
 
strabismus += 0.3f;
 
strabismus += 0.3f;
 
std::cout<<"strabismus: "<<strabismus<<std::endl;
 
 
 
}
 
}
 
 
if(_keys[SDLK_9])
 
if(_keys[SDLK_9])
 
 
{
 
{
 
 
if (strabismus > 0)
 
if (strabismus > 0)
 
 
strabismus -= 0.3f;
 
strabismus -= 0.3f;
 
std::cout<<"strabismus: "<<strabismus<<std::endl;
 
 
 
}
 
}
 
 
 
// adjust eye offset
 
// adjust eye offset
 
if(_keys[SDLK_8])
 
if(_keys[SDLK_8])
 
 
{
 
{
 
 
eyeOffset += 0.3f;
 
eyeOffset += 0.3f;
 
std::cout<<"eyeOffset: "<<eyeOffset<<std::endl;
 
 
 
}
 
}
 
 
if(_keys[SDLK_7])
 
if(_keys[SDLK_7])
 
 
{
 
{
 
 
if (eyeOffset > 0) {
 
if (eyeOffset > 0) {
 
 
eyeOffset -= 0.3f;
 
eyeOffset -= 0.3f;
 
std::cout<<"eyeOffset: "<<eyeOffset<<std::endl;
 
 
 
}
 
}
 
 
}
 
}
 
}
 
}

Revision as of 12:42, 11 July 2009

In this tutorial I will describe how to perform quadbuffered stereo rendering with Horde3D. This explicitly requires a graphics card that supports quad-buffering, i.e. rendering to two different output devices(Nvidia Quadro or ATI/AMD FireGL). This has been tested with a stereo projection system and polarization glasses.

To do this we need to render our scene from two different points of view(left and right eye) and send each frame to the corresponding output device.

Stereoscopic OpenGL Tutorial

Stereoscopic Geometry in OpenGL In this tutorial we will only use an asymmetric frustum.


Enable quadbuffering

First of all we need to tell our graphics card to enable quadbuffering if available. The following code shows how to do this using SDL. We will try to enable stereo rendering by default but it will fall back to non-stereo if the graphics card does not have quadbuffering capabilities. We will enable stereo rendering only in fullscreen mode.

SDL Stereo Setup (main.cpp)
bool setupWindow( int width, int height, bool fullscreen )
{
	const SDL_VideoInfo* videoInfo = SDL_GetVideoInfo();
	SDL_Surface* surface;

	appWidth = fullscreen ? videoInfo->current_w : width;
	appHeight = fullscreen ? videoInfo->current_h : height;

	const bool fullscreen = true;

	if(fullscreen)
	{
		// enable stereo if possible (only in fullscreen mode)
		if ( SDL_GL_SetAttribute(SDL_GL_STEREO, 1) == 0) {
			std::cout<<"Stereo flag set!"<<std::endl;
		}
		// Try to initialize the SDL_Surface with Stereo enabled
		if (!(surface =  SDL_SetVideoMode(appWidth,appHeight,32,SDL_OPENGL | SDL_FULLSCREEN | SDL_HWSURFACE))) 
		{
			// if initialization with stereo enabled failed disable the stereo flag and retry
			std::cerr << "STEREO initialization failed! Restarting..."<< std::endl;
			SDL_GL_SetAttribute(SDL_GL_STEREO, 0);
			if (!SDL_SetVideoMode(width, height, 32, SDL_OPENGL | SDL_FULLSCREEN | SDL_HWSURFACE)) {
			     std::cerr << "SDL_SetVideoMode failed: " << SDL_GetError() << std::endl;
			     SDL_Quit();
			     return 0;
			}
		}
	}else // windowed
	{	
		if (!(surface = SDL_SetVideoMode(appWidth,appHeight,32,SDL_OPENGL | SDL_HWSURFACE))) 
		{
			std::cerr << "SDL_SetVideoMode failed: " << SDL_GetError() << std::endl;
			SDL_Quit();
			return 0;
		}
		SDL_WM_SetCaption("SDL App",NULL);
	}
	// check if stereo rendering is enabled
	int ga = 0;
	if (SDL_GL_GetAttribute(SDL_GL_STEREO, &ga) == 0) {
		if (ga == 0)
			std::cout<<"No stereo rendering available."<<std::endl;
		else
			std::cout<<"Stereo rendering enabled!."<<std::endl;
	}
	return true;
}

This will also work with GLFW.

GLFW stereo flag
       glfwOpenWindowHint( GLFW_STEREO, GL_TRUE );


Render the scene

Now we need to calculate the eye offset and render the scene twice for both eyes. This is done by switching the Horde3D render buffer for each eye, moving the camera to the desired eye offset and render the scene to the specified buffer. Again, this will only be done if stereo rendering was enabled successfully.

We will calculate the eye offset by simply "strafing" the camera from its original position slightly to the left and right and rotating it inwards. This example has some keys assigned to adjust the eye offset and strabismus ("cross-eyedness") on the fly. In normal (non-stereo) mode we can switch between the eyes to check if the camera position even if stereo rendering is not available.

Render the scene (app.cpp)
float eyOffsetFactor = 3.0f;
float strabismus = 3.0f;
int eye = 0;
void Application::mainLoop( int fps )
{
	_curFPS = fps;
	keyHandler();
	if(! _freeze )
	{
		// calculate eye-offsets for stereo rendering
		float xEyeOffset = eyOffsetFactor*sinf( degToRad( _ry + 90 ) );
		float zEyeOffset = eyOffsetFactor*cosf( degToRad( _ry + 90 ) );

		// ........ 

		// Render scene
		// render stereo
		int ga = 0;
		if (SDL_GL_GetAttribute(SDL_GL_STEREO, &ga) == 0 && ga == 1) {
			Horde3D::setNodeParami(_cam, CameraNodeParams::OutputBufferIndex, 0);
			Horde3D::setNodeTransform( _cam, _x-xEyeOffset, _y, _z-zEyeOffset, _rx ,_ry-strabismus, 0, 1, 1, 1 );
			Horde3D::render( _cam );
			Horde3D::finalizeFrame();  // comment this line if you are using Horde3D 1.0.0 beta 2
			Horde3D::setNodeParami(_cam, CameraNodeParams::OutputBufferIndex, 1);
			Horde3D::setNodeTransform( _cam, _x+xEyeOffset, _y, _z+zEyeOffset, _rx ,_ry+strabismus, 0, 1, 1, 1 );
			Horde3D::render( _cam );
			Horde3D::finalizeFrame(); // comment this line if you are using Horde3D 1.0.0 beta 2
		}
		else { // non stereo
			// Set camera parameters
			if (eye == 1)  // left eye
				Horde3D::setNodeTransform( _cam, _x-xEyeOffset, _y, _z-zEyeOffset, _rx ,_ry-strabismus, 0, 1, 1, 1 );
			else if (eye == 2)  // right eye
				Horde3D::setNodeTransform( _cam, _x+xEyeOffset, _y, _z+zEyeOffset, _rx ,_ry+strabismus, 0, 1, 1, 1 );
			else if(eye == 0)  // normal camera (centered)
				Horde3D::setNodeTransform( _cam, _x, _y, _z, _rx ,_ry, 0, 1, 1, 1 );
			Horde3D::render( _cam );
			Horde3D::finalizeFrame();  // comment this line if you are using Horde3D 1.0.0 beta 2
		}
		// Remove all overlays
		Horde3D::clearOverlays();
	
		// Write all mesages to log file
		Horde3DUtils::dumpMessages();

		SDL_GL_SwapBuffers();
	}
}

void Application::keyPressEvent( SDLKey key )
{
	// add more key events here...

	// switch between eyes (0: normal/center, 1: left eye, 2: right eye)
	if( key == SDLK_F5 )	{// F5
		++eye;
		if (eye >= 3)
			eye = 0;
		std::cout<<"eye: "<<eye<<std::endl;
	}
}

void Application::keyHandler()
{
	// add more keys if desired...

	// adjust strabismus (cross eyedness)
	if(_keys[SDLK_0])
	{
		if (strabismus < 45)
			strabismus += 0.3f;
	}
	if(_keys[SDLK_9])
	{
		if (strabismus > 0)
			strabismus -= 0.3f;
	}
	// adjust eye offset
	if(_keys[SDLK_8])
	{
		eyeOffset += 0.3f;
	}
	if(_keys[SDLK_7])
	{
		if (eyeOffset > 0) {
			eyeOffset -= 0.3f;
		}
	}
}