Skip to main content
Up and Coming
April 27, 2014

Simple OpenGL example with GLFW, GLEW and OculusSDK 0.3.1

Hi,

Hope this will save you some headaches (and not cause any more).
Only win32, but with GLFW it shouldn't be too hard to port once the OculusSDK becomes cross-platform.

EDIT
This is a one-page sample, 99% plain C. It is based on the OculusSDK 0.3.1 Preview. OpenGL 3.3 and up, compatibility profile.
For nVidia systems it works as-is.
At the time of writing, for AMD systems, it needs some bug fixes and a rebuild of the SDK libraries. This "patch" is advised for nVidia users as well, but not needed.
Load the Visual Studio solution from OculusSDK/Samples. Depending on your Visual Studio [version]: LibOVR_With_Samples_[version].sln.
In CAPI_GL_DistortionRenderer.cpp (LibOVR/CAPI/GL):
Line 142, replace oColor = Color.r with oColor = vec4(oColor)
Line 349, replace oColor = Color.r with oColor = vec4(oColor)
Line 758, replace (int) DistortionMeshVBs[eyeNum]->GetSize() with (int)DistortionMeshIBs[eyeNum]->GetSize()/2
Line 763, replace (int) DistortionMeshVBs[eyeNum]->GetSize() with (int)DistortionMeshIBs[eyeNum]->GetSize()/2
Rebuild the lib (Debug and/or Release).

Thanks,
Daniel Dekkers


#include <Windows.h>
#include <GL/glew.h>
#define GLFW_EXPOSE_NATIVE_WIN32
#define GLFW_EXPOSE_NATIVE_WGL
#include <GLFW/glfw3.h>
#include <GLFW/glfw3native.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <OVR_CAPI.h>
#include <OVR_CAPI_GL.h>
#include <OVR.h>

// =============================================================================

static void ErrorCallback(int p_Error, const char* p_Description)
{
fputs(p_Description, stderr);
}

// =============================================================================

static void KeyCallback(GLFWwindow* p_Window, int p_Key, int p_Scancode, int p_Action, int p_Mods)
{
if (p_Key == GLFW_KEY_ESCAPE && p_Action == GLFW_PRESS)
glfwSetWindowShouldClose(p_Window, GL_TRUE);
}

// =============================================================================

static void RenderCube(float p_Size)
{
const float l_SizeDiv2 = p_Size*0.5f;

// A cube...
glBegin(GL_QUADS);
glNormal3f( 0.0f, 0.0f, 1.0f);
glVertex3f( l_SizeDiv2, l_SizeDiv2, l_SizeDiv2);
glVertex3f(-l_SizeDiv2, l_SizeDiv2, l_SizeDiv2);
glVertex3f(-l_SizeDiv2,-l_SizeDiv2, l_SizeDiv2);
glVertex3f( l_SizeDiv2,-l_SizeDiv2, l_SizeDiv2);
glEnd();

glBegin(GL_QUADS);
glNormal3f( 0.0f, 0.0f,-1.0f);
glVertex3f(-l_SizeDiv2,-l_SizeDiv2,-l_SizeDiv2);
glVertex3f(-l_SizeDiv2, l_SizeDiv2,-l_SizeDiv2);
glVertex3f( l_SizeDiv2, l_SizeDiv2,-l_SizeDiv2);
glVertex3f( l_SizeDiv2,-l_SizeDiv2,-l_SizeDiv2);
glEnd();

glBegin(GL_QUADS);
glNormal3f( 0.0f, 1.0f, 0.0f);
glVertex3f( l_SizeDiv2, l_SizeDiv2, l_SizeDiv2);
glVertex3f( l_SizeDiv2, l_SizeDiv2,-l_SizeDiv2);
glVertex3f(-l_SizeDiv2, l_SizeDiv2,-l_SizeDiv2);
glVertex3f(-l_SizeDiv2, l_SizeDiv2, l_SizeDiv2);
glEnd();

glBegin(GL_QUADS);
glNormal3f( 0.0f,-1.0f, 0.0f);
glVertex3f(-l_SizeDiv2,-l_SizeDiv2,-l_SizeDiv2);
glVertex3f( l_SizeDiv2,-l_SizeDiv2,-l_SizeDiv2);
glVertex3f( l_SizeDiv2,-l_SizeDiv2, l_SizeDiv2);
glVertex3f(-l_SizeDiv2,-l_SizeDiv2, l_SizeDiv2);
glEnd();

glBegin(GL_QUADS);
glNormal3f( 1.0f, 0.0f, 0.0f);
glVertex3f( l_SizeDiv2, l_SizeDiv2, l_SizeDiv2);
glVertex3f( l_SizeDiv2,-l_SizeDiv2, l_SizeDiv2);
glVertex3f( l_SizeDiv2,-l_SizeDiv2,-l_SizeDiv2);
glVertex3f( l_SizeDiv2, l_SizeDiv2,-l_SizeDiv2);
glEnd();

glBegin(GL_QUADS);
glNormal3f(-1.0f, 0.0f, 0.0f);
glVertex3f(-l_SizeDiv2,-l_SizeDiv2,-l_SizeDiv2);
glVertex3f(-l_SizeDiv2,-l_SizeDiv2, l_SizeDiv2);
glVertex3f(-l_SizeDiv2, l_SizeDiv2, l_SizeDiv2);
glVertex3f(-l_SizeDiv2, l_SizeDiv2,-l_SizeDiv2);
glEnd();
}

// ============================================================================

static void SetOpenGLState(void)
{
// Some state...
glEnable(GL_CULL_FACE);
glEnable(GL_LIGHTING);
glDisable(GL_TEXTURE_2D);
glEnable(GL_DEPTH_TEST);
glShadeModel(GL_SMOOTH);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

// Some (stationary) lights...
GLfloat l_Light0Position[] = { 5.0f, 6.0f, 3.0f, 0.0f };
GLfloat l_Light0Diffuse[] = { 1.0f, 0.8f, 0.6f, 1.0f };
glLightfv(GL_LIGHT0, GL_POSITION, l_Light0Position);
glLightfv(GL_LIGHT0, GL_DIFFUSE, l_Light0Diffuse);
glEnable(GL_LIGHT0);

GLfloat l_Light1Position[] = { -5.0f, -6.0f, 5.0f, 0.0f };
GLfloat l_Light1Diffuse[] = { 0.6f, 0.8f, 1.0f, 1.0f };
glLightfv(GL_LIGHT1, GL_POSITION, l_Light1Position);
glLightfv(GL_LIGHT1, GL_DIFFUSE, l_Light1Diffuse);
glEnable(GL_LIGHT1);

// Material...
GLfloat l_MaterialSpecular[] = { 0.3f, 0.3f, 0.3f, 1.0f };
GLfloat l_MaterialShininess[] = { 10.0f };
glMaterialfv(GL_FRONT, GL_SPECULAR, l_MaterialSpecular);
glMaterialfv(GL_FRONT, GL_SHININESS, l_MaterialShininess);
}

// =============================================================================

int main(void)
{
// Initialize LibOVR...
ovr_Initialize();

ovrHmd l_Hmd = ovrHmd_Create(0);
if (!l_Hmd) l_Hmd = ovrHmd_CreateDebug(ovrHmd_DK1);

ovrHmdDesc l_HmdDesc;
ovrHmd_GetDesc(l_Hmd, &l_HmdDesc);

ovrHmd_StartSensor(l_Hmd, ovrHmdCap_Orientation, 0);

GLFWwindow* l_Window;

glfwSetErrorCallback(ErrorCallback);

if (!glfwInit()) exit(EXIT_FAILURE);

ovrSizei l_ClientSize;
l_ClientSize.w = l_HmdDesc.Resolution.w; // 1280 for DK1...
l_ClientSize.h = l_HmdDesc.Resolution.h; // 800 for DK1...
// Create a fullscreen window with the Oculus Rift resolution...
l_Window = glfwCreateWindow(l_ClientSize.w, l_ClientSize.h, "GLFW Oculus Rift Test", glfwGetPrimaryMonitor(), NULL);
if (!l_Window)
{
glfwTerminate();
exit(EXIT_FAILURE);
}

// Print the OpenGL version we are using...
int l_Major = glfwGetWindowAttrib(l_Window, GLFW_CONTEXT_VERSION_MAJOR);
int l_Minor = glfwGetWindowAttrib(l_Window, GLFW_CONTEXT_VERSION_MINOR);
printf("OpenGL: %d.%d\n", l_Major, l_Minor);

// Make the context current for this window...
glfwMakeContextCurrent(l_Window);

// Create some lights, materials, etc...
SetOpenGLState();

// Don't forget to initialize Glew...
glewInit();

// We will do some offscreen rendering, setup FBO...
ovrSizei l_TextureSizeLeft = ovrHmd_GetFovTextureSize(l_Hmd, ovrEye_Left, l_HmdDesc.DefaultEyeFov[0], 1.0f);
ovrSizei l_TextureSizeRight = ovrHmd_GetFovTextureSize(l_Hmd, ovrEye_Right, l_HmdDesc.DefaultEyeFov[1], 1.0f);
ovrSizei l_TextureSize;
l_TextureSize.w = l_TextureSizeLeft.w + l_TextureSizeRight.w;
l_TextureSize.h = (l_TextureSizeLeft.h>l_TextureSizeRight.h ? l_TextureSizeLeft.h : l_TextureSizeRight.h);

// Create FBO...
GLuint l_FBOId;
glGenFramebuffers(1, &l_FBOId);
glBindFramebuffer(GL_FRAMEBUFFER, l_FBOId);

// The texture we're going to render to...
GLuint l_TextureId;
glGenTextures(1, &l_TextureId);
// "Bind" the newly created texture : all future texture functions will modify this texture...
glBindTexture(GL_TEXTURE_2D, l_TextureId);
// Give an empty image to OpenGL (the last "0")
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, l_TextureSize.w, l_TextureSize.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
// Linear filtering...
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

// Create Depth Buffer...
GLuint l_DepthBufferId;
glGenRenderbuffers(1, &l_DepthBufferId);
glBindRenderbuffer(GL_RENDERBUFFER, l_DepthBufferId);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, l_TextureSize.w, l_TextureSize.h);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, l_DepthBufferId);

// Set the texture as our colour attachment #0...
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, l_TextureId, 0);

// Set the list of draw buffers...
GLenum l_GLDrawBuffers[1] = { GL_COLOR_ATTACHMENT0 };
glDrawBuffers(1, l_GLDrawBuffers); // "1" is the size of DrawBuffers

// Unbind...
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);

// Oculus Rift eye configurations...
ovrEyeDesc l_Eyes[2];
l_Eyes[0].Eye = ovrEye_Left;
l_Eyes[1].Eye = ovrEye_Right;
l_Eyes[0].Fov = l_HmdDesc.DefaultEyeFov[0];
l_Eyes[1].Fov = l_HmdDesc.DefaultEyeFov[1];
l_Eyes[0].TextureSize.w = l_TextureSize.w;
l_Eyes[0].TextureSize.h = l_TextureSize.h;
l_Eyes[1].TextureSize.w = l_TextureSize.w;
l_Eyes[1].TextureSize.h = l_TextureSize.h;
l_Eyes[0].RenderViewport.Pos.x = 0;
l_Eyes[0].RenderViewport.Pos.y = 0;
l_Eyes[1].RenderViewport.Pos.x = (l_TextureSize.w+1)/2;
l_Eyes[1].RenderViewport.Pos.y = 0;
l_Eyes[0].RenderViewport.Size.w = l_TextureSize.w/2;
l_Eyes[0].RenderViewport.Size.h = l_TextureSize.h;
l_Eyes[1].RenderViewport.Size.w = l_Eyes[0].RenderViewport.Size.w;
l_Eyes[1].RenderViewport.Size.h = l_Eyes[0].RenderViewport.Size.h;

ovrGLConfig l_Cfg;
l_Cfg.OGL.Header.API = ovrRenderAPI_OpenGL;
l_Cfg.OGL.Header.Multisample = 0;
l_Cfg.OGL.Header.RTSize.w = l_ClientSize.w;
l_Cfg.OGL.Header.RTSize.h = l_ClientSize.h;
l_Cfg.OGL.WglContext = glfwGetWGLContext(l_Window);
l_Cfg.OGL.Window = glfwGetWin32Window(l_Window);
l_Cfg.OGL.GdiDc = GetDC(l_Cfg.OGL.Window);

int l_RenderCaps = 0;
int l_DistortionCaps = ovrDistortion_Chromatic | ovrDistortion_TimeWarp;
ovrEyeRenderDesc l_EyeRenderDesc[2];
ovrHmd_ConfigureRendering(l_Hmd, &l_Cfg.Config, l_RenderCaps, l_DistortionCaps, l_Eyes, l_EyeRenderDesc);

ovrGLTexture l_EyeTexture[2];
l_EyeTexture[0].OGL.Header.API = ovrRenderAPI_OpenGL;
l_EyeTexture[0].OGL.Header.TextureSize.w = l_TextureSize.w;
l_EyeTexture[0].OGL.Header.TextureSize.h = l_TextureSize.h;
l_EyeTexture[0].OGL.Header.RenderViewport = l_Eyes[0].RenderViewport;
l_EyeTexture[0].OGL.TexId = l_TextureId;

// Right eye uses the same texture, but a different rendering viewport...
l_EyeTexture[1] = l_EyeTexture[0];
l_EyeTexture[1].OGL.Header.RenderViewport = l_Eyes[1].RenderViewport;

glfwSetKeyCallback(l_Window, KeyCallback);

GLfloat l_SpinX;
GLfloat l_SpinY;

while (!glfwWindowShouldClose(l_Window))
{
l_SpinX = (GLfloat) fmod(glfwGetTime()*17.0, 360.0);
l_SpinY = (GLfloat) fmod(glfwGetTime()*23.0, 360.0);

ovrFrameTiming m_HmdFrameTiming = ovrHmd_BeginFrame(l_Hmd, 0);

// Bind the FBO...
glBindFramebuffer(GL_FRAMEBUFFER, l_FBOId);
// Clear...
glClearColor(0.2f, 0.3f, 0.4f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

for (int l_EyeIndex=0; l_EyeIndex<ovrEye_Count; l_EyeIndex++)
{
ovrEyeType l_Eye = l_HmdDesc.EyeRenderOrder[l_EyeIndex];
ovrPosef l_EyePose = ovrHmd_BeginEyeRender(l_Hmd, l_Eye);

glViewport(l_EyeRenderDesc[l_Eye].Desc.RenderViewport.Pos.x, // StartX
l_EyeRenderDesc[l_Eye].Desc.RenderViewport.Pos.y, // StartY
l_EyeRenderDesc[l_Eye].Desc.RenderViewport.Size.w, // Width
l_EyeRenderDesc[l_Eye].Desc.RenderViewport.Size.h // Height
);

// Get Projection and ModelView matrici from the device...
OVR::Matrix4f l_ProjectionMatrix = ovrMatrix4f_Projection(
l_EyeRenderDesc[l_Eye].Desc.Fov, 0.3f, 100.0f, true);
OVR::Quatf l_Orientation = OVR::Quatf(l_EyePose.Orientation);
OVR::Matrix4f l_ModelViewMatrix = OVR::Matrix4f(l_Orientation.Inverted());

// Pass matrici on to OpenGL...
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMultMatrixf(&(l_ProjectionMatrix.Transposed().M[0][0]));
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Translate for specific eye based on IPD...
glTranslatef(l_EyeRenderDesc[l_Eye].ViewAdjust.x,
l_EyeRenderDesc[l_Eye].ViewAdjust.y,
l_EyeRenderDesc[l_Eye].ViewAdjust.z);
// Multiply with orientation retrieved from sensor...
glMultMatrixf(&(l_ModelViewMatrix.Transposed().M[0][0]));
// Move back a bit to show scene in front of us...
glTranslatef(0.0f, 0.0f, -3.0f);
// Make the cube spin...
glRotatef(l_SpinX, 1.0f, 0.0f, 0.0f);
glRotatef(l_SpinY, 0.0f, 1.0f, 0.0f);

// Render...
RenderCube(1.0f);

ovrHmd_EndEyeRender(l_Hmd, l_Eye, l_EyePose, &l_EyeTexture[l_Eye].Texture);
}

// Unbind the FBO, back to normal drawing...
glBindFramebuffer(GL_FRAMEBUFFER, 0);

// Oculus wants CW orientations, avoid the problem by turning of culling...
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
ovrHmd_EndFrame(l_Hmd);
// Restore state after Oculus did it's work (-sigh-)...
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glClearDepth(1);
glUseProgramObjectARB(0);

glfwPollEvents();
}

glfwDestroyWindow(l_Window);

glfwTerminate();

ovrHmd_Destroy(l_Hmd);
ovr_Shutdown();

exit(EXIT_SUCCESS);
}

    62 replies

    DoZo1971Author
    Up and Coming
    May 2, 2014
    Yeah, I would just leave it for a while, until a new update of the SDK comes out. They seem to be working on a lot of the OpenGL issues at the moment. And I hope they buy some AMD systems in the office (2 billion should be enough).
    Pity that they don't have a nightly build or something you can follow.
    Up and Coming
    May 2, 2014
    "DoZo1971" wrote:
    Yeah, I would just leave it for a while, until a new update of the SDK comes out. They seem to be working on a lot of the OpenGL issues at the moment. And I hope they buy some AMD systems in the office (2 billion should be enough).
    Pity that they don't have a nightly build or something you can follow.


    Please let us not "leave it" for a while.

    Folks have projects that are dependent on this code.

    I'll start a separate thread in the section to file bugs.

    This NEEDS to be fixed!

    No wonder why there is mostly development for Oculus Rift with DirectX, because of stuff like this.

    And if we don't say anything nothing will happen.
    Up and Coming
    May 2, 2014
    I created this thread on the "Reporting Issues" section of the forums.

    Please keep it updated; I am sure there are quite a few people who would like to code with OpenGL for the Oculus Rift.
    DoZo1971Author
    Up and Coming
    May 2, 2014
    ;-)
    But.. the SDK3.0.1 is just a preview. It has no official status whatsoever. They are just showing "the direction they are thinking". They show the new C API and it already works. A bit.
    What we are doing now is exactly what they want. Experiment with it and let them know what works and what doesn't.
    Check these threads:
    viewtopic.php?t=7908&p=108520
    viewtopic.php?f=20&t=8213
    They are working on it... OpenGL is far behind.
    New Participant
    May 2, 2014
    Hello, I am new to Oculus, and I am happy to have OpenGL/GLFW example.
    I compiled the program on Windows 7 64-bit PC with NVidia Quadro FX using Visual Studio 2012 and SDK 0.3.1.

    The result of no-Oculus one seems fine:
    http://pds.exblog.jp/pds/1/201405/03/78/d0046778_1175076.jpg

    However, the one with Oculus when ran on Windows screen (ovrHmd_CreateDebug), it shows a messed screen like this:
    http://pds.exblog.jp/pds/1/201405/03/78/d0046778_118140.jpg

    Is this because I am running it on Windows, but not on Rift?
    I actually do not have Rift yet but waiting eagerly for it. :idea:

    If this program is used on Rift does it take a movement of the headset?

    Thank you for your help.


    P.S.
    [img] tag seems to be disabled. Is there any way to show some image on the post?
    Inspiring
    May 2, 2014
    "rsato" wrote:
    "img" tag seems to be disabled. Is there any way to show some image on the post?


    It's probably disabled until you've reached a certain number of posts (3 most likely). We've had issues with spammers, so people with fewer than 3 posts have limited posting privileges.
    Brad Davis - Developer for High Fidelity Co-author of Oculus Rift in Action
    DoZo1971Author
    Up and Coming
    May 2, 2014
    Hi rsato,

    You should see a decent image even if there is no Oculus attached.
    What version of OpenGL are you running? (it's printed in the console under the graphics window).
    4.4 (like ckoeber) or 4.3 (like me)?

    @ckoeber: what happens if you try to "force" a 4.3 context?
    By adding

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE);

    before

    glfwCreateWindow(...)



    If this program is used on Rift does it take a movement of the headset?


    Yes
    New Participant
    May 2, 2014
    Hello, DoZo1971

    The OpenGL version printed is: 3.3 GLFW_OPENGL_CORE_PROFILE.

    It did not change after I added glfwWindowHint.
    And the picture is still messed up.

    Is this because the version is too old?

    Thank you for your help. :)
    New Participant
    May 3, 2014
    Hello,
    I used this program on another workstation which natively support OpenGL 4.4 and the program worked fine without Rift.
    This sample apparently does not work with OpenGL 3.3.

    Thank you for your help!


    The first workstation I tired comes with Quadro FX 3800 and it does not seem to support OpenGL 4.X even with the latest driver.
    Inspiring
    May 3, 2014
    Anything that uses the Core profile is liable to fail in unexpected ways, since the SDK doesn't properly use vertex array objects, which are required by the core profile. What a particular driver does may be different from version to version, so I wouldn't expect consistency.

    If you're also having problems with compatibility profiles, there may be something additional wrong with the way your program manipulates GL state versus the SDK.
    Brad Davis - Developer for High Fidelity Co-author of Oculus Rift in Action