島田 雄輝
teamLab株式会社 インタラクティブエンジニア所属
仕事ではUnity C#, HLSLなどを書きます
趣味でWebGLを書きます
Demoパーティに参加したのは今回が初めてです!
@ukeyshima
SESSIONS 2023
GLSL Graphics Compo Spawn
Shadertoy: https://www.shadertoy.com/view/mdVXzd
YouTube: https://youtu.be/yJ2grQIqxvc
レイマーチングを使った作品
8シーン全て、メンガーのスポンジをもとにしている(なのでSpawn)
作品紹介 : Spawn @ukeyshima
作品紹介 : Spawn
Room
XZ方向にFoldRotate
Floor
XZ方向にRepetition
Column
YZ方向にRepetition
Tunnel
XY面でFoldRotate
Z方向にRepetition
Fold
p.x = abs(p.x)
Repetition
p.x = mod(p.x, a)
FoldRotate
p.xy = FoldRotate(p.xy, a)
@ukeyshima
Shadertoy: https://www.shadertoy.com/view/ssByDw
以前に作っていたのでそのまま流用
レイマーチング x IFS (Iterated Function System)
- IFS ・・・ FoldやFoldRotateなど、座標空間の変換をいっぱいやったやつ
パラメータちょっと変えるだけで結構いろいろな形が作れるので楽しい
メンガーのスポンジ
float Sponge(vec3 p0) {
vec4 p = vec4(p0, 1.0);
for (int n = 0; n < 3; n++) {
p.xy = foldRotate(p.xy, 4.0);
p.yz = foldRotate(p.yz, 4.0);
p = abs(p);
p *= 3.0;
p.xyz -= 2.0;
p.z += 1.0;
p.z = abs(p.z);
p.z -= 1.0;
}
return sdBox(p.xyz, vec3(1.0))/p.w;
}
@ukeyshima
Repetitionで複製したオブジェクトを乱数で移動、回転させるのは地味に面倒
↓の左みたいになっちゃったりする
Shadertoy: https://www.shadertoy.com/view/Ws23Wd
ここら辺の解決の話は大昔にQiitaに書いてたのでそのまま流用
小ネタ @ukeyshima
適当に書いてたらコンパイル時間が激ヤバ
& クラッシュ太郎になってしまった
ShaderToyで待ってみたらこうなってた
for文、if文はコンパイル時に展開可能な場合、
展開されるため、その分コンパイルが重くなる
具体的には、たぶん11136回For文を
展開してた計算になる..?
※For文がコンパイル時に展開されないように
するテクニックもあるが上手くいかなかった
コンパイル激ヤバ問題
float SpongeColumn(vec3 p) {
//座標変換
return Sponge(p);
}
float SpongeFloor(vec3 p) {
//座標変換
return Sponge(p);
}
float SpongeTunnel(vec3 p) {
//座標変換
return Sponge(p);
}
float SpongeRoom(vec3 p) {
//座標変換
return Sponge(p);
}
float Map(vec3 p)
{
if(//Scene()が3, 7のとき) return SpongeColumn(p);
else if(//Scene()が2, 6のとき) return SpongeFloor(p);
else return min(SpongeTunnel(p), SpongeRoom(p));
}
vec3 Normal(vec3 p)
{
//Map()を6回呼ぶ
}
float AO(vec3 p)
{
//Map()を4回呼ぶ
}
void RayMarch()
{
for(int i = 0; i < 32; i++)
{
//Map()を1回呼ぶ
//Normal()を4回呼ぶ
//AO()を1回呼ぶ
}
}
当初のコード
int Scene() {
if(//timeが~のとき) return 1;
//~~~~~~
if(//timeが~のとき) return 4;
}
@ukeyshima
stepなどを使って、if文をなくす
コンパイル最適化
float Phase1Frag(){ return step(PHASE0_TIME, iTime) * step(iTime, PHASE1_TIME); }
float Phase2Frag(){ return step(PHASE1_TIME, iTime) * step(iTime, PHASE2_TIME); }
float Phase3Frag(){ return step(PHASE2_TIME, iTime) * step(iTime, PHASE3_TIME); }
float Phase4Frag(){ return step(PHASE3_TIME, iTime) * step(iTime, PHASE4_TIME); }
float Phase5Frag(){ return step(PHASE4_TIME, iTime) * step(iTime, PHASE5_TIME); }
float Phase6Frag(){ return step(PHASE5_TIME, iTime) * step(iTime, PHASE6_TIME); }
float Phase7Frag(){ return step(PHASE6_TIME, iTime) * step(iTime, PHASE7_TIME); }
float Phase8Frag(){ return step(PHASE7_TIME, iTime) * step(iTime, PHASE8_TIME); }
int Scene(){
return int(
phase1Frag * 2.0 +
phase2Frag * 3.0 +
phase3Frag * 4.0 +
phase4Frag * 1.0 +
phase5Frag * 3.0 +
phase6Frag * 4.0 +
phase7Frag * 2.0 +
phase8Frag * 2.0 + 0.1);
}
@ukeyshima
for文を減らす
全部共通のSponge()を最終的に呼んでいたので、一回だけ呼ぶことにする(関数化しない)
2つのオブジェクトを出したい場合、min()を使うのではなく、バウンディングボックスを定義し、
レイがこのバウンディングボックス内であればこの座標変換をする、みたいにする
コンパイル最適化
float SpongeColumn(vec3 p) {
//座標変換
return Sponge(p);
}
float SpongeFloor(vec3 p) {
//座標変換
return Sponge(p);
}
float SpongeTunnel(vec3 p) {
//座標変換
return Sponge(p);
}
float SpongeRoom(vec3 p) {
//座標変換
return Sponge(p);
}
float Map(vec3 p)
{
if(//Scene()が3, 7のとき) return SpongeColumn(p);
else if(//Scene()が2, 6のとき) return SpongeFloor(p);
else return min(SpongeTunnel(p), SpongeRoom(p));
}
float Map(vec3 p)
{
//シーン、バウンディングボックスに基づき、座標変換
return Sponge(p);
}
Column Floor
Room
Tunnel
@ukeyshima

2023_05_25 Sessions After Party