首页 > 解决方案 > Generating a pixel-based spiral gradient

问题描述

I have a program that creates pixel-based gradients (meaning it calculates the step in the gradient for each pixel, then calculates the colour at that step, then gives the pixel that colour).

I'd like to implement spiral gradients (such as below).

enter image description here

My program can create conic gradients (as below), where each pixel is assigned a step in the gradient according to the angle between it and the midpoint (effectively mapping the midpoint-pixel angle [0...2PI] to [0...1]).

enter image description here

It would seem to me that a spiral gradient is a conic gradient with some additional function applied to it, where the gradient step for a given pixel depends not only on the angle, but on some additional non-linear function applied to the euclidean distance between the midpoint and pixel.

I envisage that a solution would take the original (x, y) pixel coordinate and displace it by some amounts in the x and y axes resulting in a new coordinate (x2, y2). Then, for each pixel, I'd simply calculate the angle between the midPoint and its new displaced coordinate (x2, y2) and use this angle as the gradient step for that pixel. But it's this displacement function that I need help with... of course, there may be other, better ways.

Below is a simple white-to-black conic gradient. I show how I imagine the displacement would work, but its the specifics about this function (the non-linearity), that I'm unable to implement.

enter image description here

My code for conic gradient:

public void conicGradient(Gradient gradient, PVector midPoint, float angle) {
    
    float rise, run;
    double t = 0;

    for (int y = 0, x; y < imageHeight; ++y) {
        rise = midPoint.y - y;
        run = midPoint.x;
        for (x = 0; x < imageWidth; ++x) {

            t = Functions.fastAtan2(rise, run) + Math.PI - angle;

            // Ensure a positive value if angle is negative.
            t = Functions.floorMod(t, PConstants.TWO_PI);

            // Divide by TWO_PI to get value in range 0...1
            step = t *= INV_TWO_PI;

            pixels[imageWidth * y + x] = gradient.ColorAt(step); // pixels is 1D pixel array

            run -= 1;
        }
    }
}

标签: algorithmgraphicsgeometryprocessinggradient

解决方案


By eye, it looks like after t = ... fastAtan2..., you just need:

t += PConstants.TWO_PI * Math.sqrt( (rise*rise + run*run) / (imageWidth * imageWidth + imageHeight * imageHeight) )

This just adds the distance from the center to the angle, with appropriate scaling.


推荐阅读