Add alpha support to Framebuffer#1
Conversation
benhansen-io
left a comment
There was a problem hiding this comment.
This will be nice to have. Still some high level questions but looking good overall.
|
Updated the PR comment with another question that came up. |
This is nice we can do it but I don't have any use case right now and can't think of any coming up so I would lean toward holding off for now. If we do end up wanting it, I think I would like to wrap a u8 in a Grayscale struct and impl Color on that instead of on u8 directly since it won't be common.
Thats fine for now
Good point. I agree its confusing so I would prefer to not have it. How about removing it from Color and only implementing a free function |
|
Does |
|
I think I'd prefer renaming |
| 1. - ((relative_dist - FALL_OFF_DISTANCE) / (1.0 - FALL_OFF_DISTANCE)) | ||
| }; | ||
| let this_color = color::dim_to(c, percent_colored); | ||
| let this_color = c.dim_to(percent_colored); |
There was a problem hiding this comment.
So in my mind ideally we should be able to have a Framebuffer and write multiple draw calls to it and the falloff should use alpha and blend with the existing values. Otherwise it feels weird that we accept a writeable Framebuffer instead of just returning one.
Oh and this dim_to definitely feels like it should be alpha based for an RGBA buffer.
I don't have graphics API experience. I spent a bit of time asking Claude trying to figure out what is common here. Claude seems to indicate that they have configurable blend states that you can set globally or per pipeline depending on the API. Thats probably overkill for us, but we could pass in an optional blend fn pointer at some point so the user gets full control of how the existing pixel and drawed pictures get blended.
I really want to move these drawing functions to be a builder pattern and have fall_off be an option on the builder but I think that should be a separate PR and doesn't have to be you implementing it. So maybe we get these draw_* functions compatible with the current situation and then come back and improve them.
There was a problem hiding this comment.
So in my mind ideally we should be able to have a Framebuffer and write multiple draw calls to it and the falloff
should use alpha and blend with the existing values.Oh and this dim_to definitely feels like it should be alpha based for an RGBA buffer.
Yeah this does sound much nicer than what we have at the moment. I'll work on making it so, hopefully without breaking anything. Agreed about dim_to.
I don't have graphics API experience.
I'm in the same boat. I agree that the approach Claude mentioned seems like a bit much. The optional function pointer would work but I also sort of don't like it. I can't think of a better option though.
There was a problem hiding this comment.
Adding @tiesselune to this review since he has game dev experience in case he has any thoughts
There was a problem hiding this comment.
The optional function pointer would work but I also sort of don't like it
I've come around on this - I think this should be fine, and it means we don't need two separate functions for scalar and "vector" blends and dims. Although, I don't think it needs to be optional - we can just pass it always and users can do |_| 0.5 if they want to just to scalar weighting
There was a problem hiding this comment.
So in my mind ideally we should be able to have a Framebuffer and write multiple draw calls to it and the falloff should use alpha and blend with the existing values. Otherwise it feels weird that we accept a writeable Framebuffer instead of just returning one.
@benhansen-io In your mind is it worth breaking these lights_plane functions for RGB to be able to implement this? I'm really struggling for a neat way to do this generically. The best way I can think of, so far, is to return to_rgb_u8 to Color and also add an alpha(&self) -> Option<u8> to Color. I can't think of a way that doesn't require adding extra functionality to Color.
There was a problem hiding this comment.
I've come around on this - I think this should be fine, and it means we don't need two separate functions for scalar and "vector" blends and dims. Although, I don't think it needs to be optional - we can just pass it always and users can do |_| 0.5 if they want to just to scalar weighting
What does |_| 0.5 represent. I thought the blending function would take two colors but I haven't throught through it all.
In your mind is it worth breaking these lights_plane functions for RGB to be able to implement this? I'm really struggling for a neat way to do this generically. The best way I can think of, so far, is to return to_rgb_u8 to Color and also add an alpha(&self) -> Option to Color. I can't think of a way that doesn't require adding extra functionality to Color.
The dim_to you have right now seems pretty close. The only thing wrong is it doesn't seem to blend with the current values in the framebuffer. For RGB it seems like instead of assuming we dim to black we should blend with the current color (which allows painting a black circle on a colored background for example) and for RGBA we should alpha blend the existing color and the new color (otherwise drawing red circle with falloff onto a green blackground would produce some black around the edges which doesn't make sense)
|
Let me know when this is ready for another review. |
5d4e04e to
43412b4
Compare
benhansen-io
left a comment
There was a problem hiding this comment.
Seems like we are getting pretty close. This will be a nice change to have.
Marking request changes so its clear when you want me to look at it again.
| 1. - ((relative_dist - FALL_OFF_DISTANCE) / (1.0 - FALL_OFF_DISTANCE)) | ||
| }; | ||
| let this_color = color::dim_to(c, percent_colored); | ||
| let this_color = c.dim_to(percent_colored); |
There was a problem hiding this comment.
I've come around on this - I think this should be fine, and it means we don't need two separate functions for scalar and "vector" blends and dims. Although, I don't think it needs to be optional - we can just pass it always and users can do |_| 0.5 if they want to just to scalar weighting
What does |_| 0.5 represent. I thought the blending function would take two colors but I haven't throught through it all.
In your mind is it worth breaking these lights_plane functions for RGB to be able to implement this? I'm really struggling for a neat way to do this generically. The best way I can think of, so far, is to return to_rgb_u8 to Color and also add an alpha(&self) -> Option to Color. I can't think of a way that doesn't require adding extra functionality to Color.
The dim_to you have right now seems pretty close. The only thing wrong is it doesn't seem to blend with the current values in the framebuffer. For RGB it seems like instead of assuming we dim to black we should blend with the current color (which allows painting a black circle on a colored background for example) and for RGBA we should alpha blend the existing color and the new color (otherwise drawing red circle with falloff onto a green blackground would produce some black around the edges which doesn't make sense)
| pub struct Framebuffer { | ||
| #[derive(Clone, PartialEq)] | ||
| #[must_use] | ||
| pub struct Framebuffer<C: Color = RGB> { |
There was a problem hiding this comment.
What do you think about a FramebufferRGBA type alias for Framebuffer::<RGBA>?
There was a problem hiding this comment.
Yeah that sounds useful
I think this might be unintuitive, since |
…pecialised impls
Leaving the others in framebuffer.rs as those make code clearer
0bb96e1 to
ea5ea15
Compare
|
@benhansen-io I thought a bit more about this:
This would be fairly trivial if we used RGBA in MainFramebuffer instead of RGB. The memory overhead would be fairly small and we could use defaults for RGB flush. What do you think? |
benhansen-io
left a comment
There was a problem hiding this comment.
I am a little lost on this PR if its for feedback or for merging. When requesting a review its helpful if you tell me if its ready to merge or if you are looking for feedback and if your looking for feedback what items you want feedback on. If its ready to merge, please include all breaking changes and how we know they don't break activities and what tests were done to ensure that.
I like the direction this is going but I feel like this is too big with breaking changes to merge. I think we should try to merge the a minimal subset of this that makes no breaking changes and add in commits from there. Also sometimes I find it really helpful to make cleanup commits before making a change commit.
I also think handling RGB and RGBA in a generic context is going to be more confusing than its worth. I think Framebuffer being generic is fine for all of the set functions (its an assigmment). I think most of the current flush functions should only be implemented for Framebuffer.
| } | ||
|
|
||
| #[allow( | ||
| clippy::cast_possible_truncation, |
There was a problem hiding this comment.
Going forward lets not put allow statements for pedantic clippy lints. They clutter up the code and are not run be default. For example there are more lines of code for this allow statement than the function definition and body.
|
|
||
| /// Blend two colors together, according to their brightness or alpha. | ||
| #[must_use] | ||
| fn blend(self, rhs: Self) -> Self; |
There was a problem hiding this comment.
We renamed blend to weighted_average and then put a new definition of blend. Do you think this definition will work and will be better for all of our current usage sites or were you planning on renaming the current usage sites to weighted average?
There was a problem hiding this comment.
Is blending based on brightness a common operation? I don't have any intuition for it.When I asked Gemini how to mix two colors from 0 to 100% it only provided the weighted average option.
| r: self.r, | ||
| g: self.g, | ||
| b: self.b, | ||
| a: mix_component(self.a, 0, percent), |
There was a problem hiding this comment.
This doesn't feel like a "dim". Its a dim if the background is black but who is to say that the background is black. Now that the flush for rgba is flushing with the current colors the background could be any color.
I am inclined to say we should define dim_to only in terms of RGB. In fact maybe we should only have this operation on RGB.
|
|
||
| fn luminance(self) -> f32 { | ||
| brightness(self) | ||
| const MAX_MAGNITUDE_SQUARED: f32 = 255.0 * 255.0 * 3.0; |
There was a problem hiding this comment.
Lets add tests for this and other functions that are easily testable in this PR.
| /// Flush this buffer to lights, blending with the current state according to `blend_fn`. | ||
| /// `blend_fn` is passed the buffer's light color, the current light color, and the light index, | ||
| /// in that order. | ||
| pub fn flush_blend<W: Fn(RGB, RGB, usize) -> RGB>(&self, blend_fn: W) { |
There was a problem hiding this comment.
Do we plan to use this function anywhere for RGB? It feel more like an RGBA function so I am inclined to leave it off until we need it.
|
|
||
| /// Blends this framebuffer with the current [`MainFramebuffer`], and writes the result back to | ||
| /// this buffer. | ||
| pub fn blend_with_current(&mut self) { |
There was a problem hiding this comment.
I don't think we need this function unless you know of a use case. If we find we need it, I would rather have a function to blend two Framebuffers and let the users call get_currently_set
| } | ||
|
|
||
| /// Flush the buffer's contents to lights, ignoring alpha level. | ||
| pub fn flush_no_alpha(&self) { |
There was a problem hiding this comment.
I think we can remove this function and let them. Its pretty easy to call .to_rgb().flush()
This PR adds support for alpha channels to our
Framebuffer. It does so by makingFramebuffergeneric overC: Color, and adding an implementation ofColorforRGBA.There are some elements of this PR that warrant further discussion:
Colorforf32oru8, and/or other scalar types would enable use of grayscaleFramebuffers. These could be useful for keeping track of alpha or brightness separately, or for use as a "mask".Framebuffer<RGBA>::alpha_blendand any operations that might seek to target only one channel, although we don't implement any such operations currently.Colordefines ablendfunction, which takes arhs_percentparameter. The use of this parameter (or rather, how to use it properly) for blending RGBA values is not clear - RGBA values can be blended entirely using their alpha values. Do we just ignore those and haveblendwork consistently for both RGB and RGBA?CC: @benhansen-io @tiesselune