Thursday, December 30, 2010

Circular and arbitrary shaped point sprites in opengl 3.3

I recently tried to convert an old OpenGL source of mine OpenGL 2.1 i guess to OpenGL 3.3 based on the new core profile. To my surprise some of the functions like glEnable(GL_POINT_SMOOTH) are deprecated in the core profile and it generates an error. That meant I could not use the built-in facility to render smooth anti-aliased roudned points. I was looking out for solutions and then I figured out a simple way of doing it using the fragment shader. In OpenGL 3.0 and above, you can use the gl_PointCoord variable to obtain the point's uv coordinates (which are in range of 0-1). This variable is what I needed. To generate the circular point sprite, you can do the following

 
#version 330
smooth out vec4 vFragColor;
uniform vec4 vColor;

void main() {

if(dot(gl_PointCoord-0.5,gl_PointCoord-0.5)>0.25)
discard;
else
vFragColor = vColor;

}

This gives the following output

Circular point sprites

Which basically offsets the uv coordinates and then solves to see if the sqrt of distance is equal to 0.5 the radius of our point. I squre both sides to get the expression i have used. Similarly, a lot of interesting shapes could be generated as i show below.

Parametric coordinates:
We can use the parametric coordinate system (http://en.wikipedia.org/wiki/Polar_coordinate_system) to generate a lot of neat shapes of particles as I show below. For the polar coordinates, we need two things, radius and angle theta,
We get radius using
 
vec2 p = gl_PointCoord* 2.0 - vec2(1.0);
float r = sqrt(dot(p,p));

For theta, we use
 
float theta = atan(p.y,p.x);

Now I show u how to generate a lot of neat shapes based on the polar coordinate expressions on the wikipedia page linked above,
Polar rose:
 
if(dot(p,p) > cos(theta*5))
discard;
else
vFragColor = vColor;

This gives the following output,
POlar rose

Round ring:
 

if(dot(p,p) > r || dot(p,p) < r*0.75)
discard;
else
vFragColor = vColor;


You can change the multiplier to change the hole size.
This gives the following output,
POlar ring

Spiral:
 
if(dot(p,p)> 5.0/cos(theta-20*r))
discard;
else
vFragColor = vColor;

This gives the following output,


Rounded star:
 

if(dot(p,p) > 0.5*(exp(cos(theta*5)*0.75)) )
discard;
else
vFragColor = vColor;

This gives the following output,
Polar star

There might be other simpler ways of generating these shapes but these are some simpler ways to generate the various point sprite shapes.

Happy OpenGL coding.
Mobeen

Monday, November 29, 2010

Order independent transparency (Dual depth peeling)

This information is extracted from the original NVIDIA whitepaper on dual depth peeling http://developer.download.nvidia.com/SDK/10.5/opengl/src/dual_depth_peeling/doc/DualDepthPeeling.pdf

To sumup this is how it is implemented.
You need two fbo (one for depth test and other for final blending), two depth textures(RG32F not depth attachments), two textures (front/back fragments) and a color blend final texture. The fbo setup code is as follows,
 

void setup_fbo() {
glGenFramebuffersEXT(1, &dualDepthfboID); 
glGenTextures (2, texID);
glGenTextures (2, backTexID);
glGenTextures (2, depthTexID);
for(int i=0;i<2 br="" i="">glBindTexture(GL_TEXTURE_RECTANGLE_ARB, depthTexID[i]);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB , GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB , GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB , GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB , GL_TEXTURE_WRAP_T, GL_CLAMP); 
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB , 0,GL_FLOAT_RG32_NV, width, height, 0, GL_RGB, GL_FLOAT, NULL);

glBindTexture(GL_TEXTURE_RECTANGLE_ARB,texID[i]);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB , GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB , GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB , GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB , GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB , 0,GL_RGBA, width, height, 0, GL_RGBA, GL_FLOAT, NULL);

glBindTexture(GL_TEXTURE_RECTANGLE_ARB,backTexID[i]);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB , GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB , GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB , GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB , GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB , 0,GL_RGBA, width, height, 0, GL_RGBA, GL_FLOAT, NULL); 
}
glGenTextures(1, &colorBlenderID);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, colorBlenderID);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGB, width, height, 0, GL_RGB, GL_FLOAT, 0);

glGenFramebuffersEXT(1, &colorBlenderFBOID);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, colorBlenderFBOID);
 
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, colorBlenderID, 0);

glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, dualDepthfboID);
CHECK_GL_ERRORS;

int j = 0;
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, depthTexID[j], 0);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_RECTANGLE_ARB, texID[j], 0);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT2_EXT, GL_TEXTURE_RECTANGLE_ARB, backTexID[j], 0);
j = 1;
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT3_EXT, GL_TEXTURE_RECTANGLE_ARB, depthTexID[j], 0);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT4_EXT, GL_TEXTURE_RECTANGLE_ARB, texID[j], 0);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT5_EXT, GL_TEXTURE_RECTANGLE_ARB, backTexID[j], 0);

glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT6_EXT, GL_TEXTURE_RECTANGLE_ARB, colorBlenderID, 0);

GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
if(status == GL_FRAMEBUFFER_COMPLETE_EXT )
printf("OK for render to texture\n");
else
printf("Problem with FBO.");

glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}

The rendering code is similar to the front to back depth peeling except that now the code initialized the depths twice once for front and next for back fragments in the same pass. This is accompalished using Min/Max blending as follows
 

//matrix manipulation stuff..
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
// ---------------------------------------------------------------------
// 1. Initialize Min-Max Depth Buffer
// ---------------------------------------------------------------------
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, dualDepthfboID);

// Render targets 1 and 2 store the front and back colors
// Clear to 0.0 and use MAX blending to filter written color
// At most one front color and one back color can be written every pass
glDrawBuffers(2, &g_drawBuffers[1]);
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);

// Render target 0 stores (-minDepth, maxDepth, alphaMultiplier)
glDrawBuffer(g_drawBuffers[0]); 
glClear(GL_COLOR_BUFFER_BIT);
glBlendEquationEXT(GL_MAX_EXT);
g_shaderDualInit.bind();  
   RenderScene(angle);  
g_shaderDualInit.unbind();

//The init shader above assigns float2(-1,1) to gl_FragCoord.xy;

// ---------------------------------------------------------------------
// 2. Dual Depth Peeling + Blending
// ---------------------------------------------------------------------

// Since we cannot blend the back colors in the geometry passes,
// we use another render target to do the alpha blending
glDrawBuffer(g_drawBuffers[6]);
glClearColor(g_backgroundColor[0], g_backgroundColor[1], g_backgroundColor[2], 0);
glClear(GL_COLOR_BUFFER_BIT);

int currId = 0;
for (int pass = 1; g_useOQ || pass < NUM_PASSES; pass++) {
currId = pass % 2;
int prevId = 1 - currId;
int bufId = currId * 3;

glDrawBuffers(2, &g_drawBuffers[bufId+1]);
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);

glDrawBuffer(g_drawBuffers[bufId+0]);
glClearColor(-MAX_DEPTH, -MAX_DEPTH, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
// Render target 0: RG32F MAX blending
// Render target 1: RGBA MAX blending
// Render target 2: RGBA MAX blending
glDrawBuffers(3, &g_drawBuffers[bufId+0]);
glBlendEquationEXT(GL_MAX_EXT);
g_shaderDualPeel.bind();
g_shaderDualPeel.bindTextureRECT("DepthBlenderTex", depthTexID[prevId], 0);
g_shaderDualPeel.bindTextureRECT("FrontBlenderTex", texID[prevId], 1);
g_shaderDualPeel.setUniform("Alpha", (float*)&g_opacity, 1);
 RenderScene(angle);
g_shaderDualPeel.unbind();

// Full screen pass to alpha-blend the back color
glDrawBuffer(g_drawBuffers[6]);
glBlendEquationEXT(GL_FUNC_ADD);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if (g_useOQ) {
glBeginQuery(GL_SAMPLES_PASSED_ARB, g_queryId);
}

g_shaderDualBlend.bind();
g_shaderDualBlend.bindTextureRECT("TempTex", backTexID[currId], 0);
  DrawFullScreenQuad();
g_shaderDualBlend.unbind();

if (g_useOQ) {
glEndQuery(GL_SAMPLES_PASSED_ARB);
GLuint sample_count;
glGetQueryObjectuiv(g_queryId, GL_QUERY_RESULT_ARB, &sample_count);
if (sample_count == 0) {
break;
}
}
}
glDisable(GL_BLEND);
// ---------------------------------------------------------------------
// 3. Final Pass
// ---------------------------------------------------------------------
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
glDrawBuffer(GL_BACK);
g_shaderDualFinal.bind();
g_shaderDualFinal.bindTextureRECT("DepthBlenderTex", depthTexID[currId], 0);
g_shaderDualFinal.bindTextureRECT("FrontBlenderTex", texID[currId], 1);
g_shaderDualFinal.bindTextureRECT("BackBlenderTex", colorBlenderID, 2);
 DrawFullScreenQuad();
g_shaderDualFinal.unbind();

Thats it. The output this gives is as follows.
Dual Depth Peeling
The inset displays the front and the back fragments. On some requests, here is the link to the source codes for this demo.
https://dl.dropboxusercontent.com/u/5513476/DualDepthPeeling.zip
Note that you will need NVIDIA OpenGL sdk to compile and run this as I am using some classes from the sdk package

Enjoy !!!

Sunday, November 28, 2010

Order independent transparency (Front to back depth peeling)

I had been looking through a lot of docs on order independent transparency. I started off with http://developer.nvidia.com/object/order_independent_transparency.html
also called Front to Back Depth Peeling.
This is how it works.

You need three fbos, two fbos for depth sorting and one for final rendering. You need five fbo attachements in all, two for depth and two for front/back fragments and one texture for the final output. These are attached to the fbo as given in the setup code:
 
void setup_FBO() {
glGenFramebuffersEXT(2, fbo);
glGenTextures (2, texID);
glGenTextures (2, depthTexID);
for(int i=0;i<2;i++) {
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, depthTexID[i]);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB , GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB , GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB , GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB , GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB , 0,GL_DEPTH_COMPONENT32F_NV, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);

glBindTexture(GL_TEXTURE_RECTANGLE_ARB,texID[i]);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB , GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB , GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB , GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB , GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB , 0,GL_RGBA, width, height, 0, GL_RGBA, GL_FLOAT, NULL);

glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo[i]);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_RECTANGLE_ARB, depthTexID[i], 0);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, texID[i], 0);
}

glGenTextures(1, &colorBlenderID);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, colorBlenderID);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_FLOAT, 0);

glGenFramebuffersEXT(1, &colorBlenderFBOID);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, colorBlenderFBOID);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_RECTANGLE_ARB, depthTexID[0], 0);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, colorBlenderID, 0);

GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
if(status == GL_FRAMEBUFFER_COMPLETE_EXT )
printf("OK for render to texture\n");
else
printf("Problem");

glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}

Next, the rendering code uses this information as follows

//matrix setup and other stuff
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, colorBlenderFBOID);
glDrawBuffer(g_drawBuffers[0]);
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
//In the first pass, we render normally with depth test enabled to get the nearest surface
glEnable(GL_DEPTH_TEST);
g_shaderFrontInit.bind();
g_shaderFrontInit.setUniform("Alpha", (float*)&g_opacity, 1);
RenderScene(angle); //do your rendering here
g_shaderFrontInit.unbind();
// ---------------------------------------------------------------------
// 2. Depth Peeling + Blending
// ---------------------------------------------------------------------
int numLayers = (NUM_PASSES - 1) * 2;
for (int layer = 1; g_useOQ || layer < numLayers; layer++) {
int currId = layer % 2;
int prevId = 1 - currId;

glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo[currId]);
glDrawBuffer(g_drawBuffers[0]);
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
if (g_useOQ) {
glBeginQuery(GL_SAMPLES_PASSED_ARB, g_queryId);
}
g_shaderFrontPeel.bind();
g_shaderFrontPeel.bindTextureRECT("DepthTex", depthTexID[prevId], 0);
g_shaderFrontPeel.setUniform("Alpha", (float*)&g_opacity, 1);
RenderScene(angle);
g_shaderFrontPeel.unbind();

if (g_useOQ) {
glEndQuery(GL_SAMPLES_PASSED_ARB);
}
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, colorBlenderFBOID);
glDrawBuffer(g_drawBuffers[0]);
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFuncSeparate(GL_DST_ALPHA, GL_ONE,GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
g_shaderFrontBlend.bind();
g_shaderFrontBlend.bindTextureRECT("TempTex", texID[currId], 0);
DrawFullScreenQuad();
g_shaderFrontBlend.unbind();
glDisable(GL_BLEND);
if (g_useOQ) {
GLuint sample_count;
glGetQueryObjectuiv(g_queryId, GL_QUERY_RESULT_ARB, &sample_count);
if (sample_count == 0) {
break;
}
}
}

// ---------------------------------------------------------------------
// 3. Final Pass
// ---------------------------------------------------------------------
//remove the render to texture and set the back buffer as render target
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
glDrawBuffer(GL_BACK);
glDisable(GL_DEPTH_TEST);
glDisable(GL_BLEND);

//attach the blending shader
g_shaderFrontFinal.bind();
g_shaderFrontFinal.setUniform("BackgroundColor", g_backgroundColor, 3);
g_shaderFrontFinal.bindTextureRECT("ColorTex", colorBlenderID, 0);
DrawFullScreenQuad();
g_shaderFrontFinal.unbind();

Thats it, this gives the following output.
Dual Depth Peeling Front to Back

Next, I will post infor on how to do dual depth peeling in opengl

Tuesday, September 21, 2010

Comparing naive path tracing to bi-directional path tracing

Naive Path Tracing with 64 samples per pixel

Naive Path Tracing with 64 samples per pixel


Bi-Directional Path Tracing with 32 samples per pixel

Bi-Directional Path Tracing with 32 samples per pixel



Here I am comparing the results of naive path tracing (top) with bi-directional path tracing (bottom). The later gives much better results at almost half the number of samples per pixel. Check the reflections on the spheres.

Tuesday, September 14, 2010

Path tracing in GLSL

Finally, I was able to implement path tracing in GLSL. The results are stunning. Currently, my path tracer handles diffuse and specular shading. Adding refraction would be easy. Willl add it in a day or two. Here is the video. The demo runs at 15 fps with 128 samples per ray.

Tuesday, September 7, 2010

Area lights and soft shadows

I added area lights and soft shadows in my GLSL raytracer. The performance is quite good. The video contains 64 shadow rays per sample.

Sunday, September 5, 2010

GLSL based GPU Raytracer

GLSL














Googling online i got a lot of links on GPU based raytracing. But I could not find a simple implementation so I thought I would give it a try. Here is the output obtained through my glsl raytracer. I will publish the details and workable source code later.