how can I update the depth value in the fragment shader?

I am trying to do the surface based convex volume clipping(Source: Klaus book). Convex clipping approach are using three render passes.

First render pass: front-facing parts of the clipping object are rendered into the first depth image. The front faces are selectively drawn by using back-face culling (with glCullFace(GL BACK)).

Second render pass: the second render pass uses the back-facing geometry to fill the second depth image (culling with glCullFace(GL FRONT)).

Third render pass: the actual volume rendering via texture slicing—a fragment program accesses both depth textures and compares the depth of a fragment with the depth structure.

I am confusing whether I am doing this approach in my code correctly. In my main. First I have created frame buffer, and two depth textures for pass 1 and pass 2. Then the following code how I am doing two passes in the main.cpp file:

///////////////////////////////////////////////////
// Begin pass 1: render proxy geometry back faces to texture.
///////////////////////////////////////////////////

//Set pass uniform variable.
glUniform1i(UniformLoc::Pass, 1);

glBindFramebuffer(GL_FRAMEBUFFER, fbo); // Render to FBO.

glBindVertexArray(cubeVAOID); //Bind cube

glEnable(GL_DEPTH_TEST); //enable depth test

glDrawBuffer(GL_COLOR_ATTACHMENT0); //Out variable in frag shader will be written to the texture attached to GL_COLOR_ATTACHMENT0.

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //clear the back buffer to preset values ; buffers currently enabled for color writing.

glCullFace(GL_FRONT); //Culls only the front faces.

glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, 0);//Draw the cube

//Bind Front texture so we can read the texture in the shader

glActiveTexture(GL_TEXTURE0);

glBindTexture(GL_TEXTURE_2D, fbo_texture_DepthFront);

int fbo_texture_DepthFront_loc = glGetUniformLocation(shader_program, "SamplerDepthFront");

if (fbo_texture_DepthFront_loc != -1)

{

glUniform1i(fbo_texture_DepthFront_loc, 0); // binding the depth texture to texture unit 0

}

///////////////////////////////////////////////////

// Begin pass 2: render proxy geometry back faces to screen

///////////////////////////////////////////////////

glUniform1i(UniformLoc::Pass, 2);

glBindVertexArray(cubeVAOID); //Bind cube

glBindFramebuffer(GL_FRAMEBUFFER, 0); // back to default

glClear(GL_COLOR_BUFFER_BIT); //Clear the FBO attached texture.

glDrawBuffer(GL_BACK); // specify which color buffers are to be drawn into; Render to back color buffer.

glDisable(GL_DEPTH_TEST);

//Bind Back texture so we can read the texture in the shader

glActiveTexture(GL_TEXTURE1);

glBindTexture(GL_TEXTURE_2D, fbo_texture_DepthBack);

int fbo_texture_DepthBack_loc = glGetUniformLocation(shader_program, "SamplerDepthBack");

if (fbo_texture_DepthBack_loc != -1)

{

glUniform1i(fbo_texture_DepthBack_loc, 1); // binding the depth texture to texture unit 1

}

glCullFace(GL_BACK); //Culls only the back faces.

glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, 0);

I am confusing what should I do in the fragment shader.
For the pass I: update the depth value for Zfront and save the updated value in depth buffer.
For pass 2: update the depth value for Zback and save the updated value in depth buffer.
Pass 3: I am doing volume probing If a vertex position outside the clip plane discard.

If the above Pass I and Pass 2 process are correct, how can I update the depth value in the fragment shader?

Cross-ref:

You don’t update the depth value in the fragment shader; in fact, the fragment shader can be empty, i.e. just

void main() {}

So long as the FBO has a valid depth attachment and you haven’t disabled depth writes via glDepthMask, the depth will automatically be written to the depth attachment. Note that you shouldn’t bind the depth texture to a sampler variable (if a texture level can be both written via a FBO attachment and read via a sampler variable, that is considered a feedback loop and the results are undefined). Also, you don’t need any colour attachments for the first two passes, and you don’t need to enable GL_DEPTH_TEST for any pass.

In the third pass, both depth textures should be bound to sampler variables (and not bound to the framebuffer). The fragment shader should compare gl_FragCoord.z to the values obtained from those textures (typically obtained using texelFetch with ivec2(gl_FragCoord.xy) as the coordinates).

Thanks for helping me with this problem.

I did not get the part of the second paragraph. Did you mean in the main.cpp file I should create another pass, that is pass 3? In the pass 3 of main.cpp file I will bound my both depth textures with the samplers variables of shader.

Also, I did not get " compare gl_FragCoord.z to the values obtained from those textures" and what texelFetch with ivec2(gl_FragCoord.xy) does? As I know, when we use texelFetch, it treats the texture as an image, so I can access exactly the content of the pixel. I am not getting how texelFetch related to the depth value.

Once again that you so much for your help.

First pass renders the front faces of the clipping volume to a depth texture. Second pass renders the back faces of the clipping volume to a different depth texture. Third pass does the actual rendering, discarding fragments which are closer than those from the first pass or farther than those from the second pass.

So in the first and second passes, a depth-format texture is bound to the depth attachment of a framebuffer object. In the third pass, the two depth textures are bound to texture units which are accessed via sampler2D variables. During rendering, gl_FragCoord.xy contain the window coordinates of the centre of the current fragment while gl_FragCoord.z contains its depth (the value which will be used for depth testing if enabled and/or written to the depth buffer if one exists). To clip the rendering, you compare the current fragment’s depth to the corresponding pixel from both depth textures, e.g.:

uniform sampler2D depth_near;
uniform sampler2D depth_far;

void main()
{
    float near = texelFetch(depth_near, ivec2(gl_FragCoord.xy), 0);
    float far  = texelFetch(depth_far , ivec2(gl_FragCoord.xy), 0);
    if (gl_FragCoord.z < near || gl_FragCoord.z > far)
        discard;
    ...
}

Thank you so much for the details and your help. Inside the raycasting, I am sending each vertex position to the plane equation. If the vertex position is positive it is discarded. texelFetch returns the vec4, so I am comparing it with the third component. Still, my depth buffer is not working. The following thing I have done in the shader:

void main()
{
                ...............................
                float clip_value_point = point_position(pos.x, pos.y, pos.z);
                if ( clip_value_point > 0)
        		{    			
        			vec4 near = texelFetch(SamplerDepthFront, ivec2(gl_FragCoord.xy), 0);
        			vec4 far  = texelFetch(SamplerDepthBack , ivec2(gl_FragCoord.xy), 0);
        			if (gl_FragCoord.z < near.a || gl_FragCoord.z > far.a)
        			discard;
        		}
        		else
        		{
        			fragColor = texture(volume, texPos);	
         		}
                 .......................
}

I have changes Pass 1,2 and 3 according to your suggestion. The following part is my Passing code.

///////////////////////////////////////////////////
// Begin pass 1: render proxy geometry back faces to texture.
 ///////////////////////////////////////////////////

//Set pass uniform variable.
glUniform1i(UniformLoc::Pass, 1);

glBindFramebuffer(GL_FRAMEBUFFER, fbo); // Render to FBO.
glBindVertexArray(cubeVAOID);		//Bind cube

//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //clear the back buffer to preset values ; buffers currently enabled for color writing.									
//glEnable(GL_DEPTH_TEST); //enable depth test	

glDrawBuffer(GL_COLOR_ATTACHMENT0); //Out variable in frag shader will be written to the texture attached to GL_COLOR_ATTACHMENT0.	
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //clear the back buffer to preset values ; buffers currently enabled for color writing.									

glCullFace(GL_FRONT);   //Culls only the front faces.
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, 0);//Draw the cube		

///////////////////////////////////////////////////
// Begin pass 2: render proxy geometry back faces to screen
///////////////////////////////////////////////////
glUniform1i(UniformLoc::Pass, 2);

glBindFramebuffer(GL_FRAMEBUFFER, fbo); // Render to FBO.
glBindVertexArray(cubeVAOID);		//Bind cube		

glDrawBuffer(GL_COLOR_ATTACHMENT1); //Out variable in frag shader will be written to the texture attached to GL_COLOR_ATTACHMENT1.	
glClear(GL_COLOR_BUFFER_BIT); //Clear the FBO attached texture.

glDrawBuffer(GL_BACK); // specify which color buffers are to be drawn into; Render to back color buffer.

//glDisable(GL_DEPTH_TEST);	

glCullFace(GL_BACK);    //Culls only the back faces.
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, 0);

///////////////////////////////////////////////////
// Begin pass 3: volume rendering via texture slicing
///////////////////////////////////////////////////
glUniform1i(UniformLoc::Pass, 3);

glBindVertexArray(cubeVAOID);		//Bind cube		
glBindFramebuffer(GL_FRAMEBUFFER, 0); // back to default
	
//Bind Front texture so we can read the texture in the shader
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, fbo_texture_DepthFront);
int fbo_texture_DepthFront_loc = glGetUniformLocation(shader_program, "SamplerDepthFront");
if (fbo_texture_DepthFront_loc != -1)
{
	glUniform1i(fbo_texture_DepthFront_loc, 0); // binding the depth texture to texture unit 0
}

//Bind Back texture so we can read the texture in the shader
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, fbo_texture_DepthBack);
int fbo_texture_DepthBack_loc = glGetUniformLocation(shader_program, "SamplerDepthBack");
if (fbo_texture_DepthBack_loc != -1)
{
	glUniform1i(fbo_texture_DepthBack_loc, 1); // binding the depth texture to texture unit 1
}

glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, 0);

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

You need to access the first component (near.x, near.r, near.s or near[0], likewise for far).

It’s not clear what you mean by

If you’re calculating a depth value rather than using the depth calculated by rasterisation, you need to use the calculated depth instead of gl_FragCoord.z.

Thank you so much for your response. I really appreciate your help.

I was using the plane as clipping geometry. Convex volume algorithm says to use a cube as clip geometry, which means volume dataset inside cube will visible and outside will be cut away.

Actually, first I implemented volume rendering using single-pass GPU ray casting(source: Crookbook, page:228)

Volume rendering single pass algo:
“First, the camera ray direction is calculated by subtracting the vertex positions from the
camera position. This gives the ray marching direction. The initial ray position (that is, the ray
entry position) is the vertex position. Then based on the ray step size, the initial ray position
is advanced in the ray direction using a loop. The volume dataset is then sampled at this
position to obtain the density value. This process is continued forward advancing the current
ray position until either the ray exits the volume dataset or the alpha value of the color is
completely saturated.”

To apply the single pass ray-casting approach the following step I did:
Step 1: First I draw a cube bound it with the VAO and VBO
Step 2: In the vertex shader calculated the texture coordinate (mapped the cube vertex position with the texture(in my case volume dataset) ). Then pass the texture coordinate to fragment shader.
Step 3: I applied the raycasting algorithm for volume rendering.

Code:(some basic part of volume rendering)

  {
         rayStart = camPos;
         rayStop = vPos;
         vec3 direction = normalize(rayStop - rayStart); 

         vec3 pos = rayStop;
         vec3 step = direction*stepSize;	

        float step_length = stepSize;

        //// Loop for ray traversal
        for (int i = 0; i < MAX_SAMPLES; i++) {

        dist += step_length;
        ......................................................
        .........................................................
        // Advance ray position along ray direction
        pos = pos + step;
        ..........................................................
        ........................................................
        // Ray termination:
        vec3 temp1 = sign(texPos-texMin);
        vec3 temp2 = sign(texMax-texPos);
        float inside = dot(temp1, temp2);		
        		
        //if the stopping condition is true we brek out of the ray marching loop
        if (inside < 3) 
        	break;
        ..........................................
        .............................................

}

My question is do I need to add another cube for clip geometry?
If I do not use another cube, the following line code will be work on ray trading?

    vec4 near = texelFetch(depth_near, ivec2(gl_FragCoord.xy), 0);
    vec4 far  = texelFetch(depth_far , ivec2(gl_FragCoord.xy), 0);
    if (gl_FragCoord.z < near.r || gl_FragCoord.z > far.r)
        discard;

I really appreciate the time you are giving me understanding volume clipping.

If you’re raycasting, there’s no need for a clip volume. While you could use near and far for the bounds of the loop, you’d need to transform them from NDC to object space. The “convex clip volume” algorithm is designed for clipping primitives which are rendered using fixed-function rasterisation (i.e. interpolating vertex coordinates).

For volumetric raycasting, I’d just render the front faces, passing the object-space position and ray direction from the vertex shader to the fragment shader. The fragment shader would iterate until one of the object-space coordinates goes outside the cube bounds.

I am trying to clear what I understand form you comment. I am using raycasting technique for volume rendering, that’s why I do not need any more volume as a clip geometry.

Do I need Pass 1, Pass 2 and Pass 3 form “convex volume clipping algorithm” or Only Pass 1 I need, where I am selecting the front face.

Right now, in the volume rendering algorith, I am calculation raystart from the camera position and ray stop with the vertex position of my cube( clip space).

Please correct me if I am wrong. Once again thank you so much for your help.

You only need one pass. Whether to render the front or back faces depends upon whether the viewpoint (camera) can be inside the cube (or close enough that the near plane intersects the cube). If it can, render the back faces and start the iteration at the viewpoint. If it can’t, render the front faces and start the iteration at the front face.