Monday, January 21, 2008

CG Shaders and FBO

After getting textures working I wanted to have my test application run using nebula3's application framework. The only way to have this working was to implement shaders and render targets.

Nebula3 makes use of effect states and glsl does not have an effect framework so it was a natural choice to use Nvidia's CG. Nvidia just released CG 2.0 so I decided to give it a try. I have to say, CG's API is pretty straight forward and easy to work with.

According to the CG specification, semantic names are case-insensitive and CG returns semantics in uppercase so if e.g. I define a semantic as ModelViewProjection in my shader, CG returns it as MODELVIEWPROJECTION . This is what I feed into nebula as a the shader variable's semantic when its created. The problem comes in when a semantic is hard coded into the application as ModelViewProjection. This throws an error since the variable ModelViewProjection cant be found. As a temporary solution, I've changed all references to shader variable semantics in the source code to upper case.

Whever a ShaderInstance is created, a clone of the effect should be created, as of this writing, CG's cgCloneEffect function is not working, as a temporary solution, whenever a shader is loaded from file, I keep a copy of the source in the shader. Whenever a shader instance is created I re-create the effect using the source in the shader.

Apart from that, I'm generally happy with CG 2.0.

As a render target implementation, I've used Frame Buffer Objects(FBO), it was pretty straight foward to set it up but there are still issues I need to fix here and there. e.g. I'm testing it on a Radeon x1650 that does not have the None Power of Two(NPOT) extension which makes rendering to a 1024 x 768 texture very, very slow. The card however supports the texture rectangle extension so when I bind a rectangular texture as a render target the rendering works fine. But another issue rears its ugly head, rectangular texture coordinates are addressed using the textures pixel dimensions not the usual 0...1 range.

Still pondering on this one but I think Ill set it up such that if the hardware supports NPOT, setup will be done normally, if the hardware has the rectangular texture extension but not the NPOT extension, a rectangular texture will be used and if the hardware supports neither of the two, some sort of pixel padding could be used to make the texture a power of two (have to research on how to do this).

Finally here is a screenshot of the testviewer application running on CG shaders.

Texturing

After a long break from nebula3 opengl development I got back into it after the holidays. My previous posts are on another blog http://larryweya.blogspot.com Blogspot have an option of moving my blog to a different email account which doesn't seem to work for me. It forces me to keep logging in and out which is really annoying so I decided to start a new blog.

Next on my list was textures, I was dreading this as I wasn't sure yet how I wanted to go about it. Initially, I thought it would be easier to use one of the open source image libraries. After trying to integrate FreeImage and OpenIL I decided to write my own parsing routines since this is a learning experience.

To start with I have only written a .dds loader (a subclass of a streamreader) which is so far working fine and supports some of the more common pixel formats (DXT1, DXT3, DXT5, X8R8G8B8, A8R8G8B8, A4R4G4B4, R5G6B5 and A1R5G5B5). The plan is to eventually have it such that the image loading class (StreamTextureLoader) keep some kind of registry that maps file extensions to specific loader classes.

From what I know, directx and nebula's texture coordinates have uv coordiante (0,0) at the top left corner while opengl has it at the bottom left, I thought I would eventually have to do some sort of flipping, perhaps of the texture on load and save time but oddly enough, the texture mapping is perfect. I had a problem with my post process rendering but this was easily fixed on the shader by deducting the v coordinate from 1 i.e. v = 1 -v;