LoginSignup
52
55

More than 5 years have passed since last update.

フラグメントシェーダーでっぽくない色の作り方を考える

Posted at

フラグメントシェーダを使うとどうしてもRGB!白・黒!みたいな色味になりがちなので、
色の操り方について、考えたので、そのまとめです。

1.RGBではなくHSBを使う

gl_FragColorに渡す色は vec4(r, g, b, a)なのでどうしてもRGBに寄りがちになります。
色をHSBで考えてから、RGBに変換して渡すと、狙った色を出しやすいです。

HSBはHue(色相)、Saturation(彩度)、Brightness(彩度)の略です。
全て、0~1の間で指定をします。色相環はは0が赤で0~1で一周します。

作例は極座標を使って、角度ごとに色相環を変化させた例です。

コードはHSB変換の例です。

//  Function from Iñigo Quiles
//  https://www.shadertoy.com/view/MsS3Wc
vec3 hsb2rgb( in vec3 c ){
    vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),
                    6.0)-3.0)-1.0,
                    0.0,
                    1.0 );
    rgb = rgb*rgb*(3.0-2.0*rgb);
    return c.z * mix(vec3(1.0), rgb, c.y);
}

void main() {
  vec3 hsb = vec3(0.0, 0.5, 0.8);
  vec3 color = hsb2rgb(hsb);

  gl_FragColor = vec4(color, 1.0);
}

コードは以下を参考にしています
https://www.shadertoy.com/view/MsS3Wc

2.mix関数を使う

こちらの色味を作る際に使用した例です。
色自体は薄い波打った円形を複数描いています。色は元々は白黒(0.0 ~ 1.0)です。

0.0 ~ 1.0の間の数値を渡すと、その数値に合わせてvec3型で色を返してくれます。

vec3 customColor(float d) {
  vec3 gray = vec3(0.9254, 0.8657, 0.8914); //グレー
  vec3 c = vec3(0.7210, 0.9118, 0.9083); //シアン
  vec3 m = vec3(0.9180, 0.2236, 0.9688); //マゼンタ
  vec3 y = vec3(1.0000, 0.9961, 0.3530); //イエロー
  vec3 color = mix(gray, y, smoothstep(0.0, 0.8, d));
  color = mix(color, m, smoothstep(0.0, 0.9, d));
  color = mix(color, c, smoothstep(0.0, 1.0, d));
  return color;
}

ポイントは mix()です。
mix(x, y, a)はxからyの間のある点aの値を取得することができます。(線形補間)

線形補間についてはこちら
https://qiita.com/niusounds/items/c4af702b06582590c82e

またaに渡す値を調節するのもポイントです。
smoothstep()で1を返す部分を微妙にずらすことで色の違いを出やすくしています。

  //グレーとイエローを混ぜる
  vec3 color = mix(gray, y, smoothstep(0.0, 0.8, d));
  //さらにマゼンタを混ぜる
  color = mix(color, m, smoothstep(0.0, 0.9, d));
 //さらにさらにシアンを混ぜる
  color = mix(color, c, smoothstep(0.0, 1.0, d));

この場合、色の混ぜる順番や、0 ~ 1の比率をいじることでいろんなタイプの色を出せるので、自分の好みの色に調整してみてください。

3.step関数を使う

step()を使って色の出現率を操り、似たような色の組み合わせを引き出す方法です。

HSBの出る確率を調整してカラーパターンを作ります。

vec3 hsb2rgb( in vec3 c ){
    vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),
                    6.0)-3.0)-1.0,
                    0.0,
                    1.0 );
    rgb = rgb*rgb*(3.0-2.0*rgb);
    return c.z * mix(vec3(1.0), rgb, c.y);
}

vec3 customColor(vec2 st, vec2 m) {
  float r = snoise(st + m);
  float h = 1.0 + step(0.5, r) + step(0.7, r) + step(0.8, r);
  float s = 1.0 + step(0.1, r) + step(0.1, r) + step(0.2, r);
  float b = 1.0 + step(0.1, r) + step(0.1, r) + step(0.2, r);

  vec3 color = vec3(fract(h/4.0 + (1.0 + m.y)*0.5), fract(s/4.0 + (1.0 + m.x)*0.5), b/6.0+0.4);
  return hsb2rgb(color);
}

コードのポイントはここです。

  // rは0.0~1.0
  float h = 1.0 + step(0.5, r) + step(0.7, r) + step(0.8, r); //1.0~4.0の数になる
  float s = 1.0 + step(0.1, r) + step(0.1, r) + step(0.2, r);
  float b = 1.0 + step(0.1, r) + step(0.1, r) + step(0.2, r);

各ブロック0.0~1.0の数字を持っています。
step(a,x)閾値(a)以下の場合0、それ以上の場合は1を返します。
上記の式では1にstep()を3回足しているので、最大の数値は4.0になります。
閾値を色々と調整してあげることで、色の出現率を調整することができます。

  vec3 color = vec3(h/4.0, s/4.0, b/4.0);
  return hsb2rgb(color);

色として使う際は4で割ってあげると0~1の値になります。

さらに調整したい場合はfract()を使って小数点の部分だけとってあげれば調整が可能です。
例えば、マウスのx座標で変更したい際は以下のようにします。

  vec3 color = vec3(fract(h/4.0 + mouse.x), s/4.0, b/4.0);
  return hsb2rgb(color);

マウスの位置によって色相が変わるけど、彩度・明度は変わらないので、雰囲気の似た別の色が出るようになります。

おわりに

最近書いたコードの中で、色にまつわるものをまとめてみました。
他にもいい例や、効率のいいやり方などあればぜひ教えてください。

52
55
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
52
55