rbgl 1.0.0 → 1.0.2

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.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/.codespellignore +4 -0
  3. data/.rubocop.yml +3 -0
  4. data/.yamllint +16 -0
  5. data/CHANGELOG.md +11 -1
  6. data/README.md +130 -74
  7. data/Rakefile +7 -0
  8. data/bench/renderer_bench.rb +283 -0
  9. data/docs/.nojekyll +1 -0
  10. data/docs/index.html +38 -0
  11. data/examples/array_test.rb +1 -1
  12. data/examples/color_test.rb +1 -1
  13. data/examples/cube_spinning.rb +1 -1
  14. data/examples/gradient.rb +1 -1
  15. data/examples/multi_return_test.rb +1 -1
  16. data/examples/plasma.rb +1 -1
  17. data/examples/sphere_raymarch.rb +1 -1
  18. data/examples/triangle_window.rb +1 -1
  19. data/exe/rbgl +42 -0
  20. data/lib/rbgl/cli/doctor.rb +155 -0
  21. data/lib/rbgl/engine/buffer.rb +55 -9
  22. data/lib/rbgl/engine/clip_space_clipper.rb +23 -3
  23. data/lib/rbgl/engine/context.rb +23 -6
  24. data/lib/rbgl/engine/framebuffer.rb +146 -41
  25. data/lib/rbgl/engine/immutable_color.rb +32 -0
  26. data/lib/rbgl/engine/rasterizer/attribute_interpolator.rb +100 -10
  27. data/lib/rbgl/engine/rasterizer/line_renderer.rb +30 -14
  28. data/lib/rbgl/engine/rasterizer/point_renderer.rb +3 -3
  29. data/lib/rbgl/engine/rasterizer/triangle_renderer.rb +72 -23
  30. data/lib/rbgl/engine/rasterizer.rb +3 -20
  31. data/lib/rbgl/engine/shader/base_shader.rb +3 -7
  32. data/lib/rbgl/engine/shader/builtins.rb +13 -10
  33. data/lib/rbgl/engine/shader/dynamic_data.rb +18 -10
  34. data/lib/rbgl/engine/texture.rb +74 -50
  35. data/lib/rbgl/engine.rb +3 -1
  36. data/lib/rbgl/gui/backend.rb +12 -7
  37. data/lib/rbgl/gui/backend_factory.rb +7 -3
  38. data/lib/rbgl/gui/cocoa/backend.rb +50 -17
  39. data/lib/rbgl/gui/cocoa/key_mapper.rb +48 -0
  40. data/lib/rbgl/gui/event.rb +4 -2
  41. data/lib/rbgl/gui/file_backend/bmp_writer.rb +2 -2
  42. data/lib/rbgl/gui/file_backend/frame_writer.rb +7 -0
  43. data/lib/rbgl/gui/file_backend/png_writer.rb +18 -0
  44. data/lib/rbgl/gui/file_backend/ppm_writer.rb +1 -0
  45. data/lib/rbgl/gui/file_backend.rb +24 -6
  46. data/lib/rbgl/gui/wayland/backend.rb +127 -55
  47. data/lib/rbgl/gui/wayland/codec.rb +7 -11
  48. data/lib/rbgl/gui/wayland/connection.rb +137 -20
  49. data/lib/rbgl/gui/wayland/event_dispatcher.rb +103 -12
  50. data/lib/rbgl/gui/wayland/global_binder.rb +6 -4
  51. data/lib/rbgl/gui/wayland/input_mapper.rb +61 -0
  52. data/lib/rbgl/gui/wayland/protocol_objects/arguments.rb +14 -2
  53. data/lib/rbgl/gui/wayland/protocol_objects/seat.rb +115 -0
  54. data/lib/rbgl/gui/wayland/protocol_objects/shm.rb +23 -12
  55. data/lib/rbgl/gui/wayland/protocol_objects/xdg_shell.rb +15 -0
  56. data/lib/rbgl/gui/wayland/protocol_objects.rb +1 -0
  57. data/lib/rbgl/gui/window/render_loop.rb +71 -18
  58. data/lib/rbgl/gui/window.rb +39 -8
  59. data/lib/rbgl/gui/x11/atom_cache.rb +4 -1
  60. data/lib/rbgl/gui/x11/backend.rb +66 -28
  61. data/lib/rbgl/gui/x11/connection.rb +216 -86
  62. data/lib/rbgl/gui/x11/event_parser.rb +13 -2
  63. data/lib/rbgl/gui/x11/key_mapper.rb +48 -0
  64. data/lib/rbgl/gui/x11/pixel_encoder.rb +164 -0
  65. data/lib/rbgl/gui/x11/request_encoder.rb +29 -28
  66. data/lib/rbgl/gui/x11/setup_parser.rb +138 -0
  67. data/lib/rbgl/gui/x11/transport.rb +52 -2
  68. data/lib/rbgl/gui/x11/x_authority.rb +107 -0
  69. data/lib/rbgl/gui.rb +2 -5
  70. data/lib/rbgl/version.rb +1 -1
  71. metadata +38 -24
  72. data/examples/chemical_heartbeat.rb +0 -166
  73. data/examples/dark_transit.rb +0 -166
  74. data/examples/fractured_orb.rb +0 -428
  75. data/examples/fractured_orb_rb.rb +0 -598
  76. data/examples/hexagonal_flow.rb +0 -333
  77. data/examples/teapot.rb +0 -362
  78. data/examples/teapot_mcu.rb +0 -344
@@ -1,428 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Fractured Orb (Metal Compute Shader)
4
- # Original: https://www.shadertoy.com/view/ttycWW
5
- # A mashup of 'Crystal Tetrahedron' and 'Buckyball Fracture'
6
- # License: Creative Commons Attribution-NonCommercial-ShareAlike 3.0
7
- # https://creativecommons.org/licenses/by-nc-sa/3.0/deed.en
8
-
9
- $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
10
-
11
- require "rbgl"
12
- require "rlsl"
13
-
14
- WIDTH = 640
15
- HEIGHT = 480
16
-
17
- shader = RLSL.define_metal(:fractured_orb_metal) do
18
- uniforms do
19
- float :time
20
- float :rand_seed
21
- end
22
-
23
- helpers(:c) do
24
- <<~MSL
25
- #define PHI 1.618033988749895f
26
- #define PI 3.14159265359f
27
- #define TAU 6.28318530718f
28
- #define MAX_DISPERSE 5
29
- #define MAX_BOUNCE 10
30
-
31
- constant float OUTER = 0.35f;
32
- constant float INNER = 0.24f;
33
-
34
- // pR - 2D rotation
35
- float2 pR(float2 p, float a) {
36
- float c = cos(a), s = sin(a);
37
- return float2(c * p.x + s * p.y, -s * p.x + c * p.y);
38
- }
39
-
40
- // smax - smooth maximum
41
- float smax(float a, float b, float r) {
42
- float ua = max(r + a, 0.0f);
43
- float ub = max(r + b, 0.0f);
44
- return min(-r, max(a, b)) + sqrt(ua * ua + ub * ub);
45
- }
46
-
47
- // vmax
48
- float vmax2(float2 v) { return max(v.x, v.y); }
49
- float vmax3(float3 v) { return max(max(v.x, v.y), v.z); }
50
-
51
- // fBox - box SDF
52
- float fBox2(float2 p, float2 b) {
53
- float2 d = abs(p) - b;
54
- return length(max(d, float2(0))) + vmax2(min(d, float2(0)));
55
- }
56
-
57
- float fBox3(float3 p, float3 b) {
58
- float3 d = abs(p) - b;
59
- return length(max(d, float3(0))) + vmax3(min(d, float3(0)));
60
- }
61
-
62
- // erot - rotate on axis
63
- float3 erot(float3 p, float3 ax, float ro) {
64
- float d = dot(ax, p);
65
- float3 dax = ax * d;
66
- float c = cos(ro), s = sin(ro);
67
- return mix(dax, p, c) + s * cross(ax, p);
68
- }
69
-
70
- // Spectrum palette (IQ)
71
- float3 spectrum(float n) {
72
- float3 a = float3(0.5f);
73
- float3 b = float3(0.5f);
74
- float3 c = float3(1.0f);
75
- float3 d = float3(0.0f, 0.33f, 0.67f);
76
- return a + b * cos(TAU * (c * n + d));
77
- }
78
-
79
- // expImpulse
80
- float expImpulse(float x, float k) {
81
- float h = k * x;
82
- return h * exp(1.0f - h);
83
- }
84
-
85
- // boolSign
86
- float boolSign(float v) {
87
- return v > 0.0f ? 1.0f : -1.0f;
88
- }
89
-
90
- float3 boolSign3(float3 v) {
91
- return float3(boolSign(v.x), boolSign(v.y), boolSign(v.z));
92
- }
93
-
94
- // Icosahedron vertex (optimized by iq)
95
- float3 icosahedronVertex(float3 p) {
96
- float3 ap = abs(p);
97
- float3 v = float3(PHI, 1.0f, 0.0f);
98
- if (ap.x + ap.z * PHI > dot(ap, v)) v = float3(1.0f, 0.0f, PHI);
99
- if (ap.z + ap.y * PHI > dot(ap, v)) v = float3(0.0f, PHI, 1.0f);
100
- return v * 0.52573111f * boolSign3(p);
101
- }
102
-
103
- // Dodecahedron vertex (optimized by iq)
104
- float3 dodecahedronVertex(float3 p) {
105
- float3 ap = abs(p);
106
- float3 v = float3(PHI);
107
- float3 v2 = float3(0.0f, 1.0f, PHI + 1.0f);
108
- float3 v3 = v2.yzx;
109
- float3 v4 = v2.zxy;
110
- if (dot(ap, v2) > dot(ap, v)) v = v2;
111
- if (dot(ap, v3) > dot(ap, v)) v = v3;
112
- if (dot(ap, v4) > dot(ap, v)) v = v4;
113
- return v * 0.35682209f * boolSign3(p);
114
- }
115
-
116
- // Object SDF
117
- float object_sdf(float3 p) {
118
- float d = length(p) - OUTER;
119
- d = max(d, -d - (OUTER - INNER));
120
- return d;
121
- }
122
-
123
- // Map function
124
- float2 map(float3 p, float anim_time) {
125
- float scale = 2.5f;
126
- p /= scale;
127
-
128
- float outerBound = length(p) - OUTER;
129
-
130
- float spin = anim_time * (PI / 2.0f) - 0.15f;
131
- p.xz = pR(p.xz, spin);
132
-
133
- // Buckyball faces
134
- float3 va = icosahedronVertex(p);
135
- float3 vb = dodecahedronVertex(p);
136
-
137
- float side = boolSign(dot(p, cross(va, vb)));
138
- float r = TAU / 5.0f * side;
139
- float3 vc = erot(vb, va, r);
140
- float3 vd = erot(vb, va, -r);
141
-
142
- float d = 1e12f;
143
- float3 pp = p;
144
-
145
- for (int i = 0; i < 4; i++) {
146
- // Animation
147
- float t = fmod(anim_time * 2.0f / 3.0f + 0.25f - dot(va.xy, float2(1.0f, -1.0f)) / 30.0f, 1.0f);
148
- if (t < 0.0f) t += 1.0f;
149
- float t2 = clamp(t * 5.0f - 1.7f, 0.0f, 1.0f);
150
- float explode = 1.0f - pow(1.0f - t2, 10.0f);
151
- explode *= 1.0f - pow(t2, 5.0f);
152
- explode += (smoothstep(0.32f, 0.34f, t) - smoothstep(0.34f, 0.5f, t)) * 0.05f;
153
- explode *= 1.4f;
154
- t2 = max(t - 0.53f, 0.0f) * 1.2f;
155
- float wobble = sin(expImpulse(t2, 20.0f) * 2.2f + pow(3.0f * t2, 1.5f) * 2.0f * TAU - PI) * smoothstep(0.4f, 0.0f, t2) * 0.2f;
156
- float anim = wobble + explode;
157
- p -= va * anim / 2.8f;
158
-
159
- // Build boundary edge of face
160
- float edgeA = dot(p, normalize(vb - va));
161
- float edgeB = dot(p, normalize(vc - va));
162
- float edgeC = dot(p, normalize(vd - va));
163
- float edge = max(max(edgeA, edgeB), edgeC) - 0.005f;
164
-
165
- d = min(d, smax(object_sdf(p), edge, 0.002f));
166
-
167
- p = pp;
168
-
169
- // Cycle faces
170
- float3 va2 = va;
171
- va = vb; vb = vc; vc = vd; vd = va2;
172
- }
173
-
174
- float bound = outerBound - 0.002f;
175
- if (bound * scale > 0.002f) {
176
- d = min(d, bound);
177
- }
178
-
179
- return float2(d * scale, 1.0f);
180
- }
181
-
182
- // Normal calculation
183
- float3 calc_normal(float3 pos, float anim_time) {
184
- float3 n = float3(0.0f);
185
- for (int i = 0; i < 4; i++) {
186
- float3 e = 0.5773f * (2.0f * float3((((i + 3) >> 1) & 1), ((i >> 1) & 1), (i & 1)) - 1.0f);
187
- n += e * map(pos + 0.001f * e, anim_time).x;
188
- }
189
- return normalize(n);
190
- }
191
-
192
- // Spherical matrix multiply
193
- float3 sphericalMatrix_mul(float2 tp, float3 v) {
194
- float theta = tp.x, phi = tp.y;
195
- float cx = cos(theta), cy = cos(phi);
196
- float sx = sin(theta), sy = sin(phi);
197
- return float3(
198
- cy * v.x + (-sy * -sx) * v.y + (-sy * cx) * v.z,
199
- cx * v.y + sx * v.z,
200
- sy * v.x + (cy * -sx) * v.y + (cy * cx) * v.z
201
- );
202
- }
203
-
204
- // Light function
205
- float3 light_func(float3 origin, float3 rayDir, float2 envOrientation) {
206
- origin = -origin;
207
- rayDir = -rayDir;
208
- origin = sphericalMatrix_mul(envOrientation, origin);
209
- rayDir = sphericalMatrix_mul(envOrientation, rayDir);
210
-
211
- float3 pos = float3(-6.0f);
212
- float3 normal = normalize(pos);
213
- float3 up = normalize(float3(-1.0f, 1.0f, 0.0f));
214
-
215
- float denom = dot(rayDir, normal);
216
- if (abs(denom) < 0.0001f) return float3(0.0f);
217
-
218
- float t = dot(pos - origin, normal) / denom;
219
- if (t < 0.0f) return float3(0.0f);
220
-
221
- float3 point = origin + t * rayDir - pos;
222
- float3 tangent = cross(normal, up);
223
- float3 bitangent = cross(normal, tangent);
224
- float2 uv_l = float2(dot(tangent, point), dot(bitangent, point));
225
-
226
- float l = smoothstep(0.75f, 0.0f, fBox2(uv_l, float2(0.5f, 2.0f)) - 1.0f);
227
- l *= smoothstep(6.0f, 0.0f, length(uv_l));
228
- return float3(l);
229
- }
230
-
231
- // Environment function
232
- float3 env_func(float3 origin, float3 rayDir, float2 envOrientation) {
233
- origin = -origin;
234
- rayDir = -rayDir;
235
- origin = sphericalMatrix_mul(envOrientation, origin);
236
- rayDir = sphericalMatrix_mul(envOrientation, rayDir);
237
-
238
- float l = smoothstep(0.0f, 1.7f, dot(rayDir, float3(0.5f, -0.3f, 1.0f))) * 0.4f;
239
- return float3(0.9f, 0.83f, 1.0f) * l;
240
- }
241
-
242
- // Simple hash
243
- float simple_hash(float seed) {
244
- return fract(sin(seed * 12.9898f) * 43758.5453f);
245
- }
246
- MSL
247
- end
248
-
249
- fragment do
250
- <<~MSL
251
- float duration = 10.0f / 3.0f;
252
- float anim_time = fmod(u.time / duration, 1.0f);
253
-
254
- float2 envOrientation = (float2(81.5f / 187.0f, 119.0f / 187.0f) * 2.0f - 1.0f) * 2.0f;
255
-
256
- // Shadertoy-style centered UV: (fragCoord - resolution/2) / resolution.y
257
- float2 screen_uv = uv - float2(resolution.x / resolution.y * 0.5f, 0.5f);
258
-
259
- float3 col = float3(0.0f);
260
- float3 BGCOL = float3(0.9f, 0.83f, 1.0f);
261
- float3 bgCol = BGCOL * 0.22f;
262
-
263
- float maxDist = 30.0f;
264
- float3 camOrigin = float3(0.0f, 0.0f, 25.0f);
265
- float3 camDir = normalize(float3(screen_uv * 0.168f, -1.0f));
266
-
267
- // First march for depth
268
- float firstLen = 0.0f;
269
- float firstResY = 0.0f;
270
- float3 firstP = camOrigin;
271
- {
272
- float len = 0.0f;
273
- float dist = 0.0f;
274
- for (int i = 0; i < 300; i++) {
275
- len += dist * 0.8f;
276
- float3 p = camOrigin + len * camDir;
277
- float2 res = map(p, anim_time);
278
- dist = res.x;
279
- if (dist < 0.001f) { firstResY = res.y; break; }
280
- if (len >= maxDist) { len = maxDist; firstResY = 0.0f; break; }
281
- }
282
- firstLen = len;
283
- firstP = camOrigin + len * camDir;
284
- }
285
-
286
- // Dispersion loop
287
- for (int disperse = 0; disperse < MAX_DISPERSE; disperse++) {
288
- float invert = 1.0f;
289
- float3 sam = float3(0.0f);
290
- float3 origin = camOrigin;
291
- float3 rayDir = camDir;
292
-
293
- float extinctionDist = 0.0f;
294
- float wavelength = float(disperse) / float(MAX_DISPERSE);
295
-
296
- float rand = simple_hash(u.rand_seed + float(disperse) * 0.1f + uv.x * 100.0f + uv.y * 1000.0f);
297
- wavelength += (rand * 2.0f - 1.0f) * 0.1f;
298
-
299
- int bounceCount = 0;
300
-
301
- for (int bounce = 0; bounce < MAX_BOUNCE; bounce++) {
302
- float hitLen, hitResY;
303
- float3 p;
304
-
305
- if (bounce == 0) {
306
- hitLen = firstLen;
307
- hitResY = firstResY;
308
- p = firstP;
309
- } else {
310
- // March
311
- float len = 0.0f;
312
- float dist = 0.0f;
313
- hitResY = 0.0f;
314
- for (int mi = 0; mi < 300; mi++) {
315
- len += dist;
316
- p = origin + len * rayDir;
317
- float2 res = map(p, anim_time);
318
- dist = res.x * invert;
319
- if (dist < 0.001f) { hitResY = res.y; break; }
320
- if (len >= maxDist / 2.0f) { len = maxDist / 2.0f; hitResY = 0.0f; break; }
321
- }
322
- hitLen = len;
323
- }
324
-
325
- if (invert < 0.0f) {
326
- extinctionDist += hitLen;
327
- }
328
-
329
- if (hitResY == 0.0f) break;
330
-
331
- float3 nor = calc_normal(p, anim_time) * invert;
332
- float3 ref = reflect(rayDir, nor);
333
-
334
- // Shade
335
- sam += light_func(p, ref, envOrientation) * 0.5f;
336
- float fresnel = 1.0f - abs(dot(rayDir, nor));
337
- sam += pow(fresnel, 5.0f) * 0.1f;
338
- sam *= float3(0.85f, 0.85f, 0.98f);
339
-
340
- // Refract
341
- float ior = mix(1.2f, 1.8f, wavelength);
342
- ior = invert < 0.0f ? ior : 1.0f / ior;
343
-
344
- float3 raf = refract(rayDir, nor, ior);
345
- bool tif = (length(raf) < 0.001f);
346
- rayDir = tif ? ref : raf;
347
-
348
- float offset = 0.01f / (abs(dot(rayDir, nor)) + 0.0001f);
349
- origin = p + offset * rayDir;
350
- invert *= -1.0f;
351
-
352
- bounceCount = bounce;
353
- }
354
-
355
- sam += (bounceCount == 0) ? bgCol : env_func(firstP, rayDir, envOrientation);
356
-
357
- if (bounceCount == 0) {
358
- col += sam * float(MAX_DISPERSE) / 2.0f;
359
- break;
360
- } else {
361
- float3 spec = spectrum(-wavelength + 0.25f);
362
- col += sam * spec;
363
- }
364
- }
365
-
366
- col /= float(MAX_DISPERSE);
367
-
368
- // Tonemap
369
- col = pow(col, float3(1.25f)) * 2.5f;
370
- col = col / 2.0f * 16.0f;
371
- col = max(float3(0.0f), col - 0.004f);
372
- col = (col * (6.2f * col + 0.5f)) / (col * (6.2f * col + 1.7f) + 0.06f);
373
-
374
- return col;
375
- MSL
376
- end
377
- end
378
-
379
- # Initialize display
380
- window = RBGL::GUI::Window.new(width: WIDTH, height: HEIGHT, title: "Fractured Orb (Metal)")
381
-
382
- unless window.metal_available?
383
- puts "Metal is not available!"
384
- exit 1
385
- end
386
-
387
- puts "Fractured Orb"
388
- puts "Original: https://www.shadertoy.com/view/ttycWW"
389
- puts "License: Creative Commons Attribution-NonCommercial-ShareAlike 3.0"
390
- puts "https://creativecommons.org/licenses/by-nc-sa/3.0/deed.en"
391
- puts "Press 'q' or Escape to quit"
392
-
393
- start_time = Time.now
394
- frame_count = 0
395
- last_fps_time = start_time
396
- running = true
397
-
398
- while running && !window.should_close?
399
- time = Time.now - start_time
400
- rand_seed = rand * 1000.0
401
-
402
- begin
403
- shader.render_metal(window.native_handle, WIDTH, HEIGHT, { time: time, rand_seed: rand_seed })
404
- rescue => e
405
- puts "Error: #{e.message}"
406
- puts e.backtrace.first(10).join("\n")
407
- running = false
408
- break
409
- end
410
-
411
- events = window.poll_events_raw
412
- events.each do |e|
413
- if e[:type] == :key_press && (e[:key] == 12 || e[:key] == "q")
414
- running = false
415
- end
416
- end
417
-
418
- frame_count += 1
419
- now = Time.now
420
- if now - last_fps_time >= 1.0
421
- fps = frame_count / (now - last_fps_time)
422
- puts "FPS: #{fps.round(1)}"
423
- frame_count = 0
424
- last_fps_time = now
425
- end
426
- end
427
-
428
- window.close