2018년 9월 디자인과 코딩을 함께 다루는 유튜브 채널을 열었습니다. "타입과 미디어" 사이트는 당분간 유지할 계획이지만, 새로운 정보는 유튜브 비디오를 통해서 소개하겠습니다. 많은 관심 부탁드립니다! — 정대인

세부 목차

우리는 stroke()fill()을 사용하여, 각각 선과 면의 색을 지정할 수 있고, noStroke()noFill()을 사용하여 각각의 색을 비활성화시킬 수 있다는 사실을 알고 있습니다. 그 외에 프로세싱에서 색을 사용하는 방법에 대해서 더 알아봅시다.

색상체계

RGB와 RGBA

컴퓨터 화면은 가산혼합방식으로 RGB 색공간을 사용합니다. 각 채널은 red, green, blue을 나타내며, 세가지 채널이 합쳐져서 빛으로 표시됩니다. 프로세싱은 기본적으로 RGB 색공간을 사용합니다.

colorMode(RGB);

또한, 각 채널의 범위를 지정할 수 있습니다. 기본값은 0에서 255까지 256색입니다.

colorMode(RGB, max);
colorMode(RGB, maxRed, maxGreen, maxBlue);
colorMode(RGB, maxRed, maxGreen, maxBlue, maxAlpha);

예를 들어서 0에서 255가 아닌 0에서 100사이의 퍼센트 값으로 색을 지정하고 싶으면 색을 사용하기 전에 다음 구문을 추가하면 됩니다.

void setup() {
  size(600, 100);
  colorMode(RGB, 100);
}

void draw() {
  for (int i = 0; i < width; i++) {
    float c = map(i, 0, width, 0, 100);
    stroke(c);
    line(i, 0, i, height);
  }
}
Your browser does not support canvas.
colorMode(RGB, 100);

HSB

RGB 컬러모드는 색의 값을 보았을 때, 한 눈에 어떤 색인지 파악이 잘 되지 않습니다. 또한, 색상휠에 익숙한 경우에는 색상값의 증가와 감소가 직관적이지 못합니다. 프로세싱은 RGB 모드보다 색을 사용하기에 더 자연스러운 HSB 컬러모드도 제공합니다. 각 채널은 hue, saturation, brightness로 설정됩니다.

colorMode(HSB, max);
colorMode(HSB, maxHue, maxSaturation, maxBrightness);
colorMode(HSB, maxHue, maxSaturation, maxBrightness, maxAlpha);

포토샵과 같은 그래픽 프로그램에 익숙한 경우, 다음과 같이 설정하면 혼란없이 색을 사용할 수 있습니다. hue의 범위를 360으로 설정하면 익숙한 색상휠을 사용하듯이 색을 사용할 수 있습니다. saturation, brightnessalpha값은 퍼센트로 설정할 수 있습니다.

colorMode(HSB, 360, 100, 100, 100);
void setup() {
  size(600, 100);
  colorMode(HSB, 360, 100, 100, 100);
}

void draw() {
  for (int i = 0; i < width; i++) {
    float c = map(i, 0, width, 0, 360);
    stroke(c, 100, 100);
    line(i, 0, i, height);
  }
}
Your browser does not support canvas.

색상값 확인하기

프로세싱의 색 선택 메뉴

프로세싱 상단 메뉴의 Tools > Color Selector를 사용해서 간단하게 HSB, RGB, hexadecimal 색상값을 확인할 수 있습니다. Color Selector나 Photoshop 등의 HSB 색상값의 경우, colorMode(360, 100, 100, 100) 함수를 호출해서 범위를 똑같이 설정해주면 따로 값을 변환할 필요가 없어서 편리합니다. 기본적으로는, HSB 색상값 또한 0에서 255사이의 값을 가집니다.

그래디언트

vertex마다 다른 색 적용하기

그래디언트를 간단하게 적용하는 방법에는 vertex마다 다른 색상값을 주는 방법이 있습니다. 버텍스 색상을 적용하기 위해서는 P2D 렌더러를 사용해야 합니다.

void setup() {
  size(600, 200, P2D);
  noStroke();
}

void draw() {
  background(200);
  for (int n = 0; n < 20; n++) {
    float x = random(width);
    float y = random(height);
    beginShape(TRIANGLE);
    for (int i = 0; i < 3; i++) {
      fill( random(100, 250), random(100, 200), random(100, 250) );
      vertex(x + i*random(-100,100), y + i*random(-100,100));
    }
    endShape();
  }
  noLoop();
}

vertex gradient

lerpColor()

lerpColor(c1, c2, amt);

lerpColor() 함수는 lerp()와 작동방식이 비슷합니다. 따로 수식을 만들지 않아도 amt를 이용해서 두 색의 사이값을 찾을 수 있습니다. 반복문 내에서 amt를 조금씩 증가시키면 부드러운 색의 변화를 표현할 수 있습니다.

int rsize = 20;
int cols;
int rows;

void setup() {
  size(600, 200);
  noStroke();

  cols = width / rsize;
  rows = height / rsize;
}

void draw() {
  color from = color(255, 80, 215);
  color to = color(180, 220, 255);

  float steps = round( map(mouseX, 0, width, 5, rows*cols) );
  for (int j = 0; j < rows; j ++) {
    for (int i = 0; i < cols; i ++) {
      color c = lerpColor(from, to, (j*cols + i)%steps/steps);
      fill(c);
      rect(i*rsize, j*rsize, rsize, rsize);
    }
  }
}
Your browser does not support canvas.

원형 그래디언트

선형 그래디언트를 vertex() 또는 line()함수를 이용해서 만든다면, 원형 그래디언트는 점점 작아지는 원을 각기 다른 색으로 그려서 만들 수 있습니다.

void setup() {
  size(600, 200);
  colorMode(HSB, 360, 100, 100);
  noStroke();
}

void draw() {
  background(0);

  for (int x = 0; x <= width; x += 200) {
    int h = (int) map( mouseX, 0, width, 30, 330 );
    drawRadialGradient(x, 100, 100, h-30, h+30);
  }
}

void drawRadialGradient(int x, int y, int diam, int startHue, int endHue) {
  for (int i = diam; i > 0; i--) {
    float h = lerp(startHue, endHue, i/float(diam));
    fill(h, 90, 90);
    ellipse(x, y, i, i);
  }
}
Your browser does not support canvas.

복잡한 도형에 그래디언트 입히기

vertex()를 사용해서 그래디언트를 표현하거나 line()함수 등을 사용해서 선형 그래디언트를 표현하는 것은 때에 따라서 유용하지만, 도형이 복잡한 모양이라면 그대로 적용하기 어렵습니다. 도형의 모양에 상관없이 그래디언트를 적용하는 방법으로는, 우리가 쉽게 만들 수 있는 사각형의 그래디언트를 먼저 만든 다음에, 그 위에 우리가 원하는 모양으로 마스크를 씌워주는 방법이 있습니다. 이 방법을 사용하면, 매 프레임마다 새로운 그래디언트를 그려주는 대신에, 처음에 한 번 만들어진 그래디언트를 계속해서 사용하게 되어서 실행 속도에도 적은 부하를 주게 되는 이점이 있습니다.

PGraphics gradient;
PGraphics maskGraphics;

void setup() {
  size(600, 200, P2D);

  gradient = createGradient(400, 200);

  maskGraphics = createGraphics(400, 200, P2D);
  maskGraphics.beginDraw();
  maskGraphics.background(0);
  maskGraphics.fill(255);
  maskGraphics.beginShape();
  maskGraphics.vertex(151, 42);
  maskGraphics.vertex(235, 160);
  maskGraphics.vertex(235, 160);
  maskGraphics.vertex(285, 101);
  maskGraphics.vertex(285, 101);
  maskGraphics.bezierVertex(285, 101, 300, 160, 344, 160);
  maskGraphics.bezierVertex(389, 160, 417, 42, 417, 42);
  maskGraphics.vertex(417, 42);
  maskGraphics.vertex(151, 42);
  maskGraphics.endShape(CLOSE);
  maskGraphics.endDraw();

  gradient.mask(maskGraphics);
}

void draw() {
  background(255, 100, 200);
  image(gradient, 0, 0);
}

PGraphics createGradient(int w, int h) {
  PGraphics grad = createGraphics(w, h, P2D);
  grad.beginDraw();
  for (int i = 0; i < w; i++) {
    float c = map(i, 0, w, 0, 255);
    grad.stroke(c);
    grad.line(i, 0, i, h);
  }
  grad.endDraw();
  return grad;
}

gradient complex

마스크 그래픽을 만들 때에는, 포토샵에서와 마찬가지로, 검은색은 투명하게 되고, 흰색은 이미지를 드러내 보입니다. 따라서 위의 예제에서도 maskGraphics를 만들 때, 배경색을 검게, 도형은 하얗게 해서 gradient가 보이도록 하고 있습니다. PGraphics 오브젝트는 오프스크린 그래픽스 버퍼(off-screen graphics butter)라고 해서, 화면 바깥에서 이미지를 구성하는 방식입니다. 이렇게 구성한 이미지는 여타 PImage와 같은 방식으로 화면에 표시해줄 수 있습니다. 모든 그리기 함수는 beginDraw()endDraw() 블럭 안에 위치해야 하며, 그리기 함수들은 함수 앞에 오브젝트 이름을 넣고 써야합니다. 예를 들어 위에서 마스크 이미지의 배경을 설정하기 위해서 maskGraphics.background(0);이라고 표기하고 있습니다.

이렇게 프로세싱 내에서 모든 것을 처리하는 방식이 번거롭게 느껴진다면, 기타 그래픽스 소프트웨어에서 만든 이미지를 PImage 오브젝트로 불러들여서 바로 그래디언트로 사용하는 방법도 고려해볼만 합니다.

블렌딩

우리가 포토샵 등에서 익숙한 블렌딩 모드(Blending modes)를 프로세싱에서도 사용해줄 수 있습니다. 업데이트 예정

색의 정렬

업데이트 예정

색 팔레트 만들기

사용할 모든 색들을 일일히 지정하지 않고, 배열을 이용하면 먼저 원하는 색의 팔레트를 만들어놓고, 그 팔레트에서 색을 뽑아서 사용할 수 있습니다.

아래의 예제는 palette라는 색상 데이터 배열에 6가지 보색 계열의 색을 담아놓고 사용하고 있습니다. HSB 색상모드와 색상 휠을 사용하여 사용하고자 하는 색의 범위를 지정해주면 보색, 유사색 등의 다양한 색상 팔레트를 만들어볼 수 있습니다.

color[] palette;

void setup() {
  size(600, 200);
  colorMode(HSB, 360, 100, 100);
  rectMode(CENTER);
  noStroke();
  palette = new color[6];
  generateColors();
}

void draw() {
  background( palette[0] );

  fill( palette[1] );
  rect(100, 100, 80, 80);
  fill( palette[2] );
  rect(200, 100, 80, 80);
  fill( palette[3] );
  rect(300, 100, 80, 80);
  fill( palette[4] );
  rect(400, 100, 80, 80);
  fill( palette[5] );
  rect(500, 100, 80, 80);
}

void generateColors() {
  for (int i = 0; i < palette.length; i++) {
    float h = random(1) > 0.5 ? random(0, 30) : random(180, 210);
    palette[i] = color(h, random(60, 100), random(80, 90));
  }
}

void mousePressed() {
  generateColors();
}
Your browser does not support canvas.

어도비의 Adobe Color CC를 참고하면 다양한 색을 먼저 실험해볼 수 있습니다.

pushStyle(), popStyle()

fill()이나 stroke()와 같이 그래픽 스타일을 결정하는 함수들은 한 번 호출하면 그 아래로 그려지는(경우에 따라서는 다음 프레임에 그려질 도형들까지) 모든 도형들에 영향을 미칩니다. 어떤 경우에는 그렇게 변경된 스타일을 되돌려놓느라고 필요 이상으로 스타일 함수들을 여러 번 사용해야 할 경우가 생기거나, 되돌려놓는 것을 깜빡한 경우에는 원하지 않는 결과가 나오게 되기도 합니다. 특히, 코드가 길어지면 길어질수록 잘못된 부분을 찾는 것은 어려워집니다. pushMatrix()popMatrix()가 일시적으로 좌표계에 영향을 미치는 것처럼, pushStyle()popStyle() 또한 그 블럭 바깥에는 영향을 미치지 않고, 블럭 안에서만 적용되는 스타일을 추가할 수 있게 해줍니다. pushStyle() 함수에 영향을 받는 스타일 함수들은 이 곳에서 확인할 수 있습니다.