-
Notifications
You must be signed in to change notification settings - Fork 3.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix polyline rendering when using the logarithmic depth buffer #8706
Conversation
Thanks for the pull request @kring!
Reviewers, don't forget to make sure that:
|
I seem to have broken a bunch of polyline tests, but I don't know how. For example, this test fails:
But if I turn that test into a Sandcastle example, it renders identically in master and in this branch. I can only assume the tests are rendering the polylines subtly differently and that's triggered a bug, but I'm having a hard time spotting it. I'll keep looking in the morning, but if anything jumps out at anyone please let me know. :) |
Awesome work here @kring! Thanks! Any concerns from you or @lilleyse to getting this into the release tomorrow? I don't think we have much of a choice, given that it fixes a regression, but also want to make sure we don't make things worse obviously. We could probably ask some of the team to hammer on this branch a little today if you think that would help at all. |
This doesn't fix a regression - my other PR (already merged) did that - but it fixes some major longstanding bugs, so I do think it's important and would love to see it go into the release today. But considering how late it is I totally understand if that's not reasonable. |
I marked this as priority next release believing it also fixes regressions, but looking at all the referenced issues at the top, these are all polyline issues users have brought up and disabled log depth in their apps as a workaround. I can help with testing and will leave it up to Sean to decide on whether this can go into the release. |
Personally I'd feel more comfortable merging it after the release. (Also I'm pretty booked today, so someone else would need to do the review) |
The test failures are fixed, this is ready now. |
b8d7fbe
to
ae60d60
Compare
I also added #7852 to the fixed list. Which means everything in #8600 (comment) has been fixed! Well, except one that we were waiting on confirmation. |
@kring your writeup was extremely helpful. I also appreciated all the code comments. I've tried all the polyline sandcastle examples I could find and barely noticed any issues. The only major artifact I noticed was in @emackey's demo, you already mentioned that thicker polylines distort the texture differently, but I see worse artifacts when zooming out. Admittedly master has its own set of artifacts too. Don't want to hold this PR up but do you have any thoughts? |
Whoops I broke everything with a typo last night. Ed's example looks good now and all the tests still pass. |
Cool, I checked out that example and others again and everything seems to be working as expected. Thanks @kring! |
Following up on #8703, this fixes most of the rest of the polyline / log depth problems.
Before this PR, (non-ground) polylines were pretty much unusable with the logarithmic depth buffer enabled. They would frequently be missing chunks or appear to be at a different depth than they should be.
For example:
Fixes #6741 (comment)
Fixes #7852
Fixes #7072
Fixes #7374
Fixes #7852
The problem was that the fragment depth values produced for polylines were straight-up wrong, but it took me forever to understand why. Polyline rendering works in screen space, so the vertex shader always produced window coordinates directly, and set
gl_Position.w
to 1.0.Now I guess I never fully appreciated just what that
w
component is good for, and maybe the author of the polyline rendering code didn't either, but it's super important. The fixed function portion of the rendering pipeline in between the vertex and fragment shaders uses that component to do perspective correct interpolation of varyings coming from the vertex shader.Without perspective-correct interpolation, varyings are interpolated based on how close on the screen the fragment is to each of the vertices that make up the triangle being rendered. Interpolation happens as if the triangle is a real 2D triangle on the screen. With perspective-correct interpolation, varyings are interpolated based on how close in actual 3D space the fragment is to each of the vertices. Interpolation happens as if the triangle is a real 3D triangle with a projection onto a 2D screen.
For polyline rendering, most of the varyings - things like texture coordinates, width, and color - should arguably be interpolated in screen space anyway, so non-perspective correct interpolation is acceptable. However, when log depth is enabled, we pass the vertex's depth from the camera to the fragment shader in a varying as well, in
v_depthFromNearPlusOne
. If this varying is not interpolated in a perspective-correct manner, the interpolated depth in the middle of the triangle will be wrong. And this was the cause of all these polyline / log depth rendering problems.As far as I know, there is no way to do perspective-correct interpolation of any varying when the vertex shader sets
gl_Position.w
to 1.0, as the polyline vertex shader was doing. So step 1 in fixing this was to set thatw
component correctly. That caused all the varyings to be perspective-correct, which I had hoped would be fine. But unfortunately it caused major artifacts.Looks ok when looking straight down:
Totally broken at an oblique angle:
It took me ages to understand why perspective-correct interpolation was such a disaster for the texture coordinates of this arrow material. This page eventually helped me understand:
https://web.cs.ucdavis.edu/~amenta/s12/perspectiveCorrect.pdf
The problem is very similar to when you texture map a normal quad at an oblique angle without accounting for perspective. Except that in this case, it's not a normal quad. Because it maintains a constant size in screen space, it's actually becoming much wider (in world space) the deeper you go into the screen. And just like in the image at the top of that PDF, that "growth" is not evenly shared between the two triangles that make up the quad. At steep angles, that creates major distortion.
Ok, so in order to avoid these artifacts, we need to interpolate the varyings (at least the texture coordinates) in screen space, even though we have an accurate
w
component. In modern OpenGL, including WebGL2, that's easily done: just slap anonperspective
qualifier on the varying and . But sadly we can't use WebGL2 everywhere yet (thanks Apple), so we're stuck with plan B. If we multiply the varying value bygl_Position.w
in the vertex shader and then divide by it in the fragment shader (actually we can multiply bygl_FragCoord.w
, which is equivalent), that effectively cancels out the math the fixed function does to account for perspective.Well that all sounds great, except that "cancels out" is pretty suspect in the wild world of 32-bit floating point arithmetic. And indeed that causes some new artifacts when zoomed in close to a long line:
That's a pretty cool visualization of floating point noise, but not a great visualization of a polyline.
At this point I thought I was sunk. My best idea was I could try clipping the line to all 6 sides of the frustum in the vertex shader, helping to keep the magnitude of that
w
in check in order to minimize the error introduced by multiplying and then dividing by it. But aside from the hassle and performance cost, I wasn't sure it would really work, considering that with log depth the frustum can still fully contain a very long line.But then I realized that the original artifacts with the perspective-correct interpolation almost entirely occurred over the width of the line, not over its length. And line widths are usually pretty small and mapped to a small number of pixels, so we can get away with more floating point imprecision.
So in this PR:
t
coordinate is interpolated in a non-perspective fashion by multiplying/dividing byw
.s
texture coordinate - are interpolated perspective-correct.And I think it looks pretty good! Only a couple of small artifacts I know about (out of of scope of this PR IMO):