LoginSignup
22
3

More than 5 years have passed since last update.

[連載 GLSL 物語] チカラが欲しいか……(最終話)

Posted at

太陽の神

突然の覚醒に、驚くどころか厨二よろしく自分はやっぱりヒーローだったのだと思い上がる、きのこの国の戦士、左利き(のきのこ)。

そしてそんな左利きの父親と浅からぬ因縁を感じさせる、たけのこの国の戦士、隊長。

ふたりの激しいぶつかり合いは三日三晩続いたという。

互いにダメージも蓄積し、もはや戦い続けるのも難しくなってきているなか、たけのこ隊長は左利きにこう切り出した。

隊長「ぐぬぬ……このまま戦い続けても決着はつかんだろう。認めたくはないが……貴様は強い。全盛期の権造さんよりも上かもしれん」

左「へへへ……お、おれはまだ戦えるぞ……」

強がってはみたものの、左利きも既に限界だった。

隊長は冷静な眼差しのまま続けた。

隊長「いや、お前に免じて今回は我々は引き上げよう。このまま戦い続ければ、お前も権造さんの二の舞いになりかねん。そうなっては我々も困るのだ」

左「ど……どういうことだ! あんた、親父について何か知っているのか!!」

そう、左利きは自分の実の父である権造さんの最後について、聞かされていなかった。

戦乱の英雄である、たけのこ狩り名人の権造さん……しかし彼が今どこで、なにをしているのか、いや彼が生きているのかどうかさえ、きのこの国では極秘事項として秘匿とされている。きのこの国の軍上層部は、実の息子である左利きに対してさえ、このことに関しては一切の情報を与えていなかったのだ。

左「あんたにこんなことを頼める義理じゃないことはわかってる……だが、親父は……親父はどうなっちまったんだ……」

左利きの様子に、たけのこ隊長は驚きを隠さなかった。

隊長「まさか……お前は権造さんとたけのこ王との戦いを知らんのか? たけのこの国では常識だぞ……」

たけのこ隊長はとつとつと語った。

きのこの戦士のなかには、太陽神の加護を受けている一族がおり、権造さんも、そして左利きも、恐らくその太陽神の一族なのだという。太陽神の一族は、ピンチになると太陽神の加護のチカラにより、強力なパワーを発揮する。しかしそれは一時的なもので、そのチカラを行使し続ければやがて 本物の太陽神を現世に召喚 してしまうというのだ。

左「そういえば、あんたに殺されそうになったとき、俺には『チカラが欲しいか』とどこからともなく声が聞こえたんだ」

隊長「それはお前たち一族を守護する太陽神だろう。お前の親父、権造さんは、先の大乱で太陽神を召喚し、戦争を終結させたんだ。今お前にそれをやられたら我々どころか、お前もただではすまんだろう。お前にこれ以上、チカラを使わせたくはない」

左「隊長……」

三日三晩戦い続けたふたりの間には、種族の確執を超えた、不思議な友情が芽生えていたのかもしれない。

しかし、そんな戦士たちの束の間の友情を遮るかのように、突然、空が巨大な影に覆われてしまう!

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 に貼り付けて実行してね!)

kinocotakenoco.jpg

左「な、なんだこの影は!」

隊長「ま、まさか!」

左「うっ……また、また……声が……ッ!」

???「チカラが……欲しいか……」

あまりにも巨大な影に、大地がまるで飲み込まれていくようだ。

権造さんが戦争を終結させたほどの、超巨大なチカラ……太陽神が、今ふたたび左利きの激しい闘志によって召喚されてしまったのだ!

ズゴゴオゴゴオゴゴゴゴゴゴ……

隊長「まずい! 総員退避だ! いそげ!」

左「そんな馬鹿な!」

aporo_01.jpg

左利きは、雲の隙間から現れた「ソレ」を見た。

その場にいたたけのこの国の戦士たちも、逃げる足を止め、呆然と立ち尽くした。

きのこ黙示録(第五百十二章)

それは チョコというにはあまりにも大きすぎた

大きく 分厚く 重く そしてギザギザ過ぎた

それは 正に太陽神アポロ(ン)だった

※出典:よいこのきのこ図書館

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 に貼り付けて実行してね!)

aporo_02.jpg

あああああああああああああああああああ!!!!!

このオチが言いたいためだけに!

たったそれだけのために!

なんで俺はこんな阿呆なことを思いついてしまったんだろうか……

どうか……どうかみなさん許してください……

あああああ……

疲れた……

この連載で登場したシェーダに使われているいくつかのテクニックについては、いずれ日をあらためて解説記事を書こうと思います。

こんなクッソくだらない記事を最後まで読んでくださった方……マジでありがとうございます! シェーダを使って表現するための、わずかばかりにでもヒントになったとしたらうれしいです。

※この物語はフィクションです。実在の人物や団体などとは関係ありません。

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