抖动 (Floyd-Steinberg) 仅更新 p5.js 中的部分图形对象

Posted

技术标签:

【中文标题】抖动 (Floyd-Steinberg) 仅更新 p5.js 中的部分图形对象【英文标题】:Dithering (Floyd-Steinberg) only updates part of graphics object in p5.js 【发布时间】:2022-01-22 13:10:41 【问题描述】:

我正在尝试在 P5.js 草图中实现 Floyd-Steinberg 抖动,方法是在图形对象中(在设置中)预抖动一堆圆圈,然后稍后再绘制它们。

但是,我一直遇到只有部分圆圈抖动,其余部分看起来正常的问题。欢迎提出任何建议,因为我真的不知道发生了什么。

setup():

let circs;
function setup() 
  //...
  createCanvas(1000,1000);
  let size = 200;
  circs = [];
  circs.push(
    gfx: createGraphics(size, size),
    size: size,
    color: color(random(255))
  );
  for (let i = 0; i < circs.length; i++)
    dither(circs[i]);
  // ...

draw():

function draw() 
  if (!paused) 
    background(bg);
    drawShadow(4);  // just a call to the drawingContext shadow

    for (let i = 0; i < circs.length; i++) 
      push();
      translate(width / 2, height / 2);
      imageMode(CENTER);
      image(circs[i].gfx, 0, 0);
      pop();
    
  

floyd-steinberg - based on https://openprocessing.org/sketch/1192123

function index(x, y, g) 
  return (x + y * g.width) * 4;


function dither(g) 
  g.loadPixels();
  for (let y = 0; y < g.height - 1; y++) 
    for (let x = 1; x < g.width - 1; x++) 
      let oldr = g.pixels[index(x, y, g)];
      let oldg = g.pixels[index(x, y, g) + 1];
      let oldb = g.pixels[index(x, y, g) + 2];

      let factor = 1.0;
      let newr = round((factor * oldr) / 255) * (255 / factor);
      let newg = round((factor * oldg) / 255) * (255 / factor);
      let newb = round((factor * oldb) / 255) * (255 / factor);

      g.pixels[index(x, y, g)] = newr;
      g.pixels[index(x, y, g) + 1] = newg;
      g.pixels[index(x, y, g) + 2] = newb;

      g.pixels[index(x + 1, y, g)] += ((oldr - newr) * 7) / 16.0;
      g.pixels[index(x + 1, y, g) + 1] += ((oldr - newr) * 7) / 16.0;
      g.pixels[index(x + 1, y, g) + 2] += ((oldr - newr) * 7) / 16.0;

      g.pixels[index(x - 1, y + 1, g)] += ((oldr - newr) * 3) / 16.0;
      g.pixels[index(x - 1, y + 1, g) + 1] += ((oldr - newr) * 3) / 16.0;
      g.pixels[index(x - 1, y + 1, g) + 2] += ((oldr - newr) * 3) / 16.0;

      g.pixels[index(x, y + 1, g)] += ((oldr - newr) * 5) / 16.0;
      g.pixels[index(x, y + 1, g) + 1] += ((oldr - newr) * 5) / 16.0;
      g.pixels[index(x, y + 1, g) + 2] += ((oldr - newr) * 5) / 16.0;

      g.pixels[index(x + 1, y + 1, g)] += ((oldr - newr) * 1) / 16.0;
      g.pixels[index(x + 1, y + 1, g) + 1] += ((oldr - newr) * 1) / 16.0;
      g.pixels[index(x + 1, y + 1, g) + 2] += ((oldr - newr) * 1) / 16.0;
    
  
  g.updatePixels();

我不确定我缺少什么,因为抖动算法在高度和宽度上循环,然后应该更新,但我认为我缺少一些东西。

【问题讨论】:

【参考方案1】:

p5.Graphics 对象有一个从草图继承的pixelDensity。当像素密度大于 1 时,对于高 DPI 显示器,您需要在计算像素索引时考虑到这一点:

function index(x, y, g) 
  const d = g.pixelDensity();
  return (x + y * g.width * d) * 4;

当您处理像素时,您需要将 x 和 y 的最大值加倍。

下面是pixelDensity效果的演示(以及你是否处理它):

let g;
function setup() 
  createCanvas(400, 400);
  g = createGraphics(width, height);
  redrawGraphics();
  noLoop();
  setInterval(
    () => 
      redrawGraphics(frameCount % 2);
      redraw();
    ,
    2000
  );


function index(x, y, g, d) 
  return (x + y * g.width * d) * 4;


function redrawGraphics(hdpi) 
  const d = hdpi ? pixelDensity() : 1;
  
  g.background(0);
  g.loadPixels();
  for (let y = 0; y < height * 2; y++) 
    for (let x = 0; x < width * 2; x++) 
      let ix = index(x, y, g, d);
      let r = map(sin((x - y) / width * TWO_PI), -1, 1, 0, 255);
      
      g.pixels[ix] = r;
      g.pixels[ix + 1] = 0;
      g.pixels[ix + 2] = 0;
      g.pixels[ix + 3] = 255;
    
  
  g.updatePixels();


function draw() 
  image(g, 0, 0);
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"&gt;&lt;/script&gt;

【讨论】:

啊,像素密度。不知道我是怎么错过的,非常感谢!

以上是关于抖动 (Floyd-Steinberg) 仅更新 p5.js 中的部分图形对象的主要内容,如果未能解决你的问题,请参考以下文章

ps转16位图少了字节

如何在此处正确实现去抖动时间,以便在纯 JavaScript 中每个时间限制仅触发一次?

js:防抖动与节流

DeepFaceLab:手动提取高精度脸图,减少抖动!

如何仅在用户摇动 iPhone 时播放音频文件?

unity3d 摄像机抖动效果 CameraShake