2019年9月のシェーダーを紹介する

f:id:kaneta1011:20190929202951p:plain
サムネ用

はじめに

毎月やっている月毎に制作したシェーダーの紹介第8回目です。

今月はShadertoyで一つシェーダーを作ったので簡単な解説をします。

Energy Lab

インスピレーション

9月頭あたりにこのシェーダーを見て、金色のケージっぽいオブジェクトかっこいいなと思ったので今回登場させました。

www.shadertoy.com f:id:kaneta1011:20190929204742p:plain

また以前からパイプをエネルギーが伝っているようなシーンを作ってみたかったので合わせて今回のシェーダーになりました。

形状

f:id:kaneta1011:20190929224827p:plain

ケージの形状はトーラスをpmodで二重に複製しています。

vec2 cage(vec3 p) {
    p.y += sin(time) * 0.1;
    p.xy *= rot(-pi*0.5);
    p.yz *= rot(time * 0.5);
    p.yz = pmod(p.yz, 7.0);
    p.yx = pmod(p.yx, 7.0);
    return vec2(torus(p, 0.025, 0.55), MAT_CAGE);
}

ステージのモデリングには例に漏れず、Mercuryのhg_sdfを活用させていただいています。 mercury.sexy

特にパイプは fOpPipe を使用して、元のステージとboxを組み合わせて這うパイプを表現しています。

f:id:kaneta1011:20190929231717p:plain
fOpPipe前

f:id:kaneta1011:20190929231748p:plain
fOpPipe後

ライティング

f:id:kaneta1011:20190929202951p:plain

n kbデモを意識してコンパクトでそれなりの見た目になる物を用意して使っています。

Diffuseに正規化Lambert、Specularに正規化BlinPhongを使用したもので、反射率はフレネルを考慮した方が良い結果になりますが、マテリアル毎に一定としています。

vec3 light(vec3 p, vec3 n, vec3 v, vec3 lp, vec3 baseColor, float roughness, float reflectance, float metallic, vec3 radiance) {
    vec3 ref = mix(vec3(reflectance), baseColor, metallic);

    vec3 l = lp - p;
    float len = length(l);
    l /= len;
    
    vec3 h = normalize(l + v);
    
    vec3 diffuse = mix(1.0 - ref, vec3(0.0), metallic) * baseColor / pi;
    
    float m = roughnessToExponent(roughness);
    vec3 specular = ref * pow( max( 0.0, dot( n, h ) ), m ) * ( m + 9.0 ) / ( 9.0 * pi );
    return (diffuse + specular) * radiance * max(0.0, dot(l, n)) / (len*len);
}

今まで、複数回のリフレクションに対応するシェーダーを書いたことが無かったので、今回はそれに対応しました。

シェーダー内では再帰関数を利用できないので、依存関係にならないように処理を小さい粒度で関数化して、レンダリングの後方でトレースをループすることで実現しました。

vec3 shade(vec3 p, vec3 ray, vec2 mat) {
    vec3 baseColor, emission;
    float roughness, metallic, reflectance;
    
    getSurfaceParams(p, mat, baseColor, emission, roughness, reflectance, metallic);
    vec3 n = normal(p);
    
    vec3 result = evalLights(p, n, ray, baseColor, roughness, reflectance, metallic) + emission;
    vec3 f0 = vec3(1.0);
    for(int i=0; i<1; i++) {
        f0 *= mix(vec3(reflectance), baseColor, metallic);
        vec3 secondPos;
        vec2 secondMat;
        float depth;
        ray = reflect(ray, n);
        trace(p + n * 0.001, ray, 100.0, 24, secondPos, secondMat, depth);
        getSurfaceParams(p, secondMat, baseColor, emission, roughness, reflectance, metallic);
        n = normal(secondPos);
        p = secondPos;
        result += (evalLights(secondPos, n, ray, baseColor, roughness, reflectance, metallic) + emission) * f0;
    }
    
    return result;
}

ポストエフェクト

過去に紹介したものばかりなので箇条書きで書きます

さいごに

来月はGLSLスクールがあるので頑張って資料作ります!

10月は何を作るかまだ未定ですが、もし何か作ったら紹介します。