GLSLのminifierを比較してみた

これは[KLab Engineer Advent Calendar 2019](https://qiita.com/advent-calendar/2019/klab)の17日目の記事です。

# 背景

OpenGLやWebGLでは、Direct3Dのようにシェーダーのプリコンパイルをサポートしておらず[^1]、GLSLをランタイム時にコンパイルするしかありません。
そのため、GLSLのファイルサイズがそのままダウンロードサイズに影響してしまいます。

Unity等のゲームエンジンでは、HLSL（ShaderLab）からGLSL等にトランスパイルする際にコードを圧縮[^2]できますが、
ゲームエンジンを使わない場合でもminifier（圧縮ツール）を利用することで同様に圧縮することでダウンロードサイズを削減できます。

今回、以下3つのminifierに対して圧縮率を比較検証しました。

- [Shader Minifier](https://github.com/laurentlb/Shader_Minifier)
- [glsl-optimizer](https://github.com/aras-p/glsl-optimizer)
- [glsl-minifier](https://github.com/TimvanScherpenzeel/glsl-minifier)

[^1]: 2017年に登場したOpenGL 4.6からVulkan同様のシェーダー中間表現SPIR-Vが標準化されましたが、WebGLとOpenGL ESについては執筆時点では未対応です。
[^2]: 圧縮といっても、コードサイズの最小化は目的としておらず、コメントと空行が除去され、インライン展開などが処理されるような感じです。シェーダーのインスペクタから「Compile and show code」ボタンをクリックすると、各プラットフォーム向けにトランスパイルされたシェーダーを確認できます。

# 比較結果

| ツール名 | GLSL ES 1.0のコードの圧縮率 | GLSL ES 3.0のコードの圧縮率 |
|---|---:|---:|
| Shader Minifier | 41.6 % | 41.7 % |
| glsl-optimizer | 118.9 % | 118.9 % |
| glsl-minifier | 53.7 % | 未対応 |

**最も圧縮率が高かったのはShader Minifier**でした。

なお、glsl-optimizerでは適用後の方がファイルサイズが大きくなってしまいました。
これは、glsl-optimizerが圧縮というよりも実行速度の向上を目的とした最適化ツールだったようで、
比較対象として不適切だったと思われます。

# 比較方法

筆者制作のレイマーチング作品『[Shinto Shrine Archway](https://neort.io/art/bhqc0hs3p9fdnjll5kr0)』のフラグメントシェーダーを各minifierで圧縮し、圧縮率（圧縮後のファイルサイズ ／ 圧縮前のファイルサイズ）を比較しました。

2つバージョンのGLSLで検証しました。

1. GLSL ES 1.0（OpenGL ES 2.0 / WebGL 1.0）
2. GLSL ES 3.0（OpenGL ES 3.0 / WebGL 2.0）

なお、圧縮対象のフラグメントシェーダーのファイルサイズは、GLSL ES 1.0の場合は7471 byte、GLSL ES 3.0の場合は7502byteです。

## 圧縮前のコード（GLSL ES 1.0）

<details><summary>archway_es1.glsl（7471 byte）</summary><div>

```glsl:archway_es1.glsl
// Ported from my shadertoy work: https://www.shadertoy.com/view/XttSWN

// BEGIN: shadertoy porting template
// https://gam0022.net/blog/2019/03/04/porting-from-shadertoy-to-glslsandbox/
// precision highp float;// shader_minifier のエラー回避のためにコメントアウト

uniform vec2 resolution;
uniform float time;
uniform vec2 mouse;

#define iResolution resolution
#define iTime time
#define iMouse mouse

void mainImage(out vec4 fragColor, in vec2 fragCoord);

void main(void) {
    vec4 col;
    mainImage(col, gl_FragCoord.xy);
    gl_FragColor = col;
}
// END: shadertoy porting template


// consts
const float EPS = 1e-4;
const float OFFSET = EPS * 10.0;
const float PI = 3.14159;
const float INF = 1e+10;

const vec3 lightDir = vec3( -0.48666426339228763, 0.8111071056538127, -0.3244428422615251 );
const vec3 backgroundColor = vec3( 0.0 );
const vec3 gateColor = vec3( 1.0, 0.1, 0.1 );

const float totalTime = 75.0;

// globals
vec3 cPos, cDir;
float normalizedGlobalTime = 0.0;
//vec3 illuminationColor;

struct Intersect {
    bool isHit;

    vec3 position;
    float distance;
    vec3 normal;

    int material;
    vec3 color;
};

const int BASIC_MATERIAL = 0;
const int MIRROR_MATERIAL = 1;


// distance functions
vec3 opRep( vec3 p, float interval ) {
    return mod( p, interval ) - 0.5 * interval;
}

vec2 opRep( vec2 p, float interval ) {
    return mod( p, interval ) - 0.5 * interval;
}

float opRep( float x, float interval ) {
    return mod( x, interval ) - 0.5 * interval;
}

float sphereDist( vec3 p, vec3 c, float r ) {
    return length( p - c ) - r;
}

float sdCappedCylinder( vec3 p, vec2 h ) {
    vec2 d = abs(vec2(length(p.xz),p.y)) - h;
    return min(max(d.x,d.y),0.0) + length(max(d,0.0));
}

float udBox( vec3 p, vec3 b )
{
    return length(max(abs(p)-b,0.0));
}

float udFloor( vec3 p ){
    float t1 = 1.0;
    float t2 = 3.0;
    float d = -0.5;
    for( float i = 0.0; i < 3.0; i++ ) {
        float f = pow( 2.0, i );
    	d += 0.1 / f * ( sin( f * t1 * p.x + t2 * iTime ) + sin( f * t1 * p.z + t2 * iTime ) );
    }
	return dot(p,vec3(0.0,1.0,0.0)) - d;
}

float dGate( vec3 p ) {
    p.y -= 1.3 * 0.5;

    float r = 0.05;
    float left  = sdCappedCylinder( p - vec3( -1.0, 0.0, 0.0 ),  vec2(r, 1.3));
    float right = sdCappedCylinder( p - vec3( 1.0,  0.0, 0.0 ),  vec2(r, 1.3));

    float ty = 0.02 * p.x * p.x;
    float tx = 0.5 * ( p.y - 1.3 );
    float katsura  = udBox( p - vec3( 0.0, 1.3 + ty, 0.0 ), vec3( 1.7 + tx, r * 2.0 + ty, r ) );

    float kan = udBox( p - vec3( 0.0, 0.7, 0.0 ), vec3( 1.3, r, r ) );
    float gakuduka = udBox( p - vec3( 0.0, 1.0, 0.0), vec3( r, 0.3, r ) );

    return min( min( left, right ), min( gakuduka, min( katsura, kan ) ) );
}

float dRepGate( vec3 p ) {
    if ( normalizedGlobalTime <= 0.5 ) {
        p.z = opRep( p.z, 1.0 + 20.0 * cos( PI * normalizedGlobalTime ) );
    } else {
        p.xz = opRep( p.xz, 10.0  );
    }
    return dGate( p );
}

float sceneDistance( vec3 p ) {
    return udFloor( p );
}


// color functions
vec3 hsv2rgb( vec3 c ) {

    vec4 K = vec4( 1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0 );
    vec3 p = abs( fract( c.xxx + K.xyz ) * 6.0 - K.www );
    return c.z * mix( K.xxx, clamp( p - K.xxx, 0.0, 1.0 ), c.y );

}

Intersect minIntersect( Intersect a, Intersect b ) {
    if ( a.distance < b.distance ) {
        return a;
    } else {
        return b;
    }
}

Intersect sceneIntersect( vec3 p ) {

    Intersect a;
    a.distance = udFloor( p );
    a.material = MIRROR_MATERIAL;
    // return minIntersect( a, b );
    return a;
}

vec3 getNormal( vec3 p ) {
	vec2 e = vec2( 1.0, -1.0 ) * 0.001;
	return normalize(
		e.xyy * sceneDistance( p + e.xyy ) + e.yyx * sceneDistance( p + e.yyx ) +
		e.yxy * sceneDistance( p + e.yxy ) + e.xxx * sceneDistance( p + e.xxx ) );
}

float getShadow( vec3 ro, vec3 rd ) {

    float h = 0.0;
    float c = 0.0;
    float r = 1.0;
    float shadowCoef = 0.5;

    for ( float t = 0.0; t < 50.0; t++ ) {

        h = sceneDistance( ro + rd * c );

        if ( h < EPS ) return shadowCoef;

        r = min( r, h * 16.0 / c );
        c += h;

    }

    return 1.0 - shadowCoef + r * shadowCoef;

}

Intersect getRayColor( vec3 origin, vec3 ray ) {

    // marching loop
    float dist, minDist, trueDepth;
    float depth = 0.0;
    vec3 p = origin;
    int count = 0;
    Intersect nearest;

    // first pass (water)
    for ( int i = 0; i < 120; i++ ){

        dist = sceneDistance( p );
        depth += dist;
        p = origin + depth * ray;

        count = i;
        if ( abs(dist) < EPS ) break;

    }

    if ( abs(dist) < EPS ) {

        nearest = sceneIntersect( p );
        nearest.position = p;
        nearest.normal = getNormal(p);
        nearest.distance = depth;
        float diffuse = clamp( dot( lightDir, nearest.normal ), 0.1, 1.0 );
        float specular = pow( clamp( dot( reflect( lightDir, nearest.normal ), ray ), 0.0, 1.0 ), 6.0 );
        //float shadow = getShadow( p + nearest.normal * OFFSET, lightDir );

        if ( nearest.material == BASIC_MATERIAL ) {
        } else if ( nearest.material == MIRROR_MATERIAL ) {
            nearest.color = vec3( 0.5, 0.7, 0.8 ) * diffuse + vec3( 1.0 ) * specular;
        }

        nearest.isHit = true;

    } else {

        nearest.color = backgroundColor;
        nearest.isHit = false;

    }
    nearest.color = clamp( nearest.color - 0.1 * nearest.distance, 0.0, 1.0 );

    // second pass (gates)
    p = origin;
    depth = 0.0;
    minDist = INF;
    for ( int i = 0; i < 20; i++ ){
        dist = dRepGate( p );
        minDist = min(dist, minDist);
        /*if ( dist < minDist ) {
            minDist = dist;
            trueDepth = depth;
        }*/
        depth += dist;
        p = origin + depth * ray;
        if ( i == 9 && normalizedGlobalTime <= 0.5 ) {
            break;
        }
    }

    if ( abs(dist) < EPS ) {
        nearest.color += gateColor;
    } else {
        nearest.color += gateColor * clamp( 0.05 / minDist, 0.0, 1.0 );
    }

    return nearest;

}

void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
    normalizedGlobalTime = mod( iTime / totalTime, 1.0 );

    // fragment position
    vec2 p = ( fragCoord.xy * 2.0 - iResolution.xy ) / min(  iResolution.x,  iResolution.y );

    // camera and ray
    if ( normalizedGlobalTime < 0.7 ) {
        cPos = vec3( 0.0, 0.6 + 0.4 * cos( iTime ), 3.0 * iTime );
        cDir = normalize( vec3( 0.0, -0.1, 1.0 ) );
    } else {
        cPos = vec3( 0.0, 0.6 + 0.4 * cos( iTime ) + 50.0 * ( normalizedGlobalTime - 0.7 ), 3.0 * iTime );
        cDir = normalize( vec3( 0.0, -0.1 - ( normalizedGlobalTime - 0.7 ), 1.0 ) );
    }
    vec3 cSide = normalize( cross( cDir, vec3( 0.0, 1.0 ,0.0 ) ) );
    vec3 cUp   = normalize( cross( cSide, cDir ) );
    float targetDepth = 1.3;
    vec3 ray = normalize( cSide * p.x + cUp * p.y + cDir * targetDepth );

    // Illumination Color
    // illuminationColor = hsv2rgb( vec3( iTime * 0.02 + 0.6, 1.0, 1.0 ) );

    vec3 color = vec3( 0.0 );
    float alpha = 1.0;
    Intersect nearest;

    for ( int i = 0; i < 3; i++ ) {

        nearest = getRayColor( cPos, ray );

        color += alpha * nearest.color;
        alpha *= 0.5;
        ray = normalize( reflect( ray, nearest.normal ) );
        cPos = nearest.position + nearest.normal * OFFSET;

        if ( !nearest.isHit || nearest.material != MIRROR_MATERIAL ) break;

    }

    fragColor = vec4(color, 1.0);

}
```

</div></details>

## 圧縮前のコード（GLSL ES 3.0）

<details><summary>archway_es3.glsl（7502 byte）</summary><div>

```glsl:archway_es3.glsl
#version 300 es
// Ported from my shadertoy work: https://www.shadertoy.com/view/XttSWN

// BEGIN: shadertoy porting template
// https://gam0022.net/blog/2019/03/04/porting-from-shadertoy-to-glslsandbox/
// precision highp float;// shader_minifier のエラー回避のためにコメントアウト

uniform vec2 resolution;
uniform float time;
uniform vec2 mouse;

#define iResolution resolution
#define iTime time
#define iMouse mouse

void mainImage(out vec4 fragColor, in vec2 fragCoord);

out vec4 outColor;
void main(void) {
    vec4 col;
    mainImage(col, gl_FragCoord.xy);
    outColor = col;
}
// END: shadertoy porting template


// consts
const float EPS = 1e-4;
const float OFFSET = EPS * 10.0;
const float PI = 3.14159;
const float INF = 1e+10;

const vec3 lightDir = vec3( -0.48666426339228763, 0.8111071056538127, -0.3244428422615251 );
const vec3 backgroundColor = vec3( 0.0 );
const vec3 gateColor = vec3( 1.0, 0.1, 0.1 );

const float totalTime = 75.0;

// globals
vec3 cPos, cDir;
float normalizedGlobalTime = 0.0;
//vec3 illuminationColor;

struct Intersect {
    bool isHit;

    vec3 position;
    float distance;
    vec3 normal;

    int material;
    vec3 color;
};

const int BASIC_MATERIAL = 0;
const int MIRROR_MATERIAL = 1;


// distance functions
vec3 opRep( vec3 p, float interval ) {
    return mod( p, interval ) - 0.5 * interval;
}

vec2 opRep( vec2 p, float interval ) {
    return mod( p, interval ) - 0.5 * interval;
}

float opRep( float x, float interval ) {
    return mod( x, interval ) - 0.5 * interval;
}

float sphereDist( vec3 p, vec3 c, float r ) {
    return length( p - c ) - r;
}

float sdCappedCylinder( vec3 p, vec2 h ) {
    vec2 d = abs(vec2(length(p.xz),p.y)) - h;
    return min(max(d.x,d.y),0.0) + length(max(d,0.0));
}

float udBox( vec3 p, vec3 b )
{
    return length(max(abs(p)-b,0.0));
}

float udFloor( vec3 p ){
    float t1 = 1.0;
    float t2 = 3.0;
    float d = -0.5;
    for( float i = 0.0; i < 3.0; i++ ) {
        float f = pow( 2.0, i );
    	d += 0.1 / f * ( sin( f * t1 * p.x + t2 * iTime ) + sin( f * t1 * p.z + t2 * iTime ) );
    }
	return dot(p,vec3(0.0,1.0,0.0)) - d;
}

float dGate( vec3 p ) {
    p.y -= 1.3 * 0.5;

    float r = 0.05;
    float left  = sdCappedCylinder( p - vec3( -1.0, 0.0, 0.0 ),  vec2(r, 1.3));
    float right = sdCappedCylinder( p - vec3( 1.0,  0.0, 0.0 ),  vec2(r, 1.3));

    float ty = 0.02 * p.x * p.x;
    float tx = 0.5 * ( p.y - 1.3 );
    float katsura  = udBox( p - vec3( 0.0, 1.3 + ty, 0.0 ), vec3( 1.7 + tx, r * 2.0 + ty, r ) );

    float kan = udBox( p - vec3( 0.0, 0.7, 0.0 ), vec3( 1.3, r, r ) );
    float gakuduka = udBox( p - vec3( 0.0, 1.0, 0.0), vec3( r, 0.3, r ) );

    return min( min( left, right ), min( gakuduka, min( katsura, kan ) ) );
}

float dRepGate( vec3 p ) {
    if ( normalizedGlobalTime <= 0.5 ) {
        p.z = opRep( p.z, 1.0 + 20.0 * cos( PI * normalizedGlobalTime ) );
    } else {
        p.xz = opRep( p.xz, 10.0  );
    }
    return dGate( p );
}

float sceneDistance( vec3 p ) {
    return udFloor( p );
}


// color functions
vec3 hsv2rgb( vec3 c ) {

    vec4 K = vec4( 1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0 );
    vec3 p = abs( fract( c.xxx + K.xyz ) * 6.0 - K.www );
    return c.z * mix( K.xxx, clamp( p - K.xxx, 0.0, 1.0 ), c.y );

}

Intersect minIntersect( Intersect a, Intersect b ) {
    if ( a.distance < b.distance ) {
        return a;
    } else {
        return b;
    }
}

Intersect sceneIntersect( vec3 p ) {

    Intersect a;
    a.distance = udFloor( p );
    a.material = MIRROR_MATERIAL;
    // return minIntersect( a, b );
    return a;
}

vec3 getNormal( vec3 p ) {
	vec2 e = vec2( 1.0, -1.0 ) * 0.001;
	return normalize(
		e.xyy * sceneDistance( p + e.xyy ) + e.yyx * sceneDistance( p + e.yyx ) +
		e.yxy * sceneDistance( p + e.yxy ) + e.xxx * sceneDistance( p + e.xxx ) );
}

float getShadow( vec3 ro, vec3 rd ) {

    float h = 0.0;
    float c = 0.0;
    float r = 1.0;
    float shadowCoef = 0.5;

    for ( float t = 0.0; t < 50.0; t++ ) {

        h = sceneDistance( ro + rd * c );

        if ( h < EPS ) return shadowCoef;

        r = min( r, h * 16.0 / c );
        c += h;

    }

    return 1.0 - shadowCoef + r * shadowCoef;

}

Intersect getRayColor( vec3 origin, vec3 ray ) {

    // marching loop
    float dist, minDist, trueDepth;
    float depth = 0.0;
    vec3 p = origin;
    int count = 0;
    Intersect nearest;

    // first pass (water)
    for ( int i = 0; i < 120; i++ ){

        dist = sceneDistance( p );
        depth += dist;
        p = origin + depth * ray;

        count = i;
        if ( abs(dist) < EPS ) break;

    }

    if ( abs(dist) < EPS ) {

        nearest = sceneIntersect( p );
        nearest.position = p;
        nearest.normal = getNormal(p);
        nearest.distance = depth;
        float diffuse = clamp( dot( lightDir, nearest.normal ), 0.1, 1.0 );
        float specular = pow( clamp( dot( reflect( lightDir, nearest.normal ), ray ), 0.0, 1.0 ), 6.0 );
        //float shadow = getShadow( p + nearest.normal * OFFSET, lightDir );

        if ( nearest.material == BASIC_MATERIAL ) {
        } else if ( nearest.material == MIRROR_MATERIAL ) {
            nearest.color = vec3( 0.5, 0.7, 0.8 ) * diffuse + vec3( 1.0 ) * specular;
        }

        nearest.isHit = true;

    } else {

        nearest.color = backgroundColor;
        nearest.isHit = false;

    }
    nearest.color = clamp( nearest.color - 0.1 * nearest.distance, 0.0, 1.0 );

    // second pass (gates)
    p = origin;
    depth = 0.0;
    minDist = INF;
    for ( int i = 0; i < 20; i++ ){
        dist = dRepGate( p );
        minDist = min(dist, minDist);
        /*if ( dist < minDist ) {
            minDist = dist;
            trueDepth = depth;
        }*/
        depth += dist;
        p = origin + depth * ray;
        if ( i == 9 && normalizedGlobalTime <= 0.5 ) {
            break;
        }
    }

    if ( abs(dist) < EPS ) {
        nearest.color += gateColor;
    } else {
        nearest.color += gateColor * clamp( 0.05 / minDist, 0.0, 1.0 );
    }

    return nearest;

}

void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
    normalizedGlobalTime = mod( iTime / totalTime, 1.0 );

    // fragment position
    vec2 p = ( fragCoord.xy * 2.0 - iResolution.xy ) / min(  iResolution.x,  iResolution.y );

    // camera and ray
    if ( normalizedGlobalTime < 0.7 ) {
        cPos = vec3( 0.0, 0.6 + 0.4 * cos( iTime ), 3.0 * iTime );
        cDir = normalize( vec3( 0.0, -0.1, 1.0 ) );
    } else {
        cPos = vec3( 0.0, 0.6 + 0.4 * cos( iTime ) + 50.0 * ( normalizedGlobalTime - 0.7 ), 3.0 * iTime );
        cDir = normalize( vec3( 0.0, -0.1 - ( normalizedGlobalTime - 0.7 ), 1.0 ) );
    }
    vec3 cSide = normalize( cross( cDir, vec3( 0.0, 1.0 ,0.0 ) ) );
    vec3 cUp   = normalize( cross( cSide, cDir ) );
    float targetDepth = 1.3;
    vec3 ray = normalize( cSide * p.x + cUp * p.y + cDir * targetDepth );

    // Illumination Color
    // illuminationColor = hsv2rgb( vec3( iTime * 0.02 + 0.6, 1.0, 1.0 ) );

    vec3 color = vec3( 0.0 );
    float alpha = 1.0;
    Intersect nearest;

    for ( int i = 0; i < 3; i++ ) {

        nearest = getRayColor( cPos, ray );

        color += alpha * nearest.color;
        alpha *= 0.5;
        ray = normalize( reflect( ray, nearest.normal ) );
        cPos = nearest.position + nearest.normal * OFFSET;

        if ( !nearest.isHit || nearest.material != MIRROR_MATERIAL ) break;

    }

    fragColor = vec4(color, 1.0);

}
```

</div></details>

## 検証環境

- Mac Book Air Early 2015
- macOS Catalina

# 検証の詳細

以下、各minifierの実行方法と結果を紹介します。

## Shader Minifier

有名なデモ制作チーム、[Ctrl-Alt-Test](http://www.ctrl-alt-test.fr/)によるツールです。

F#で実装されており、同じバイナリが Windows OS でも macOS でも動作します。

ビルド済みのバイナリは[GitHubのreleases](https://github.com/laurentlb/Shader_Minifier/releases)からダウンロードできます。

### 使い方

```bash
mono shader_minifier.exe --help
	-o <string>: Set the output filename (default is shader_code.h)
	-v: Verbose, display additional information
	--hlsl: Use HLSL (default is GLSL)
	--format <string>: Can be: c-variables (default), c-array, js, nasm, or none
	--field-names <string>: Choose the field names for vectors: 'rgba', 'xyzw', or 'stpq'
	--preserve-externals: Do not rename external values (e.g. uniform)
	--preserve-all-globals: Do not rename functions and global variables
	--no-renaming: Do not rename anything
	--no-renaming-list <string>: Comma-separated list of functions to preserve
	--no-sequence: Do not use the comma operator trick
	--smoothstep: Use IQ's smoothstep trick
	-- ...: Stop parsing command line
	--help: display this list of options
	-help: display this list of options
```

### minifyの実行

#### GLSL ES 1.0

```bash
mono shader_minifier.exe archway_es1.glsl -o archway_es1.shader_minifier.min.glsl --format none
```

```glsl:archway_es1.shader_minifier.min.glsl
uniform vec2 m;uniform float i;uniform vec2 v;
#define iResolution resolution
#define iTime time
#define iMouse mouse
const float y=.0001,r=y*10.,f=3.14159,n=1e+10;const vec3 l=vec3(-.486664,.811107,-.324443),x=vec3(0.),e=vec3(1.,.1,.1);const float z=75.;vec3 c,a;float d=0.;struct Intersect{bool isHit;vec3 position;float distance;vec3 normal;int material;vec3 color;};const int t=0,k=1;vec3 s(vec3 v,float i){return mod(v,i)-.5*i;}vec2 p(vec2 v,float i){return mod(v,i)-.5*i;}float h(float v,float i){return mod(v,i)-.5*i;}float h(vec3 v,vec3 i,float y){return length(v-i)-y;}float w(vec3 v,vec2 i){vec2 m=abs(vec2(length(v.xz),v.y))-i;return min(max(m.x,m.y),0.)+length(max(m,0.));}float I(vec3 v,vec3 i){return length(max(abs(v)-i,0.));}float I(vec3 v){float i=1.,a=3.,r=-.5;for(float f=0.;f<3.;f++){float m=pow(2.,f);r+=.1/m*(sin(m*i*v.x+a*iTime)+sin(m*i*v.z+a*iTime));}return dot(v,vec3(0.,1.,0.))-r;}float h(vec3 v){v.y-=.65;float i=.05,m=w(v-vec3(-1.,0.,0.),vec2(i,1.3)),y=w(v-vec3(1.,0.,0.),vec2(i,1.3)),a=.02*v.x*v.x,r=.5*(v.y-1.3),f=I(v-vec3(0.,1.3+a,0.),vec3(1.7+r,i*2.+a,i)),e=I(v-vec3(0.,.7,0.),vec3(1.3,i,i)),l=I(v-vec3(0.,1.,0.),vec3(i,.3,i));return min(min(m,y),min(l,min(f,e)));}float p(vec3 v){if(d<=.5)v.z=h(v.z,1.+20.*cos(f*d));else v.xz=h(v.xz,10.);return h(v);}float s(vec3 v){return I(v);}vec3 w(vec3 v){vec4 i=vec4(1.,2./3.,1./3.,3.);vec3 m=abs(fract(v.xxx+i.xyz)*6.-i.www);return v.z*mix(i.xxx,clamp(m-i.xxx,0.,1.),v.y);}Intersect o(Intersect v,Intersect i){if(v.distance<i.distance)return v;else return i;}Intersect o(vec3 v){Intersect i;i.distance=I(v);i.material=k;return i;}vec3 u(vec3 v){vec2 i=vec2(1.,-1.)*.001;return normalize(i.xyy*s(v+i.xyy)+i.yyx*s(v+i.yyx)+i.yxy*s(v+i.yxy)+i.xxx*s(v+i.xxx));}float u(vec3 v,vec3 i){float m=0.,f=0.,a=1.,r=.5;for(float e=0.;e<50.;e++){m=s(v+i*f);if(m<y)return r;a=min(a,m*16./f);f+=m;}return 1.-r+a*r;}Intersect b(vec3 v,vec3 i){float m,f,a,r=0.;vec3 c=v;int z=0;Intersect I;for(int h=0;h<120;h++){m=s(c);r+=m;c=v+r*i;z=h;if(abs(m)<y)break;}if(abs(m)<y){I=o(c);I.position=c;I.normal=u(c);I.distance=r;float h=clamp(dot(l,I.normal),.1,1.),g=pow(clamp(dot(reflect(l,I.normal),i),0.,1.),6.);if(I.material==t);else if(I.material==k)I.color=vec3(.5,.7,.8)*h+vec3(1.)*g;I.isHit=true;}else I.color=x,I.isHit=false;I.color=clamp(I.color-.1*I.distance,0.,1.);c=v;r=0.;f=n;for(int h=0;h<20;h++){m=p(c);f=min(m,f);r+=m;c=v+r*i;if(h==9&&d<=.5){break;}}if(abs(m)<y)I.color+=e;else I.color+=e*clamp(.05/f,0.,1.);return I;}void g(out vec4 v,in vec2 i){d=mod(iTime/z,1.);vec2 m=(i.xy*2.-iResolution.xy)/min(iResolution.x,iResolution.y);if(d<.7)c=vec3(0.,.6+.4*cos(iTime),3.*iTime),a=normalize(vec3(0.,-.1,1.));else c=vec3(0.,.6+.4*cos(iTime)+50.*(d-.7),3.*iTime),a=normalize(vec3(0.,-.1-(d-.7),1.));vec3 f=normalize(cross(a,vec3(0.,1.,0.))),l=normalize(cross(f,a));float y=1.3;vec3 e=normalize(f*m.x+l*m.y+a*y),I=vec3(0.);float h=1.;Intersect n;for(int t=0;t<3;t++){n=b(c,e);I+=h*n.color;h*=.5;e=normalize(reflect(e,n.normal));c=n.position+n.normal*r;if(!n.isHit||n.material!=k)break;}v=vec4(I,1.);}void main(){vec4 v;g(v,gl_FragCoord.xy);gl_FragColor=v;}
```

7471 byte -> 3110 byte (41.6 %)

#### GLSL ES 3.0

```bash
mono shader_minifier.exe archway_es3.glsl -o archway_es3.shader_minifier.min.glsl --format none
```

```glsl:archway_es3.shader_minifier.min.glsl
#version 300 es
uniform vec2 m;uniform float i;uniform vec2 v;
#define iResolution resolution
#define iTime time
#define iMouse mouse
out vec4 r;const float y=.0001,l=y*10.,f=3.14159,n=1e+10;const vec3 x=vec3(-.486664,.811107,-.324443),z=vec3(0.),e=vec3(1.,.1,.1);const float t=75.;vec3 c,a;float d=0.;struct Intersect{bool isHit;vec3 position;float distance;vec3 normal;int material;vec3 color;};const int p=0,k=1;vec3 s(vec3 v,float i){return mod(v,i)-.5*i;}vec2 h(vec2 v,float i){return mod(v,i)-.5*i;}float w(float v,float i){return mod(v,i)-.5*i;}float h(vec3 v,vec3 i,float y){return length(v-i)-y;}float I(vec3 v,vec2 i){vec2 m=abs(vec2(length(v.xz),v.y))-i;return min(max(m.x,m.y),0.)+length(max(m,0.));}float o(vec3 v,vec3 i){return length(max(abs(v)-i,0.));}float I(vec3 v){float i=1.,l=3.,r=-.5;for(float f=0.;f<3.;f++){float m=pow(2.,f);r+=.1/m*(sin(m*i*v.x+l*iTime)+sin(m*i*v.z+l*iTime));}return dot(v,vec3(0.,1.,0.))-r;}float h(vec3 v){v.y-=.65;float i=.05,m=I(v-vec3(-1.,0.,0.),vec2(i,1.3)),y=I(v-vec3(1.,0.,0.),vec2(i,1.3)),l=.02*v.x*v.x,f=.5*(v.y-1.3),a=o(v-vec3(0.,1.3+l,0.),vec3(1.7+f,i*2.+l,i)),e=o(v-vec3(0.,.7,0.),vec3(1.3,i,i)),x=o(v-vec3(0.,1.,0.),vec3(i,.3,i));return min(min(m,y),min(x,min(a,e)));}float o(vec3 v){if(d<=.5)v.z=w(v.z,1.+20.*cos(f*d));else v.xz=w(v.xz,10.);return h(v);}float s(vec3 v){return I(v);}vec3 w(vec3 v){vec4 i=vec4(1.,2./3.,1./3.,3.);vec3 m=abs(fract(v.xxx+i.xyz)*6.-i.www);return v.z*mix(i.xxx,clamp(m-i.xxx,0.,1.),v.y);}Intersect u(Intersect v,Intersect i){if(v.distance<i.distance)return v;else return i;}Intersect u(vec3 v){Intersect i;i.distance=I(v);i.material=k;return i;}vec3 b(vec3 v){vec2 i=vec2(1.,-1.)*.001;return normalize(i.xyy*s(v+i.xyy)+i.yyx*s(v+i.yyx)+i.yxy*s(v+i.yxy)+i.xxx*s(v+i.xxx));}float b(vec3 v,vec3 i){float m=0.,f=0.,l=1.,r=.5;for(float e=0.;e<50.;e++){m=s(v+i*f);if(m<y)return r;l=min(l,m*16./f);f+=m;}return 1.-r+l*r;}Intersect T(vec3 v,vec3 i){float m,f,l,r=0.;vec3 c=v;int t=0;Intersect a;for(int I=0;I<120;I++){m=s(c);r+=m;c=v+r*i;t=I;if(abs(m)<y)break;}if(abs(m)<y){a=u(c);a.position=c;a.normal=b(c);a.distance=r;float I=clamp(dot(x,a.normal),.1,1.),g=pow(clamp(dot(reflect(x,a.normal),i),0.,1.),6.);if(a.material==p);else if(a.material==k)a.color=vec3(.5,.7,.8)*I+vec3(1.)*g;a.isHit=true;}else a.color=z,a.isHit=false;a.color=clamp(a.color-.1*a.distance,0.,1.);c=v;r=0.;f=n;for(int I=0;I<20;I++){m=o(c);f=min(m,f);r+=m;c=v+r*i;if(I==9&&d<=.5){break;}}if(abs(m)<y)a.color+=e;else a.color+=e*clamp(.05/f,0.,1.);return a;}void g(out vec4 v,in vec2 i){d=mod(iTime/t,1.);vec2 m=(i.xy*2.-iResolution.xy)/min(iResolution.x,iResolution.y);if(d<.7)c=vec3(0.,.6+.4*cos(iTime),3.*iTime),a=normalize(vec3(0.,-.1,1.));else c=vec3(0.,.6+.4*cos(iTime)+50.*(d-.7),3.*iTime),a=normalize(vec3(0.,-.1-(d-.7),1.));vec3 f=normalize(cross(a,vec3(0.,1.,0.))),r=normalize(cross(f,a));float y=1.3;vec3 e=normalize(f*m.x+r*m.y+a*y),x=vec3(0.);float I=1.;Intersect n;for(int z=0;z<3;z++){n=T(c,e);x+=I*n.color;I*=.5;e=normalize(reflect(e,n.normal));c=n.position+n.normal*l;if(!n.isHit||n.material!=k)break;}v=vec4(x,1.);}void main(){vec4 v;g(v,gl_FragCoord.xy);r=v;}
```

7502 byte -> 3126 byte (41.7 %)

### 注意点

precision指定はエラーになりました。

```bash
Parse error: Error in archway.glsl: Ln: 5 Col: 17
precision highp float;
                ^
Expecting: '('

  at Parse+ParseImpl+runParser@347.Invoke (System.String message) [0x00001] in <5b13c9dcc40096cda7450383dcc9135b>:0
  at Microsoft.FSharp.Core.PrintfImpl.go@523-3[b,c,d] (System.String fmt, System.Int32 len, Microsoft.FSharp.Core.FSharpFunc`2[T,TResult] outputChar, Microsoft.FSharp.Core.FSharpFunc`2[T,TResult] outa, b os, Microsoft.FSharp.Core.FSharpFunc`2[T,TResult] finalize, Microsoft.FSharp.Collections.FSharpList`1[T] args, System.Int32 i) [0x00027] in <5b13c9dcc40096cda7450383dcc9135b>:0
  at Microsoft.FSharp.Core.PrintfImpl.run@521[b,c,d] (Microsoft.FSharp.Core.FSharpFunc`2[T,TResult] initialize, System.String fmt, System.Int32 len, Microsoft.FSharp.Collections.FSharpList`1[T] args) [0x00039] in <5b13c9dcc40096cda7450383dcc9135b>:0
  at Microsoft.FSharp.Core.PrintfImpl.capture@540[b,c,d] (Microsoft.FSharp.Core.FSharpFunc`2[T,TResult] initialize, System.String fmt, System.Int32 len, Microsoft.FSharp.Collections.FSharpList`1[T] args, System.Type ty, System.Int32 i) [0x00039] in <5b13c9dcc40096cda7450383dcc9135b>:0
  at <StartupCode$FSharp-Core>.$Reflect+Invoke@720-4[T1,T2].Invoke (T1 inp) [0x00001] in <5b13c9dcc40096cda7450383dcc9135b>:0
  at main.minify (System.String filename, System.String content) [0x00021] in <5b13c9dcc40096cda7450383dcc9135b>:0
  at main.minifyFile (System.String file) [0x00014] in <5b13c9dcc40096cda7450383dcc9135b>:0
  at main.run (System.String[] files) [0x00031] in <5b13c9dcc40096cda7450383dcc9135b>:0
```

今回はprecision指定をコメントアウトすることで回避しました。

```diff
-precision highp float;
+// precision highp float;// Shader Minifier のエラー回避のためにコメントアウト
```

## glsl-optimizer

C++で実装されており、ソースコードを各環境でビルドすることで Windows OS でも macOS でも動作します。

macOSの場合は、[リポジトリ](https://github.com/aras-p/glsl-optimizer)をclone してから `cmake . && make` で簡単にビルドができました。

### 使い方

```bash
./glsl-optimizer/glslopt
Usage: glslopt <-f|-v> <input shader> [<output shader>]
	-f : fragment shader (default)
	-v : vertex shader
	-1 : target OpenGL (default)
	-2 : target OpenGL ES 2.0
	-3 : target OpenGL ES 3.0

	If no output specified, output is to [input].out.
```

### minifyの実行

#### GLSL ES 1.0

```bash
./glsl-optimizer/glslopt archway_es1.glsl archway_es1.glslopt.min.glsl -2
```

<details><summary>archway_es1.glslopt.min.glsl</summary><div>

```glsl:aarchway_es1.glslopt.min.glsl
uniform highp vec2 resolution;
uniform highp float time;
highp vec3 cPos;
highp vec3 cDir;
highp float normalizedGlobalTime;
void main ()
{
  highp float alpha_2;
  highp vec3 color_3;
  highp vec3 ray_4;
  highp float tmpvar_5;
  tmpvar_5 = (float(mod ((time / 75.0), 1.0)));
  normalizedGlobalTime = tmpvar_5;
  highp vec2 tmpvar_6;
  tmpvar_6 = (((gl_FragCoord.xy * 2.0) - resolution) / min (resolution.x, resolution.y));
  if ((tmpvar_5 < 0.7)) {
    highp vec3 tmpvar_7;
    tmpvar_7.x = 0.0;
    tmpvar_7.y = (0.6 + (0.4 * cos(time)));
    tmpvar_7.z = (3.0 * time);
    cPos = tmpvar_7;
    cDir = vec3(0.0, -0.09950372, 0.9950371);
  } else {
    highp vec3 tmpvar_8;
    tmpvar_8.x = 0.0;
    tmpvar_8.y = ((0.6 + (0.4 * 
      cos(time)
    )) + (50.0 * (tmpvar_5 - 0.7)));
    tmpvar_8.z = (3.0 * time);
    cPos = tmpvar_8;
    highp vec3 tmpvar_9;
    tmpvar_9.xz = vec2(0.0, 1.0);
    tmpvar_9.y = (-0.1 - (tmpvar_5 - 0.7));
    cDir = normalize(tmpvar_9);
  };
  highp vec3 tmpvar_10;
  tmpvar_10 = normalize(((cDir.yzx * vec3(0.0, 0.0, 1.0)) - (cDir.zxy * vec3(1.0, 0.0, 0.0))));
  ray_4 = normalize(((
    (tmpvar_10 * tmpvar_6.x)
   + 
    (normalize(((tmpvar_10.yzx * cDir.zxy) - (tmpvar_10.zxy * cDir.yzx))) * tmpvar_6.y)
  ) + (cDir * 1.3)));
  color_3 = vec3(0.0, 0.0, 0.0);
  alpha_2 = 1.0;
  for (highp int i_1 = 0; i_1 < 3; i_1++) {
    highp vec3 origin_11;
    origin_11 = cPos;
    highp vec3 ray_12;
    ray_12 = ray_4;
    bool tmpvar_15;
    highp vec3 tmpvar_16;
    highp float tmpvar_17;
    highp vec3 tmpvar_18;
    highp vec3 tmpvar_19;
    highp vec3 p_20;
    highp float depth_21;
    highp float minDist_22;
    highp float dist_23;
    depth_21 = 0.0;
    p_20 = cPos;
    for (highp int i_14 = 0; i_14 < 120; i_14++) {
      highp float tmpvar_24;
      highp float d_25;
      highp float tmpvar_26;
      tmpvar_26 = (3.0 * time);
      d_25 = (-0.5 + (0.1 * (
        sin((p_20.x + tmpvar_26))
       + 
        sin((p_20.z + tmpvar_26))
      )));
      d_25 = (d_25 + (0.05 * (
        sin(((2.0 * p_20.x) + tmpvar_26))
       + 
        sin(((2.0 * p_20.z) + tmpvar_26))
      )));
      d_25 = (d_25 + (0.025 * (
        sin(((4.0 * p_20.x) + tmpvar_26))
       + 
        sin(((4.0 * p_20.z) + tmpvar_26))
      )));
      tmpvar_24 = (p_20.y - d_25);
      dist_23 = tmpvar_24;
      depth_21 = (depth_21 + tmpvar_24);
      p_20 = (origin_11 + (depth_21 * ray_12));
      highp float tmpvar_27;
      tmpvar_27 = abs(tmpvar_24);
      if ((tmpvar_27 < 0.0001)) {
        break;
      };
    };
    highp float tmpvar_28;
    tmpvar_28 = abs(dist_23);
    if ((tmpvar_28 < 0.0001)) {
      highp float tmpvar_29;
      tmpvar_29 = (3.0 * time);
      tmpvar_16 = p_20;
      highp vec3 p_30;
      p_30 = (p_20 + vec3(0.001, -0.001, -0.001));
      highp float d_31;
      d_31 = (-0.5 + (0.1 * (
        sin((p_30.x + tmpvar_29))
       + 
        sin((p_30.z + tmpvar_29))
      )));
      d_31 = (d_31 + (0.05 * (
        sin(((2.0 * p_30.x) + tmpvar_29))
       + 
        sin(((2.0 * p_30.z) + tmpvar_29))
      )));
      d_31 = (d_31 + (0.025 * (
        sin(((4.0 * p_30.x) + tmpvar_29))
       + 
        sin(((4.0 * p_30.z) + tmpvar_29))
      )));
      highp vec3 p_32;
      p_32 = (p_20 + vec3(-0.001, -0.001, 0.001));
      highp float d_33;
      d_33 = (-0.5 + (0.1 * (
        sin((p_32.x + tmpvar_29))
       + 
        sin((p_32.z + tmpvar_29))
      )));
      d_33 = (d_33 + (0.05 * (
        sin(((2.0 * p_32.x) + tmpvar_29))
       + 
        sin(((2.0 * p_32.z) + tmpvar_29))
      )));
      d_33 = (d_33 + (0.025 * (
        sin(((4.0 * p_32.x) + tmpvar_29))
       + 
        sin(((4.0 * p_32.z) + tmpvar_29))
      )));
      highp vec3 p_34;
      p_34 = (p_20 + vec3(-0.001, 0.001, -0.001));
      highp float d_35;
      d_35 = (-0.5 + (0.1 * (
        sin((p_34.x + tmpvar_29))
       + 
        sin((p_34.z + tmpvar_29))
      )));
      d_35 = (d_35 + (0.05 * (
        sin(((2.0 * p_34.x) + tmpvar_29))
       + 
        sin(((2.0 * p_34.z) + tmpvar_29))
      )));
      d_35 = (d_35 + (0.025 * (
        sin(((4.0 * p_34.x) + tmpvar_29))
       + 
        sin(((4.0 * p_34.z) + tmpvar_29))
      )));
      highp vec3 p_36;
      p_36 = (p_20 + vec3(0.001, 0.001, 0.001));
      highp float d_37;
      d_37 = (-0.5 + (0.1 * (
        sin((p_36.x + tmpvar_29))
       + 
        sin((p_36.z + tmpvar_29))
      )));
      d_37 = (d_37 + (0.05 * (
        sin(((2.0 * p_36.x) + tmpvar_29))
       + 
        sin(((2.0 * p_36.z) + tmpvar_29))
      )));
      d_37 = (d_37 + (0.025 * (
        sin(((4.0 * p_36.x) + tmpvar_29))
       + 
        sin(((4.0 * p_36.z) + tmpvar_29))
      )));
      highp vec3 tmpvar_38;
      tmpvar_38 = normalize(((
        ((vec3(0.001, -0.001, -0.001) * (p_30.y - d_31)) + (vec3(-0.001, -0.001, 0.001) * (p_32.y - d_33)))
       + 
        (vec3(-0.001, 0.001, -0.001) * (p_34.y - d_35))
      ) + (vec3(0.001, 0.001, 0.001) * 
        (p_36.y - d_37)
      )));
      tmpvar_18 = tmpvar_38;
      tmpvar_17 = depth_21;
      tmpvar_19 = ((vec3(0.5, 0.7, 0.8) * clamp (
        dot (vec3(-0.4866643, 0.8111071, -0.3244428), tmpvar_38)
      , 0.1, 1.0)) + vec3(pow (clamp (
        dot ((vec3(-0.4866643, 0.8111071, -0.3244428) - (2.0 * (
          dot (tmpvar_38, vec3(-0.4866643, 0.8111071, -0.3244428))
         * tmpvar_38))), ray_4)
      , 0.0, 1.0), 6.0)));
      tmpvar_15 = bool(1);
    } else {
      tmpvar_19 = vec3(0.0, 0.0, 0.0);
      tmpvar_15 = bool(0);
    };
    highp vec3 tmpvar_39;
    tmpvar_39 = clamp ((tmpvar_19 - (0.1 * tmpvar_17)), 0.0, 1.0);
    tmpvar_19 = tmpvar_39;
    p_20 = cPos;
    depth_21 = 0.0;
    minDist_22 = 1e+10;
    for (highp int i_13 = 0; i_13 < 20; i_13++) {
      highp vec3 p_40;
      p_40 = p_20;
      if ((normalizedGlobalTime <= 0.5)) {
        highp float interval_41;
        interval_41 = (1.0 + (20.0 * cos(
          (3.14159 * normalizedGlobalTime)
        )));
        p_40.z = ((float(mod (p_20.z, interval_41))) - (0.5 * interval_41));
      } else {
        p_40.xz = ((vec2(mod (p_40.xz, 10.0))) - 5.0);
      };
      highp vec3 p_42;
      p_42.xz = p_40.xz;
      p_42.y = (p_20.y - 0.65);
      highp vec3 p_43;
      p_43 = (p_42 - vec3(-1.0, 0.0, 0.0));
      highp vec2 tmpvar_44;
      tmpvar_44.x = sqrt(dot (p_43.xz, p_43.xz));
      tmpvar_44.y = p_43.y;
      highp vec2 tmpvar_45;
      tmpvar_45 = (abs(tmpvar_44) - vec2(0.05, 1.3));
      highp vec2 tmpvar_46;
      tmpvar_46 = max (tmpvar_45, 0.0);
      highp vec3 p_47;
      p_47 = (p_42 - vec3(1.0, 0.0, 0.0));
      highp vec2 tmpvar_48;
      tmpvar_48.x = sqrt(dot (p_47.xz, p_47.xz));
      tmpvar_48.y = p_47.y;
      highp vec2 tmpvar_49;
      tmpvar_49 = (abs(tmpvar_48) - vec2(0.05, 1.3));
      highp vec2 tmpvar_50;
      tmpvar_50 = max (tmpvar_49, 0.0);
      highp float tmpvar_51;
      tmpvar_51 = ((0.02 * p_40.x) * p_40.x);
      highp vec3 tmpvar_52;
      tmpvar_52.xz = vec2(0.0, 0.0);
      tmpvar_52.y = (1.3 + tmpvar_51);
      highp vec3 tmpvar_53;
      tmpvar_53.x = (1.7 + (0.5 * (p_42.y - 1.3)));
      tmpvar_53.y = (0.1 + tmpvar_51);
      tmpvar_53.z = 0.05;
      highp vec3 tmpvar_54;
      tmpvar_54 = max ((abs(
        (p_42 - tmpvar_52)
      ) - tmpvar_53), 0.0);
      highp vec3 tmpvar_55;
      tmpvar_55 = max ((abs(
        (p_42 - vec3(0.0, 0.7, 0.0))
      ) - vec3(1.3, 0.05, 0.05)), 0.0);
      highp vec3 tmpvar_56;
      tmpvar_56 = max ((abs(
        (p_42 - vec3(0.0, 1.0, 0.0))
      ) - vec3(0.05, 0.3, 0.05)), 0.0);
      highp float tmpvar_57;
      tmpvar_57 = min (min ((
        min (max (tmpvar_45.x, tmpvar_45.y), 0.0)
       + 
        sqrt(dot (tmpvar_46, tmpvar_46))
      ), (
        min (max (tmpvar_49.x, tmpvar_49.y), 0.0)
       + 
        sqrt(dot (tmpvar_50, tmpvar_50))
      )), min (sqrt(
        dot (tmpvar_56, tmpvar_56)
      ), min (
        sqrt(dot (tmpvar_54, tmpvar_54))
      , 
        sqrt(dot (tmpvar_55, tmpvar_55))
      )));
      dist_23 = tmpvar_57;
      minDist_22 = min (tmpvar_57, minDist_22);
      depth_21 = (depth_21 + tmpvar_57);
      p_20 = (origin_11 + (depth_21 * ray_12));
      if (((i_13 == 9) && (normalizedGlobalTime <= 0.5))) {
        break;
      };
    };
    highp float tmpvar_58;
    tmpvar_58 = abs(dist_23);
    if ((tmpvar_58 < 0.0001)) {
      tmpvar_19 = (tmpvar_39 + vec3(1.0, 0.1, 0.1));
    } else {
      tmpvar_19 = (tmpvar_19 + (vec3(1.0, 0.1, 0.1) * clamp (
        (0.05 / minDist_22)
      , 0.0, 1.0)));
    };
    color_3 = (color_3 + (alpha_2 * tmpvar_19));
    alpha_2 = (alpha_2 * 0.5);
    ray_4 = normalize((ray_4 - (2.0 * 
      (dot (tmpvar_18, ray_4) * tmpvar_18)
    )));
    cPos = (tmpvar_16 + (tmpvar_18 * 0.0009999999));
    if (!(tmpvar_15)) {
      break;
    };
  };
  mediump vec4 tmpvar_59;
  tmpvar_59.w = 1.0;
  tmpvar_59.xyz = color_3;
  gl_FragColor = tmpvar_59;
}

```

</div></details>

7471 byte -> 8885 byte (118.9 %)

ファイルサイズは圧縮後のほうが増えてしまいました。

インライン展開などが行われ、変数名も置き換えられているのですが、置き換えられた変数名が長かったり、インデントの空白のためにファイルサイズが増えたようです。

#### GLSL ES 3.0

```bash
./glsl-optimizer/glslopt archway_es3.glsl archway_es3.glslopt.min.glsl -3
```

<details><summary>archway_es3.glslopt.min.glsl</summary><div>

```glsl:archway_es3.glslopt.min.glsl
#version 300 es
uniform highp vec2 resolution;
uniform highp float time;
out highp vec4 outColor;
highp vec3 cPos;
highp vec3 cDir;
highp float normalizedGlobalTime;
void main ()
{
  highp float alpha_2;
  highp vec3 color_3;
  highp vec3 ray_4;
  highp float tmpvar_5;
  tmpvar_5 = (float(mod ((time / 75.0), 1.0)));
  normalizedGlobalTime = tmpvar_5;
  highp vec2 tmpvar_6;
  tmpvar_6 = (((gl_FragCoord.xy * 2.0) - resolution) / min (resolution.x, resolution.y));
  if ((tmpvar_5 < 0.7)) {
    highp vec3 tmpvar_7;
    tmpvar_7.x = 0.0;
    tmpvar_7.y = (0.6 + (0.4 * cos(time)));
    tmpvar_7.z = (3.0 * time);
    cPos = tmpvar_7;
    cDir = vec3(0.0, -0.09950372, 0.9950371);
  } else {
    highp vec3 tmpvar_8;
    tmpvar_8.x = 0.0;
    tmpvar_8.y = ((0.6 + (0.4 * 
      cos(time)
    )) + (50.0 * (tmpvar_5 - 0.7)));
    tmpvar_8.z = (3.0 * time);
    cPos = tmpvar_8;
    highp vec3 tmpvar_9;
    tmpvar_9.xz = vec2(0.0, 1.0);
    tmpvar_9.y = (-0.1 - (tmpvar_5 - 0.7));
    cDir = normalize(tmpvar_9);
  };
  highp vec3 tmpvar_10;
  tmpvar_10 = normalize(((cDir.yzx * vec3(0.0, 0.0, 1.0)) - (cDir.zxy * vec3(1.0, 0.0, 0.0))));
  ray_4 = normalize(((
    (tmpvar_10 * tmpvar_6.x)
   + 
    (normalize(((tmpvar_10.yzx * cDir.zxy) - (tmpvar_10.zxy * cDir.yzx))) * tmpvar_6.y)
  ) + (cDir * 1.3)));
  color_3 = vec3(0.0, 0.0, 0.0);
  alpha_2 = 1.0;
  for (highp int i_1 = 0; i_1 < 3; i_1++) {
    highp vec3 origin_11;
    origin_11 = cPos;
    highp vec3 ray_12;
    ray_12 = ray_4;
    bool tmpvar_15;
    highp vec3 tmpvar_16;
    highp float tmpvar_17;
    highp vec3 tmpvar_18;
    highp vec3 tmpvar_19;
    highp vec3 p_20;
    highp float depth_21;
    highp float minDist_22;
    highp float dist_23;
    depth_21 = 0.0;
    p_20 = cPos;
    for (highp int i_14 = 0; i_14 < 120; i_14++) {
      highp float tmpvar_24;
      highp float d_25;
      highp float tmpvar_26;
      tmpvar_26 = (3.0 * time);
      d_25 = (-0.5 + (0.1 * (
        sin((p_20.x + tmpvar_26))
       + 
        sin((p_20.z + tmpvar_26))
      )));
      d_25 = (d_25 + (0.05 * (
        sin(((2.0 * p_20.x) + tmpvar_26))
       + 
        sin(((2.0 * p_20.z) + tmpvar_26))
      )));
      d_25 = (d_25 + (0.025 * (
        sin(((4.0 * p_20.x) + tmpvar_26))
       + 
        sin(((4.0 * p_20.z) + tmpvar_26))
      )));
      tmpvar_24 = (p_20.y - d_25);
      dist_23 = tmpvar_24;
      depth_21 = (depth_21 + tmpvar_24);
      p_20 = (origin_11 + (depth_21 * ray_12));
      highp float tmpvar_27;
      tmpvar_27 = abs(tmpvar_24);
      if ((tmpvar_27 < 0.0001)) {
        break;
      };
    };
    highp float tmpvar_28;
    tmpvar_28 = abs(dist_23);
    if ((tmpvar_28 < 0.0001)) {
      highp float tmpvar_29;
      tmpvar_29 = (3.0 * time);
      tmpvar_16 = p_20;
      highp vec3 p_30;
      p_30 = (p_20 + vec3(0.001, -0.001, -0.001));
      highp float d_31;
      d_31 = (-0.5 + (0.1 * (
        sin((p_30.x + tmpvar_29))
       + 
        sin((p_30.z + tmpvar_29))
      )));
      d_31 = (d_31 + (0.05 * (
        sin(((2.0 * p_30.x) + tmpvar_29))
       + 
        sin(((2.0 * p_30.z) + tmpvar_29))
      )));
      d_31 = (d_31 + (0.025 * (
        sin(((4.0 * p_30.x) + tmpvar_29))
       + 
        sin(((4.0 * p_30.z) + tmpvar_29))
      )));
      highp vec3 p_32;
      p_32 = (p_20 + vec3(-0.001, -0.001, 0.001));
      highp float d_33;
      d_33 = (-0.5 + (0.1 * (
        sin((p_32.x + tmpvar_29))
       + 
        sin((p_32.z + tmpvar_29))
      )));
      d_33 = (d_33 + (0.05 * (
        sin(((2.0 * p_32.x) + tmpvar_29))
       + 
        sin(((2.0 * p_32.z) + tmpvar_29))
      )));
      d_33 = (d_33 + (0.025 * (
        sin(((4.0 * p_32.x) + tmpvar_29))
       + 
        sin(((4.0 * p_32.z) + tmpvar_29))
      )));
      highp vec3 p_34;
      p_34 = (p_20 + vec3(-0.001, 0.001, -0.001));
      highp float d_35;
      d_35 = (-0.5 + (0.1 * (
        sin((p_34.x + tmpvar_29))
       + 
        sin((p_34.z + tmpvar_29))
      )));
      d_35 = (d_35 + (0.05 * (
        sin(((2.0 * p_34.x) + tmpvar_29))
       + 
        sin(((2.0 * p_34.z) + tmpvar_29))
      )));
      d_35 = (d_35 + (0.025 * (
        sin(((4.0 * p_34.x) + tmpvar_29))
       + 
        sin(((4.0 * p_34.z) + tmpvar_29))
      )));
      highp vec3 p_36;
      p_36 = (p_20 + vec3(0.001, 0.001, 0.001));
      highp float d_37;
      d_37 = (-0.5 + (0.1 * (
        sin((p_36.x + tmpvar_29))
       + 
        sin((p_36.z + tmpvar_29))
      )));
      d_37 = (d_37 + (0.05 * (
        sin(((2.0 * p_36.x) + tmpvar_29))
       + 
        sin(((2.0 * p_36.z) + tmpvar_29))
      )));
      d_37 = (d_37 + (0.025 * (
        sin(((4.0 * p_36.x) + tmpvar_29))
       + 
        sin(((4.0 * p_36.z) + tmpvar_29))
      )));
      highp vec3 tmpvar_38;
      tmpvar_38 = normalize(((
        ((vec3(0.001, -0.001, -0.001) * (p_30.y - d_31)) + (vec3(-0.001, -0.001, 0.001) * (p_32.y - d_33)))
       + 
        (vec3(-0.001, 0.001, -0.001) * (p_34.y - d_35))
      ) + (vec3(0.001, 0.001, 0.001) * 
        (p_36.y - d_37)
      )));
      tmpvar_18 = tmpvar_38;
      tmpvar_17 = depth_21;
      tmpvar_19 = ((vec3(0.5, 0.7, 0.8) * clamp (
        dot (vec3(-0.4866643, 0.8111071, -0.3244428), tmpvar_38)
      , 0.1, 1.0)) + vec3(pow (clamp (
        dot ((vec3(-0.4866643, 0.8111071, -0.3244428) - (2.0 * (
          dot (tmpvar_38, vec3(-0.4866643, 0.8111071, -0.3244428))
         * tmpvar_38))), ray_4)
      , 0.0, 1.0), 6.0)));
      tmpvar_15 = bool(1);
    } else {
      tmpvar_19 = vec3(0.0, 0.0, 0.0);
      tmpvar_15 = bool(0);
    };
    highp vec3 tmpvar_39;
    tmpvar_39 = clamp ((tmpvar_19 - (0.1 * tmpvar_17)), 0.0, 1.0);
    tmpvar_19 = tmpvar_39;
    p_20 = cPos;
    depth_21 = 0.0;
    minDist_22 = 1e+10;
    for (highp int i_13 = 0; i_13 < 20; i_13++) {
      highp vec3 p_40;
      p_40 = p_20;
      if ((normalizedGlobalTime <= 0.5)) {
        highp float interval_41;
        interval_41 = (1.0 + (20.0 * cos(
          (3.14159 * normalizedGlobalTime)
        )));
        p_40.z = ((float(mod (p_20.z, interval_41))) - (0.5 * interval_41));
      } else {
        p_40.xz = ((vec2(mod (p_40.xz, 10.0))) - 5.0);
      };
      highp vec3 p_42;
      p_42.xz = p_40.xz;
      p_42.y = (p_20.y - 0.65);
      highp vec3 p_43;
      p_43 = (p_42 - vec3(-1.0, 0.0, 0.0));
      highp vec2 tmpvar_44;
      tmpvar_44.x = sqrt(dot (p_43.xz, p_43.xz));
      tmpvar_44.y = p_43.y;
      highp vec2 tmpvar_45;
      tmpvar_45 = (abs(tmpvar_44) - vec2(0.05, 1.3));
      highp vec2 tmpvar_46;
      tmpvar_46 = max (tmpvar_45, 0.0);
      highp vec3 p_47;
      p_47 = (p_42 - vec3(1.0, 0.0, 0.0));
      highp vec2 tmpvar_48;
      tmpvar_48.x = sqrt(dot (p_47.xz, p_47.xz));
      tmpvar_48.y = p_47.y;
      highp vec2 tmpvar_49;
      tmpvar_49 = (abs(tmpvar_48) - vec2(0.05, 1.3));
      highp vec2 tmpvar_50;
      tmpvar_50 = max (tmpvar_49, 0.0);
      highp float tmpvar_51;
      tmpvar_51 = ((0.02 * p_40.x) * p_40.x);
      highp vec3 tmpvar_52;
      tmpvar_52.xz = vec2(0.0, 0.0);
      tmpvar_52.y = (1.3 + tmpvar_51);
      highp vec3 tmpvar_53;
      tmpvar_53.x = (1.7 + (0.5 * (p_42.y - 1.3)));
      tmpvar_53.y = (0.1 + tmpvar_51);
      tmpvar_53.z = 0.05;
      highp vec3 tmpvar_54;
      tmpvar_54 = max ((abs(
        (p_42 - tmpvar_52)
      ) - tmpvar_53), 0.0);
      highp vec3 tmpvar_55;
      tmpvar_55 = max ((abs(
        (p_42 - vec3(0.0, 0.7, 0.0))
      ) - vec3(1.3, 0.05, 0.05)), 0.0);
      highp vec3 tmpvar_56;
      tmpvar_56 = max ((abs(
        (p_42 - vec3(0.0, 1.0, 0.0))
      ) - vec3(0.05, 0.3, 0.05)), 0.0);
      highp float tmpvar_57;
      tmpvar_57 = min (min ((
        min (max (tmpvar_45.x, tmpvar_45.y), 0.0)
       + 
        sqrt(dot (tmpvar_46, tmpvar_46))
      ), (
        min (max (tmpvar_49.x, tmpvar_49.y), 0.0)
       + 
        sqrt(dot (tmpvar_50, tmpvar_50))
      )), min (sqrt(
        dot (tmpvar_56, tmpvar_56)
      ), min (
        sqrt(dot (tmpvar_54, tmpvar_54))
      , 
        sqrt(dot (tmpvar_55, tmpvar_55))
      )));
      dist_23 = tmpvar_57;
      minDist_22 = min (tmpvar_57, minDist_22);
      depth_21 = (depth_21 + tmpvar_57);
      p_20 = (origin_11 + (depth_21 * ray_12));
      if (((i_13 == 9) && (normalizedGlobalTime <= 0.5))) {
        break;
      };
    };
    highp float tmpvar_58;
    tmpvar_58 = abs(dist_23);
    if ((tmpvar_58 < 0.0001)) {
      tmpvar_19 = (tmpvar_39 + vec3(1.0, 0.1, 0.1));
    } else {
      tmpvar_19 = (tmpvar_19 + (vec3(1.0, 0.1, 0.1) * clamp (
        (0.05 / minDist_22)
      , 0.0, 1.0)));
    };
    color_3 = (color_3 + (alpha_2 * tmpvar_19));
    alpha_2 = (alpha_2 * 0.5);
    ray_4 = normalize((ray_4 - (2.0 * 
      (dot (tmpvar_18, ray_4) * tmpvar_18)
    )));
    cPos = (tmpvar_16 + (tmpvar_18 * 0.0009999999));
    if (!(tmpvar_15)) {
      break;
    };
  };
  highp vec4 tmpvar_59;
  tmpvar_59.w = 1.0;
  tmpvar_59.xyz = color_3;
  outColor = tmpvar_59;
}

```

</div></details>

7502 byte -> 8920 byte (118.9 %)

GLSL ES 1.0と同様に圧縮後の方がファイルサイズが増えました。

## glsl-minifier

node.jsで実装されており、 [リポジトリ](https://github.com/TimvanScherpenzeel/glsl-minifier)をclone してから `npm install` することで、Windows OS でも macOS でも動作します。

### 使い方

```bash
node glsl-minifier/bin/glsl-minifier.js
usage: glsl-minifier.js [-h] [-v] -i INPUT -o OUTPUT [-sT {vertex,fragment}]
                        [-sV {2,3}]

glsl-minifier.js: error: Argument "-i/--input" is required
```

### minifyの実行

#### GLSL ES 1.0

```bash
node glsl-minifier/bin/glsl-minifier.js -i archway_es1.glsl -o archway_es1.glsl-minifier.min.glsl
```

```glsl
uniform highp vec2 resolution;uniform highp float time;highp vec3 a;highp vec3 b;highp float c;void main(){highp float d;highp vec3 e;highp vec3 f;highp float g;g=(float(mod((time/75.0),1.0)));c=g;highp vec2 h;h=(((gl_FragCoord.xy*2.0)-resolution)/min(resolution.x,resolution.y));if((g<0.7)){highp vec3 i;i.x=0.0;i.y=(0.6+(0.4*cos(time)));i.z=(3.0*time);a=i;b=vec3(0.0,-0.09950372,0.9950372);}else{highp vec3 j;j.x=0.0;j.y=((0.6+(0.4*cos(time)))+(50.0*(g-0.7)));j.z=(3.0*time);a=j;highp vec3 k;k.xz=vec2(0.0,1.0);k.y=(-0.1-(g-0.7));b=normalize(k);}highp vec3 l;l=normalize(((b.yzx*vec3(0.0,0.0,1.0))-(b.zxy*vec3(1.0,0.0,0.0))));f=normalize((((l*h.x)+(normalize(((l.yzx*b.zxy)-(l.zxy*b.yzx)))*h.y))+(b*1.3)));e=vec3(0.0,0.0,0.0);d=1.0;for(highp int m=0;m<3;m++){highp vec3 n;n=a;highp vec3 o;o=f;bool p;highp vec3 q;highp float r;highp vec3 s;highp vec3 t;highp vec3 u;highp float v;highp float w;highp float x;v=0.0;u=a;for(highp int y=0;y<120;y++){highp float z;highp float A;highp float B;B=(3.0*time);A=(-0.5+(0.1*(sin((u.x+B))+sin((u.z+B)))));A=(A+(0.05*(sin(((2.0*u.x)+B))+sin(((2.0*u.z)+B)))));A=(A+(0.025*(sin(((4.0*u.x)+B))+sin(((4.0*u.z)+B)))));z=(u.y-A);x=z;v=(v+z);u=(n+(v*o));highp float C;C=abs(z);if((C<0.0001)){break;}}highp float D;D=abs(x);if((D<0.0001)){highp float E;E=(3.0*time);q=u;highp vec3 F;F=(u+vec3(0.001,-0.001,-0.001));highp float G;G=(-0.5+(0.1*(sin((F.x+E))+sin((F.z+E)))));G=(G+(0.05*(sin(((2.0*F.x)+E))+sin(((2.0*F.z)+E)))));G=(G+(0.025*(sin(((4.0*F.x)+E))+sin(((4.0*F.z)+E)))));highp vec3 H;H=(u+vec3(-0.001,-0.001,0.001));highp float I;I=(-0.5+(0.1*(sin((H.x+E))+sin((H.z+E)))));I=(I+(0.05*(sin(((2.0*H.x)+E))+sin(((2.0*H.z)+E)))));I=(I+(0.025*(sin(((4.0*H.x)+E))+sin(((4.0*H.z)+E)))));highp vec3 J;J=(u+vec3(-0.001,0.001,-0.001));highp float K;K=(-0.5+(0.1*(sin((J.x+E))+sin((J.z+E)))));K=(K+(0.05*(sin(((2.0*J.x)+E))+sin(((2.0*J.z)+E)))));K=(K+(0.025*(sin(((4.0*J.x)+E))+sin(((4.0*J.z)+E)))));highp vec3 L;L=(u+vec3(0.001,0.001,0.001));highp float M;M=(-0.5+(0.1*(sin((L.x+E))+sin((L.z+E)))));M=(M+(0.05*(sin(((2.0*L.x)+E))+sin(((2.0*L.z)+E)))));M=(M+(0.025*(sin(((4.0*L.x)+E))+sin(((4.0*L.z)+E)))));highp vec3 N;N=normalize(((((vec3(0.001,-0.001,-0.001)*(F.y-G))+(vec3(-0.001,-0.001,0.001)*(H.y-I)))+(vec3(-0.001,0.001,-0.001)*(J.y-K)))+(vec3(0.001,0.001,0.001)*(L.y-M))));s=N;r=v;t=((vec3(0.5,0.7,0.8)*clamp(dot(vec3(-0.4866643,0.8111071,-0.3244428),N),0.1,1.0))+vec3(pow(clamp(dot((vec3(-0.4866643,0.8111071,-0.3244428)-(2.0*(dot(N,vec3(-0.4866643,0.8111071,-0.3244428))*N))),f),0.0,1.0),6.0)));p=bool(1);}else{t=vec3(0.0,0.0,0.0);p=bool(0);}highp vec3 O;O=clamp((t-(0.1*r)),0.0,1.0);t=O;u=a;v=0.0;w=1e+10;for(highp int P=0;P<20;P++){highp vec3 Q;Q=u;if((c<=0.5)){highp float R;R=(1.0+(20.0*cos((3.14159*c))));Q.z=((float(mod(u.z,R)))-(0.5*R));}else{Q.xz=((vec2(mod(Q.xz,10.0)))-5.0);}highp vec3 S;S.xz=Q.xz;S.y=(u.y-0.65);highp vec3 T;T=(S-vec3(-1.0,0.0,0.0));highp vec2 U;U.x=sqrt(dot(T.xz,T.xz));U.y=T.y;highp vec2 V;V=(abs(U)-vec2(0.05,1.3));highp vec2 W;W=max(V,0.0);highp vec3 X;X=(S-vec3(1.0,0.0,0.0));highp vec2 Y;Y.x=sqrt(dot(X.xz,X.xz));Y.y=X.y;highp vec2 Z;Z=(abs(Y)-vec2(0.05,1.3));highp vec2 ba;ba=max(Z,0.0);highp float bb;bb=((0.02*Q.x)*Q.x);highp vec3 bc;bc.xz=vec2(0.0,0.0);bc.y=(1.3+bb);highp vec3 bd;bd.x=(1.7+(0.5*(S.y-1.3)));bd.y=(0.1+bb);bd.z=0.05;highp vec3 be;be=max((abs((S-bc))-bd),0.0);highp vec3 bf;bf=max((abs((S-vec3(0.0,0.7,0.0)))-vec3(1.3,0.05,0.05)),0.0);highp vec3 bg;bg=max((abs((S-vec3(0.0,1.0,0.0)))-vec3(0.05,0.3,0.05)),0.0);highp float bh;bh=min(min((min(max(V.x,V.y),0.0)+sqrt(dot(W,W))),(min(max(Z.x,Z.y),0.0)+sqrt(dot(ba,ba)))),min(sqrt(dot(bg,bg)),min(sqrt(dot(be,be)),sqrt(dot(bf,bf)))));x=bh;w=min(bh,w);v=(v+bh);u=(n+(v*o));if(((P==9)&&(c<=0.5))){break;}}highp float bi;bi=abs(x);if((bi<0.0001)){t=(O+vec3(1.0,0.1,0.1));}else{t=(t+(vec3(1.0,0.1,0.1)*clamp((0.05/w),0.0,1.0)));}e=(e+(d*t));d=(d*0.5);f=normalize((f-(2.0*(dot(s,f)*s))));a=(q+(s*0.0009999999));if(!(p)){break;}}mediump vec4 bj;bj.w=1.0;bj.xyz=e;gl_FragColor=bj;}
```

7471 -> 4012 byte (53.7 %)

#### GLSL ES 3.0

```bash
node glsl-minifier/bin/glsl-minifier.js -i archway_es3.glsl -o archway_es3.glsl-minifier.min.glsl -sV 3
/Users/gam0022/Dropbox/glsl-minifier-sandbox/glsl-minifier/lib/optimizer/glsl-optimizer-asm.js:10
var Module;if(!Module)Module=(typeof Module!=="undefined"?Module:null)||{};var moduleOverrides={};for(var key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}var ENVIRONMENT_IS_WEB=typeof window==="object";var ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof require==="function"&&!ENVIRONMENT_IS_WEB;var ENVIRONMENT_IS_WORKER=typeof importScripts==="function";var ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER;if(ENVIRONMENT_IS_NODE){if(!Module["print"])Module["print"]=function print(x){process["stdout"].write(x+"\n")};if(!Module["printErr"])Module["printErr"]=function printErr(x){process["stderr"].write(x+"\n")};var nodeFS=require("fs");var nodePath=require("path");Module["read"]=function read(filename,binary){filename=nodePath["normalize"](filename);var ret=nodeFS["readFileSync"](filename);if(!ret&&filename!=nodePath["resolve"](filename)){

Error: parameter is not allowed here at line 4
    at unexpected (/Users/gam0022/Dropbox/glsl-minifier-sandbox/glsl-minifier/node_modules/glsl-parser/lib/index.js:661:11)
    at parameter_or_not (/Users/gam0022/Dropbox/glsl-minifier-sandbox/glsl-minifier/node_modules/glsl-parser/lib/index.js:415:18)
    at /Users/gam0022/Dropbox/glsl-minifier-sandbox/glsl-minifier/node_modules/glsl-parser/lib/index.js:701:16
    at parse_decl (/Users/gam0022/Dropbox/glsl-minifier-sandbox/glsl-minifier/node_modules/glsl-parser/lib/index.js:379:6)
    at write (/Users/gam0022/Dropbox/glsl-minifier-sandbox/glsl-minifier/node_modules/glsl-parser/lib/index.js:190:18)
    at reader (/Users/gam0022/Dropbox/glsl-minifier-sandbox/glsl-minifier/node_modules/glsl-parser/lib/index.js:166:5)
    at DestroyableTransform.write [as _transform] (/Users/gam0022/Dropbox/glsl-minifier-sandbox/glsl-minifier/node_modules/glsl-parser/stream.js:16:17)
    at DestroyableTransform.Transform._read (/Users/gam0022/Dropbox/glsl-minifier-sandbox/glsl-minifier/node_modules/readable-stream/lib/_stream_transform.js:184:10)
    at DestroyableTransform.Transform._write (/Users/gam0022/Dropbox/glsl-minifier-sandbox/glsl-minifier/node_modules/readable-stream/lib/_stream_transform.js:172:12)
    at doWrite (/Users/gam0022/Dropbox/glsl-minifier-sandbox/glsl-minifier/node_modules/readable-stream/lib/_stream_writable.js:237:10)
```

エラーになってしまいました。

glsl-minifierの作者に問い合わせたところ、GLSL ES 3.0（WebGL 2.0）は未対応とのことでした。

- [Not work in GLSL ES 3.0 · Issue #3 · TimvanScherpenzeel/glsl-minifier](https://github.com/TimvanScherpenzeel/glsl-minifier/issues/3)

# おわりに

GLSLのminifierを探していたところ、いくつも候補が見つかり、どれを採用すべきか見極めるのが大変でした。

今回は圧縮率という観点で比較しましたが、実行速度なども比較するべきかと思います。

ちなみに、今回検証に使った3種以外にも、下記のような様々なGLSLのminifierが存在します。

- [glslx](https://github.com/evanw/glslx)
- [webpack-glsl-minify](https://github.com/leosingleton/webpack-glsl-minify)
- [flowtsohg/glsl-minifier](https://github.com/flowtsohg/glsl-minifier)
- [glslmin](https://github.com/chrisdickinson/glslmin)