1
\$\begingroup\$

I've been unable to make it work and I'm not sure whether it's a problem with my code or if this can even be done. I'm sure there is a way to prove whether it is possible mathematically but I don't know how to approach that either.

I'm trying to do a gradient fill on a circle where the center pixel is BLUE and all points on the edge are RED. Each point within the circle will be a blend of those two colors based on their distance from the center.

Consider this example:

enter image description here

I already have every point along the edge of the circle which I can draw across to fill it. To do a gradient in the southwest quadrant, I thought that I could linear interpolate between A->B to form a gradient and then draw a line C->D starting with RED to the color interpolated between A->B.

Would this be equivalent to using the distance between each point and the origin to linear interpolate the color?

UPDATE

For those interested in seeing what this approximation looks like, I did manage to get it working.

WHITE -> BLACK

enter image description here

RED -> BLUE

enter image description here

The grayscale image is misleading because there are 256 shades of brightness between pure black and pure white and only 127 between any R, G, and B primary colors in 24-bit RGB.

Here is the red/blue image desaturated and adjusted to emphasize how this approximation is drawing the gradient:

enter image description here

\$\endgroup\$
  • \$\begingroup\$ It sounds like you have an approximation that's worth trying. How did you find the result looked when you tried it? Was it close enough to your desired appearance, or were there visible artifacts you'd like to correct? \$\endgroup\$ – DMGregory Oct 8 '19 at 12:25
  • 1
    \$\begingroup\$ @DMGregory: I've updated my question with some samples for you. \$\endgroup\$ – Zhro Oct 9 '19 at 2:04
  • \$\begingroup\$ @Zhro Thanks for adding those, it's really good to see how it works out when drawn. \$\endgroup\$ – Bilkokuya Oct 9 '19 at 9:21
1
\$\begingroup\$

Mathematically, it is not equivalent.

I want to prefix this by saying; "but if it works for you, is more performant, and the player won't notice - then by all means still do it. Games are nothing but smoke and mirrors."


The easiest way to check whether this will work or not, is to test a simple linear interpolation on a circle of radius 1.

Here, we can intuitively see that the value in the center will be 0, and the value at the edges will be "1". That is, on this unit circle, the value of any point - will be equal to the distance of that point from the center.

This makes it far easier to reason about; we can now pick any point - and knowing how far from the center it is, we know what the value should be. We can then apply your interpolation method; and if the values match, we know your method is correct.


Using the following image:

Circle image, showing how the parts will be used in below equations.

It's clear that any radius coming from the center of the circle (marked O), must start at distance 0 and interpolate linearly to distance 1. As such, the center of such a radius, would be at a value 0.5 - as marked.

For a radius at 45*, the same applies. However choosing such an angle makes it far easier to do the following calculations.

It can be seen that the center of this 45* line, (value 0.5), projects onto the x and y axes, at a certain distance. These are marked A (y-axis) and B (x-axis).

Using your method, if we interpolate on the y-axis from O->A and then interpolate from there to B on the x-axis, we should get the same value as the distance calculation.


From basic trig, we can calculate the A and B components from the 45* angle, and the length of the hypotenuse (which is intuitively 0.5):

 O->A = 0.5 * cos(45degrees)
      = 1 / (2sqrt(2)))

 O->B = 0.5 * sin(45degrees)
      = 1 / (2sqrt(2)))

From this, we can then apply your interpolation method:

Starting at 1/(2sqrt(2)) up the y-axis, we interpolate in the direction of the x-axis, along B units.

The general form of linear interpolation is:

value = (1-t)(start) + (t)(end)

Noting that t is not simply 1/(2sqrt(2)), as the x-coordinate of the circle's edge is not simply 1 at this point, but in fact sqrt(7/8).

As such, to get our parameter (t) into the range 0->1, as needed, we need to multiply by the reciprocal of this. Giving t as: t = sqrt(8)/(2*sqrt(2)*sqrt(7))

Meaning for this case:

value = (1 - t)(OA) + (t)(1)
      = (1 - (sqrt(8)/(2sqrt(2)sqrt(7))))(1/(2sqrt(2)) + (sqrt(8)/(2sqrt(2)sqrt(7)))(1)
      = ((2*sqrt(2)*(sqrt(7)) - (sqrt(8)) + (2*sqrt(2)*sqrt(8)) )/ (8*sqrt(7))
      = approximates to ~0.598

Which unfortunately shows that the method you've developed is not a mathematical equivalent. (The value we know should be at this point was 0.5)


However!

Like most game dev "tricks", I have to emphasize that if in practice it looks good, and it is more performant than your alternatives (and there's no reason you need to be mathematically accurate, e.g. using it for another mechanic) - then by all means go ahead.

There is a difference, but arguably it may not be a significant one. The best thing you can do is generate both, and visually decide if the difference is worth the (potential) performance improvement.

Also, if this is genuinely a performance bottleneck (i.e. you've measured it), then you may wish to consider alternative techniques such as simply pre-generating the circle as a texture.


Note: if somebody is good at explaining images in words, for those with accessibility needs - please do edit. I unfortunately couldn't think of any sensible way to describe the most important parts.

\$\endgroup\$
  • 1
    \$\begingroup\$ This isn't a performance bottleneck. I'm just experimenting with drawing gradients and wanted to understand how to approach this problem mathematically. Your detailed response and visual aid have been excellent for this. \$\endgroup\$ – Zhro Oct 8 '19 at 21:30
  • \$\begingroup\$ @Zhro Well I'm really glad I could help then! \$\endgroup\$ – Bilkokuya Oct 8 '19 at 21:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.