glslkit-webgl 0.1.0.pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +31 -0
- data/LICENSE.txt +21 -0
- data/README.md +98 -0
- data/glslkit-webgl.gemspec +37 -0
- data/lib/glslkit/webgl/context.rb +228 -0
- data/lib/glslkit/webgl/embed.rb +158 -0
- data/lib/glslkit/webgl/errors.rb +39 -0
- data/lib/glslkit/webgl/geometry.rb +64 -0
- data/lib/glslkit/webgl/live_reload.rb +107 -0
- data/lib/glslkit/webgl/matrix.rb +77 -0
- data/lib/glslkit/webgl/program.rb +205 -0
- data/lib/glslkit/webgl/reload_result.rb +28 -0
- data/lib/glslkit/webgl/texture.rb +27 -0
- data/lib/glslkit/webgl/version.rb +7 -0
- data/lib/glslkit/webgl.rb +48 -0
- data/sample/app.rb +52 -0
- data/sample/error_panel.rb +68 -0
- data/sample/generated/cube_shaders.rb +82 -0
- data/sample/generated/neon_shaders.rb +162 -0
- data/sample/generated-broken/neon-broken_shaders.rb +162 -0
- data/sample/index.html +15 -0
- data/sample/neon-error.html +82 -0
- data/sample/neon-error.rb +119 -0
- data/sample/neon.html +22 -0
- data/sample/neon.rb +22 -0
- data/sample/shaders/common/color.glsl +5 -0
- data/sample/shaders/common/sdf.glsl +11 -0
- data/sample/shaders/cube.frag +8 -0
- data/sample/shaders/cube.vert +9 -0
- data/sample/shaders/neon.frag +79 -0
- data/sample/shaders/neon.vert +5 -0
- data/sample/shaders-broken/common/color.glsl +5 -0
- data/sample/shaders-broken/common/sdf.glsl +11 -0
- data/sample/shaders-broken/neon-broken.frag +79 -0
- data/sample/shaders-broken/neon-broken.vert +5 -0
- data/shim/glslkit-webgl.js +49 -0
- metadata +105 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# 自動生成 (rake glslkit:embed)。手で編集しない。
|
|
4
|
+
# 再生成するには: bundle exec rake glslkit:embed[shaders_dir,out_dir]
|
|
5
|
+
#
|
|
6
|
+
# MANIFESTのgenerated_atが1970-01-01なのはバグではない。生成の
|
|
7
|
+
# 冪等性(同じ入力から同じ出力になること)のための固定値であり、
|
|
8
|
+
# 実行時刻を使うと同じ入力でも実行のたびにgit diffが出てしまう。
|
|
9
|
+
# 再現性はstages.*.digest(実データから計算される)が担保している。
|
|
10
|
+
|
|
11
|
+
module CubeShaders
|
|
12
|
+
VERTEX = <<'GLSLKIT_VERTEX_SRC'.freeze
|
|
13
|
+
#version 300 es
|
|
14
|
+
layout(location = 0) in vec3 a_position;
|
|
15
|
+
layout(location = 1) in vec2 a_uv;
|
|
16
|
+
uniform mat4 u_mvp;
|
|
17
|
+
out vec2 v_uv;
|
|
18
|
+
void main() {
|
|
19
|
+
v_uv = a_uv;
|
|
20
|
+
gl_Position = u_mvp * vec4(a_position, 1.0);
|
|
21
|
+
}
|
|
22
|
+
GLSLKIT_VERTEX_SRC
|
|
23
|
+
|
|
24
|
+
FRAGMENT = <<'GLSLKIT_FRAGMENT_SRC'.freeze
|
|
25
|
+
#version 300 es
|
|
26
|
+
precision mediump float;
|
|
27
|
+
in vec2 v_uv;
|
|
28
|
+
uniform sampler2D u_texture;
|
|
29
|
+
out vec4 frag_color;
|
|
30
|
+
void main() {
|
|
31
|
+
frag_color = texture(u_texture, v_uv);
|
|
32
|
+
}
|
|
33
|
+
GLSLKIT_FRAGMENT_SRC
|
|
34
|
+
|
|
35
|
+
MANIFEST = {"schema_version"=>1,
|
|
36
|
+
"generated_at"=>"1970-01-01T00:00:00Z",
|
|
37
|
+
"generator"=>"glslkit/0.1.0.pre",
|
|
38
|
+
"programs"=>
|
|
39
|
+
{"cube"=>
|
|
40
|
+
{"digest"=>
|
|
41
|
+
"a3743db5891346c02adf840ae7246c1b7e911c4511eab0290dcc81a7e80eb08a",
|
|
42
|
+
"stages"=>
|
|
43
|
+
{"vertex"=>
|
|
44
|
+
{"path"=>"cube.vert",
|
|
45
|
+
"digest"=>
|
|
46
|
+
"8f1de9ac637f6872e2563399c33af571bc74741e651726eb2ece6e69c03ce433",
|
|
47
|
+
"url"=>"cube.vert"},
|
|
48
|
+
"fragment"=>
|
|
49
|
+
{"path"=>"cube.frag",
|
|
50
|
+
"digest"=>
|
|
51
|
+
"92a077871371847d2a1cd568bc082d5bc2e840293e1c0cecb0ae39559e114dc1",
|
|
52
|
+
"url"=>"cube.frag"}},
|
|
53
|
+
"attributes"=>
|
|
54
|
+
[{"name"=>"a_position", "type"=>"vec3", "location"=>0, "array_size"=>1},
|
|
55
|
+
{"name"=>"a_uv", "type"=>"vec2", "location"=>1, "array_size"=>1}],
|
|
56
|
+
"uniforms"=>
|
|
57
|
+
[{"name"=>"u_mvp",
|
|
58
|
+
"type"=>"mat4",
|
|
59
|
+
"array_size"=>1,
|
|
60
|
+
"setter"=>"uniformMatrix4fv",
|
|
61
|
+
"matrix"=>true,
|
|
62
|
+
"sampler"=>false,
|
|
63
|
+
"components"=>16,
|
|
64
|
+
"element_count"=>16,
|
|
65
|
+
"stages"=>["vertex"]},
|
|
66
|
+
{"name"=>"u_texture",
|
|
67
|
+
"type"=>"sampler2D",
|
|
68
|
+
"array_size"=>1,
|
|
69
|
+
"setter"=>"uniform1iv",
|
|
70
|
+
"matrix"=>false,
|
|
71
|
+
"sampler"=>true,
|
|
72
|
+
"components"=>1,
|
|
73
|
+
"element_count"=>1,
|
|
74
|
+
"stages"=>["fragment"]}],
|
|
75
|
+
"uniform_blocks"=>[],
|
|
76
|
+
"outputs"=>[{"name"=>"frag_color", "type"=>"vec4", "location"=>nil}]}}}.freeze
|
|
77
|
+
|
|
78
|
+
SOURCE_MAPS = {
|
|
79
|
+
vertex: {"version"=>1, "files"=>["cube.vert"], "segments"=>[[2, 0, 2]]},
|
|
80
|
+
fragment: {"version"=>1, "files"=>["cube.frag"], "segments"=>[[2, 0, 2]]}
|
|
81
|
+
}.freeze
|
|
82
|
+
end
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# 自動生成 (rake glslkit:embed)。手で編集しない。
|
|
4
|
+
# 再生成するには: bundle exec rake glslkit:embed[shaders_dir,out_dir]
|
|
5
|
+
#
|
|
6
|
+
# MANIFESTのgenerated_atが1970-01-01なのはバグではない。生成の
|
|
7
|
+
# 冪等性(同じ入力から同じ出力になること)のための固定値であり、
|
|
8
|
+
# 実行時刻を使うと同じ入力でも実行のたびにgit diffが出てしまう。
|
|
9
|
+
# 再現性はstages.*.digest(実データから計算される)が担保している。
|
|
10
|
+
|
|
11
|
+
module NeonShaders
|
|
12
|
+
VERTEX = <<'GLSLKIT_VERTEX_SRC'.freeze
|
|
13
|
+
#version 300 es
|
|
14
|
+
layout(location = 0) in vec2 a_position;
|
|
15
|
+
void main() {
|
|
16
|
+
gl_Position = vec4(a_position, 0.0, 1.0);
|
|
17
|
+
}
|
|
18
|
+
GLSLKIT_VERTEX_SRC
|
|
19
|
+
|
|
20
|
+
FRAGMENT = <<'GLSLKIT_FRAGMENT_SRC'.freeze
|
|
21
|
+
#version 300 es
|
|
22
|
+
precision highp float;
|
|
23
|
+
|
|
24
|
+
uniform float u_time;
|
|
25
|
+
uniform vec2 u_resolution;
|
|
26
|
+
out vec4 frag_color;
|
|
27
|
+
|
|
28
|
+
#define PI 3.14159265359
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
mat2 rotate2d(float angle) {
|
|
32
|
+
float c = cos(angle), s = sin(angle);
|
|
33
|
+
return mat2(c, -s, s, c);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
float sdTorus(vec3 p, vec2 t) {
|
|
37
|
+
vec2 q = vec2(length(p.xz) - t.x, p.y);
|
|
38
|
+
return length(q) - t.y;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
vec3 palette(float t) {
|
|
42
|
+
return 0.55 + 0.45 * cos(6.28318 * (vec3(0.02, 0.18, 0.38) * t + vec3(0.0, 0.18, 0.42)));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
float scene(vec3 p) {
|
|
46
|
+
p.xz *= rotate2d(u_time * 0.32);
|
|
47
|
+
p.xy *= rotate2d(sin(u_time * 0.27) * 0.55);
|
|
48
|
+
|
|
49
|
+
float angle = atan(p.z, p.x);
|
|
50
|
+
float pulse = 0.10 * sin(angle * 7.0 + u_time * 2.4);
|
|
51
|
+
float portal = sdTorus(p, vec2(1.25, 0.17 + pulse));
|
|
52
|
+
|
|
53
|
+
vec3 orb = p;
|
|
54
|
+
orb.xz *= rotate2d(-u_time * 0.7);
|
|
55
|
+
orb -= vec3(1.25, sin(u_time * 1.8) * 0.22, 0.0);
|
|
56
|
+
float satellite = length(orb) - 0.11;
|
|
57
|
+
return min(portal, satellite);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
vec3 normalAt(vec3 p) {
|
|
61
|
+
vec2 e = vec2(0.0015, 0.0);
|
|
62
|
+
float d = scene(p);
|
|
63
|
+
return normalize(vec3(
|
|
64
|
+
scene(p + e.xyy) - d,
|
|
65
|
+
scene(p + e.yxy) - d,
|
|
66
|
+
scene(p + e.yyx) - d
|
|
67
|
+
));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
float raymarch(vec3 origin, vec3 direction, out vec3 point) {
|
|
71
|
+
float distanceTravelled = 0.0;
|
|
72
|
+
for (int i = 0; i < 96; i++) {
|
|
73
|
+
point = origin + direction * distanceTravelled;
|
|
74
|
+
float distanceToScene = scene(point);
|
|
75
|
+
if (distanceToScene < 0.001 || distanceTravelled > 12.0) break;
|
|
76
|
+
distanceTravelled += distanceToScene * 0.72;
|
|
77
|
+
}
|
|
78
|
+
return distanceTravelled;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
void main() {
|
|
82
|
+
vec2 uv = (gl_FragCoord.xy * 2.0 - u_resolution) / u_resolution.y;
|
|
83
|
+
float vignette = max(0.0, 1.0 - dot(uv, uv) * 0.28);
|
|
84
|
+
|
|
85
|
+
vec3 origin = vec3(0.0, 0.0, 4.2);
|
|
86
|
+
vec3 direction = normalize(vec3(uv, -1.8));
|
|
87
|
+
vec3 point;
|
|
88
|
+
float travelled = raymarch(origin, direction, point);
|
|
89
|
+
|
|
90
|
+
vec3 color = vec3(0.006, 0.008, 0.025);
|
|
91
|
+
float rays = pow(max(0.0, 1.0 - length(uv)), 5.0);
|
|
92
|
+
color += vec3(0.12, 0.03, 0.30) * rays;
|
|
93
|
+
|
|
94
|
+
if (travelled < 12.0) {
|
|
95
|
+
vec3 normal = normalAt(point);
|
|
96
|
+
vec3 light = normalize(vec3(0.7, 1.2, 2.0));
|
|
97
|
+
float diffuse = max(dot(normal, light), 0.0);
|
|
98
|
+
float rim = pow(1.0 - max(dot(normal, -direction), 0.0), 2.4);
|
|
99
|
+
float bands = 0.5 + 0.5 * sin(atan(point.z, point.x) * 9.0 - u_time * 3.0);
|
|
100
|
+
vec3 neon = palette(bands + u_time * 0.06);
|
|
101
|
+
color += neon * (0.18 + diffuse * 0.65 + rim * 2.4);
|
|
102
|
+
color += pow(max(dot(reflect(-light, normal), -direction), 0.0), 32.0);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
float glowDistance = abs(length(uv) - 0.47 - sin(atan(uv.y, uv.x) * 7.0 + u_time) * 0.018);
|
|
106
|
+
color += palette(u_time * 0.07 + length(uv)) * 0.012 / max(glowDistance, 0.008);
|
|
107
|
+
|
|
108
|
+
color *= vignette;
|
|
109
|
+
color = pow(color, vec3(0.82));
|
|
110
|
+
frag_color = vec4(color, 1.0);
|
|
111
|
+
}
|
|
112
|
+
GLSLKIT_FRAGMENT_SRC
|
|
113
|
+
|
|
114
|
+
MANIFEST = {"schema_version"=>1,
|
|
115
|
+
"generated_at"=>"1970-01-01T00:00:00Z",
|
|
116
|
+
"generator"=>"glslkit/0.1.0.pre",
|
|
117
|
+
"programs"=>
|
|
118
|
+
{"neon"=>
|
|
119
|
+
{"digest"=>
|
|
120
|
+
"b7fb9408aaff4ba38dd6a9be7429fb99962e9878a085e5e034bf1d9f8b3ea5a7",
|
|
121
|
+
"stages"=>
|
|
122
|
+
{"vertex"=>
|
|
123
|
+
{"path"=>"neon.vert",
|
|
124
|
+
"digest"=>
|
|
125
|
+
"9cf045b60aee6e157a1b7b83b2ce299dd479f8d2d16d2418914db2fedeb37627",
|
|
126
|
+
"url"=>"neon.vert"},
|
|
127
|
+
"fragment"=>
|
|
128
|
+
{"path"=>"neon.frag",
|
|
129
|
+
"digest"=>
|
|
130
|
+
"71c73491886569fc5546df1510755b3023a784aec25b635f31cdcc76facece27",
|
|
131
|
+
"url"=>"neon.frag"}},
|
|
132
|
+
"attributes"=>
|
|
133
|
+
[{"name"=>"a_position", "type"=>"vec2", "location"=>0, "array_size"=>1}],
|
|
134
|
+
"uniforms"=>
|
|
135
|
+
[{"name"=>"u_time",
|
|
136
|
+
"type"=>"float",
|
|
137
|
+
"array_size"=>1,
|
|
138
|
+
"setter"=>"uniform1fv",
|
|
139
|
+
"matrix"=>false,
|
|
140
|
+
"sampler"=>false,
|
|
141
|
+
"components"=>1,
|
|
142
|
+
"element_count"=>1,
|
|
143
|
+
"stages"=>["fragment"]},
|
|
144
|
+
{"name"=>"u_resolution",
|
|
145
|
+
"type"=>"vec2",
|
|
146
|
+
"array_size"=>1,
|
|
147
|
+
"setter"=>"uniform2fv",
|
|
148
|
+
"matrix"=>false,
|
|
149
|
+
"sampler"=>false,
|
|
150
|
+
"components"=>2,
|
|
151
|
+
"element_count"=>2,
|
|
152
|
+
"stages"=>["fragment"]}],
|
|
153
|
+
"uniform_blocks"=>[],
|
|
154
|
+
"outputs"=>[{"name"=>"frag_color", "type"=>"vec4", "location"=>nil}]}}}.freeze
|
|
155
|
+
|
|
156
|
+
SOURCE_MAPS = {
|
|
157
|
+
vertex: {"version"=>1, "files"=>["neon.vert"], "segments"=>[[2, 0, 2]]},
|
|
158
|
+
fragment: {"version"=>1,
|
|
159
|
+
"files"=>["neon.frag", "common/sdf.glsl", "common/color.glsl"],
|
|
160
|
+
"segments"=>[[2, 0, 2], [10, 1, 2], [20, 2, 2], [24, 0, 12]]}
|
|
161
|
+
}.freeze
|
|
162
|
+
end
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# 自動生成 (rake glslkit:embed)。手で編集しない。
|
|
4
|
+
# 再生成するには: bundle exec rake glslkit:embed[shaders_dir,out_dir]
|
|
5
|
+
#
|
|
6
|
+
# MANIFESTのgenerated_atが1970-01-01なのはバグではない。生成の
|
|
7
|
+
# 冪等性(同じ入力から同じ出力になること)のための固定値であり、
|
|
8
|
+
# 実行時刻を使うと同じ入力でも実行のたびにgit diffが出てしまう。
|
|
9
|
+
# 再現性はstages.*.digest(実データから計算される)が担保している。
|
|
10
|
+
|
|
11
|
+
module NeonBrokenShaders
|
|
12
|
+
VERTEX = <<'GLSLKIT_VERTEX_SRC'.freeze
|
|
13
|
+
#version 300 es
|
|
14
|
+
layout(location = 0) in vec2 a_position;
|
|
15
|
+
void main() {
|
|
16
|
+
gl_Position = vec4(a_position, 0.0, 1.0);
|
|
17
|
+
}
|
|
18
|
+
GLSLKIT_VERTEX_SRC
|
|
19
|
+
|
|
20
|
+
FRAGMENT = <<'GLSLKIT_FRAGMENT_SRC'.freeze
|
|
21
|
+
#version 300 es
|
|
22
|
+
precision highp float;
|
|
23
|
+
|
|
24
|
+
uniform float u_time;
|
|
25
|
+
uniform vec2 u_resolution;
|
|
26
|
+
out vec4 frag_color;
|
|
27
|
+
|
|
28
|
+
#define PI 3.14159265359
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
mat2 rotate2d(float angle) {
|
|
32
|
+
float c = cos(angle), s = sin(angle)
|
|
33
|
+
return mat2(c, -s, s, c);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
float sdTorus(vec3 p, vec2 t) {
|
|
37
|
+
vec2 q = vec2(length(p.xz) - t.x, p.y);
|
|
38
|
+
return length(q) - t.y;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
vec3 palette(float t) {
|
|
42
|
+
return 0.55 + 0.45 * cos(6.28318 * (vec3(0.02, 0.18, 0.38) * t + vec3(0.0, 0.18, 0.42)));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
float scene(vec3 p) {
|
|
46
|
+
p.xz *= rotate2d(u_time * 0.32);
|
|
47
|
+
p.xy *= rotate2d(sin(u_time * 0.27) * 0.55);
|
|
48
|
+
|
|
49
|
+
float angle = atan(p.z, p.x);
|
|
50
|
+
float pulse = 0.10 * sin(angle * 7.0 + u_time * 2.4);
|
|
51
|
+
float portal = sdTorus(p, vec2(1.25, 0.17 + pulse));
|
|
52
|
+
|
|
53
|
+
vec3 orb = p;
|
|
54
|
+
orb.xz *= rotate2d(-u_time * 0.7);
|
|
55
|
+
orb -= vec3(1.25, sin(u_time * 1.8) * 0.22, 0.0);
|
|
56
|
+
float satellite = length(orb) - 0.11;
|
|
57
|
+
return min(portal, satellite);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
vec3 normalAt(vec3 p) {
|
|
61
|
+
vec2 e = vec2(0.0015, 0.0);
|
|
62
|
+
float d = scene(p);
|
|
63
|
+
return normalize(vec3(
|
|
64
|
+
scene(p + e.xyy) - d,
|
|
65
|
+
scene(p + e.yxy) - d,
|
|
66
|
+
scene(p + e.yyx) - d
|
|
67
|
+
));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
float raymarch(vec3 origin, vec3 direction, out vec3 point) {
|
|
71
|
+
float distanceTravelled = 0.0;
|
|
72
|
+
for (int i = 0; i < 96; i++) {
|
|
73
|
+
point = origin + direction * distanceTravelled;
|
|
74
|
+
float distanceToScene = scene(point);
|
|
75
|
+
if (distanceToScene < 0.001 || distanceTravelled > 12.0) break;
|
|
76
|
+
distanceTravelled += distanceToScene * 0.72;
|
|
77
|
+
}
|
|
78
|
+
return distanceTravelled;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
void main() {
|
|
82
|
+
vec2 uv = (gl_FragCoord.xy * 2.0 - u_resolution) / u_resolution.y;
|
|
83
|
+
float vignette = max(0.0, 1.0 - dot(uv, uv) * 0.28);
|
|
84
|
+
|
|
85
|
+
vec3 origin = vec3(0.0, 0.0, 4.2);
|
|
86
|
+
vec3 direction = normalize(vec3(uv, -1.8));
|
|
87
|
+
vec3 point;
|
|
88
|
+
float travelled = raymarch(origin, direction, point);
|
|
89
|
+
|
|
90
|
+
vec3 color = vec3(0.006, 0.008, 0.025);
|
|
91
|
+
float rays = pow(max(0.0, 1.0 - length(uv)), 5.0);
|
|
92
|
+
color += vec3(0.12, 0.03, 0.30) * rays;
|
|
93
|
+
|
|
94
|
+
if (travelled < 12.0) {
|
|
95
|
+
vec3 normal = normalAt(point);
|
|
96
|
+
vec3 light = normalize(vec3(0.7, 1.2, 2.0));
|
|
97
|
+
float diffuse = max(dot(normal, light), 0.0);
|
|
98
|
+
float rim = pow(1.0 - max(dot(normal, -direction), 0.0), 2.4);
|
|
99
|
+
float bands = 0.5 + 0.5 * sin(atan(point.z, point.x) * 9.0 - u_time * 3.0);
|
|
100
|
+
vec3 neon = palette(bands + u_time * 0.06);
|
|
101
|
+
color += neon * (0.18 + diffuse * 0.65 + rim * 2.4);
|
|
102
|
+
color += pow(max(dot(reflect(-light, normal), -direction), 0.0), 32.0);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
float glowDistance = abs(length(uv) - 0.47 - sin(atan(uv.y, uv.x) * 7.0 + u_time) * 0.018);
|
|
106
|
+
color += palette(u_time * 0.07 + length(uv)) * 0.012 / max(glowDistance, 0.008);
|
|
107
|
+
|
|
108
|
+
color *= vignette;
|
|
109
|
+
color = pow(color, vec3(0.82));
|
|
110
|
+
frag_color = vec4(color, 1.0);
|
|
111
|
+
}
|
|
112
|
+
GLSLKIT_FRAGMENT_SRC
|
|
113
|
+
|
|
114
|
+
MANIFEST = {"schema_version"=>1,
|
|
115
|
+
"generated_at"=>"1970-01-01T00:00:00Z",
|
|
116
|
+
"generator"=>"glslkit/0.1.0.pre",
|
|
117
|
+
"programs"=>
|
|
118
|
+
{"neon-broken"=>
|
|
119
|
+
{"digest"=>
|
|
120
|
+
"623464715d1428d30dc76fde676b3d68e8362d199b2992afdbec9d755a1ac478",
|
|
121
|
+
"stages"=>
|
|
122
|
+
{"vertex"=>
|
|
123
|
+
{"path"=>"neon-broken.vert",
|
|
124
|
+
"digest"=>
|
|
125
|
+
"9cf045b60aee6e157a1b7b83b2ce299dd479f8d2d16d2418914db2fedeb37627",
|
|
126
|
+
"url"=>"neon-broken.vert"},
|
|
127
|
+
"fragment"=>
|
|
128
|
+
{"path"=>"neon-broken.frag",
|
|
129
|
+
"digest"=>
|
|
130
|
+
"ed19c9ae89d14241a3e5f0ec39dbed94a70b4666e5eeb468d9d618b388194a15",
|
|
131
|
+
"url"=>"neon-broken.frag"}},
|
|
132
|
+
"attributes"=>
|
|
133
|
+
[{"name"=>"a_position", "type"=>"vec2", "location"=>0, "array_size"=>1}],
|
|
134
|
+
"uniforms"=>
|
|
135
|
+
[{"name"=>"u_time",
|
|
136
|
+
"type"=>"float",
|
|
137
|
+
"array_size"=>1,
|
|
138
|
+
"setter"=>"uniform1fv",
|
|
139
|
+
"matrix"=>false,
|
|
140
|
+
"sampler"=>false,
|
|
141
|
+
"components"=>1,
|
|
142
|
+
"element_count"=>1,
|
|
143
|
+
"stages"=>["fragment"]},
|
|
144
|
+
{"name"=>"u_resolution",
|
|
145
|
+
"type"=>"vec2",
|
|
146
|
+
"array_size"=>1,
|
|
147
|
+
"setter"=>"uniform2fv",
|
|
148
|
+
"matrix"=>false,
|
|
149
|
+
"sampler"=>false,
|
|
150
|
+
"components"=>2,
|
|
151
|
+
"element_count"=>2,
|
|
152
|
+
"stages"=>["fragment"]}],
|
|
153
|
+
"uniform_blocks"=>[],
|
|
154
|
+
"outputs"=>[{"name"=>"frag_color", "type"=>"vec4", "location"=>nil}]}}}.freeze
|
|
155
|
+
|
|
156
|
+
SOURCE_MAPS = {
|
|
157
|
+
vertex: {"version"=>1, "files"=>["neon-broken.vert"], "segments"=>[[2, 0, 2]]},
|
|
158
|
+
fragment: {"version"=>1,
|
|
159
|
+
"files"=>["neon-broken.frag", "common/sdf.glsl", "common/color.glsl"],
|
|
160
|
+
"segments"=>[[2, 0, 2], [10, 1, 2], [20, 2, 2], [24, 0, 12]]}
|
|
161
|
+
}.freeze
|
|
162
|
+
end
|
data/sample/index.html
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<title>glslkit-webgl textured cube</title>
|
|
6
|
+
<style>
|
|
7
|
+
body { margin: 0; min-height: 100vh; display: grid; place-items: center; background: #161923; }
|
|
8
|
+
canvas { width: min(80vw, 640px); height: min(80vw, 640px); border: 1px solid #3a4055; }
|
|
9
|
+
</style>
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<canvas id="canvas" width="640" height="640"></canvas>
|
|
13
|
+
<script src="../shim/glslkit-webgl.js" data-entry="./app.rb"></script>
|
|
14
|
+
</body>
|
|
15
|
+
</html>
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="ja">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>glslkit-webgl compile error demo</title>
|
|
7
|
+
<style>
|
|
8
|
+
* { box-sizing: border-box; }
|
|
9
|
+
body {
|
|
10
|
+
margin: 0; min-height: 100vh; display: flex; flex-direction: column; align-items: center;
|
|
11
|
+
gap: 20px; padding: 32px 16px;
|
|
12
|
+
background: radial-gradient(circle at 50% 15%, #1c1430, #05060c 70%);
|
|
13
|
+
font: 15px/1.6 system-ui, sans-serif; color: #ece9ff;
|
|
14
|
+
}
|
|
15
|
+
h1 {
|
|
16
|
+
font-size: 14px; letter-spacing: .1em; text-transform: uppercase; color: #b9a6ff;
|
|
17
|
+
margin: 0; text-align: center;
|
|
18
|
+
}
|
|
19
|
+
canvas {
|
|
20
|
+
width: min(70vw, 480px); height: min(70vw, 480px); border-radius: 14px;
|
|
21
|
+
box-shadow: 0 0 60px #6b2bff22, inset 0 0 0 1px #ffffff14; background: #0a0a12;
|
|
22
|
+
}
|
|
23
|
+
#status { font-size: 13px; color: #8f88b8; min-height: 1.2em; text-align: center; }
|
|
24
|
+
#panel {
|
|
25
|
+
width: min(92vw, 640px); border: 1px solid #3a2f66; border-radius: 12px;
|
|
26
|
+
background: #12101f; padding: 20px 24px; display: none;
|
|
27
|
+
}
|
|
28
|
+
#panel.visible { display: block; }
|
|
29
|
+
#panel h2 {
|
|
30
|
+
margin: 0 0 14px; font-size: 13px; color: #ff6b81; letter-spacing: .08em;
|
|
31
|
+
text-transform: uppercase;
|
|
32
|
+
}
|
|
33
|
+
.row { display: flex; gap: 12px; margin: 8px 0; font-size: 14px; align-items: baseline; }
|
|
34
|
+
.row .label {
|
|
35
|
+
width: 64px; flex: none; color: #8f88b8; font-size: 11px; letter-spacing: .06em;
|
|
36
|
+
text-transform: uppercase;
|
|
37
|
+
}
|
|
38
|
+
.row .value { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
|
|
39
|
+
#err-file { color: #ffd166; font-weight: 700; font-size: 16px; }
|
|
40
|
+
/* 抜粋が取得できたときだけ枠を出す(fetch失敗時に空の帯が残らないように)。 */
|
|
41
|
+
#excerpt.has-content {
|
|
42
|
+
margin-top: 14px; background: #0a0a12; border-radius: 8px; padding: 10px 0;
|
|
43
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12.5px;
|
|
44
|
+
color: #a9a3d6; overflow-x: auto;
|
|
45
|
+
}
|
|
46
|
+
#excerpt .line { display: block; padding: 1px 14px; white-space: pre; }
|
|
47
|
+
#excerpt .line.error-line { background: #4a1030; color: #ffb3c6; }
|
|
48
|
+
details { margin-top: 14px; }
|
|
49
|
+
summary { cursor: pointer; color: #8f88b8; font-size: 12.5px; }
|
|
50
|
+
#err-raw-log {
|
|
51
|
+
white-space: pre-wrap; background: #0a0a12; border-radius: 8px; padding: 12px;
|
|
52
|
+
margin-top: 8px; font-size: 12px; color: #8f88b8; overflow-x: auto;
|
|
53
|
+
}
|
|
54
|
+
button {
|
|
55
|
+
background: #6b2bff; color: white; border: none; border-radius: 8px; padding: 10px 20px;
|
|
56
|
+
font: inherit; font-weight: 600; cursor: pointer; margin-top: 18px;
|
|
57
|
+
}
|
|
58
|
+
button:hover { background: #7c40ff; }
|
|
59
|
+
</style>
|
|
60
|
+
</head>
|
|
61
|
+
<body>
|
|
62
|
+
<h1>glslkit-webgl · compile error → original file & line</h1>
|
|
63
|
+
<canvas id="canvas" width="480" height="480"></canvas>
|
|
64
|
+
<p id="status">読み込み中…</p>
|
|
65
|
+
|
|
66
|
+
<div id="panel">
|
|
67
|
+
<h2>Shader compile error</h2>
|
|
68
|
+
<div class="row"><span class="label">stage</span><span class="value" id="err-stage"></span></div>
|
|
69
|
+
<div class="row"><span class="label">file</span><span class="value" id="err-file"></span></div>
|
|
70
|
+
<div class="row"><span class="label">line</span><span class="value" id="err-line"></span></div>
|
|
71
|
+
<div class="row"><span class="label">message</span><span class="value" id="err-message"></span></div>
|
|
72
|
+
<div id="excerpt"></div>
|
|
73
|
+
<details>
|
|
74
|
+
<summary>raw compiler log</summary>
|
|
75
|
+
<pre id="err-raw-log"></pre>
|
|
76
|
+
</details>
|
|
77
|
+
<button id="retry">正常版に切り替えて描画する</button>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
<script src="../shim/glslkit-webgl.js" data-entry="./neon-error.rb"></script>
|
|
81
|
+
</body>
|
|
82
|
+
</html>
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# M10d: シェーダのコンパイルエラーが、includeされた元ファイルの名前と行番号を
|
|
4
|
+
# 指したままRubyの例外として上がってくることを見せるデモ。
|
|
5
|
+
#
|
|
6
|
+
# 壊れているのは common/sdf.glsl 側(webgl/sample/shaders-broken/)であり、
|
|
7
|
+
# neon.frag 直下ではない — エラーメッセージが「どのファイルの include 先が
|
|
8
|
+
# 壊れているか」まで教えてくれることが本gemの中核価値なので、あえて
|
|
9
|
+
# include の奥で壊している。
|
|
10
|
+
require_relative "../lib/glslkit/webgl"
|
|
11
|
+
require_relative "error_panel"
|
|
12
|
+
require_relative "generated-broken/neon-broken_shaders"
|
|
13
|
+
require_relative "generated/neon_shaders"
|
|
14
|
+
|
|
15
|
+
document = JS.global[:document]
|
|
16
|
+
|
|
17
|
+
set_text = ->(id, value) { document.call(:getElementById, id)[:textContent] = value.to_s }
|
|
18
|
+
set_status = ->(value) { set_text.call("status", value) }
|
|
19
|
+
|
|
20
|
+
build_program = ->(ctx, shaders, name) {
|
|
21
|
+
ctx.program(Glslkit::Manifest.parse(shaders::MANIFEST), name,
|
|
22
|
+
vertex: shaders::VERTEX, fragment: shaders::FRAGMENT,
|
|
23
|
+
source_maps: {
|
|
24
|
+
vertex: Glslkit::SourceMap.from_h(shaders::SOURCE_MAPS[:vertex]),
|
|
25
|
+
fragment: Glslkit::SourceMap.from_h(shaders::SOURCE_MAPS[:fragment])
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
fullscreen_triangle = {a_position: {data: [-1.0, -1.0, 3.0, -1.0, -1.0, 3.0], components: 2}}
|
|
30
|
+
|
|
31
|
+
start_render_loop = ->(ctx, program, geometry) {
|
|
32
|
+
ctx.viewport
|
|
33
|
+
program.set(:u_resolution, [ctx.width.to_f, ctx.height.to_f])
|
|
34
|
+
ctx.loop do |seconds|
|
|
35
|
+
program.set(:u_time, seconds)
|
|
36
|
+
ctx.draw(geometry)
|
|
37
|
+
end
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# 壊れた行の前後を、includeされた元ファイル(common/sdf.glsl)自体から
|
|
41
|
+
# fetchして見せる(参考情報。任意)。file:// で開く等でfetchが失敗しても
|
|
42
|
+
# パネル本体(file/line/message/raw_log)の表示は壊さない。
|
|
43
|
+
#
|
|
44
|
+
# M11: 元は .await ベースだったが、spike/09-fetch.htmlの実機確認で
|
|
45
|
+
# 「JS::Object#await は RubyVM#evalAsync/RbValue#callAsync からしか呼べない」
|
|
46
|
+
# ことが判明した。この.rbは webgl/shim/glslkit-webgl.js
|
|
47
|
+
# (JS::RequireRemote.instance.load 経由)でロードされ、evalAsyncされているのは
|
|
48
|
+
# そのロード処理自体であって、ロードされたこのファイルのトップレベルは
|
|
49
|
+
# その対象に含まれない — そのため.awaitは常に失敗し、ここは(rescueで
|
|
50
|
+
# 握り潰されて)一度も動いていなかった可能性が高い。.then + Procに書き換える。
|
|
51
|
+
#
|
|
52
|
+
# M11f: 「参考情報なので握り潰す」自体は変えないが、Glslkit::WebGL.debugが
|
|
53
|
+
# trueのときだけconsoleに理由を出すようにする。M10dの見た目上の不具合が
|
|
54
|
+
# 実は.awaitの失敗だったのに気づけなかった反省(このファイル自身の履歴)。
|
|
55
|
+
debug_warn = ->(reason) { JS.global[:console].call(:warn, "[neon-error] source excerpt skipped: #{reason}") if Glslkit::WebGL.debug }
|
|
56
|
+
|
|
57
|
+
show_source_excerpt = ->(file, line) do
|
|
58
|
+
next unless line
|
|
59
|
+
|
|
60
|
+
on_response = proc do |response|
|
|
61
|
+
if response[:ok] == JS::True
|
|
62
|
+
response.call(:text)
|
|
63
|
+
else
|
|
64
|
+
debug_warn.call("fetch #{file} responded with status=#{response[:status]}")
|
|
65
|
+
nil
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
on_text = proc do |text|
|
|
69
|
+
next if text.nil?
|
|
70
|
+
|
|
71
|
+
lines = text.to_s.split("\n", -1)
|
|
72
|
+
from = [line - 3, 0].max
|
|
73
|
+
to = [line + 2, lines.length - 1].min
|
|
74
|
+
|
|
75
|
+
container = document.call(:getElementById, "excerpt")
|
|
76
|
+
(from..to).each do |index|
|
|
77
|
+
line_number = index + 1
|
|
78
|
+
span = document.call(:createElement, "span")
|
|
79
|
+
span[:className] = (line_number == line) ? "line error-line" : "line"
|
|
80
|
+
span[:textContent] = format("%3d| %s", line_number, lines[index])
|
|
81
|
+
container.call(:appendChild, span)
|
|
82
|
+
end
|
|
83
|
+
# fetchが成功して実際に行を追加できたときだけ枠を見せる。file://で開いた
|
|
84
|
+
# 場合などfetchが失敗すると、ここに到達せず空の帯が残ることはない。
|
|
85
|
+
container[:className] = "has-content"
|
|
86
|
+
end
|
|
87
|
+
on_rejected = proc { |error| debug_warn.call("fetch #{file} rejected: #{error}") }
|
|
88
|
+
JS.global.fetch(file).call(:then, on_response).call(:then, on_text).call(:catch, on_rejected)
|
|
89
|
+
rescue => e
|
|
90
|
+
debug_warn.call("synchronous error before fetch: #{e.class}: #{e.message}")
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
document.call(:getElementById, "retry").call(:addEventListener, "click") do
|
|
94
|
+
ctx = Glslkit::WebGL.context("#canvas")
|
|
95
|
+
program = build_program.call(ctx, NeonShaders, "neon")
|
|
96
|
+
geometry = ctx.geometry(program: program, attributes: fullscreen_triangle)
|
|
97
|
+
|
|
98
|
+
document.call(:getElementById, "panel")[:className] = ""
|
|
99
|
+
set_status.call("正常版で描画中です。")
|
|
100
|
+
start_render_loop.call(ctx, program, geometry)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
begin
|
|
104
|
+
ctx = Glslkit::WebGL.context("#canvas")
|
|
105
|
+
build_program.call(ctx, NeonBrokenShaders, "neon-broken")
|
|
106
|
+
set_status.call("想定外: 壊れているはずのシェーダが正常にコンパイルされました。")
|
|
107
|
+
rescue Glslkit::WebGL::CompileError => error
|
|
108
|
+
described = NeonErrorPanel.describe(error)
|
|
109
|
+
|
|
110
|
+
set_text.call("err-stage", described[:stage])
|
|
111
|
+
set_text.call("err-file", described[:file])
|
|
112
|
+
set_text.call("err-line", described[:line])
|
|
113
|
+
set_text.call("err-message", described[:message])
|
|
114
|
+
set_text.call("err-raw-log", described[:raw_log])
|
|
115
|
+
document.call(:getElementById, "panel")[:className] = "visible"
|
|
116
|
+
set_status.call("意図的に壊したシェーダの読み込みに失敗しました。")
|
|
117
|
+
|
|
118
|
+
show_source_excerpt.call(error.file, error.line) if described[:resolved]
|
|
119
|
+
end
|
data/sample/neon.html
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="ja">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>glslkit-webgl neon portal</title>
|
|
7
|
+
<style>
|
|
8
|
+
* { box-sizing: border-box; }
|
|
9
|
+
body { margin: 0; min-height: 100vh; display: grid; place-items: center; overflow: hidden;
|
|
10
|
+
background: radial-gradient(circle at 50% 35%, #17112d, #05060c 65%); }
|
|
11
|
+
canvas { width: min(92vw, 900px); height: min(92vh, 900px); border-radius: 18px;
|
|
12
|
+
box-shadow: 0 0 80px #753cff33, inset 0 0 0 1px #ffffff18; }
|
|
13
|
+
aside { position: fixed; left: 24px; bottom: 20px; color: #dcd7ff;
|
|
14
|
+
font: 500 13px/1.5 system-ui, sans-serif; letter-spacing: .08em; opacity: .7; }
|
|
15
|
+
</style>
|
|
16
|
+
</head>
|
|
17
|
+
<body>
|
|
18
|
+
<canvas id="canvas" width="900" height="900"></canvas>
|
|
19
|
+
<aside>GLSLKIT · RUBY.WASM · WEBGL2</aside>
|
|
20
|
+
<script src="../shim/glslkit-webgl.js" data-entry="./neon.rb"></script>
|
|
21
|
+
</body>
|
|
22
|
+
</html>
|
data/sample/neon.rb
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../lib/glslkit/webgl"
|
|
4
|
+
require_relative "generated/neon_shaders"
|
|
5
|
+
|
|
6
|
+
ctx = Glslkit::WebGL.context("#canvas")
|
|
7
|
+
program = ctx.program(Glslkit::Manifest.parse(NeonShaders::MANIFEST), "neon",
|
|
8
|
+
vertex: NeonShaders::VERTEX, fragment: NeonShaders::FRAGMENT,
|
|
9
|
+
source_maps: {
|
|
10
|
+
vertex: Glslkit::SourceMap.from_h(NeonShaders::SOURCE_MAPS[:vertex]),
|
|
11
|
+
fragment: Glslkit::SourceMap.from_h(NeonShaders::SOURCE_MAPS[:fragment])
|
|
12
|
+
})
|
|
13
|
+
screen = ctx.geometry(program: program, attributes: {
|
|
14
|
+
a_position: {data: [-1.0, -1.0, 3.0, -1.0, -1.0, 3.0], components: 2}
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
ctx.viewport
|
|
18
|
+
program.set(:u_resolution, [ctx.width.to_f, ctx.height.to_f])
|
|
19
|
+
ctx.loop do |seconds|
|
|
20
|
+
program.set(:u_time, seconds)
|
|
21
|
+
ctx.draw(screen)
|
|
22
|
+
end
|