But! That wasn't going to stop me from researching and validating the right way to alpha blend two pixels together.
Now in reality, the task isn't so much for pixels, but rather for heightfield data. Now often heightfield data is just a single-channel pixel anyway, but in our engine heights are represented as floats. To expand the feature set of our terrain engine, our heightfield data needed to be modified to include layers and an alpha channel. So each heightfield sample is now two channels -- height and alpha.
Of course, googling and grabbing an algorithm isn't all there was to it. I wasn't really going to be satisfied with the answers I found unless I could test it myself. Sure, I could plug the code in and see what results I get, but it was much faster to throw up my favorite web app -- Desmos Graphing Calculator.
Compared to my last documented use of Desmos, this is absurdly straightforward. I'm visualizing the alpha channel along the x axis and the height channel along the y axis. I'll admit, It's a bit disorienting until you get used to it. Regardless, a/h3 is the resulting value. a/h1 and a/h2 are the inputs. Alpha values are normalized, so we're working with ranges between 0 and 1.
The formula for the output ends up looking like this in code. As usual this code is for illustrating the formula -- not for being performant code.
struct HeightSample { float height, alpha; } void Blend(HeightSample& out, const HeightSample& a, const HeightSample& b) { out.height = a.alpha * a.height + b.alpha * (1.0f - a.alpha) * b.height; out.alpha = a.alpha + ((1.0f - a.alpha) * b.alpha); }It's interesting to note that while the alpha blending is a commutative operation (that is, if you swap the input values, the output is the same), the same it not true of the height channel. This is because sample
const HeightSample& a
is considered the "top" pixel. They are not even commutative if both alpha values are 0.5. This isn't particularly intuitive, so I thought it was worth mentioning.
No comments:
Post a Comment