Followers 0

# Help with GPU Pro 5 Hi-Z Screen Space Reflections

## 78 posts in this topic

Hi Bruzer100,

I was also preparing to start implementing something similar, and was disappointed to find that the code was unavailable, especially since several places in the chapter specifically tell the reader to consult the source.  I'm sure when I do start my implementation in the next few days, I'll probably run into similar issues like the ones you've come across, so if you wouldn't mind sharing what hurdles you've had to work around that weren't called out, or even want to share your implementation, it would be highly appreciated.  I'll bookmark this thread so that when I do start my implementation I can add anything I find to be valuable, especially if it was left out of the book's chapter.

Thanks,

WFP

0

##### Share on other sites

Hi guys,

Same boat as you. Are you doing the ray marching in screen or view space?

On page 174, it's not clear to me how the function intersectDepthPlane should work:

float3 o = intersectDepthPlane(p.xy, d.xy, -p.z);

Is this a type-o or am I missing something? Should it be 'p.xyz' since it should be re-projecting the point onto the near plane (o.z = 0)? The method can't assume the point returned is always at z=0 since it's also used to calculate the tmpRay position during the ray march (which needs to keep track of the .z component)

I would expect the method to look like this: (?)

float3 intersectDepthPlane(float3 p, float2 d, float z)

{

return p + float3(d, 1) * z;

}

Not sure how intersectCellBoundary works either with the crossStep and crossOffset... (why need these two helper variables? Why saturate the cross direction?)

Cheers!

Jp

0

##### Share on other sites

Also:

static const float2 hiZSize = cb_screenSize; // not sure if correct - this is mip level 0 size

I was also wondering about this. To me it would make sense that this should be the size of the mip_level we are starting the ray march from. Since it is used to do the first cell boundary test, but the name of the variable seems to imply otherwise :) Not sure either. Feels great to have other people to chat about this :D

Jp

0

##### Share on other sites
Hey Jp,

Great to see you're making some good headway on this. I'm looking forward to seeing how your translation to HLSL works out.

I realize I forgot to answer a question of yours yesterday, but I think you figured out the answer anyway - I am doing the ray marching in screen space.

Regarding the min-max tracing, what the author means is that when you're creating your hi-z buffer, you save not only the minimum depth value [min(min(value.x, value.y), min(value.z, value.w))], you also store the maximum value as well [max(max(value.x, value.y), max(value.z, value.w))]. What this gives you is a better estimation of the depth of the object at the pixel you're currently processing. You can use this in the ray-tracing pass to walk behind an object - if the current ray depth (from O + D * t) is outside the range of [min, max] at that pixel, you know it's not intersecting and can continue marching along that ray without further processing at the current position. I do not have this in my implementation yet, as I'm just trying to get the basics working first.

That's a good idea you had concerning the hiZSize, and this evening when I get back home (mine is a hobby project at the moment, so I work on it in my free time), I will try setting it to something like hiZSize = cb_screenSize / exp2(HI_Z_START_LEVEL). One of my issues could very well be that I'm not taking a large enough step away from the starting point to begin with.

Glad to have another person to bounce ideas back and forth with!

-WFP

(Edit: formatting and grammar)
0

##### Share on other sites

Hi,

I've spent just a moment this evening so far working some more on this, and it does seem that changing the hiZSize to what was mentioned above helps out some.  It is now defined as

static const float2 hiZSize = cb_screenSize / exp2(HIZ_START_LEVEL);

It still needs some refinement, but I feel confident that either a small linear search or a bias like Bruzer mentioned will help.

The main issues I'm facing now are these stair-step-like artifacts that show up.  I've attached links to two images showing what I'm talking about.

Any idea what causes artifacts like this?  Bruzer mentioned some stair-like artifacts he was seeing due to using a floor() command where he didn't need one, but I only have the one there to make sure the cell is an integer and removing that doesn't remove the artifacts.

I'm almost wondering if I'm not doing something in the "setup" code incorrectly - that is, the code leading up to the hiZTrace() call where I'm getting the position and ray direction.  Maybe someone could give it a once-over to see if I've missed something there?

Thanks,

WFP

https://www.dropbox.com/s/3rby7uwi2vugnw0/screenshot_4.png

https://www.dropbox.com/s/hs6ygesn1ez7sw4/screenshot_4_annotated.png

0

##### Share on other sites

Only some minor updates to provide at the moment.  I've been able to confirm a few suspicions from my previous posts.

The first is that using power of two textures removes the stair-step artifacts.

The second is that setting the HIZ_CROSS_EPSILON to what I mentioned in the post above did indeed remove the interlaced lines.  I also found through testing though, that I could move the HIZ_START_LEVEL and HIZ_STOP_LEVEL to 0.0f and leave the epsilon to be the texel sizes and it would also remove the interlaced lines.  With either of these setups, the results were dramatically better and the only noticeable artifact in the ray-tracing portion is the nonsensical intersection stuff that can be solved by properly using the min/max buffer.  Here's what I landed on for HIZ_CROSS_EPSILON and it works well on both start/stop levels I've tested on (2.0f and 0.0f).

static const float2 HIZ_CROSS_EPSILON = float2(texelWidth, texelHeight) * exp2(HIZ_START_LEVEL + 1.0f);


I've included another screenshot to show the same scene (a stack of boxes - i.e., wonderful programmer art) with the interlaced lines gone.

If anyone has any ideas to get rid of my power of two texture size constraints that would be most appreciated.  The only thing I can think of right now is copying the necessary resources to a power of two texture before starting the effect, but I feel like that's bound to introduce its own set of problems, especially from copying the depth buffer to a different size.  Any other ideas?

Screenshot at 1024x512:

https://www.dropbox.com/s/7qfs04tvx8vpf09/screenshot_6.png

Edit:  grammar

Edited by WFP
0

##### Share on other sites

Just wanted to check in on this thread.  Still haven't gotten much of anywhere removing the stair-like artifacts without forcing the mip level to 0 (which we know is wrong).  I tried using a trilinear sampler instead of the point sampler, but as I expected all that did was make the stair artifacts into slopes, but they still noticeably exists.

@jgrenier Have you had any time to port your code to HLSL and if so have you had any luck with it or experienced artifacts similar to what I'm seeing?

@Bruzer100 Could you tell us about the samplers you used during the different steps of building out your hi-z, convolution, and integration buffers?  I'm using a point sampler for everything but the cone-tracing step (which in my code is currently disabled), and am wondering if perhaps I'm using an incorrect addressing mode or border mode (I currently use clamped borders).

Thanks,

WFP

0

##### Share on other sites

Quick question. Regarding the visibility buffer. Isn't there a "- minZ" missing on page 173. If this is to be the percentage of empty volume of a cell, it doesn't make sense to me that we do the integration with the fine values directly. i.e. integration should be:

float4 integration = (fineZ.xyzw - minZ) * abs(coarseVolume) * visibility.xyzw;

Or am I missing something?

2

##### Share on other sites

Even the 4.9 (page 159) figure doesn't really make sense to me either. To me it looks like MIP-1 should have visibilities calculated as [25%, 100%] (since 1/4 of the first two MIP-0 cell is empty). I feel like I'm missing something here :(

0

##### Share on other sites

Hey Jp,

Regarding your first question - honestly I'm not sure.  I seem to have "better" results when I use the code presented in the book for the visibility pass (although I do include a divide by 0 check on that first division).  That being said, I currently have the cone-tracing part of the technique disabled in mine as mentioned in one of my above comments as I'm still a ways from figuring out the ray marching part.  When I do enable it, the results are not even to the point where I think it would be useful to post an image of them, so there's a lot of work I need to do on it, but I haven't been spending much time or energy on it due to the issues I've been having getting the ray marching to work.

As for your second question, I think the book is correct in the diagram provided, if not a little confusing to look at.  The first four bars represent mip level 0 and all have 100% visibility.  The next two bars, the grey and the white, represent mip level 1, which they're obtaining by just accounting for the two nearest bars - halving the resolution of the first mip level (in the actual implementation, this is four values instead of two like shown in the book).  This gives the 50% and 100% values as shown.  And obviously along these lines the final blackish bar is the combination of the mip level 1 values into mip level 2.  When going down a mip level in the visibility buffer, the value can always be the same or less than the value before it in the visibility buffer mip chain, but never above that value.

If I've missed something or misunderstood your question, let me know and I'll try to update my explanation. :)

-WFP

0

##### Share on other sites

Hey Jp,

Thanks for the explanation and seeing it drawn out helped clarify for me a lot what you were getting at.  I definitely think you're onto something, and your output looks inline with what I would expect from the visibility buffer.  Whenever I get to the cone-tracing step you can bet that I'll try out what you've got above and see where that puts things.  Thanks for the update!

-WFP

0

##### Share on other sites

Thinking about it, if really the output is meant to be "the percentage of empty voxel volume relative to the total volume of the cells", then (I think) we should calculate the integration value as:

Reading page 172/173, I think visibility is supposed to be "the percentage of empty space within the minimum and maximum of a depth cell" modulated with the visibility of the previous mip.

So I also think that there is an error on the pre-integration pass, but the correct code would be:
float4 integration = (fineZ.xyzw - minZ) * abs (coarseVolume) * visibility.xyzw;

This makes MIP 1 on page 159 diagram correct but I still have no idea how the 37.5% visibility on MIP 2 was calculated.

Can one of you try the line of code above in your implementation and see how it looks? I haven't had time to implement the article myself.

Btw, has anyone tried to contact the article author about the source code? I wasn't able to find it anywhere.

Edited by TiagoCosta
2

## Create an account

Register a new account