Skip to content
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

Add cutoff to mip and minip volume projection for fragment discard #2308

Merged
merged 10 commits into from Mar 20, 2022

Conversation

brisvag
Copy link
Collaborator

@brisvag brisvag commented Mar 4, 2022

This PR takes out some code from the solution to the issues in #2305.

Specifically, see this comment and this other comment.

In practice, the new attributes allow to set a lower bound to MIP projection (and higher bound to MINIP) beyond which fragments are discarded.

@almarklein
Copy link
Member

What is the actual use-case for this?

These cutoffs feel to me as ideally being inf/-inf (I wonder if its safe to actually use these values in a shader), and making them settable feels odd to me.

Maybe these use-cases could be solved differently, e.g. by setting clim and using a colormap with alpha=0 in some places. That is more work, but might be more correct. Thought?

@brisvag
Copy link
Collaborator Author

brisvag commented Mar 9, 2022

I agree that by default the cutoffs should be the min and max possible values of the data type... Not sure if -inf and inf would be safe in the shader, or if we need to detect them somehow either in the shader or outside, based on the dtype of the texture.

Notice that on main they are harcdoded to -99999 and 99999, which is definitely not good. I left those numbers for now just to avoid changing behaviour.

Yes, colormap withg alpha would be the right way to do it, if only transparency in vispy wasn't broken :P as you may remember, I went down the deferred rendering rabbit hole to implement OIT, but that's a massive amount of work and probably not gonna happen any time soon (if at all before vispy 2.0).

@almarklein
Copy link
Member

almarklein commented Mar 9, 2022

if only transparency in vispy wasn't broken

I think it should work by doing something like this near the end of the shader (after the colormap, at least):

if (fragColor.a <= 0.0) {
    discard;
}

@brisvag
Copy link
Collaborator Author

brisvag commented Mar 9, 2022

Notice that on main they are harcdoded to -99999 and 99999, which is definitely not good. I left those numbers for now just to avoid changing behaviour.

Note that this PR does change behavior no matter what we choose (to keep the current limits or to use the extremes), because previously if the ray casting detected only a maximum that was lowe than -99999, it would be clipped to white. Now it either gets removed, or distinguished correctly from -1000000.

@brisvag
Copy link
Collaborator Author

brisvag commented Mar 9, 2022

if only transparency in vispy wasn't broken

I think it should work by doing something like this near the end of the shader (after the colormap, at least):

if (fragColor.a <= 0.0) {
    discard;
}

This would work for a=0, but make a mess of anything approaching 0, which will happen on the edges of objects.

@almarklein
Copy link
Member

This would work for a=0, but make a mess of anything approaching 0, which will happen on the edges of objects.

Right, due to the linear interpolation of the colormap. mmm ...

Note that this PR does change behavior no matter what we choose (to keep the current limits or to use the extremes), because previously if the ray casting detected only a maximum that was lowe than -99999, it would be clipped to white. Now it either gets removed, or distinguished correctly from -1000000.

Yeah, I agree that the current behavior was technically incorrect.

After some more thought I can see how it makes sense to be able to hide/discard rays that don't meet a certain value in MIP, and this seems like the best way to do this. 👍

@brisvag
Copy link
Collaborator Author

brisvag commented Mar 9, 2022

So would you suggest keeping -99999 or moving to some form of -inf?

@almarklein
Copy link
Member

Shall we try using inf? I'm a bit scared of odd results on specific drivers, but we can revert if we do.

@almarklein
Copy link
Member

almarklein commented Mar 9, 2022

Living on the edge 🤘

@brisvag
Copy link
Collaborator Author

brisvag commented Mar 9, 2022

It seems that our best bet is to use max and min float values... the internet says:

FLT_MAX 3.402823466e+38
FLT_MIN 1.175494351e-38

Inf can be obtained apparently with 1.0 / 0.0, but it's not guaranteed to work for glsl < 4.1.

@djhoese
Copy link
Member

djhoese commented Mar 9, 2022

1.175494351e-38

Isn't that the smallest representable decimal? I mean, shouldn't the negative of the max be the minimum?

Edit: That number is "The minimum positive normal value", but zero would still be less than that.

@brisvag
Copy link
Collaborator Author

brisvag commented Mar 9, 2022

That number is "The minimum positive normal value", but zero would still be less than that.

Yup, oops, I just found the same as well! Googled too superficially. Obviously they should be opposites.

@brisvag
Copy link
Collaborator Author

brisvag commented Mar 9, 2022

Turns out we can rely on np.finfo to generate those values for us.

@almarklein
Copy link
Member

What happens if you do float('inf')? Floats in shaders are not necessarily 32 bit. I suspect that they can still represent inf/-inf though. I suppose the max of a 32bit float might also work, it will likely become either inf or the max in the shader's float representation. I have no idea which approach would be safer, tbh.

@brisvag
Copy link
Collaborator Author

brisvag commented Mar 9, 2022

Yeah... I would hope that floats are converted (and clipped) somewhere in the gloo or program stuff... propbably they are equivalent, but I feel like it's a safer bet that clipping works properly rather than inf, since inf was apparently never a first class citizen before 4.1.

@almarklein
Copy link
Member

I would hope that floats are converted (and clipped) somewhere in the gloo or program stuff...

I don't think we do any of that in vispy - it's up to the driver to do (or not do) this.

@djhoese
Copy link
Member

djhoese commented Mar 10, 2022

Similar experience here with NaN. The CI jobs that use NaNs in the images are xfail because the GL drivers on the CI machines apparently can't handle NaN. On those machines NaN is just replaced with 0 if I remember correctly.

@brisvag
Copy link
Collaborator Author

brisvag commented Mar 10, 2022

So, I added some tests to see if things are working properly. I get failures here, but I think they are correct. I'm a bit tired so I might have done something very wrong...

@brisvag
Copy link
Collaborator Author

brisvag commented Mar 16, 2022

Ok, I tracked down the issue as far as I could. It turns out we're back to canvas sizing issues, like some time ago in another PR (forgot which...). Basically, very small canvas sizes and qt seem to not play well together. Things seem to go awry when the canvas context manager is entered, and self.show() gets called. Somewhere in there, the canvas gets resized to (75, 50), at least on my machine. This obviously messes up our assumptions about where colors are supposed to be. I fixed the tests by explicitly setting the canvas just before rendering.

Try this on your machine:

from vispy.scene import SceneCanvas

c = SceneCanvas(size=(40, 40))
c.size  # output is correct
c.show()
c.size  # output is wrong

In fact, it seems that anything below (75, 50) gets resized to that.

EDIT: this was meant as a comment to #2305, but likely applies here too.

Comment on lines 412 to 414
if (maxi > -1) {
gl_FragColor = applyColormap(maxval);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping in mind that I don't know much about volume rendering, is this just an optimization or does it actually fix things showing up when they shouldn't be there?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It fixes things to behave the same as the other visuals. Without this change, it was defaulting to always setting the value from the zeroth step of the raycasting, even when there was nothing (i.e: beyond the cutoff). Basically, the cutoff was not being used :P

@djhoese
Copy link
Member

djhoese commented Mar 16, 2022

Strange find. Nice job. Any idea why the other tests that use a 40x40 canvas size work?

@brisvag
Copy link
Collaborator Author

brisvag commented Mar 16, 2022

Strange find. Nice job. Any idea why the other tests that use a 40x40 canvas size work?

I think it's just by luck and trial and error. I've had issues before with this, and now it's clear why. When this last happened, I changed my approach until this was no longer causing issues (for example by dynamically calculating the center of the rendered image rather than passing a specific index). Other tests simply don't care because the whole canvas is supposed to be the same color, or they just check that a color is shown somewhere but don't care where.

For the future, I suggest we never use below 75x50 to avoid any issues :P

@djhoese
Copy link
Member

djhoese commented Mar 16, 2022

Sounds good!

@brisvag
Copy link
Collaborator Author

brisvag commented Mar 16, 2022

Just a note: we should double check in between merging this and #2305, cause there's probably going to be some conflicts. I would suggest merging this last.

@brisvag
Copy link
Collaborator Author

brisvag commented Mar 19, 2022

All good from my side; conflicts are solved and tests pass locally. @djhoese let me know if there are unclear things; otherwise I think this is ready to go in as well!

@djhoese
Copy link
Member

djhoese commented Mar 20, 2022

Given the passing tests and the prior approval from Almar, let's merge this...

@djhoese djhoese merged commit 4283e6d into vispy:main Mar 20, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants