Is there a better way to draw gradient colored ellipse?

Welcome to the forum! :slight_smile:

There is a more efficient way, but efficient ways are not always easy. By programming the GPU you can create gradients, but it’s a different language called GLSL. If you’re starting with Processing then you can look at this in a year or two :slight_smile:

The reason it’s faster it’s because there are no loops. The GPU calculates all the pixel colors in parallel. I’ll leave it here just in case someone needs faster gradients:

PShader gradient;
void setup() {
  size(800, 800, P2D);

  gradient = new PShader(this, new String[] {
      "uniform mat4 transformMatrix;", 
      "attribute vec4 position;", 
      "attribute vec4 color;", 
      "varying vec4 va_color;", 
      "varying vec2 va_pos;",
      "void main() {", 
      "  gl_Position = transformMatrix * position;", 
      "  va_color = color;",
      "  va_pos = position.xy;",
      "}"
    }, new String[] {
      "uniform vec2 center;",
      "uniform vec3 centerColor;",
      "uniform float radius;",
      "uniform bool linear;",
      "varying vec4 va_color;", 
      "varying vec2 va_pos;",
      "void main() {",
      "  float d = (linear ? distance(center.y, va_pos.y) : distance(center, va_pos))/radius;",
      "  vec3 c = mix(centerColor / 255.0, va_color.rgb, min(d*d, 1.0));",
      "  gl_FragColor = vec4(c, va_color.a);", 
      "}"
    });
}

void draw() {
  float x = random(width);
  float y = random(height);
  float radius = random(20, 200);

  shader(gradient); // activate the shader. Next shapes are affected by it.
  gradient.set("center", x, y); // set the center of the gradient
  gradient.set("radius", radius); // set the radius of the gradient
  gradient.set("linear", false); // two modes: linear and radial
  gradient.set("centerColor", random(255), random(255), random(255)); // use 3 floats here!
  fill(random(255), random(255), random(255)); // the outer color
  noStroke();
  circle(x, y, radius * 2); // you can draw any shape, not just circles
  resetShader(); // deactivate the shader. Next shapes use default rendering
  // ...draw something else here
}

The “linear” mode I implemented is just a vertical gradient. You can change .y to .x to make it horizontal. To allow any angle it needs some work. Instead of center and radius we should define startPos and endPos for the shader.

Note that center and radius do not need to match the shape you draw. You can draw a radial gradient in a square. Or set the center of the gradient in the top-left corner of the window and the shape on the center. You can experiment :slight_smile:

Another note: centerColor must be 3 floats. It won’t work if you use integers. Fortunately random() produces floats.

One place to learn about shaders is the https://thebookofshaders.com/ (oops, link was wrong! fixed)

ps. Forgot to mention that the gradient is exponential. You can replace d*d with d to distribute the colors uniformly instead. Or with pow(d, 3.0) for a more extreme exponential distribution.

4 Likes