Как сделать черные пиксели в PImage прозрачными? - PullRequest
1 голос
/ 16 марта 2020

Следующий код создает эффект восходящего дыма.

PImage buffer1;
PImage buffer2;
PImage cooling;
PImage buffer3;
float yInitial = 0.0;
void setup() {
  size(600, 200);
  buffer1 = createImage(width, 200, RGB);
  buffer2 = createImage(width, 200, RGB);
  buffer3 = createImage(width, 200, RGB);
  cooling = createImage(width, 200, RGB);
}
void newLine(int rows) {
  //create row of white pixels
  buffer1.loadPixels();
  for (int x = 0; x<  buffer1.width; x++){
    for(int j = 0; j< rows; j++){
      //find location to draw pixels
      int y = buffer1.height - (j+1); 
      int index = x + y * buffer1.width;
      buffer1.pixels[index] = color(255);
    }
  }
  buffer1.updatePixels();
}
void cool(){
  cooling.loadPixels();
  //start x at 0
  float xoff = 0.0;
  float increment = 0.02;
  for (int x = 0; x < cooling.width; x++){
    xoff += increment;
    //start y at 0
    float yoff = yInitial;
    for (int y = 0; y < cooling.height; y++){
      yoff += increment;
      //calculate noise and enlarge
      float n = noise(xoff, yoff);
      if(n<0.4)
        n = 0;
      float bright = noise(xoff, yoff) *25;
      //set pixel to grayscale value
      cooling.pixels[x+y*cooling.width]= color(bright);
    }
  }
  cooling.updatePixels();
  yInitial += increment;
}

void draw(){
  cool();
  newLine(10);
  background(0);
  buffer1.loadPixels();
  buffer2.loadPixels();
  //look through all x and y coordinates and find the neightboring pixels colour
  for (int x = 1; x < buffer1.width-1; x++) {
    for (int y = 1; y < buffer1.height-1; y++) {
      int index0 = x + y * buffer1.width;
      int index1 = (x+1) + y * buffer1.width;
      int index2 = (x-1) + y * buffer1.width;
      int index3 = x + (y+1) * buffer1.width;
      int index4 = x + (y-1) * buffer1.width;
      color c1 = buffer1.pixels[index1];
      color c2 = buffer1.pixels[index2];
      color c3 = buffer1.pixels[index3];
      color c4 = buffer1.pixels[index4];
      color c5 = cooling.pixels[index0];
      float newC = brightness(c1) + brightness(c2)+ brightness(c3) + brightness(c4);
      newC = newC - brightness(c5);
      newC = newC / 4;
      buffer2.pixels[index4] = color(newC);
    }
  }
  buffer2.updatePixels();

  //swap
  PImage temp = buffer1;
  buffer1 = buffer2;
  buffer2 = temp;
  image(buffer2, 0, 0);
}

Я собираюсь использовать этот код для рисования движущегося дыма над изображением, которое будет на заднем плане (я еще этого не вставил) ). Для этого я попытался сделать все черные пиксели в PImage прозрачными. Это должно означать, что над фоновым изображением будет нарисован только дым. Затем я изменил все прозрачные пиксели обратно на черный после рисования изображения. Однако, когда я добавил это, дым полностью перестал вытягиваться. Что я сделал не так, и это правильный способ попытаться нарисовать дым поверх фонового изображения? Вот мой код:

PImage buffer1;
PImage buffer2;
PImage cooling;
PImage buffer3;
float yInitial = 0.0;
void setup() {
  size(600, 200);
  buffer1 = createImage(width, 200, RGB);
  buffer2 = createImage(width, 200, RGB);
  buffer3 = createImage(width, 200, RGB);
  cooling = createImage(width, 200, RGB);
}
void newLine(int rows) {
  //create row of white pixels
  buffer1.loadPixels();
  for (int x = 0; x<  buffer1.width; x++){
    for(int j = 0; j< rows; j++){
      //find location to draw pixels
      int y = buffer1.height - (j+1); 
      int index = x + y * buffer1.width;
      buffer1.pixels[index] = color(255);
    }
  }
  buffer1.updatePixels();
}
void cool(){
  cooling.loadPixels();
  //start x at 0
  float xoff = 0.0;
  float increment = 0.02;
  for (int x = 0; x < cooling.width; x++){
    xoff += increment;
    //start y at 0
    float yoff = yInitial;
    for (int y = 0; y < cooling.height; y++){
      yoff += increment;
      //calculate noise and enlarge
      float n = noise(xoff, yoff);
      if(n<0.4)
        n = 0;
      float bright = noise(xoff, yoff) *25;
      //set pixel to grayscale value
      cooling.pixels[x+y*cooling.width]= color(bright);
    }
  }
  cooling.updatePixels();
  yInitial += increment;
}

void draw(){
  cool();
  newLine(10);
  buffer1.loadPixels();
  buffer2.loadPixels();
  //look through all x and y coordinates and find the neightboring pixels colour
  for (int x = 1; x < buffer1.width-1; x++) {
    for (int y = 1; y < buffer1.height-1; y++) {
      int index0 = x + y * buffer1.width;
      int index1 = (x+1) + y * buffer1.width;
      int index2 = (x-1) + y * buffer1.width;
      int index3 = x + (y+1) * buffer1.width;
      int index4 = x + (y-1) * buffer1.width;
      color c1 = buffer1.pixels[index1];
      color c2 = buffer1.pixels[index2];
      color c3 = buffer1.pixels[index3];
      color c4 = buffer1.pixels[index4];
      color c5 = cooling.pixels[index0];
      float newC = brightness(c1) + brightness(c2)+ brightness(c3) + brightness(c4);
      newC = newC - brightness(c5);
      newC = newC / 4;
      buffer2.pixels[index4] = color(newC);
      //make the black pixels transparent
      if(color(buffer2.pixels[index0])==color(0));
        buffer2.pixels[index0] = color(0,0);
      if(color(buffer1.pixels[index0])==color(0));
        buffer1.pixels[index0] = color(0,0);
    }
  }
  buffer2.updatePixels();

  //swap
  PImage temp = buffer1;
  buffer1 = buffer2;
  buffer2 = temp;
  image(buffer2, 0, 0);
  //set transparent pixels back to black
  for (int x = 1; x < buffer1.width-1; x++) {
    for (int y = 1; y < buffer1.height-1; y++) {
      int index0 = x + y * buffer1.width;
      if(color(buffer2.pixels[index0])==color(0,0))
        buffer2.pixels[index0] = color(0);
      if(color(buffer1.pixels[index0])==color(0,0));
        buffer1.pixels[index0] = color(0);
    }
  }
}

1 Ответ

0 голосов
/ 15 апреля 2020

Это довольно крутой эффект дыма.

Проще создать эффект дыма, используя blendMode(). То, что вам нужно, это что-то вроде blendMode(SCREEN); или blendMode(ADD);, которое сделает черные пиксели на изображении с эффектом прозрачными.

composited smoke effect over red circle

PImage buffer1;
PImage buffer2;
PImage cooling;
PImage buffer3;
float yInitial = 0.0;

void setup() {
  size(600, 200);
  fill(192,0,0);
  // change blend mode to treat black pixels as transparent
  blendMode(SCREEN);

  buffer1 = createImage(width, 200, RGB);
  buffer2 = createImage(width, 200, RGB);
  buffer3 = createImage(width, 200, RGB);
  cooling = createImage(width, 200, RGB);
}
void newLine(int rows) {
  //create row of white pixels
  buffer1.loadPixels();
  for (int x = 0; x<  buffer1.width; x++){
    for(int j = 0; j< rows; j++){
      //find location to draw pixels
      int y = buffer1.height - (j+1); 
      int index = x + y * buffer1.width;
      buffer1.pixels[index] = color(255);
    }
  }
  buffer1.updatePixels();
}
void cool(){
  cooling.loadPixels();
  //start x at 0
  float xoff = 0.0;
  float increment = 0.02;
  for (int x = 0; x < cooling.width; x++){
    xoff += increment;
    //start y at 0
    float yoff = yInitial;
    for (int y = 0; y < cooling.height; y++){
      yoff += increment;
      //calculate noise and enlarge
      float n = noise(xoff, yoff);
      if(n<0.4)
        n = 0;
      float bright = noise(xoff, yoff) *25;
      //set pixel to grayscale value
      cooling.pixels[x+y*cooling.width]= color(bright);
    }
  }
  cooling.updatePixels();
  yInitial += increment;
}

void updateSmokeEffect(){
  cool();
  newLine(10);
  buffer1.loadPixels();
  buffer2.loadPixels();
  //look through all x and y coordinates and find the neightboring pixels colour
  for (int x = 1; x < buffer1.width-1; x++) {
    for (int y = 1; y < buffer1.height-1; y++) {
      int index0 = x + y * buffer1.width;
      int index1 = (x+1) + y * buffer1.width;
      int index2 = (x-1) + y * buffer1.width;
      int index3 = x + (y+1) * buffer1.width;
      int index4 = x + (y-1) * buffer1.width;
      color c1 = buffer1.pixels[index1];
      color c2 = buffer1.pixels[index2];
      color c3 = buffer1.pixels[index3];
      color c4 = buffer1.pixels[index4];
      color c5 = cooling.pixels[index0];
      float newC = brightness(c1) + brightness(c2)+ brightness(c3) + brightness(c4);
      newC = newC - brightness(c5);
      newC = newC / 4;
      buffer2.pixels[index4] = color(newC);
    }
  }
  buffer2.updatePixels();

  swapBuffers();
}

void swapBuffers(){
  //swap
  PImage temp = buffer1;
  buffer1 = buffer2;
  buffer2 = temp;
}

void draw(){
  background(0);
  // test drawing something in the background
  ellipse(mouseX,mouseY,30,30);

  updateSmokeEffect();
  // render blended image on top
  image(buffer2, 0, 0);
}

Вы можете использовать blendMode(BLEND), чтобы вернуться к режиму смешивания по умолчанию:

void draw(){
  background(0);
  blendMode(ADD);
  // test drawing something in the background
  ellipse(mouseX,mouseY,30,30);

  updateSmokeEffect();
  // render blended image on top
  image(buffer2, 0, 0);

  blendMode(BLEND);
  // test drawing something in the foreground
  ellipse(mouseX + 35,mouseY,30,30);
}

mixing blend modes to render a circle in the background behind the smoke effect and one in the foreground in front of the smoke effect

Дополнительно вы можете оформить заказ PGraphics* От 1027 * до mimi c слоев, хотя для таких пиксельных эффектов вы можете многое сделать с помощью PImage (и это set() / copy() / blend() / et c. Методов)

Вас также может заинтересовать шейдеры : фрагментные шейдеры, в частности для "пиксельных" манипуляций на GPU

...