これはKLab Engineer Advent Calendar 2019の17日目の記事です。
背景
OpenGLやWebGLでは、Direct3Dのようにシェーダーのプリコンパイルをサポートしておらず1、GLSLをランタイム時にコンパイルするしかありません。
そのため、GLSLのファイルサイズがそのままダウンロードサイズに影響してしまいます。
Unity等のゲームエンジンでは、HLSL(ShaderLab)からGLSL等にトランスパイルする際にコードを圧縮2できますが、
ゲームエンジンを使わない場合でもminifier(圧縮ツール)を利用することで同様に圧縮することでダウンロードサイズを削減できます。
今回、以下3つのminifierに対して圧縮率を比較検証しました。
比較結果
ツール名 | 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』のフラグメントシェーダーを各minifierで圧縮し、圧縮率(圧縮後のファイルサイズ / 圧縮前のファイルサイズ)を比較しました。
2つバージョンのGLSLで検証しました。
- GLSL ES 1.0(OpenGL ES 2.0 / WebGL 1.0)
- 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)
archway_es1.glsl(7471 byte)
// 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);
}
圧縮前のコード(GLSL ES 3.0)
archway_es3.glsl(7502 byte)
#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);
}
検証環境
- Mac Book Air Early 2015
- macOS Catalina
検証の詳細
以下、各minifierの実行方法と結果を紹介します。
Shader Minifier
有名なデモ制作チーム、Ctrl-Alt-Testによるツールです。
F#で実装されており、同じバイナリが Windows OS でも macOS でも動作します。
ビルド済みのバイナリはGitHubのreleasesからダウンロードできます。
使い方
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
mono shader_minifier.exe archway_es1.glsl -o archway_es1.shader_minifier.min.glsl --format none
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
mono shader_minifier.exe archway_es3.glsl -o archway_es3.shader_minifier.min.glsl --format none
#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指定はエラーになりました。
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指定をコメントアウトすることで回避しました。
-precision highp float;
+// precision highp float;// Shader Minifier のエラー回避のためにコメントアウト
glsl-optimizer
C++で実装されており、ソースコードを各環境でビルドすることで Windows OS でも macOS でも動作します。
macOSの場合は、リポジトリをclone してから cmake . && make
で簡単にビルドができました。
使い方
./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
./glsl-optimizer/glslopt archway_es1.glsl archway_es1.glslopt.min.glsl -2
archway_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;
}
7471 byte -> 8885 byte (118.9 %)
ファイルサイズは圧縮後のほうが増えてしまいました。
インライン展開などが行われ、変数名も置き換えられているのですが、置き換えられた変数名が長かったり、インデントの空白のためにファイルサイズが増えたようです。
GLSL ES 3.0
./glsl-optimizer/glslopt archway_es3.glsl archway_es3.glslopt.min.glsl -3
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;
}
7502 byte -> 8920 byte (118.9 %)
GLSL ES 1.0と同様に圧縮後の方がファイルサイズが増えました。
glsl-minifier
node.jsで実装されており、 リポジトリをclone してから npm install
することで、Windows OS でも macOS でも動作します。
使い方
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
node glsl-minifier/bin/glsl-minifier.js -i archway_es1.glsl -o archway_es1.glsl-minifier.min.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
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)は未対応とのことでした。
おわりに
GLSLのminifierを探していたところ、いくつも候補が見つかり、どれを採用すべきか見極めるのが大変でした。
今回は圧縮率という観点で比較しましたが、実行速度なども比較するべきかと思います。
ちなみに、今回検証に使った3種以外にも、下記のような様々なGLSLのminifierが存在します。