Inloggen
NL
games/doom-jumphp / src / Engine / HdShaders.php · commit 8e1cb9f
<?phpnamespace App\Engine;/** The GLSL ES 1.00 shaders of the HD renderer. */final class HdShaders{    /** Shared by the world and the sprites: sector heights and lights live in a 256 x 2 texture. */    private const HEIGHT = <<<'GLSL'        uniform sampler2D u_sectors;        float sectorHeight(float s, float which) {            vec4 t = texture2D(u_sectors, vec2((s + 0.5) / 256.0, 0.25));            vec2 hl = which < 0.5 ? t.rg : t.ba;            return (hl.x * 65280.0 + hl.y * 255.0) / 8.0 - 2048.0;        }        GLSL;    public const WORLD_VS = "attribute vec4 a_pos;\nattribute vec4 a_tex;\nattribute vec4 a_info;\nattribute vec4 a_clamp;\n"        . "uniform mat4 u_vp;\nuniform vec2 u_texSize;\n" . self::HEIGHT . <<<'GLSL'        varying vec2 v_uv;        varying vec3 v_world;        varying vec3 v_normal;        varying vec3 v_tangent;        varying float v_light;        varying vec2 v_ao;        void main() {            float z = sectorHeight(a_pos.z, a_pos.w);            if (a_clamp.z > 0.5) {                float c = sectorHeight(a_clamp.x, a_clamp.y);                z = a_clamp.z < 1.5 ? min(z, c) : max(z, c);            }            vec3 w = vec3(a_pos.x, a_pos.y, z);            if (a_info.y < 0.5) {                float vref = sectorHeight(a_tex.y, a_tex.z) + a_tex.w;                v_uv = vec2(a_tex.x, vref - z) / u_texSize;                v_normal = vec3(a_info.z, a_info.w, 0.0);                v_tangent = vec3(-a_info.w, a_info.z, 0.0);            } else {                v_uv = vec2(w.x, -w.y) / 64.0;                v_normal = vec3(0.0, 0.0, a_info.y < 1.5 ? 1.0 : -1.0);                v_tangent = vec3(1.0, 0.0, 0.0);            }            v_light = texture2D(u_sectors, vec2((a_info.x + 0.5) / 256.0, 0.75)).r;            // height above the floor / below the ceiling of the sector seen: contact shadows of the walls            v_ao = a_info.y < 0.5 ? vec2(z - sectorHeight(a_info.x, 0.0), sectorHeight(a_info.x, 1.0) - z) : vec2(100.0);            v_world = w;            gl_Position = u_vp * vec4(w.x, w.z, -w.y, 1.0);        }        GLSL;    /** Lighting shared by walls / floors and sprites. */    private const LIGHT = <<<'GLSL'        uniform vec3 u_eye;        uniform vec4 u_lpos[8];        uniform vec4 u_lcol[8];        uniform vec3 u_fog;        uniform float u_gamma;        vec3 shade(vec3 base, vec3 world, vec3 n, float sector, float gloss) {            float dist = length(world - u_eye);            // the original's diminishing light, smooth            float amb = sector * sector * 1.2 * clamp(1.3 - dist / 1500.0, 0.3, 1.0) + 0.035;            amb *= 0.82 + 0.18 * dot(n, normalize(vec3(0.45, 0.25, 0.85)));            vec3 lit = vec3(amb);            vec3 v = normalize(u_eye - world);            for (int i = 0; i < 8; i++) {                float r = u_lpos[i].w;                if (r > 0.0) {                    vec3 d = u_lpos[i].xyz - world;                    float dl = max(length(d), 1.0);                    float att = clamp(1.0 - dl / r, 0.0, 1.0);                    att *= att;                    vec3 l = d / dl;                    lit += u_lcol[i].rgb * att * (0.3 + 0.7 * max(dot(n, l), 0.0));                    vec3 h = normalize(l + v);                    lit += u_lcol[i].rgb * att * pow(max(dot(n, h), 0.0), 28.0) * gloss;                }            }            vec3 col = base * lit * u_gamma;            float f = clamp((dist - 250.0) / 2400.0, 0.0, 0.8);            return mix(col, u_fog * (0.4 + sector), f);        }        GLSL;    private const PRECISION = "#ifdef GL_FRAGMENT_PRECISION_HIGH\nprecision highp float;\n#else\nprecision mediump float;\n#endif\n";    public const WORLD_FS = self::PRECISION . "uniform sampler2D u_tex;\nuniform vec2 u_texSize;\n" . self::LIGHT . <<<'GLSL'        varying vec2 v_uv;        varying vec3 v_world;        varying vec3 v_normal;        varying vec3 v_tangent;        varying float v_light;        varying vec2 v_ao;        float luma(vec3 c) { return dot(c, vec3(0.3, 0.59, 0.11)); }        void main() {            vec4 c = texture2D(u_tex, v_uv);            if (c.a < 0.5) discard;            // relief from the picture: brighter texels stand out            vec2 px = 1.0 / (u_texSize * 2.0);            float l0 = luma(c.rgb);            float lx = luma(texture2D(u_tex, v_uv + vec2(px.x, 0.0)).rgb);            float ly = luma(texture2D(u_tex, v_uv + vec2(0.0, px.y)).rgb);            vec3 b = cross(v_normal, v_tangent);            vec3 n = normalize(v_normal - (lx - l0) * 3.0 * v_tangent + (ly - l0) * 3.0 * b);            // texture detail: a fine grain so that close walls are not flat colour blocks            float grain = fract(sin(dot(floor(v_uv * u_texSize * 4.0), vec2(12.9898, 78.233))) * 43758.5453);            vec3 base = c.rgb * (0.94 + 0.12 * grain);            base *= (0.5 + 0.5 * smoothstep(0.0, 36.0, v_ao.x)) * (0.65 + 0.35 * smoothstep(0.0, 28.0, v_ao.y));            gl_FragColor = vec4(shade(base, v_world, n, v_light, 0.25 + l0 * 0.8), 1.0);        }        GLSL;    public const SPRITE_VS = <<<'GLSL'        attribute vec2 a_corner;        uniform mat4 u_vp;        uniform vec3 u_center;        uniform vec4 u_rect;        uniform vec3 u_right;        varying vec2 v_uv;        varying vec3 v_world;        void main() {            float x = -u_rect.x + a_corner.x * u_rect.z;            float z = u_rect.y - a_corner.y * u_rect.w;            vec3 w = u_center + u_right * x + vec3(0.0, 0.0, z);            v_uv = a_corner;            v_world = w;            gl_Position = u_vp * vec4(w.x, w.z, -w.y, 1.0);        }        GLSL;    public const SPRITE_FS = self::PRECISION . "uniform sampler2D u_tex;\nuniform float u_sector;\nuniform float u_bright;\n" . self::LIGHT . <<<'GLSL'        varying vec2 v_uv;        varying vec3 v_world;        void main() {            vec4 c = texture2D(u_tex, v_uv);            if (c.a < 0.5) discard;            vec3 n = normalize(vec3(u_eye.xy - v_world.xy, 0.0));            vec3 col = u_bright > 0.5 ? c.rgb * 1.25 : shade(c.rgb, v_world, n, u_sector, 0.2);            gl_FragColor = vec4(col, 1.0);        }        GLSL;    /** Screen-space picture (the weapon in the hands): a_corner 0..1, u_rect in NDC. */    public const SCREEN_VS = <<<'GLSL'        attribute vec2 a_corner;        uniform vec4 u_rect;        varying vec2 v_uv;        void main() {            v_uv = a_corner;            gl_Position = vec4(u_rect.x + a_corner.x * u_rect.z, u_rect.y - a_corner.y * u_rect.w, 0.0, 1.0);        }        GLSL;    public const SCREEN_FS = <<<'GLSL'        precision mediump float;        uniform sampler2D u_tex;        uniform vec3 u_light;        varying vec2 v_uv;        void main() {            vec4 c = texture2D(u_tex, v_uv);            if (c.a < 0.5) discard;            // a soft key light from above, like the room lights falling on the gun            float top = 1.0 - v_uv.y * 0.35;            gl_FragColor = vec4(c.rgb * u_light * top, 1.0);        }        GLSL;    /** The sky behind everything: a panorama turning with the view. */    public const SKY_VS = <<<'GLSL'        attribute vec2 a_corner;        varying vec2 v_uv;        void main() {            v_uv = a_corner;            gl_Position = vec4(a_corner * 2.0 - 1.0, 0.999, 1.0);        }        GLSL;    public const SKY_FS = <<<'GLSL'        precision mediump float;        uniform sampler2D u_tex;        uniform float u_angle;        uniform float u_tanHalf;        uniform float u_aspect;        varying vec2 v_uv;        void main() {            float yaw = u_angle + atan((0.5 - v_uv.x) * 2.0 * u_tanHalf);            float u = -yaw / 6.2831853 * 4.0;            float row = 100.0 - (v_uv.y - 0.5) * 200.0 * 1.0;            vec3 c = texture2D(u_tex, vec2(u, clamp(row / 128.0, 0.0, 1.0))).rgb;            gl_FragColor = vec4(c * 1.05, 1.0);        }        GLSL;    /** Post-processing of the whole picture: bloom, damage / pickup tint, vignette, filmic curve. */    public const POST = <<<'GLSL'        uniform vec4 u_tint;        void main() {            vec2 px = 1.0 / u_resolution;            vec3 c = texture2D(u_frame, v_uv).rgb;            vec3 bloom = vec3(0.0);            for (int i = 0; i < 8; i++) {                float a = float(i) * 0.785398;                vec2 d = vec2(cos(a), sin(a));                bloom += max(texture2D(u_frame, v_uv + d * px * 3.0).rgb - 0.55, 0.0);                bloom += max(texture2D(u_frame, v_uv + d * px * 8.0).rgb - 0.55, 0.0) * 0.7;            }            c += bloom * 0.22;            c = mix(c, u_tint.rgb, u_tint.a);            vec2 v = v_uv - 0.5;            c *= 1.0 - dot(v, v) * 0.85;            c = c * (1.0 + c * 0.15) / (1.0 + c * 0.35);            gl_FragColor = vec4(c * 1.08, 1.0);        }        GLSL;}