3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

学園アイドルマスターの制服模様をShaderで作った

Posted at

はじめに

学園アイドルマスターの制服模様をShaderToyで作りました。誰がなんと言おうとファンアートです。

公式が制服の柄を公開しており、これを見た瞬間に『Shaderで作れそうだな』と感じたので作成しました。
制作時間は8時間ほどです。

ソースコード

解説&反省点

解説の前提は

  • ShaderToyを描いたことがある
  • SDFなどの知識がある程度ある
    です。

反省点については

  • 文字数を少なくできた可能性がある
    を観点としています。処理の速さなどはあまり考えて無いです。

mapメソッドについて

四角の模様

image.png
image.png
四角模様はこの二つです。ソースコードでquad-1quad-2とコメントしてある部分が該当箇所です。

// a:幅 x:方向
float squareWave(float a, float x)
{
    return step(a, fract(x + a));
}

このメソッドを使って四角を作ります。
image.png

メソッドをグラフにするとこうなります。
任意の幅(0~1)の矩形波を作り、これで縞模様を作ります。
縦線か横線しか作れませんがグリッド線を最終的に作り合成するので結果的に四角に見えます。

このメソッドだけを使ったサンプルです。
アニメーションするので縞模様がどのように作られているかわかりやすいと思います。

// r:横向きか縦向きかを決める
// f:表示するかを決める
squareWave(.85, mix(p.x, p.y, r) * 7.9) * f;

メソッドを利用し、幅0.85で縞模様を作り、UVを7.9倍(8.0じゃないのはこっちのほうが見た目がよかったから)することで1つのマス目に複数四角があるようにします。

quad-1quad-2の違いは四角の分割数が6か8かの違いだけです。

反省点
上記の通り、分割数が6か8かの違いだけなので1つの処理にまとめられた気がします。
四角が関係してる月模様と、この四角模様(2つ)の合計3要素を全部1つの処理で作成し、月は別で追加処理、その後回転させる
という処理を作ればもっと文字数を少なくできたと思っています。

image.png

星の模様はこれです。ソースコードでstarとコメントがしてある部分が該当箇所です。

vec2 p2,p3;
p2 = p3 = p;
p2.x = abs(p2.x); p3.y = abs(p3.y);
p2.x -= .83; p3.y -= .83;
float s0, s1;
s0 = max(sdCircle(p2, 0.6), -sdCircle(p2, 0.57));
s1 = max(sdCircle(p3, 0.6), -sdCircle(p3, 0.57));
d += step(min(s0, s1), .001) * f;

全部で4つの大きめの円中心から離れた位置に作成し星の模様に見えるようにしてます。
absを使うことで2回の計算で済むように作り、それを組み合わせることで作成しています。
手順としては、

  • 0.6の円aと0.57の円bをmax(a, -b)でブーリアンの差分合成をして円の線s0を作る。
  • s1の分も同じく作る。
  • min(s0, s1)のブーリアンの加算合成で4つ分の円の線ができる
    といった感じです。

反省点
この模様を見たときにサイクロイドを使って描けるのではないのかと思ったのですが、知識がないため作れませんしでした。
あと、absをうまく使えばs0とs1と1つにするのではなく、1つ作るだけで4つ分計算できるはずなのですがうまくいかなかったのでX軸とY軸の2つ作る結果となってしまいました。

image.png
月の模様はこれです。ソースコードでmoonとコメントがしてある部分が該当箇所です。

vec2 p5 = p;
p5 = mix(vec2(p5.x + .5, p5.y), vec2(p5.x, p5.y + .5), r);
float m;
m = min(1.-squareWave(.9, mix(p.y, p.x, r) * 8.), sdCircle(p5, .5));
d += step(max(m, -sdCircle(p5, .47)), .001) * f;

これは単純で、上記の四角模様と円をブーリアン合成することで作っています。

  • 円をX方向かY方向にずらす(回転によって変わる)
  • 線と円をブーリアン合成
  • ちょっと小さい円をブーリアンの差分合成して線にして完成

反省点
線の時に書きましたが、これをmoonとして計算するのではなく、線と同じ種類として一括で計算したほうが文字数を少なくできた気がします。

H

image.png
Hの模様はこれです。ソースコードでhとコメントがしてある部分が該当箇所です。
そもそもこれがHを表してるのかはわかりませんが。まぁ多分初星のHでしょう。たぶん

p6 *= rot(mix(0., 1.57079, r));
float l = step(abs(p6.y), .02) * step(abs(p6.x), .25); // horizontal line
l += step(abs(fract(p6.x*2.+.5)), .07) * step(abs(p6.y), .42); // vertical line
l += step(max(sdCircle(p6, 0.5), -sdCircle(p6, 0.47)), .001);

Hについては横線+縦線+円で作ってます。
円に関しては前までと作り方は変わりません
縦横線は左右対称なのでabsを使い、stepX方向とY方向で使い、乗算することで箱を作成します。なのでSDFは使って無いです。

反省点
この記事を書いてる時にabs使う必要性あったかなと思い始めました。特に横線に関してはいらない気がします。中心からのXの長さをそのまま書けば作れるので。
逆に縦線は2本あるのでabsを使うのは正解だったと思ってます。

色塗り

vec3 b0, b1, b2, b3; // Gradually darker
b0 = vec3(.761, .863, .996);
b1 = vec3(.635, .788, .949);
b2 = vec3(.494, .643, .847);
b3 = vec3(.373, .533, .745);

最初に色を定義します。これは学マスの公式画像にスポイト機能で色を調べて数値にしました。

vec2 uvf = abs(mod(uv * 2., 1.)) - .5;
vec2 uvd = floor(uv * 2.);
    
float d = 1. - map(uvf, uvd - 1.);
    
vec3 col = vec3(d);
vec2 d2, d20;
    
d2 = mod(uvd, 2.);
    
d20 = d2 * .5;
col = fract(d20.x + d20.y) * 2. * b2 * d;
col += step(.1, d2.x * d2.y) * b1 * d;
col += step(d2.x, .1) * step(d2.y, .1) * b3 * d;
col += b0 * (1.-d);
    
fragColor = vec4(col,1.);

image.png
uvfはuvをn倍した小数点以下の部分が入ってます。グラフのように0~1しか入ってません。
この方法はグリッド模様を使うときに効果的です。グリッドはマス目ごとに0~1のuvが欲しいからです。

image.png
uvdはuvをn倍した整数部が行ってます。グラフのようになります。
この方法もグリッド模様を作るときに効果的です。整数部分だけがわかることで現在のマス目が何番目のマス目なのか把握することができます。

col = fract(d20.x + d20.y) * 2. * b2 * d;  // 1
col += step(.1, d2.x * d2.y) * b1 * d;  // 2
col += step(d2.x, .1) * step(d2.y, .1) * b3 * d; // 3
col += b0 * (1.-d);  //4

作成した何番目のマス目なのか保存してるd20を使用して塗分けます。
image.png
1に関してはXとYのマス目番号の合計が偶数のマス目だけ塗ってます。
赤と白の合計が偶数ならb2の色になります

image.png
2に関してはXとYのマス目をかけて1になったらb1の色で塗ってます

image.png
3に関してはXとYのマス目の両方が0ならb3で塗ります。

4に関してはdで線をどこに引くのか入ってるので都合を合わせるために白黒を反転させて塗ります。

反省点
image.png
modを使ってこれを一つの塊として扱って塗ればもっと文字数を削減できたと思いました。

その他

どのマスにどの模様を書くのかを最悪な分岐で処理してます。

if((uv2.x == 7. && uv2.y == 0.) || (uv2.x == 2. && uv2.y == 0.) || 
       (uv2.x == 5. && uv2.y == 3.) || (uv2.x == 0. && uv2.y == 3.) ||
       (uv2.x == 6. && uv2.y == 2.) || (uv2.x == 1. && uv2.y == 2.) ||
       (uv2.x == 8. && uv2.y == 1.) || (uv2.x == 3. && uv2.y == 1.))
    {
        f = 1.;
    }

どうにかしたかったのですがわりと模様の位置がばらばらで規則性が見えてこなかったのでこれしか思いつきませんでした。もう少し賢いやり方して文字数を少なくしたいです。約3000文字使ってるんで…

3
2
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
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?