Complex gradient loop

I tried with the oklab code below and it didn’t seem to help much. Still get the banding with the occasional grays – it just seems to move them around a bit. Let me know if you can improve it.

Use o to toggle oklab vs rgb.

PImage inputs;
PGraphics grad;
PShader gradShader;

int N = 4;
float strength = 1.;
boolean bShowPoints = true;
boolean bSaveFrame = false;
boolean bOklab = true;

void setup() {
  size( 800, 600, P3D );
  //fullScreen( P3D, SPAN );
  colorMode( HSB, 1, 1, 1, 1 );
  grad = createGraphics( width, height, P3D );
  gradShader = new PShader( this, gradVert, gradFrag );
  initControlPoints();
  gradShader.set( "strength", strength );
  gradShader.set( "bOklab", bOklab );
  noLoop();
}

void initControlPoints() {
  inputs = createImage( N, 2, ARGB );
  inputs.loadPixels();
  for( int i=0; i<N; i++ ) {
    int x = (int)random(65536);
    int y = (int)random(65536);
    color c = color( random(1), random(0.25, 1), random(0.25, 1) );
    inputs.pixels[ i ] = (x << 16) | y;
    inputs.pixels[ i+N ] = c;
  }
  inputs.updatePixels();
  gradShader.set( "inputs", inputs );
  gradShader.set( "N", N );
}

void draw() {
  gradShader.set( "bShowPoints", bShowPoints );
  grad.beginDraw();
  grad.noStroke();
  grad.shader( gradShader );
  grad.rect( 0, 0, grad.width, grad.height );
  grad.resetShader();
  grad.endDraw();
  image( grad, 0, 0 );
  if( bShowPoints ) {
    text( str(N)+"    "+str(strength), 8, 16 );
    text( bOklab ? "oklab" : "rgb", 8, 32 );
  }
  if( bSaveFrame ) {
    String fileName = getClass().getSimpleName() + "-" +
      nf(year(),4) + "-" + nf(month(),2) + "-" + nf(day(),2) + "-" +
      nf(hour(),2) + "-" + nf(minute(),2) + "-" + nf(second(),2) + ".png";
    save( fileName );
    println( "saved " + fileName );
    bSaveFrame = false;
  }
}

void keyPressed() {
  if( keyCode == ENTER ) initControlPoints();
  else if( keyCode == DOWN ) { 
    strength /= 1.1;    
    gradShader.set( "strength", strength );
  }
  else if( keyCode == UP ) { 
    strength *= 1.1;    
    gradShader.set( "strength", strength );
  }
  else if( keyCode == LEFT ) { N = max(N-1, 1);  initControlPoints(); }
  else if( keyCode == RIGHT ) { N++;  initControlPoints(); }
  else if( key == ' ' ) bShowPoints = !bShowPoints;
  else if( key == '+' ) bSaveFrame = true;
  else if( key == 'o' ) { 
    bOklab = !bOklab;
    gradShader.set( "bOklab", bOklab );
  }
  redraw();
}


String[] gradVert = {"""
#version 330
uniform mat4 transformMatrix;
in vec4 position;
void main() {
  gl_Position = transformMatrix * position;
}
"""};


String[] gradFrag = {"""
#version 330
precision highp float;
uniform vec2 resolution;
uniform sampler2D inputs;
uniform int N;
uniform bool bShowPoints;
uniform bool bOklab;
uniform float strength;
out vec4 fragColor;

// oklab code from https://www.shadertoy.com/view/ttcyRS by IQ
    // https://bottosson.github.io/posts/oklab
    const mat3 kCONEtoLMS = mat3(                
         0.4121656120,  0.2118591070,  0.0883097947,
         0.5362752080,  0.6807189584,  0.2818474174,
         0.0514575653,  0.1074065790,  0.6302613616);
    const mat3 kLMStoCONE = mat3(
         4.0767245293, -1.2681437731, -0.0041119885,
        -3.3072168827,  2.6093323231, -0.7034763098,
         0.2307590544, -0.3411344290,  1.7068625689);

void main() {
  float aspect = resolution.x/resolution.y;
  vec2 uv = gl_FragCoord.xy / resolution.y;
  vec3 col = vec3(0.);
  float totw = 0.;

  for( int i=0; i<N; i++ ) {
    vec4 posData = texelFetch( inputs, ivec2( i, 0 ), 0 ) * 255.0;
    float x = (posData.a * 256 + posData.r)*aspect;
    float y = posData.g * 256 + posData.b;
    vec2 pos = vec2( x, y ) / 65536.0;
    vec3 c = texelFetch( inputs, ivec2( i, 1 ), 0 ).rgb;
    c = pow( c, vec3(2.2) );  // un-gamma correct
    if( bOklab ) c = pow( kCONEtoLMS*c, vec3( 1./3. ) );
    float d = length( uv - pos );
    float w = exp( -d*d*N*strength );
    if( w > 0 ) {
      col += c * w;
      totw += w;
    }
  }
  if( totw > 0. ) col /= totw;
  if( bOklab ) col = kLMStoCONE*(col*col*col);
  col = pow( col, vec3( 1./2.2 ) );  // gamma correct

  if( bShowPoints ) {
    for( int i=0; i<N; i++ ) {
      vec4 posData = texelFetch( inputs, ivec2( i, 0 ), 0 ) * 255.0;
      float x = (posData.a * 256 + posData.r)*aspect;
      float y = posData.g * 256 + posData.b;
      vec2 pos = vec2( x, y ) / 65536.0;
      vec3 c = texelFetch( inputs, ivec2( i, 1 ), 0 ).rgb;
      float d = length( uv - pos );
      if( d < 0.01 ) col = vec3(1.);
      if( d < 0.007 ) col = c;
    }
  }

  fragColor = vec4( col, 1. );
}
"""};

The worst gray-banding seems to happen from red to blue. Maybe we just can’t interpolate nicely across the horseshoe.

GradientShader-2023-09-28-07-52-14

2 Likes