volume rendering (using glsl) with ray casting algorithm

toolchainX picture toolchainX · Feb 28, 2012 · Viewed 19.1k times · Source

I am learning volume rendering using ray casting algorithm. I have found a good demo and tuturial in here. but the problem is that I have a ATI graphic card instead of nVidia which make me can't using the cg shader in the demo, so I want to change the cg shader to glsl shader. I have gone through the red book (7 edition) of OpenGL, but not familiar with glsl and cg. does anyone can help me change the cg shader in the demo to glsl? or is there any materials to the simplest demo of volume rendering using ray casting (of course in glsl). here is the cg shader of the demo. and it can work on my friend's nVidia graphic card. what most confusing me is that I don't know how to translate the entry part of cg to glsl, for example:

struct vertex_fragment
 {
   float4 Position    : POSITION; // For the rasterizer
   float4 TexCoord    : TEXCOORD0; 
   float4 Color       : TEXCOORD1;
   float4 Pos         : TEXCOORD2;
 };

what's more, I can write a program bind 2 texture object with 2 texture unit to the shader provided that I assign two texcoord when draw the screen, for example

glMultiTexCoord2f(GL_TEXTURE0, 1.0, 0.0);

glMultiTexCoord2f(GL_TEXTURE1, 1.0, 0.0);

In the demo the program will bind to two texture (one 2D for backface_buffer one 3D for volume texture), but with only one texture unit like glMultiTexCoord3f(GL_TEXTURE1, x, y, z); I think the GL_TEXTURE1 unit is for the volume texture, but which one (texure unit) is for the backface_buffer? as far as I know in order to bind texture obj in a shader, I must get a texture unit to bind for example:

glLinkProgram(p);   
texloc = glGetUniformLocation(p, "tex");
volume_texloc = glGetUniformLocation(p, "volume_tex");
stepsizeloc = glGetUniformLocation(p, "stepsize");
glUseProgram(p);
glUniform1i(texloc, 0); 
glUniform1i(volume_texloc, 1); 
glUniform1f(stepsizeloc, stepsize);
  //When rendering an object with this program.
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, backface_buffer);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_3D, volume_texture);

the program is compiled fine and linked ok. but I only got -1 of all three location(texloc, volume_texloc and stepsizeloc). I know it may be optimized out. anyone can help me translate the cg shader to glsl shader?

Edit: If you are interest in modern OpenGL API implementation(C++ source code) with glsl:Volume_Rendering_Using_GLSL

Answer

toolchainX picture toolchainX · Feb 29, 2012

Problem solved. the glsl version of the demo:

vertex shader

void main()
{
    gl_Position = gl_ModelViewProjectionMatrix*gl_Vertex;
    //gl_FrontColor = gl_Color;
    gl_TexCoord[2] = gl_Position;
    gl_TexCoord[0] = gl_MultiTexCoord1;
    gl_TexCoord[1] = gl_Color;
}

fragment shader

uniform sampler2D tex;
uniform sampler3D volume_tex;
uniform float stepsize;

void main()
{
    vec2 texc = ((gl_TexCoord[2].xy/gl_TexCoord[2].w) + 1) / 2;
    vec4 start = gl_TexCoord[0];
    vec4 back_position = texture2D(tex, texc);
    vec3 dir = vec3(0.0);
    dir.x = back_position.x - start.x;
    dir.y = back_position.y - start.y;
    dir.z = back_position.z - start.z;
    float len = length(dir.xyz); // the length from front to back is calculated and used to terminate the ray
    vec3 norm_dir = normalize(dir);
    float delta = stepsize;
    vec3 delta_dir = norm_dir * delta;
    float delta_dir_len = length(delta_dir);
    vec3 vect = start.xyz;
    vec4 col_acc = vec4(0,0,0,0); // The dest color
    float alpha_acc = 0.0;                // The  dest alpha for blending
    float length_acc = 0.0;
    vec4 color_sample; // The src color 
    float alpha_sample; // The src alpha

    for(int i = 0; i < 450; i++)
    {
      color_sample = texture3D(volume_tex,vect);
      //  why multiply the stepsize?
      alpha_sample = color_sample.a*stepsize;
      // why multply 3?
      col_acc   += (1.0 - alpha_acc) * color_sample * alpha_sample*3 ;
      alpha_acc += alpha_sample;
      vect += delta_dir;
      length_acc += delta_dir_len;
      if(length_acc >= len || alpha_acc > 1.0) 
        break; // terminate if opacity > 1 or the ray is outside the volume
    }

    gl_FragColor =  col_acc;
}

if you seen the original shader of cg there is only a little difference between cg and glsl. the most difficult part to translate the demo to glsl version is that the cg function in the opengl such as:

param = cgGetNamedParameter(program, par); 
cgGLSetTextureParameter(param, tex); 
cgGLEnableTextureParameter(param);

encapsulate the process of texture unit and multitexture activation (using glActiveTexture) and deactivation, which is very important in this demo as it used the fixed pipeline as well as programmable pipeline. here is the key segment changed in the function void raycasting_pass() of main.cpp of the demo in Peter Triers GPU raycasting tutorial:

function raycasting_pass

void raycasting_pass()
{
    // specify which texture to bind
    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, 
        GL_TEXTURE_2D, final_image, 0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    glUseProgram(p);
    glUniform1f(stepsizeIndex, stepsize);
    glActiveTexture(GL_TEXTURE1);
    glEnable(GL_TEXTURE_3D);
    glBindTexture(GL_TEXTURE_3D, volume_texture);
    glUniform1i(volume_tex, 1); 
    glActiveTexture(GL_TEXTURE0);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, backface_buffer);
    glUniform1i(tex, 0); 

    glUseProgram(p);
    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);
    drawQuads(1.0,1.0, 1.0);  // Draw a cube
    glDisable(GL_CULL_FACE);
    glUseProgram(0);
    // recover to use only one texture unit as for the fixed pipeline
    glActiveTexture(GL_TEXTURE1);
    glDisable(GL_TEXTURE_3D);
    glActiveTexture(GL_TEXTURE0);
}

That's it.