OpenGL 3.0 context

“Great” news : OpenGL 3.0 is out! Well, after skimming over the new specs there’s really no new stuff – besindes of some manufacturer specific extensions going EXT or ARB and the deprecated markings sig. NVIDIA announced full GL3.0 support on G80 series with driver version 181.00+ so I tried to play around with the new context type – here’s some code 🙂

#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002
#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
#define WGL_CONTEXT_FLAGS_ARB 0x2094

typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC) (HDC hDC, HGLRC hShareContext, const int* attribList);

void GLWindow::CreateGL() {
    if(m_hWnd == NULL) {
        throw std::exception("Window handle cannot be NULL");
    }    // If we have no window, we cannot continue

    // If any of the following steps fail, we shutdown any OpenGL elements that have been started and exit
    if (!(m_hDC=GetDC(m_hWnd))) {                        // Retrieve the Device Context for this window
        ShutdownGL();
        throw std::exception("Could not get window Device Context");
    }

    //Create the PixelFormatDescriptor, which describes to OpenGL the pixel properties such as depth, color, and alpha channels
    PIXELFORMATDESCRIPTOR pfd;
    ZeroMemory( &pfd, sizeof(PIXELFORMATDESCRIPTOR) );
    pfd.nVersion = 1;                                                                // The PFD version, always 1
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;        // The properties of the PFD, in this case OpenGL support and double buffering
    pfd.iPixelType = PFD_TYPE_RGBA;                                                    // The type of Pixels we're dealing with
    pfd.cColorBits = m_displaySettings.colorBits;                                    // The color depth of the window
    pfd.cAlphaBits = m_displaySettings.alphaBits;                                    // The alpha depth of the window
    pfd.cDepthBits = m_displaySettings.depthBits;                                    // The number of bits to use for the depth buffer
    pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);                                        // The size of this structure

    int pixelFormat;
    if (!(pixelFormat = ChoosePixelFormat(m_hDC,&pfd))) {
        ShutdownGL();
        throw std::exception("Could not find a suitable Pixel Format");
    }

    if(!SetPixelFormat(m_hDC,pixelFormat,&pfd)) {            // Set the format of the Device Context
        ShutdownGL();
        throw std::exception("Could not set the Pixel Format");
    }

    // NV GL 3.0
    HGLRC tempContext = NULL;
    if(!(tempContext = wglCreateContext(m_hDC))) {                    // Bind the render context for drawing
        ShutdownGL();
        throw std::exception("Could not create temp. Render Context");
    }

    if(!wglMakeCurrent(m_hDC, tempContext)) {                    // Bind the render context for drawing
        wglDeleteContext(tempContext);
        ShutdownGL();
        throw std::exception("Could not make temp. Render Context current");
    }

    int attribs[] = {
        WGL_CONTEXT_MAJOR_VERSION_ARB, 3,//we want a 3.0 context
        WGL_CONTEXT_MINOR_VERSION_ARB, 0,//and it shall be forward compatible so that we can only use up to date functionality
        WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
        0 //zero indicates the end of the array
    };

    PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL; //pointer to the method
    wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) wglGetProcAddress("wglCreateContextAttribsARB");

    if(wglCreateContextAttribsARB == NULL) { //OpenGL 3.0 is not supported
        // Bind the render context for drawing
        wglDeleteContext(tempContext);
        ShutdownGL();
        throw std::exception("Cannot get Proc Adress for GL 3.0 CreateContextAttribs");
    }

    if (!(m_hRC=wglCreateContextAttribsARB(m_hDC,0, attribs))) {
        wglDeleteContext(tempContext);
        ShutdownGL();
        throw std::exception("Can't Create A GL 3.0 Rendering Context.");
    }
    wglDeleteContext(tempContext);

    if(!wglMakeCurrent(m_hDC,m_hRC)) { // Try To Activate The Rendering Context
        ShutdownGL();
        throw std::exception("Could not make Render Context current");
    }
}

The key is the PFNWGLCREATECONTEXTATTRIBSARBPROC function pointer that is supplied by the opengl dll (see its declation on line 5). Using this layout we can fetch the function using a casted wglGetProcAddress() to the new function “wglCreateContextAttribsARB”. So the difference with GL3.0 here is to use a different method when creating a GL-context. The former method would still create an GL2 context. Alternatively one may use a new version of a utility/ext wrapper (glut, glew, glext, etc.) which supplies the new function directly.

Here’s the complete test project: gl30context_sample_code.

Advertisements

DICOM viewer with .NET

After an application interview at Zeiss Meditec I was given a homework with two excercise. The first is about parsing some patient data from xml files with DTD which is not really new stuff for me. The second excercise is an implementation of a DICOM file viewer for a given example VL dicom file. The task tells to use whether C++, Java or C# and because I didn’t do C# for a while I started looking for a good .NET dicom library.

Looking at some OpenSource projects: they’re horrible! Most of the libs are in a pre- alpha- testing- doNotUseInProductiveEnvironments phase and the example viewers of some libs could not read meditec’s VL dicom file at all. Quite promising appeared GrassRoots as it’s a C++ based lib that is wrapped to C#, Python, etc. using swig. But I encountered some issues and as the documentation is close to zero I continued my lib-search.

Finally with mDCM I found a quite easy-to-use .NET C# implementation which was very fast to integrate into my little viewer app. I used a Utility class to access the library methods using the Dicom.Data namespace. To open a dicom file I used:

bool success = false;
m_fileformat = new DicomFileFormat();
try {
m_fileformat.Load(filename, DicomReadOptions.Default |
DicomReadOptions.KeepGroupLengths |
DicomReadOptions.DeferLoadingLargeElements |
DicomReadOptions.DeferLoadingPixelData);

success = true;
}
catch (Exception e)
{
m_lasterror = "Error parsing file! : " + e.Message + "n";
return false;
}

return success;

Where m_fileformat is a Dicom.Data.DicomFileFormat data member. After that I read the pixel data to obtain the enclosed image:

DcmPixelData pixeldata = new DcmPixelData(m_fileformat.Dataset);
if (pixeldata.NumberOfFrames == 0)
{
m_lasterror = "No image data found" + "n";
return false;
}
else if (pixeldata.NumberOfFrames == 1) // currently only first img
{
MemoryStream strm = null;
try
{
strm = new MemoryStream(pixeldata.GetFrameDataU8(0));
img = System.Drawing.Image.FromStream(strm);
}
catch (Exception ex)
{
m_lasterror = "Error reading image from dicom file (" + ex.ToString() + ")" + "n";
return false;
}
return true;
}

As one can see, most likely only 8bit-per-channel images will be supported here (GetFrameDataU8() returns a plain byte[]). Moreover when testing with other sample dicom files this threw exceptions in many cases, so I guess I should look for another .NET lib or alternatively switch to Java or C++ 😦

=> zdicomviewer sourcecode and documentation

Direct3D9 Depth Buffer access

If you have to deal with Depth Buffer reading in Direct3D9, you’ll propably encounter the documentation saying that you cannot read the GPU depth buffer directly. But there’s a workaround with special buffer formats on NVIDIA Cards (RAWZ on older cards and INTZ on G80+ series) and on ATI (DF16/24, I think). There’s also a paragraph in the G80 documentation by NVIDIA (see http://developer.nvidia.com/object/gpu_programming_guide.html). I think GTA4 is a game that uses the formats. Here’s some code I have been playing around with using Microsofts DirectX SDK of November 2008:

// decls

IDirect3DTexture9* m_pSMZTexture;   // rawz for using dss as input for next shader
LPDIRECT3DSURFACE9 m_pBackBuffer;   // default

// creates NVIDIA INTZ Format DSS. Use as DSS and render scene. Then use as input texture for another pass.
BOOL vmD3D::SetupDepthBufferAccess()
{
if(!m_pd3dDevice)
return FALSE;

if(FAILED(m_pd3dDevice->CreateTexture(TEXDEPTH_WIDTH, TEXDEPTH_HEIGHT, 1,
D3DUSAGE_DEPTHSTENCIL, (D3DFORMAT)MAKEFOURCC('I','N','T','Z'),
D3DPOOL_DEFAULT, &m_pSMZTexture, NULL)))
{
return FALSE;
}

return TRUE;
}

This actually creates an INTZ-Format DSS. Be sure to use the same dimensions of the render target you’ll bind on the same drawcall. Next, before your dracall bind the dss:

// setdss (our custom zsurf) -> intz!
// first fetch surface from tex at level 0
IDirect3DSurface9 *pSMZSurf = NULL;
m_pSMZTexture->GetSurfaceLevel(0, &pSMZSurf);
// set dss:
if(FAILED(m_pd3dDevice->SetDepthStencilSurface(pSMZSurf)))
return FALSE;
pSMZSurf->Release();

When drawing, there’s no need for any shader stuff if you simply want to output depth-only values (just write black color for example). But obviosly your vertex shader should do the needed transformations (multiply your WorldProjectionMatrix for example). After drawing with the intz-dss bound, you can use the texture as shader input for a next render pass. Here you can simply render a Full-Screen Quad to visualize the depth values:

if(FAILED(pFxDisplayDepth->SetTexture("DepthMap", m_pSMZTexture)))
return FALSE;

//set render target back to normal back buffer / depth buffer (or disable Z - as it's a dumb quad!)
if(FAILED(m_pd3dDevice->SetRenderTarget(0, m_pBackBuffer)))
return FALSE;

Then just draw the quad, i.e. with lazy UP RHW drawing:

UINT uPasses;
if (SUCCEEDED(pFxDisplayDepth->Begin(&uPasses, 0)))
{
for (UINT uPass = 0; uPass BeginPass(uPass);
m_pd3dDevice->SetFVF( m_pQuad->FVF );
m_pd3dDevice->DrawPrimitiveUP( D3DPT_TRIANGLESTRIP, 2, m_pQuad->m_Vertex, sizeof( FullScreenQuad::Vertex ) );
pFxDisplayDepth->EndPass();
}
pFxDisplayDepth->End();
}

Be aware that Microsoft PIX cannot handle the INTZ resource, so you won’t be able to see a preview or the metadata of the INTZ-Surface.

Startup

Omg, I’m blogging! Well, first maybe some stuff I’ve been doing at present. This will mainly be GPU rendering and Direct3D/OpenGL topics. There’re many interesting blogs caring about such issues:

BrainDump by flo
Flo is lead dev at RadonLabs in Berlin (Nebula2+3 engines, Drakensang game for example)
http://flohofwoe.blogspot.com/

Diary of a Graphics Programmer
http://diaryofagraphicsprogrammer.blogspot.com
Blog of Wolgang Engel (ShaderX books, Rockstar games dev)

http://realtimecollisiondetection.net
Christer Ericson’s blog (Sony dev)

http://drkappa.blogspot.com
Dr. Kappa’s Korner.  Graphics blog.

http://www.realtimerendering.com/blog
Real-Time Rendering blog, by sackboy from LittleBIGPlanet project, There’s also a book with that name ^^

http://levelofdetail.wordpress.com
LOD stuff, but also posts about shadows, Radiosity, etc.