[SphereTracing:領域操作関数]ハニカム構造

![cut.png](https://qiita-image-store.s3.amazonaws.com/0/2585/f6182db8-7480-2867-43dd-973a6f5489fc.png)

#はじめに
[「Shadertoy はじめました - Qiita」](http://qiita.com/keim_at_si/items/bc8da5535e47b1ada905)の子記事です。[My First Shader Sound](https://www.shadertoy.com/view/MtGSWc) （※音が出ます）を作った際にいろいろ気づいたトピックスを小さめの記事にしていきたいと思います。
この記事では、Sphere Tracing において距離関数 (distance function) と並んで重要な性質である領域操作関数 (domain oparations) について書きます。[My First Shader Sound](https://www.shadertoy.com/view/MtGSWc)で用いたハニカム構造繰り返し配置をベースに書きますが、この配置自体は特殊でも何でもないため、ビジュアルプログラミングに慣れた人には物足りない内容かもしれません。
iq氏によるリファレンスサンプルから一歩進んで、オリジナル形状の距離関数を定義したい人にとって理解の一助につながれば幸いです。
（「距離関数」や「領域操作」など固有名詞を直訳で表現してしまっていますが、これらの呼称は一般的とは限りませんので、ご注意ください。）

#無限繰り返し
[iq氏による距離関数リファレンスサンプル](http://iquilezles.org/www/articles/distfunctions/distfunctions.htm)に書かれているように、形状の無限繰り返し（Repetition）は 座標値の剰余(mod)によって非常に簡単に記述することが出来ます。

```repetition.glsl
float opRep( vec3 p, vec3 c ) {
    vec3 q = mod(p,c)-0.5*c;
    return primitve( q );
}
```

距離関数 ```primitive(q)``` に点座標を渡す前に、繰り返し距離の剰余にするだけで無限繰り返しになります。サンプルはx,y,z方向すべての繰り返しですが、x,zに対して剰余を返せば[My First Shader Sound](https://www.shadertoy.com/view/MtGSWc) のようにxz平面上の繰り返しを表現できます。

これは、１点から最近傍形状までの距離のみで（進行方向ベクトルを考慮せずに）交点を求めてしまう Sphere Tracing ならではの性質です。剰余を求める成分をxyz座標系ではなく極座標系にすれば円周・球面上での繰り返しも簡単に表現できますし、[折り畳みやフラクタル、回転対象](http://gam0022.net/blog/2017/03/02/raymarching-fold/)なども組み合わせれば、工夫次第で実に様々な無限配置を表現できます。

**無限繰り返し＝「全画面に広がる大量（大型）オブジェクト」**を、ここまで簡単に記述できる手法は他にない（と思う）ため、Sphere Tracing によるシェーダアート作品のインパクトは、かなりの部分この性質に担保されているように思います。

ここら辺は自分ももっといろいろ実験して掘り下げていけたらと思っています。

#無限繰り返し？
「繰り返し」という表現をしていますが、**同じ形状を繰り返す必要はありません**。

一つの距離関数で定義さえできれば、どんなものでも無限に配置することができます。例えば、位置毎に三角、四角、丸など様々な形状を返すような距離関数を定義して無限に繰り返せば、様々な形状を無限にばらまく事ができます。位置毎に違った回転を加えた距離関数を定義すれば、バラバラな姿勢の形状繰り返しを表現できます。
[My First Shader Sound](https://www.shadertoy.com/view/MtGSWc) では、**同心円状の位置に応じて形状の伸縮を変えた距離関数**を定義しているため、単純な無限繰り返しでも位置によって形状が異なり、全体では同心円状にうねりが広がるようなアニメーションになります。

時間変化する同じ形状の繰り返しだけでも非常にインパクトがあり、それだけでも非常に魅力的なシェーダアートはいっぱいありますが、時間だけでなく**位置に応じて違う形状を返すとバリエーションが広がります**。ここら辺の話は[Shadertoy はじめました week #3](http://qiita.com/keim_at_si/items/73e4c2008dd846e82310)でもう少し深く触れたいです。触れないかもしれません。~~そもそもそこまで飽きずにたどり着くか分りません。~~

#ハニカム構造繰り返し
[男の子ならみんな大好き六角柱](http://qiita.com/keim_at_si/items/74592439e76052c4f99f)と切っても切れないのが、[ハニカム構造](https://ja.wikipedia.org/wiki/%E3%83%8F%E3%83%8B%E3%82%AB%E3%83%A0%E6%A7%8B%E9%80%A0)です。

[iq氏による距離関数リファレンスサンプル](http://iquilezles.org/www/articles/distfunctions/distfunctions.htm)では軸並行の繰り返しまでしか書いてませんので、ハニカム構造の繰り返しは自分で考えないといけません。ビジュアルプログラミングの世界ではよくある話題なので、新鮮味はないですが、代表的な方法は3つあります。
##1. 座標値をSkew変換する
xy平面で繰り返した形状を、単位ベクトルの長さを維持しつつ30度傾けて [Skew (せん断)変換](https://ja.wikipedia.org/wiki/%E3%81%9B%E3%82%93%E6%96%AD%E5%86%99%E5%83%8F)する事で、ハニカム構造が現れます。[WikiPediaのこの図](https://ja.wikipedia.org/wiki/%E3%81%9B%E3%82%93%E6%96%AD%E5%86%99%E5%83%8F#/media/File:Academ_Study_about_a_periodic_tiling_by_regular_polygons.svg)が非常に直観的な説明になっているので、ここでは特に深い説明はしません。「2次元の座標変換を一回噛ませる」だけという非常に軽い計算でハニカム構造が得られます。形状も一緒にSkew変換で歪ませてしまうので、変換前の形状を定義する必要がありますが、例えば直角三角形が正三角形に変換されたりするので、うまく考えれば面白い効果が得られます。

##2. 1列づつずらして配置する
シンプルに各列で領域半分づつずらして配置することでハニカム構造になります。特に難しい話ではないのですが、今回みたいに六角柱が密接していると少し面倒くさい問題が発生します。
この方法は、長方形の繰り返し領域が半分づつずれてレンガ状に配置されますが（図中点線）、密接している六角柱では噛みあっている部分が繰り返し範囲からはみ出てしまいます（図中赤い囲み内の三角形）。1つの繰り返し領域内の形状は1つの距離関数でしか表現できないため、この繰り返し範囲からはみ出た部分は描画されず、角が切れてしまいます。
![スケッチ (1).png](https://qiita-image-store.s3.amazonaws.com/0/2585/cbc945ce-35e7-6ca1-1ed8-064044181c53.png)


##3. ２つのxy座標繰り返しを互い違いに配置する
1列づつずらす方法の亜流ですが、2つの列に分けてOR合成すれば、互いの列がかみ合った密集六角柱を定義できます。円でハニカム構造配置を描くと下のようになるのですが、青円だけに注目するとシンプルなxy軸での繰り返しになっているのがわかると思います。
![honeycom](https://s3.amazonaws.com/grapher/exports/96cw78pbx8.png)
https://www.desmos.com/calculator/96cw78pbx8

縦繰り返し幅は、辺の長さ1の正三角形の高さの4倍なので$2\sqrt3$です。せん断変換を利用する方法と比べると、2つの距離関数を計算して合成する形になるので計算が重くなりますが、列ごとに違う形状関数を利用できるので、今回のように色を変えるだけでなく、いろいろな応用が利くとおもいます。

#感想
何かととっつきにくい Sphere Tracing ですが、正直レンダリングの部分は既存シェーダの流用でかなりの事はできてしまうので、結局は距離関数 (distance function) と領域操作関数 (domain oparations)（と変形関数(deformation)）でどれだけバリエーションを出せるかの勝負なのかなと、[Shadertoy](https://www.shadertoy.com/)を徘徊して感じました。Shadertoyは4年以上運営されていて、初期から変態シェーダのオンパレード（そもそも[主催がワールドクラスの変態コーダ](https://www.google.co.jp/search?q=rgba#q=rgba+demoscene&*)）だったので、ネタが出尽くしているようにも見えますが、テーマを絞った検索をかけてみると、意外にやられてないネタがあるかもなーというのが、最近の感想です（検索が甘いだけかも。あと自分の環境だと動かないものが結構ある。そもそも過去の遺産が膨大すぎて、全部把握するのが不可能に近い。）。

なんにしても、自分はまだシェーダアートを初めて2か月程度の超ひよっこなので、車輪の再発明上等、諸先輩の胸を借りるつもりでぶつかっていこうと思います。