太陽の神
突然の覚醒に、驚くどころか厨二よろしく自分はやっぱりヒーローだったのだと思い上がる、きのこの国の戦士、左利き(のきのこ)。
そしてそんな左利きの父親と浅からぬ因縁を感じさせる、たけのこの国の戦士、隊長。
ふたりの激しいぶつかり合いは三日三晩続いたという。
互いにダメージも蓄積し、もはや戦い続けるのも難しくなってきているなか、たけのこ隊長は左利きにこう切り出した。
隊長「ぐぬぬ……このまま戦い続けても決着はつかんだろう。認めたくはないが……貴様は強い。全盛期の権造さんよりも上かもしれん」
左「へへへ……お、おれはまだ戦えるぞ……」
強がってはみたものの、左利きも既に限界だった。
隊長は冷静な眼差しのまま続けた。
隊長「いや、お前に免じて今回は我々は引き上げよう。このまま戦い続ければ、お前も権造さんの二の舞いになりかねん。そうなっては我々も困るのだ」
左「ど……どういうことだ! あんた、親父について何か知っているのか!!」
そう、左利きは自分の実の父である権造さんの最後について、聞かされていなかった。
戦乱の英雄である、たけのこ狩り名人の権造さん……しかし彼が今どこで、なにをしているのか、いや彼が生きているのかどうかさえ、きのこの国では極秘事項として秘匿とされている。きのこの国の軍上層部は、実の息子である左利きに対してさえ、このことに関しては一切の情報を与えていなかったのだ。
左「あんたにこんなことを頼める義理じゃないことはわかってる……だが、親父は……親父はどうなっちまったんだ……」
左利きの様子に、たけのこ隊長は驚きを隠さなかった。
隊長「まさか……お前は権造さんとたけのこ王との戦いを知らんのか? たけのこの国では常識だぞ……」
たけのこ隊長はとつとつと語った。
きのこの戦士のなかには、太陽神の加護を受けている一族がおり、権造さんも、そして左利きも、恐らくその太陽神の一族なのだという。太陽神の一族は、ピンチになると太陽神の加護のチカラにより、強力なパワーを発揮する。しかしそれは一時的なもので、そのチカラを行使し続ければやがて 本物の太陽神を現世に召喚 してしまうというのだ。
左「そういえば、あんたに殺されそうになったとき、俺には『チカラが欲しいか』とどこからともなく声が聞こえたんだ」
隊長「それはお前たち一族を守護する太陽神だろう。お前の親父、権造さんは、先の大乱で太陽神を召喚し、戦争を終結させたんだ。今お前にそれをやられたら我々どころか、お前もただではすまんだろう。お前にこれ以上、チカラを使わせたくはない」
左「隊長……」
三日三晩戦い続けたふたりの間には、種族の確執を超えた、不思議な友情が芽生えていたのかもしれない。
しかし、そんな戦士たちの束の間の友情を遮るかのように、突然、空が巨大な影に覆われてしまう!
precision mediump float;
uniform vec2 resolution;
uniform vec2 mouse;
uniform float time;
uniform sampler2D backbuffer;
struct Intersect{
float dist;
vec3 color;
};
const float PI = 3.1415926;
const float PI2 = PI * 2.0;
const vec3 cColor = vec3(0.25, 0.1, 0.0);
const vec3 fColor = vec3(0.2, 0.7, 0.1);
const vec3 kColor = vec3(0.9, 0.8, 0.4);
float smoothMin(float d1, float d2, float k){
float h = exp(-k * d1) + exp(-k * d2);
return -log(h) / k;
}
float dChocokino(vec3 p){
vec3 q = (p + vec3(0.0, -0.55, 0.0)) * vec3(1.0, 1.2, 1.0);
float len = sin(atan(q.z, q.x) * 7.0) * 0.01 * PI;
return length(q) - 1.0 + len + step(p.y, -0.4) + pow(p.y, 1.0);
}
float dCylinderkino(vec3 p, vec2 r){
float s = -cos(p.y * 2.0) * 0.1 - 0.05;
float l = length(p + vec3(0.0, 1.25, 0.0)) - 0.4;
vec3 q = p + vec3(s, 0.5, 0.0);
vec2 d = abs(vec2(length(q.xz), q.y)) - r;
return smoothMin(min(max(d.x, d.y), 0.0), l, 32.0) + length(max(d, 0.0)) - 0.2;
}
float waveline(vec2 p){
float f = smoothstep(0.975, 1.0, cos(p.x * 2.5) - (p.y * 2.5) - 0.75);
float g = smoothstep(0.975, 1.0, cos(p.x * PI + PI) - (p.y * 4.0) - 0.5);
float h = smoothstep(0.975, 1.0, cos(p.x * 2.5) - (p.y * 2.5) + 0.5);
return mix(mix(mix(0.0, 0.3, h), 0.4, g), 0.6, f);
}
float dChoco(vec3 p){
float e = 1.0 - p.y * 0.7;
float w = waveline(vec2(atan(p.z, p.x) / PI, p.y + 0.25)) * 0.15;
return length((p + vec3(0.0, -0.5, 0.0)) * vec3(e, 0.5, e)) + p.y - 0.5 + step(p.y, -1.2) - w;
}
float dCylinder(vec3 p, vec2 r){
vec2 d = abs(vec2(length(p.xz), p.y)) - r;
return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - 0.25 + step(-1.0, p.y);
}
float dFloor(vec3 p){
return dot(p, vec3(0.0, 1.0, 0.0)) + 1.5;
}
Intersect distanceHub(vec3 p){
Intersect i;
float flor = dFloor(p);
vec3 q = p + vec3(3.0, 0.0, 0.0);
if(p.x < 0.0){
float chocokino = dChocokino(q);
float cylinkino = dCylinderkino(q, vec2(0.1, 0.9));
i.dist = min(min(chocokino, flor), cylinkino);
i.color = cylinkino < min(chocokino, flor) ? kColor : chocokino < flor ? cColor : fColor;
}else{
q = p;
if(p.x > 6.0){
q = vec3(mod(q, 3.0) - 1.5);
}else{
q = q - vec3(3.0, 0.0, 0.0);
}
q = vec3(q.x, p.y, q.z);
float choco = dChoco(q);
float cylin = dCylinder(q, vec2(0.5, 2.0));
i.dist = min(cylin, min(choco, flor));
i.color = cylin < min(choco, flor) ? kColor : choco < flor ? cColor : fColor;
}
return i;
}
vec3 genNormal(vec3 p){
float d = 0.001;
return normalize(vec3(
distanceHub(p + vec3( d, 0.0, 0.0)).dist - distanceHub(p + vec3( -d, 0.0, 0.0)).dist,
distanceHub(p + vec3(0.0, d, 0.0)).dist - distanceHub(p + vec3(0.0, -d, 0.0)).dist,
distanceHub(p + vec3(0.0, 0.0, d)).dist - distanceHub(p + vec3(0.0, 0.0, -d)).dist
));
}
void main(){
vec2 p = (gl_FragCoord.xy * 2.0 - resolution) / min(resolution.x, resolution.y);
float f = mod(time, 10.0);
float t = exp(smoothstep(8.0, 9.0, f) * 4.0) - 1.0;
float u = smoothstep(0.0, 1.0, f);
vec3 cPos = vec3(0.0, t, 5.0);
vec3 cDir = vec3(0.0, 0.0, -1.0);
vec3 cUp = vec3(0.0, 1.0, 0.0);
vec3 cSide = cross(cDir, cUp);
float targetDepth = 1.0;
vec3 ray = normalize(cSide * p.x + cUp * p.y + cDir * targetDepth);
float dist = 0.0;
float rLen = 0.0;
vec3 rPos = cPos;
Intersect intersect;
for(int i = 0; i < 256; ++i){
intersect = distanceHub(rPos);
rLen += intersect.dist * 0.5;
rPos = cPos + ray * rLen;
}
if(abs(intersect.dist) < 0.001){
float fog = smoothstep(0.0, 30.0, length(rPos - cPos));
vec3 normal = genNormal(rPos);
vec3 light = normalize(vec3(mouse + 2.0, 3.0));
float diff = max(dot(normal, light), 0.1);
vec3 eye = reflect(normalize(rPos - cPos), normal);
float speculer = clamp(dot(eye, light), 0.0, 1.0);
vec3 specColor = pow(speculer, 20.0) + cColor;
float d = min(1.0, smoothstep(14.5, 15.0, length(rPos - vec3(25.0 - f * 2.0, 0.0, 0.0))) + 0.5);
vec3 destColor = vec3(diff) * intersect.color + fog + specColor;
gl_FragColor = vec4(destColor * d * u, 1.0);
}else{
gl_FragColor = vec4(vec3(u), 1.0);
}
}
(GLSL Editor に貼り付けて実行してね!)
左「な、なんだこの影は!」
隊長「ま、まさか!」
左「うっ……また、また……声が……ッ!」
???「チカラが……欲しいか……」
あまりにも巨大な影に、大地がまるで飲み込まれていくようだ。
権造さんが戦争を終結させたほどの、超巨大なチカラ……太陽神が、今ふたたび左利きの激しい闘志によって召喚されてしまったのだ!
ズゴゴオゴゴオゴゴゴゴゴゴ……
隊長「まずい! 総員退避だ! いそげ!」
左「そんな馬鹿な!」
左利きは、雲の隙間から現れた「ソレ」を見た。
その場にいたたけのこの国の戦士たちも、逃げる足を止め、呆然と立ち尽くした。
きのこ黙示録(第五百十二章)
それは チョコというにはあまりにも大きすぎた
大きく 分厚く 重く そしてギザギザ過ぎた
それは 正に太陽神アポロ(ン)だった
※出典:よいこのきのこ図書館
precision mediump float;
uniform vec2 resolution;
uniform vec2 mouse;
uniform float time;
uniform sampler2D backbuffer;
struct Intersect{
float dist;
vec3 color;
};
const float PI = 3.1415926;
const float PI2 = PI * 2.0;
const vec3 chocoColor = vec3(0.25, 0.1, 0.0);
const vec3 berryColor = vec3(0.75, 0.3, 0.6);
const vec3 floorColor = vec3(0.2, 0.7, 0.1);
const vec3 aporoColor = vec3(0.0, 0.9, 0.7);
float rnd(vec2 n) {
return fract(sin(dot(n, vec2(12.9898, 4.1414))) * 43758.5453);
}
float noise(vec2 p){
vec2 v = floor(p);
vec2 u = fract(p);
u = u * u * (3.0 - 2.0 * u);
float r = mix(
mix(rnd(v), rnd(v + vec2(1.0, 0.0)), u.x),
mix(rnd(v + vec2(0.0, 1.0)), rnd(v + vec2(1.0, 1.0)), u.x),
u.y
);
return r * r;
}
float snoise(vec2 p){
float n = 0.0;
for(float i = 0.0; i < 4.0; ++i){
float v = pow(2.0, 2.0 + i);
float w = pow(2.0, -1.0 - i);
n += noise(p * v) * w;
}
return n;
}
float circle(vec2 p, vec2 o, float r, vec2 offset){
return step(length(p - o - offset) - r, 0.0);
}
float arc(vec2 p, vec2 o, float r, float h, vec2 offset){
return step(length(p - o - offset) - r, 0.0) * step(0.0, length(p - o - offset) - h);
}
float rect(vec2 p, vec2 o, vec2 q, vec2 offset){
vec2 r = p - o - offset;
return step(abs(r.x) - abs(q.x), 0.0) * step(abs(r.y) - abs(q.y), 0.0);
}
float ellipse(vec2 p, vec2 o, vec2 q, vec2 offset){
return rect(p, o, q, offset) + circle(p, o + vec2(0.0, q.y), q.x, offset) + circle(p, o - vec2(0.0, q.y), q.x, offset);
}
vec2 rotation(vec2 p, float r){
float s = sin(r);
float c = cos(r);
return mat2(c, s, -s, c) * p;
}
vec4 aporo(vec2 p, vec2 offset){
vec2 q = rotation(p - offset, radians(22.5)) + offset;
float c = 0.0;
// a
c += rect(p, vec2(-0.65, 0.25), vec2(0.2, 0.05), offset);
c += ellipse(p, vec2(-0.45, 0.125), vec2(0.1, 0.075), offset);
c += rect(q, vec2(-0.6, -0.275), vec2(0.055, 0.125), offset);
c += circle(p, vec2(-0.7625, -0.1), 0.12, offset);
// po
c += rect(p, vec2(-0.1, 0.235), vec2(0.2, 0.05), offset);
c += rect(p, vec2(-0.05, 0.1), vec2(0.05, 0.3), offset);
c += rect(p, vec2(-0.1775, 0.02), vec2(0.05, 0.115), offset);
c += rect(p, vec2(0.0775, 0.02), vec2(0.05, 0.115), offset);
c += arc(p, vec2(0.175, 0.245), 0.11, 0.05, offset);
c += circle(p, vec2(-0.25, -0.1), 0.12, offset);
c += circle(p, vec2(0.15, -0.1), 0.12, offset);
// ro
c += rect(p, vec2(0.565, 0.2475), vec2(0.15, 0.05), offset);
c += rect(p, vec2(0.565, -0.17), vec2(0.15, 0.05), offset);
c += ellipse(p, vec2(0.4125, 0.04), vec2(0.1, 0.1575), offset);
c += ellipse(p, vec2(0.725, 0.04), vec2(0.1, 0.1575), offset);
return vec4(aporoColor * min(c, 1.0), 0.0);
}
float dChoco(vec3 p){
float rad = time * 0.2;
mat3 m = mat3(cos(rad), 0.0, -sin(rad), 0.0, 1.0, 0.0, sin(rad), 0.0, cos(rad));
vec3 q = m * (p + vec3(0.0, -0.5, 0.0));
float len = sin(atan(q.z, q.x) * 25.0) * 0.1 * PI - 0.2;
float edge = 0.005 / (p.y + 1.25);
return min(length(q) - 1.0 + p.y, max(length(q) - 1.05 + p.y, length(q) - 0.9 + len + p.y)) + edge + step(p.y, -1.25) * 10.0;
}
float dFloor(vec3 p){
float n = snoise(p.xz);
return dot(p, vec3(0.0, 1.0, 0.0)) + 1.0 - n * 0.15;
}
Intersect distanceHub(vec3 p){
vec3 q = p - vec3(0.0, 2.5 - min(time * 0.25, 2.25), 0.0);
float choco = dChoco(q);
float flor = dFloor(p);
Intersect i;
i.dist = min(choco, flor);
i.color = q.y > -0.85 ? berryColor : choco < flor ? chocoColor : floorColor;
return i;
}
vec3 genNormal(vec3 p){
float d = 0.001;
return normalize(vec3(
distanceHub(p + vec3( d, 0.0, 0.0)).dist - distanceHub(p + vec3( -d, 0.0, 0.0)).dist,
distanceHub(p + vec3(0.0, d, 0.0)).dist - distanceHub(p + vec3(0.0, -d, 0.0)).dist,
distanceHub(p + vec3(0.0, 0.0, d)).dist - distanceHub(p + vec3(0.0, 0.0, -d)).dist
));
}
void main(){
vec2 p = (gl_FragCoord.xy * 2.0 - resolution) / min(resolution.x, resolution.y);
vec2 v = vec2(0.5 - snoise(vec2(time * 10.0, time)), 0.5 - snoise(vec2(time, time * time)));
float b = smoothstep(9.0, 9.5, time) * (1.0 - smoothstep(9.25, 12.0, time));
float c = smoothstep(12.0, 15.0, time);
vec3 cPos = vec3(0.0, 0.5, 4.0) + vec3(v.xyx) * 0.5 * b;
vec3 cDir = vec3(0.0, 0.0, -1.0);
vec3 cUp = vec3(0.0, 1.0, 0.0);
vec3 cSide = cross(cDir, cUp);
float targetDepth = 2.0;
vec3 ray = normalize(cSide * p.x + cUp * p.y + cDir * targetDepth);
float dist = 0.0;
float rLen = 0.0;
vec3 rPos = cPos;
Intersect intersect;
for(int i = 0; i < 512; ++i){
intersect = distanceHub(rPos);
rLen += intersect.dist * 0.15;
rPos = cPos + ray * rLen;
}
vec4 destColor = vec4(vec3(0.0), 1.0);
if(abs(intersect.dist) < 0.001){
float fog = smoothstep(0.0, 10.0, length(rPos - cPos));
vec3 normal = genNormal(rPos);
vec3 light = normalize(vec3(mouse + 2.0, 3.0));
float diff = max(dot(normal, light), 0.1);
vec3 eye = reflect(normalize(rPos - cPos), normal);
float speculer = clamp(dot(eye, light), 0.0, 1.0);
vec3 specColor = pow(speculer, 20.0) + chocoColor;
float f = smoothstep(5.0, 9.0, time) * 3.0;
float s = smoothstep(0.0, 1.5 + f, rPos.y);
destColor = vec4(vec3(diff) * intersect.color + s + fog + specColor, 1.0);
}else{
destColor = vec4(1.0);
}
gl_FragColor = destColor - aporo(p.xy, vec2(0.0, 2.5 - c * 2.0));
}
(GLSL Editor に貼り付けて実行してね!)
あああああああああああああああああああ!!!!!
このオチが言いたいためだけに!
たったそれだけのために!
なんで俺はこんな阿呆なことを思いついてしまったんだろうか……
どうか……どうかみなさん許してください……
あああああ……
疲れた……
この連載で登場したシェーダに使われているいくつかのテクニックについては、いずれ日をあらためて解説記事を書こうと思います。
こんなクッソくだらない記事を最後まで読んでくださった方……マジでありがとうございます! シェーダを使って表現するための、わずかばかりにでもヒントになったとしたらうれしいです。
※この物語はフィクションです。実在の人物や団体などとは関係ありません。