If we adjust the algorithm described on page 173 to work with the 2d case, here's what I get for the first cell of MIP-1:


If we adjust the algorithm described on page 173 to work with the 2d case, here's what I get for the first cell of MIP-1:
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
Hi everyone. I'm now stuck with the cone pass.
From what I understand, the cone tracing consist of sampling each circles along the cone (from the reflection point to the to the reflection incident point, i.e. from the big circles to the small circles) and approximating the integral by weighting each sample appropriately. The article says something like "we intersect each sphere with the hi-z structure and weight them by finding out how much they are in front, in the middle or behind the coarse depth cells".... I've tried and and got rubbish:
I've tried both of the following ways to weight in the cone's spheres. Got equally crappy results (min/max represent the value stored in the hi-z structure at a particular mip-level)
Any thoughts? What am I not getting? Grrrr.
Jp
So I've finally figured out what was causing the stair artifacts we were seeing when running in anything but power of two texture sizes. In the article, the author uses offsets of -1 in obtaining his other three points for comparison, but it turns out that, at least for my NVIDIA card (760 GTX), the opposite needed to be true. Using offsets in the positive direction (see below) alleviated the stair artifacts that were showing up. There seems to be an implementation difference in how ATI and NVIDIA cards handle this, because the code worked with -1 offsets on the ATI card that it was tested on. I still need to follow up to make sure changing the sign to positive doesn't break the technique on those cards, but at the very least, at least we have an answer for what was causing it. :) I've posted the modified HiZ buffer construction pixel shader that I use below, as well as a screenshot running at 1536x864 with no stair artifacts showing up. Next steps are filtering this buffer to fill in the tiny artifacts/gaps that show up (as well as temporal stability, etc., eventually), and then applying the cone-tracing step, which Jp is doing some great work on. :)
-WFP
Screenshot: https://www.dropbox.com/s/l70nv650e75z3bw/screenshot_9.png?dl=0
HiZ_PS.hlsl:
struct VertexOut
{
float4 posH : SV_POSITION;
float2 tex : TEXCOORD;
};
SamplerState sampPointClamp : register(s0);
Texture2D hiZBuffer : register(t0);
float2 main(VertexOut pIn) : SV_TARGET
{
float2 texcoords = pIn.tex;
float4 minDepth = 0.0f;
float4 maxDepth = 0.0f;
// sample level zero since only one mip level is available with the bound SRV
float2 tx = hiZBuffer.SampleLevel(sampPointClamp, texcoords, 0.0f, int2(0, 0)).rg;
minDepth.r = tx.r;
maxDepth.r = tx.g;
float2 ty = hiZBuffer.SampleLevel(sampPointClamp, texcoords, 0.0f, int2(0, 1)).rg;
minDepth.g = ty.r;
maxDepth.g = ty.g;
float2 tz = hiZBuffer.SampleLevel(sampPointClamp, texcoords, 0.0f, int2(1, 0)).rg;
minDepth.b = tz.r;
maxDepth.b = tz.g;
float2 tw = hiZBuffer.SampleLevel(sampPointClamp, texcoords, 0.0f, int2(1, 1)).rg;
minDepth.a = tw.r;
maxDepth.a = tw.g;
return float2(
min(min(minDepth.r, minDepth.g), min(minDepth.b, minDepth.a)),
max(max(maxDepth.r, maxDepth.g), max(maxDepth.b, maxDepth.a)));
}
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.
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.
Hi TiagoCosta,
There definitely seems to be a few things off in the implementation provided in the book. I tried yours out and didn't see much difference, but that could be because we're still wrestling with the actual cone-tracing part itself.
One thing I noticed this afternoon that I had messed up on in the original code I posted to this thread was that I had mixed up some parameters in the cone tracing step. For the method isoscelesTriangleInRadius - I was sending the parameters in backwards. The code should read:
float isoscelesTriangleInRadius(float a, float h)
{
float a2 = a * a;
float fh2 = 4.0f * h * h;
return (a * (sqrt(a2 + fh2) - a)) / (4.0f * h);
}
and should be called from the loop with:
// calculate in-radius of the isosceles triangle
float incircleSize = isoscelesTriangleInRadius(oppositeLength, adjacentLength);
The difference is that I had originally posted adjacentLength as the first parameter, followed by oppositeLength - obviously incorrect, and easily verified here: http://mathworld.wolfram.com/IsoscelesTriangle.html
I'm also wondering if isoscelesTriangleOpposite should be using half the cone angle, since we're basically splitting the cone into two halves to make it a right triangle for finding its sides.
float isoscelesTriangleOpposite(float adjacentLength, float coneTheta)
{
// should this be like below with the additional *0.5f term?
return 2.0f * tan(coneTheta * 0.5f) * adjacentLength;
}
I'm not positive on that yet, though, so don't want to steer anyone in the wrong direction if it's incorrect.
EDIT: A little verifying with scratch paper says yes, the cone angle should be halved as shown above. If anyone sees an error in that, please let me know.
Regarding contacting the author, I think several people have already. He's responded to a few posts on his twitter account making it sound like he was unable to release the code for whatever reason: https://twitter.com/YasinUludag/with_replies. The disappointing thing is that the article is obviously incomplete and directs the user to the source for a better understanding at multiple points. Not to mention that Section 4.12 'Acknowledgments' has enough people mentioned, including leadership roles, that surely someone should have had the whereabouts to speak up and stop the article from going out if it were going to be released in an incomplete state. Oh well, we're making some good progress on it, and I'm hoping we can all find a solution together .
Thanks!
WFP
Hi Tiago, not sure if you saw my post #18 but I proposed the same solution. I think it's probably what was intended.
Hi Tiago, not sure if you saw my post #18 but I proposed the same solution. I think it's probably what was intended.
Yes, I missed that post! Do you have any idea how the 37.5% was calculated? By applying the modified pre integration pass, Mip 2 should also have 50% visibility, however I'm not sure if it is correct
Anyway the formula on post #21 is probably incorrect because it should only calculate the percentage of empty volume between the coarser cell's min and max z, right?
Do you have any idea how the 37.5% was calculated
No idea how that 37.5% value is gotten. Also it doesn't make sense that summing a quarter of each newly calculated values (dot(0.25,a,b,c,d)) can keep the property VisibilityN <= VisibilityN-1. Doesn't make sense.
Here's an example of the case that needs to be solved. (note: you can see the color values that will be fetched by each sphere, the bigger the sphere, the blurrier the fetch). This is the case where a reflection approaches an edge. On the top image the ray hits the sphere, and on the bottom image, the ray hits the background and all of the contribution comes from the edge sphere (so the reflection result is the blurred sky). The question is how should the weight of each of those spheres be set so that there is a smooth transition between the left and the right reflection?
Are you weighting the spheres using the visibility buffer? The article mentions 3 weighting methods: basic averaging, distance-based weighting, and hierarchical pre-integrated visibility buffer weighting.
Btw, shouldn't each of those circles have a solid color (single texture fetch)?