HELLO WEBGL

par davidnurdin · Démos et expériences
1,0 (1)
▶ Jouer Sources Éditer GitHub
games/hello-webgl / src / Gfx / Shaders.php · commit 8d554d3
<?phpnamespace App\Gfx;/** * GLSL sources (WebGL 1 / GLSL ES 1.00) and the meshes of the demo. */final class Shaders{    /** Vertex layout of the meshes: x y z, u v, shade (6 floats). */    public const STRIDE = 6;    public const VERTEX = <<<'GLSL'        attribute vec3 a_pos;        attribute vec2 a_uv;        attribute float a_shade;        uniform mat4 u_mvp;        varying vec2 v_uv;        varying float v_shade;        varying float v_depth;        void main() {            v_uv = a_uv;            v_shade = a_shade;            gl_Position = u_mvp * vec4(a_pos, 1.0);            v_depth = gl_Position.w;        }        GLSL;    // texture x tint x face light, with a fog towards the sky colour    public const FRAGMENT = <<<'GLSL'        precision mediump float;        uniform sampler2D u_tex;        uniform vec3 u_tint;        uniform vec3 u_fog;        varying vec2 v_uv;        varying float v_shade;        varying float v_depth;        void main() {            vec3 color = texture2D(u_tex, v_uv).rgb * u_tint * v_shade;            float fog = clamp((v_depth - 6.0) / 14.0, 0.0, 1.0);            gl_FragColor = vec4(mix(color, u_fog, fog), 1.0);        }        GLSL;    /** Post-processing shaders: only main(), the engine adds u_frame, v_uv, u_time... (JumPHP\Gl\WebGl). */    public const POST = [        'NONE' => null,        'CRT' => <<<'GLSL'            void main() {                vec2 c = v_uv * 2.0 - 1.0;                vec2 uv = v_uv + c * dot(c, c) * 0.04;            // curved glass                if (uv.x < 0.0 || uv.y < 0.0 || uv.x > 1.0 || uv.y > 1.0) { gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); return; }                vec3 color = texture2D(u_frame, uv).rgb;                float line = sin(3.14159 * fract(uv.y * u_view.y * u_resolution.y / u_viewport.w));   // fract: precise in mediump                color *= 0.75 + 0.25 * line * line;               // scanlines (one per game pixel row)                color *= 1.0 - 0.35 * dot(c, c);                  // vignette                gl_FragColor = vec4(color * 1.15, 1.0);            }            GLSL,        'WAVE' => <<<'GLSL'            void main() {                vec2 uv = v_uv;                uv.x += sin(uv.y * 18.0 + u_time * 3.0) * 0.006;                uv.y += cos(uv.x * 12.0 + u_time * 2.0) * 0.004;                gl_FragColor = vec4(texture2D(u_frame, uv).rgb, 1.0);            }            GLSL,        '4 COLOURS' => <<<'GLSL'            uniform vec3 u_dark;            uniform vec3 u_light;            void main() {                vec3 color = texture2D(u_frame, v_uv).rgb;                float l = dot(color, vec3(0.299, 0.587, 0.114));                // ordered dither on game pixels, 4 levels                vec2 p = floor((v_uv * u_resolution - u_viewport.xy) / u_viewport.zw * u_view);                float d = mod(p.x + p.y * 2.0, 4.0) / 4.0 - 0.375;                float level = floor(clamp(l + d * 0.25, 0.0, 0.999) * 4.0) / 3.0;                gl_FragColor = vec4(mix(u_dark, u_light, level), 1.0);            }            GLSL,    ];    /**     * A cube of size 1 centred on 0: 36 vertices, each face its own light.     * @return float[]     */    public static function cube(): array    {        // faces: normal axis, sign, light        $faces = [[0, 1, 0.85], [0, -1, 0.6], [1, 1, 1.0], [1, -1, 0.45], [2, 1, 0.75], [2, -1, 0.55]];        $out = [];        foreach ($faces as [$axis, $sign, $light]) {            $corner = function (float $a, float $b) use ($axis, $sign): array {                $p = [0.0, 0.0, 0.0];                $p[$axis] = 0.5 * $sign;                $p[($axis + 1) % 3] = $a * $sign;                $p[($axis + 2) % 3] = $b;                return $p;            };            $quad = [[-0.5, -0.5, 0, 1], [0.5, -0.5, 1, 1], [0.5, 0.5, 1, 0], [-0.5, -0.5, 0, 1], [0.5, 0.5, 1, 0], [-0.5, 0.5, 0, 0]];            foreach ($quad as [$a, $b, $u, $v]) {                array_push($out, ...[...$corner($a, $b), $u, $v, $light]);            }        }        return $out;    }    /**     * The floor: one big square at y = 0, its texture repeated $tiles times.     * @return float[]     */    public static function floor(float $size, float $tiles): array    {        $s = $size / 2;        $quad = [[-$s, -$s, 0, 0], [$s, -$s, $tiles, 0], [$s, $s, $tiles, $tiles], [-$s, -$s, 0, 0], [$s, $s, $tiles, $tiles], [-$s, $s, 0, $tiles]];        $out = [];        foreach ($quad as [$x, $z, $u, $v]) {            array_push($out, $x, 0.0, $z, $u, $v, 0.9);        }        return $out;    }    /** 16 x 16 RGBA checker with a border: the texture of the cubes and the floor. */    public static function checker(array $a, array $b, array $border = [255, 255, 255]): string    {        $out = '';        for ($y = 0; $y < 16; $y++) {            for ($x = 0; $x < 16; $x++) {                $c = (($x >> 2) + ($y >> 2)) % 2 ? $a : $b;                if ($x === 0 || $y === 0 || $x === 15 || $y === 15) {                    $c = $border;                }                $out .= chr($c[0]) . chr($c[1]) . chr($c[2]) . chr(255);            }        }        return $out;    }}