Possibility to write message to Daniel Rákos.


#1

Hi guys!

Does anybody know a way to write message to Daniel Rákos, the author of many good articles on http://rastergrid.com ?

The fact is his posts did help me a lot.
It seems I have found a bug in his HiZ algorithm, and I need to notice him about that.

But I can see the http://rastergrid.com is not live enough.

Could you answer the question or let me know if my action doesn’t matter?

Thank you!


#2

Try:

Doesn’t look like he’s posted here for ~5 years, so I linked to his Twitter page as well.


#3

Hi,

I was just informed that people are looking for me here :slight_smile:

Indeed it’s quite possible the HiZ demo has a bug as I recall executing it a while back and noticed some popping artifacts. Maybe it’s the bug you’ve found, not sure as I didn’t look into the issue.

Feel free to post the info on the bug here (assuming moderators don’t get mad about hijacking the forum for such purpose), or send me a PM here, on twitter or wherever else you find me.
I don’t promise that I’ll post a fixed version of the demo on the blog right away, but I’ll try to do so when I get a chance to clean it all up (indeed it has been sleeping for a long time, just as my forum account). Probably I should also just grab all the source and executables and post it on github instead.

Cheers,
Daniel


#4

Hi, guys!

Photon, thank you for help!

Daniel, glad to see you here!

In HiZ demo You are culling objects using bounding boxes.
If no one of vertices located inside the frustum, then object be rejected.

So, if You have huge flat object, there is interesting case appeared.
See picture (sorry for format of that, but it most easy way to make it):

As show my picture, such object definitely must be drawn.

There was one point.

There is the second:

You are perform frustum filter and if the object pass it, then You are perform Occlusion filter.
Even if disable frustum filter and try to use just ‘Occlusion filter’, the huge flat object make renderer do z-fighting.
The object been drawn each odd frame, and it been occluded by himself each even frame.
I feel my english not correct. I’m not native speaker, sorry for that. I hope you will understand what I mean.

I can’t localize the reason of second point yet.
I’m going to implement tests on GPU side with compute shader to be able to catch reasons of such problems easy.
But it long story, because I’m a beginner and OpenGl isn’t my basic activity, just hobby.

May be, You can realize the reason immediately using whole your opengl experience according the first point with BB and frustum.
Also, I’m not sure if it is not my own bug.

I hope it will be useful for You.

Best, regards.

PS: thank you for yours articles!


#5

[QUOTE=nimelord;397995]In HiZ demo You are culling objects using bounding boxes.
If no one of vertices located inside the frustum, then object be rejected.

So, if You have huge flat object, there is interesting case appeared.[/QUOTE]

That’s not exactly how it works. It actually doesn’t just determine whether each vertex is located inside the frustum, but rather does something similar to the Cohen-Sutherland algorithm.

It counts in the elements of outOfBounds whether any of the vertices are outside a particular clip plane. 6 elements for the 6 planes of the frustum.
It only actually culls the object if all 8 corner points of the bounding box fall outside of a particular clip plane (one of the elements of the outOfBounds array is 8).

Thus your example in the picture should not get culled because there’s not a single clip plane for which all 8 points are outside, there will be only 4 points outside of the left, right, bottom, and top clip planes, and 0 points outside of the front and rear clip planes.

Now I’m not saying there isn’t a bug somewhere in the shader, but looking at its source the logic is as described above.

[QUOTE=nimelord;397995]You are perform frustum filter and if the object pass it, then You are perform Occlusion filter.
Even if disable frustum filter and try to use just ‘Occlusion filter’, the huge flat object make renderer do z-fighting.
The object been drawn each odd frame, and it been occluded by himself each even frame.
I feel my english not correct. I’m not native speaker, sorry for that. I hope you will understand what I mean.

I can’t localize the reason of second point yet.
I’m going to implement tests on GPU side with compute shader to be able to catch reasons of such problems easy.
But it long story, because I’m a beginner and OpenGl isn’t my basic activity, just hobby.[/QUOTE]

IIRC I was simply using the previous frame’s depth buffer which is the traditional and simple way to do Hi-Z culling, but also inaccurate, as obviously due to camera movement testing against the previous frame’s depth buffer can result in false positives and false negatives. Better alternatives are doing a depth-reprojection according to the new camera location, etc.

Not sure that’s the root cause why you see a flickering effect if that’s what you mean by z-fighting, but it’s possible. Though the term z-fighting is actually what is used to describe the phenomenon when two pieces of geometry are almost at the same location in space and partially overlap each other on the screen due to precision limitations, which I can’t seem to see how could happen due to Hi-Z if you don’t actually have overlapping objects.

I know this doesn’t help a whole lot, but it’s been a very long time since I wrote that code.


#6

[QUOTE=aqnuep;397996]That’s not exactly how it works. It actually doesn’t just determine whether each vertex is located inside the frustum, but rather does something similar to the Cohen-Sutherland algorithm.

It counts in the elements of outOfBounds whether any of the vertices are outside a particular clip plane. 6 elements for the 6 planes of the frustum.
It only actually culls the object if all 8 corner points of the bounding box fall outside of a particular clip plane (one of the elements of the outOfBounds array is .

Thus your example in the picture should not get culled because there’s not a single clip plane for which all 8 points are outside, there will be only 4 points outside of the left, right, bottom, and top clip planes, and 0 points outside of the front and rear clip planes.[/QUOTE]

I looked at shader more carefully, and of course You are right.
There is no problem in frustum culligh path.

Yes, I’m using reprojected depth buffer from previous frame.
Also I can say that movement is not reason, because the problem can be reproduced without any movements between frames.

The flickering effect is not related with ‘z-fighting’. I was wrong using that term.

The problem definitely located in the ‘occlusion culling’ pass.
Actually I can reproduce the flickering with only cube.
When the camera is very close to it’s face I’m making some precessions and in the some orientation I can catch the flickering state.

It may be problem is when some of vertices (A, B, C) of ‘bounding box’ is behind the frustum, and any of others is located farter than the fartest pixel of a intersection area (colored in the blue: current HiZ layer)?

see picture:
[ATTACH=CONFIG]1960[/ATTACH]

What is happening with vertices behind the frustum in such case?


#7

My guess is that due to precision issues or the wrong use of comparison operations your object occludes itself every even frame.

The solution is to make sure you use > vs >= as the culling criteria (as in my sample) and potentially apply a small bias (e.g. BoundingBoxDepth - EPSILON > HiZDepth) to account for any precision issues you might have in your pipe (e.g. due to using a lower precision depth buffer). Also make sure you correctly calculate the max for both the bounding box and the 4 samples from the Hi-Z image.

In addition, you might want to look into your use (or lack of use) of depth clamping and its effect on your algorithm if you’re having problems when the object intersects the near or far plane.


#8

I’m pretty sure that case is the nearest BB vertices located behind the frustum , but other vertices (with positive w components) behind the farthest pixel of HiZ.
I mean the case is when the camera inside the bounding box.

For example see a stump:
[ATTACH=CONFIG]1961[/ATTACH]

When the camera is located to close the stump and stump surface covers whole frame, then camera located inside the BB.
And in such position every pixel of stump surface located more close to camera then all other pixels of BB which located in front of near z pane of camera frustum.

I’m using exact your vertex shader (excluding some names).

When whole BB located in front of camera, then HiZ Occlusion Culling is working well.

I’ll use this case to implement auto tests on the GPU side.
I think it is enough not to be like a blind kitten.
Every time I do not know that is happening of GPU side. :slight_smile:

Daniel, thank you very much for the consultations.

PS: If I will localize the bug on your side, I’ll send you PM on this forum. :slight_smile:


#9

[QUOTE=aqnuep;398053]My guess is that due to precision issues or the wrong use of comparison operations your object occludes itself every even frame.

The solution is to make sure you use > vs >= as the culling criteria (as in my sample) and potentially apply a small bias (e.g. BoundingBoxDepth - EPSILON > HiZDepth) to account for any precision issues you might have in your pipe (e.g. due to using a lower precision depth buffer). Also make sure you correctly calculate the max for both the bounding box and the 4 samples from the Hi-Z image.

In addition, you might want to look into your use (or lack of use) of depth clamping and its effect on your algorithm if you’re having problems when the object intersects the near or far plane.[/QUOTE]

It seems I found the bug. :slight_smile:

In a test stand I used just box.
I placed the camera very close in front of the box and rotated it to right through 3 degrees.

The culling vertex shader from “Mountains” demo:

#version 400 core

uniform transform {
	mat4 ModelViewMatrix;
	mat4 ProjectionMatrix;
	mat4 InfProjectionMatrix;
	mat4 MVPMatrix;
	vec4 Viewport;
} Transform;

uniform vec2 Offset;
uniform vec3 Extent;

uniform sampler2D HiZBuffer;

vec4 BoundingBox[8];

subroutine int CullRoutineType(vec3 InstPos);

subroutine(CullRoutineType)
int PassThrough(vec3 InstPos) {
	/* always return 1 */
	return 1;
}

subroutine(CullRoutineType)
int InstanceCloudReduction(vec3 InstPos) {
    /* create the bounding box of the object */
    BoundingBox[0] = Transform.MVPMatrix * vec4( InstPos + vec3( Extent.x, Extent.y, Extent.z), 1.0 );
    BoundingBox[1] = Transform.MVPMatrix * vec4( InstPos + vec3(-Extent.x, Extent.y, Extent.z), 1.0 );
    BoundingBox[2] = Transform.MVPMatrix * vec4( InstPos + vec3( Extent.x,-Extent.y, Extent.z), 1.0 );
    BoundingBox[3] = Transform.MVPMatrix * vec4( InstPos + vec3(-Extent.x,-Extent.y, Extent.z), 1.0 );
    BoundingBox[4] = Transform.MVPMatrix * vec4( InstPos + vec3( Extent.x, Extent.y,-Extent.z), 1.0 );
    BoundingBox[5] = Transform.MVPMatrix * vec4( InstPos + vec3(-Extent.x, Extent.y,-Extent.z), 1.0 );
    BoundingBox[6] = Transform.MVPMatrix * vec4( InstPos + vec3( Extent.x,-Extent.y,-Extent.z), 1.0 );
    BoundingBox[7] = Transform.MVPMatrix * vec4( InstPos + vec3(-Extent.x,-Extent.y,-Extent.z), 1.0 );
   
    /* check how the bounding box resides regarding to the view frustum */   
    int outOfBound[6] = int[6]( 0, 0, 0, 0, 0, 0 );

    for (int i=0; i<8; i++)
    {
        if ( BoundingBox[i].x >  BoundingBox[i].w ) outOfBound[0]++;
        if ( BoundingBox[i].x < -BoundingBox[i].w ) outOfBound[1]++;
        if ( BoundingBox[i].y >  BoundingBox[i].w ) outOfBound[2]++;
        if ( BoundingBox[i].y < -BoundingBox[i].w ) outOfBound[3]++;
        if ( BoundingBox[i].z >  BoundingBox[i].w ) outOfBound[4]++;
        if ( BoundingBox[i].z < -BoundingBox[i].w ) outOfBound[5]++;
    }

	int inFrustum = 1;

    for (int i=0; i<6; i++)
        if ( outOfBound[i] == 8 ) inFrustum = 0;

    return inFrustum;
}

subroutine(CullRoutineType)
int HiZOcclusionCull(vec3 InstPos) {
    /* first do instance cloud reduction */
	if ( InstanceCloudReduction(InstPos) == 0 ) return 0;
	
	/* perform perspective division for the bounding box */
	for (int i=0; i<8; i++)
		BoundingBox[i].xyz /= BoundingBox[i].w;
	
	/* calculate screen space bounding rectangle */
	vec2 BoundingRect[2];
	BoundingRect[0].x = min( min( min( BoundingBox[0].x, BoundingBox[1].x ),
								  min( BoundingBox[2].x, BoundingBox[3].x ) ),
							 min( min( BoundingBox[4].x, BoundingBox[5].x ),
								  min( BoundingBox[6].x, BoundingBox[7].x ) ) ) / 2.0 + 0.5;
	BoundingRect[0].y = min( min( min( BoundingBox[0].y, BoundingBox[1].y ),
								  min( BoundingBox[2].y, BoundingBox[3].y ) ),
							 min( min( BoundingBox[4].y, BoundingBox[5].y ),
								  min( BoundingBox[6].y, BoundingBox[7].y ) ) ) / 2.0 + 0.5;
	BoundingRect[1].x = max( max( max( BoundingBox[0].x, BoundingBox[1].x ),
								  max( BoundingBox[2].x, BoundingBox[3].x ) ),
							 max( max( BoundingBox[4].x, BoundingBox[5].x ),
								  max( BoundingBox[6].x, BoundingBox[7].x ) ) ) / 2.0 + 0.5;
	BoundingRect[1].y = max( max( max( BoundingBox[0].y, BoundingBox[1].y ),
								  max( BoundingBox[2].y, BoundingBox[3].y ) ),
							 max( max( BoundingBox[4].y, BoundingBox[5].y ),
								  max( BoundingBox[6].y, BoundingBox[7].y ) ) ) / 2.0 + 0.5;
	/* then the linear depth value of the front-most point */
	float InstanceDepth = min( min( min( BoundingBox[0].z, BoundingBox[1].z ),
									min( BoundingBox[2].z, BoundingBox[3].z ) ),
							   min( min( BoundingBox[4].z, BoundingBox[5].z ),
									min( BoundingBox[6].z, BoundingBox[7].z ) ) );

	/* now we calculate the bounding rectangle size in viewport coordinates */
	float ViewSizeX = (BoundingRect[1].x-BoundingRect[0].x) * Transform.Viewport.y;
	float ViewSizeY = (BoundingRect[1].y-BoundingRect[0].y) * Transform.Viewport.z;
	
	/* now we calculate the texture LOD used for lookup in the depth buffer texture */
	float LOD = ceil( log2( max( ViewSizeX, ViewSizeY ) / 2.0 ) );
	
	/* finally fetch the depth texture using explicit LOD lookups */
	vec4 Samples;
	Samples.x = textureLod( HiZBuffer, vec2(BoundingRect[0].x, BoundingRect[0].y), LOD ).x;
	Samples.y = textureLod( HiZBuffer, vec2(BoundingRect[0].x, BoundingRect[1].y), LOD ).x;
	Samples.z = textureLod( HiZBuffer, vec2(BoundingRect[1].x, BoundingRect[1].y), LOD ).x;
	Samples.w = textureLod( HiZBuffer, vec2(BoundingRect[1].x, BoundingRect[0].y), LOD ).x;
	float MaxDepth = max( max( Samples.x, Samples.y ), max( Samples.z, Samples.w ) );
	
	/* if the instance depth is bigger than the depth in the texture discard the instance */
	return ( InstanceDepth > MaxDepth ) ? 0 : 1;
}

subroutine uniform CullRoutineType CullRoutine; 

layout(location = 0) in vec3 InstancePosition;

out vec3 InstPos;
flat out int Visible;

void main(void) {
	InstPos.x = InstancePosition.x + Offset.x;
	InstPos.y = InstancePosition.y;
	InstPos.z = InstancePosition.z + Offset.y;
	Visible = CullRoutine(InstPos);
}

Some values from the shader:

  1. BoundingBox[] after initialization:

v0 (0,33  0,87  0,03  0,03)
v1 (-0,33  0,87  -0,03  -0,02)
v2 (0,33  -0,87  0,03  0,03)
v3 (-0,33  -0,87  -0,03  -0,02)
v4 (0,29  0,87  1,02  1,03)
v5 (-0,36  0,87  0,97  0,97)
v6 (0,29  -0,87  1,02  1,03)
v7 (-0,36  -0,87  0,97  0,97)

As we can see v1 and v3 have negative value because those vertices located behind the camera.
But visible surface of the cube is in front of the camera.

  1. BoundingBox[] after translation:

the translation:


for (int i=0; i<8; i++)
		BoundingBox[i].xyz /= BoundingBox[i].w;

BoundingBox[]:


(11,57  30,75  0,93  0,03)
(13,49  -35,83  1,08  -0,02)
(11,57  -30,75  0,93  0,03)
(13,49  35,83  1,08  -0,02)
(0,28  0,84  1,00  1,03)
(-0,37  0,89  1,00  0,97)
(0,28  -0,84  1,00  1,03)
(-0,37  -0,89  1,00  0,97)

Int the next step the algorithm select min z component of all vertices of translated BoundingBox[].
As we can see v0 and v2 have min z, but the algorithm must use v1 and v3 as we already know.
It happens because v1 and v3 have negative w component.

As I understand the Homogeneous coordinates, z component shouldn’t change the sign.

So, the fix is to make translation of BoundingBox[] in other way:


  for (int i=0; i<8; i++) {
    BoundingBox[i].xy /= BoundingBox[i].w;
    BoundingBox[i].z /= abs(BoundingBox[i].w);
  }

Now my app culling objects correctly.

Let me know please, am I right or I just have even number of bugs in my code, now?

Have a good day!