rbgl 0.1.0 → 1.0.1

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 (81) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -0
  3. data/CHANGELOG.md +20 -2
  4. data/README.md +130 -74
  5. data/Rakefile +7 -0
  6. data/bench/renderer_bench.rb +283 -0
  7. data/docs/.nojekyll +1 -0
  8. data/docs/index.html +38 -0
  9. data/examples/array_test.rb +1 -1
  10. data/examples/color_test.rb +1 -1
  11. data/examples/cube_spinning.rb +1 -1
  12. data/examples/gradient.rb +1 -1
  13. data/examples/multi_return_test.rb +1 -1
  14. data/examples/plasma.rb +1 -1
  15. data/examples/sphere_raymarch.rb +1 -1
  16. data/examples/triangle_window.rb +1 -1
  17. data/exe/rbgl +42 -0
  18. data/lib/rbgl/cli/doctor.rb +155 -0
  19. data/lib/rbgl/engine/buffer.rb +150 -40
  20. data/lib/rbgl/engine/clip_space_clipper.rb +163 -0
  21. data/lib/rbgl/engine/context.rb +133 -63
  22. data/lib/rbgl/engine/framebuffer.rb +153 -48
  23. data/lib/rbgl/engine/immutable_color.rb +32 -0
  24. data/lib/rbgl/engine/pipeline.rb +38 -7
  25. data/lib/rbgl/engine/rasterizer/attribute_interpolator.rb +195 -0
  26. data/lib/rbgl/engine/rasterizer/line_renderer.rb +88 -0
  27. data/lib/rbgl/engine/rasterizer/point_renderer.rb +35 -0
  28. data/lib/rbgl/engine/rasterizer/triangle_renderer.rb +136 -0
  29. data/lib/rbgl/engine/rasterizer.rb +65 -171
  30. data/lib/rbgl/engine/shader/base_shader.rb +51 -0
  31. data/lib/rbgl/engine/shader/builtins.rb +257 -0
  32. data/lib/rbgl/engine/shader/dynamic_data.rb +73 -0
  33. data/lib/rbgl/engine/shader.rb +5 -318
  34. data/lib/rbgl/engine/texture.rb +264 -51
  35. data/lib/rbgl/engine.rb +4 -1
  36. data/lib/rbgl/gui/backend.rb +21 -34
  37. data/lib/rbgl/gui/backend_factory.rb +89 -0
  38. data/lib/rbgl/gui/cocoa/backend.rb +59 -46
  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 +63 -0
  42. data/lib/rbgl/gui/file_backend/frame_writer.rb +35 -0
  43. data/lib/rbgl/gui/file_backend/png_writer.rb +18 -0
  44. data/lib/rbgl/gui/file_backend/ppm_writer.rb +26 -0
  45. data/lib/rbgl/gui/file_backend.rb +50 -55
  46. data/lib/rbgl/gui/wayland/backend.rb +214 -60
  47. data/lib/rbgl/gui/wayland/codec.rb +50 -0
  48. data/lib/rbgl/gui/wayland/connection.rb +188 -231
  49. data/lib/rbgl/gui/wayland/event_dispatcher.rb +165 -0
  50. data/lib/rbgl/gui/wayland/global_binder.rb +46 -0
  51. data/lib/rbgl/gui/wayland/input_mapper.rb +61 -0
  52. data/lib/rbgl/gui/wayland/protocol_objects/arguments.rb +63 -0
  53. data/lib/rbgl/gui/wayland/protocol_objects/display.rb +54 -0
  54. data/lib/rbgl/gui/wayland/protocol_objects/seat.rb +115 -0
  55. data/lib/rbgl/gui/wayland/protocol_objects/shm.rb +157 -0
  56. data/lib/rbgl/gui/wayland/protocol_objects/surface.rb +33 -0
  57. data/lib/rbgl/gui/wayland/protocol_objects/xdg_shell.rb +60 -0
  58. data/lib/rbgl/gui/wayland/protocol_objects.rb +8 -0
  59. data/lib/rbgl/gui/window/event_dispatcher.rb +30 -0
  60. data/lib/rbgl/gui/window/render_loop.rb +111 -0
  61. data/lib/rbgl/gui/window.rb +74 -84
  62. data/lib/rbgl/gui/x11/atom_cache.rb +29 -0
  63. data/lib/rbgl/gui/x11/backend.rb +73 -49
  64. data/lib/rbgl/gui/x11/connection.rb +284 -219
  65. data/lib/rbgl/gui/x11/event_parser.rb +71 -0
  66. data/lib/rbgl/gui/x11/key_mapper.rb +48 -0
  67. data/lib/rbgl/gui/x11/pixel_encoder.rb +164 -0
  68. data/lib/rbgl/gui/x11/request_encoder.rb +155 -0
  69. data/lib/rbgl/gui/x11/setup_parser.rb +138 -0
  70. data/lib/rbgl/gui/x11/transport.rb +81 -0
  71. data/lib/rbgl/gui/x11/x_authority.rb +107 -0
  72. data/lib/rbgl/gui.rb +3 -5
  73. data/lib/rbgl/version.rb +1 -1
  74. metadata +63 -24
  75. data/examples/chemical_heartbeat.rb +0 -166
  76. data/examples/dark_transit.rb +0 -166
  77. data/examples/fractured_orb.rb +0 -428
  78. data/examples/fractured_orb_rb.rb +0 -598
  79. data/examples/hexagonal_flow.rb +0 -333
  80. data/examples/teapot.rb +0 -362
  81. data/examples/teapot_mcu.rb +0 -344
@@ -1,344 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Teapot - Raymarched Bezier Curves (Metal Compute Shader)
4
- # Original: https://www.shadertoy.com/view/MdKcDw
5
- # GPU-accelerated version
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
- # Generate control points as Metal constants
18
- def generate_teapot_constants
19
- body = [
20
- [0.0, 0.0], [0.64, 0.0], [0.64, 0.03],
21
- [0.8, 0.12], [0.8, 0.3], [0.8, 0.48],
22
- [0.64, 0.9], [0.6, 0.93], [0.56, 0.9],
23
- [0.56, 0.96], [0.12, 1.02], [0.0, 1.05],
24
- [0.16, 1.14], [0.2, 1.2], [0.0, 1.2]
25
- ]
26
- spout = [
27
- [1.16, 0.96], [1.04, 0.9], [1.0, 0.72],
28
- [0.92, 0.48], [0.72, 0.42]
29
- ]
30
- handle = [
31
- [-0.6, 0.78], [-1.16, 0.84], [-1.16, 0.63],
32
- [-1.2, 0.42], [-0.72, 0.24]
33
- ]
34
-
35
- body_init = body.map.with_index { |(x, y), i| "constant float2 A#{i} = float2(#{x}f, #{y}f);" }.join("\n")
36
- spout_init = spout.map.with_index { |(x, y), i| "constant float2 T1_#{i} = float2(#{x}f, #{y}f);" }.join("\n")
37
- handle_init = handle.map.with_index { |(x, y), i| "constant float2 T2_#{i} = float2(#{x}f, #{y}f);" }.join("\n")
38
-
39
- <<~MSL
40
- // Control points as constants
41
- #{body_init}
42
- #{spout_init}
43
- #{handle_init}
44
-
45
- // Pre-computed: normalize(float3(1.0f, 0.72f, 1.0f))
46
- constant float3 L = float3(0.6302f, 0.4537f, 0.6302f);
47
- constant float3 Y_VEC = float3(0.0f, 1.0f, 0.0f);
48
-
49
- // Material properties
50
- constant float2 lo = float2(0.450f, 0.048f);
51
- constant float2 alpha_m = float2(0.045f, 0.068f);
52
- constant float3 Scale = float3(1.0f, 20.0f, 10.0f);
53
- constant float3 surfaceColor = float3(0.45f, 0.54f, 1.0f);
54
- constant float ONE_OVER_PI = 0.31830988618f;
55
- MSL
56
- end
57
-
58
- shader = RLSL.define_metal(:teapot_metal) do
59
- uniforms do
60
- float :time
61
- vec2 :mouse
62
- end
63
-
64
- helpers(:c) do
65
- teapot_constants = generate_teapot_constants
66
-
67
- <<~MSL
68
- #{teapot_constants}
69
-
70
- // Cross product 2D
71
- #define U(a,b) ((a).x*(b).y-(b).x*(a).y)
72
-
73
- // Distance to quadratic Bezier curve
74
- float2 bezier_dist(float2 m, float2 n, float2 o, float3 p) {
75
- float2 q = float2(p.x, p.y);
76
- m = m - q;
77
- n = n - q;
78
- o = o - q;
79
-
80
- float x = U(m, o);
81
- float y = 2.0f * U(n, m);
82
- float z = 2.0f * U(o, n);
83
-
84
- float2 i = o - m;
85
- float2 j = o - n;
86
- float2 k = n - m;
87
-
88
- float2 s = float2(
89
- 2.0f * (x * i.x + y * j.x + z * k.x),
90
- 2.0f * (x * i.y + y * j.y + z * k.y)
91
- );
92
-
93
- float dot_s = dot(s, s);
94
- if (dot_s < 1e-10f) dot_s = 1e-10f;
95
-
96
- float2 r = float2(
97
- m.x + (y * z - x * x) * s.y / dot_s,
98
- m.y - (y * z - x * x) * s.x / dot_s
99
- );
100
-
101
- float denom = x + x + y + z;
102
- if (abs(denom) < 1e-10f) denom = 1e-10f;
103
-
104
- float t = clamp((U(r, i) + 2.0f * U(k, r)) / denom, 0.0f, 1.0f);
105
-
106
- float2 jk = j - k;
107
- r.x = m.x + t * (k.x + k.x + t * jk.x);
108
- r.y = m.y + t * (k.y + k.y + t * jk.y);
109
-
110
- return float2(sqrt(dot(r, r) + p.z * p.z), t);
111
- }
112
-
113
- // Smooth minimum for blending
114
- float smin(float a, float b, float k) {
115
- float h = clamp(0.5f + 0.5f * (b - a) / k, 0.0f, 1.0f);
116
- return mix(b, a, h) - k * h * (1.0f - h);
117
- }
118
-
119
- // Scene distance function
120
- float scene_dist(float3 p) {
121
- // Spout
122
- float2 h = bezier_dist(T1_2, T1_3, T1_4, p);
123
-
124
- // Handle
125
- float handle_d = min(
126
- bezier_dist(T2_0, T2_1, T2_2, p).x,
127
- bezier_dist(T2_2, T2_3, T2_4, p).x
128
- ) - 0.06f;
129
-
130
- // Spout
131
- float spout_hole = abs(bezier_dist(T1_0, T1_1, T1_2, p).x - 0.07f) - 0.01f;
132
- float spout_body = h.x * (1.0f - 0.75f * h.y) - 0.08f;
133
- float spout_d = max(p.y - 0.9f, min(spout_hole, spout_body));
134
-
135
- float b = min(handle_d, spout_d);
136
-
137
- // Body (rotation symmetry)
138
- float r_xz = sqrt(p.x * p.x + p.z * p.z);
139
- float3 qq = float3(r_xz, p.y, 0.0f);
140
-
141
- float a = 99.0f;
142
- a = min(a, (bezier_dist(A0, A1, A2, qq).x - 0.015f) * 0.7f);
143
- a = min(a, (bezier_dist(A2, A3, A4, qq).x - 0.015f) * 0.7f);
144
- a = min(a, (bezier_dist(A4, A5, A6, qq).x - 0.015f) * 0.7f);
145
- a = min(a, (bezier_dist(A6, A7, A8, qq).x - 0.015f) * 0.7f);
146
- a = min(a, (bezier_dist(A8, A9, A10, qq).x - 0.015f) * 0.7f);
147
- a = min(a, (bezier_dist(A10, A11, A12, qq).x - 0.015f) * 0.7f);
148
- a = min(a, (bezier_dist(A12, A13, A14, qq).x - 0.015f) * 0.7f);
149
-
150
- return smin(a, b, 0.02f);
151
- }
152
-
153
- // Normal calculation
154
- float3 calc_normal(float3 p, float3 ray, float t, float res_x) {
155
- float eps = 0.4f * t / res_x;
156
-
157
- float d0 = scene_dist(p);
158
- float dx = scene_dist(p + float3(eps, 0.0f, 0.0f)) - d0;
159
- float dy = scene_dist(p + float3(0.0f, eps, 0.0f)) - d0;
160
- float dz = scene_dist(p + float3(0.0f, 0.0f, eps)) - d0;
161
-
162
- float3 grad = float3(dx, dy, dz);
163
-
164
- float d = dot(grad, ray);
165
- if (d > 0.0f) {
166
- grad = grad - ray * d;
167
- }
168
-
169
- return normalize(grad);
170
- }
171
-
172
- // Build orthonormal basis
173
- void basis(float3 n, thread float3* xp, thread float3* yp) {
174
- float a = n.y / (1.0f + n.z + 1e-10f);
175
- float b = n.y * a;
176
- float c = -n.x * a;
177
- *xp = float3(n.z + b, c, -n.x);
178
- *yp = float3(c, 1.0f - b, -n.y);
179
- }
180
-
181
- // BRDF
182
- float3 compute_brdf(float3 n, float3 l, float3 h, float3 r, float3 tang, float3 binorm) {
183
- float e1 = dot(h, tang) / alpha_m.x;
184
- float e2 = dot(h, binorm) / alpha_m.y;
185
- float hn = dot(h, n);
186
- float E = -2.0f * ((e1 * e1 + e2 * e2) / (1.0f + hn));
187
-
188
- float cos_i = dot(n, l);
189
- float cos_r = dot(n, r);
190
- float denom = sqrt(abs(cos_i * cos_r) + 1e-6f);
191
-
192
- float brdf = lo.x * ONE_OVER_PI +
193
- lo.y * (1.0f / denom) * (1.0f / (4.0f * 3.14159265f * alpha_m.x * alpha_m.y)) * exp(E);
194
-
195
- float intensity = Scale.x * lo.x * ONE_OVER_PI +
196
- Scale.y * lo.y * cos_i * brdf +
197
- Scale.z * hn * lo.y;
198
-
199
- return surfaceColor * intensity;
200
- }
201
-
202
- // HSV to RGB
203
- float3 hsv2rgb_smooth(float h, float s, float v) {
204
- float3 rgb = float3(
205
- abs(fmod(h * 6.0f + 0.0f, 6.0f) - 3.0f) - 1.0f,
206
- abs(fmod(h * 6.0f + 4.0f, 6.0f) - 3.0f) - 1.0f,
207
- abs(fmod(h * 6.0f + 2.0f, 6.0f) - 3.0f) - 1.0f
208
- );
209
- rgb = clamp(rgb, 0.0f, 1.0f);
210
- rgb = rgb * rgb * (3.0f - 2.0f * rgb);
211
-
212
- return float3(
213
- v * (1.0f + s * (rgb.x - 1.0f)),
214
- v * (1.0f + s * (rgb.y - 1.0f)),
215
- v * (1.0f + s * (rgb.z - 1.0f))
216
- );
217
- }
218
- MSL
219
- end
220
-
221
- fragment do
222
- <<~MSL
223
- float2 q = float2(uv.x * resolution.y / resolution.x, uv.y);
224
- float2 p = q * 2.0f - float2(1.0f, 1.0f);
225
- p.x *= resolution.x / resolution.y;
226
-
227
- // Camera with mouse control
228
- float mouse_x = u.mouse.x / resolution.x;
229
- float mouse_y = u.mouse.y / resolution.y;
230
- float cam_angle = 5.0f + 0.2f * u.time + 4.0f * mouse_x;
231
-
232
- float3 origin = float3(cos(cam_angle), 0.7f - mouse_y, sin(cam_angle)) * 3.5f;
233
- float3 w = normalize(Y_VEC * 0.4f - origin);
234
- float3 cam_u = normalize(cross(w, Y_VEC));
235
- float3 cam_v = cross(cam_u, w);
236
-
237
- float3 ray = normalize(cam_u * p.x + cam_v * p.y + w + w);
238
-
239
- // Raymarching
240
- float t = 0.0f;
241
- float h = 0.1f;
242
- for (int i = 0; i < 48; i++) {
243
- if (h < 0.0001f || t > 4.7f) break;
244
- h = scene_dist(origin + ray * t);
245
- t += h;
246
- }
247
-
248
- // Background
249
- float3 color = mix(
250
- hsv2rgb_smooth(0.5f + u.time * 0.02f, 0.35f, 0.4f),
251
- hsv2rgb_smooth(-0.5f + u.time * 0.02f, 0.35f, 0.7f),
252
- q.y
253
- );
254
-
255
- if (h < 0.001f) {
256
- float3 hit = origin + ray * t;
257
- float3 n = calc_normal(hit, ray, t, resolution.x);
258
-
259
- float3 V = normalize(origin - hit);
260
- float3 H = normalize(L + V);
261
- float3 R = normalize(n * 2.0f * dot(n, L) - L);
262
-
263
- float3 tang, binorm;
264
- basis(n, &tang, &binorm);
265
-
266
- color = compute_brdf(n, L, H, R, tang, binorm);
267
-
268
- // Shadows
269
- float shadow = 1.0f;
270
- float j = 0.0f;
271
- for (int i = 0; i < 20; i++) {
272
- j += 0.02f;
273
- shadow = min(shadow, scene_dist(hit + L * j) / j);
274
- }
275
- }
276
-
277
- // Vignette
278
- float vignette = pow(16.0f * q.x * q.y * (1.0f - q.x) * (1.0f - q.y), 0.16f);
279
- color = color * vignette;
280
-
281
- return color;
282
- MSL
283
- end
284
- end
285
-
286
- # Initialize display
287
- window = RBGL::GUI::Window.new(width: WIDTH, height: HEIGHT, title: "Teapot Metal (GPU)")
288
-
289
- puts "Teapot Metal - GPU Compute Shader"
290
- puts "Original: https://www.shadertoy.com/view/MdKcDw"
291
- puts "License: Creative Commons Attribution-NonCommercial-ShareAlike 3.0"
292
- puts "https://creativecommons.org/licenses/by-nc-sa/3.0/deed.en"
293
- puts "Move mouse to rotate camera"
294
- puts "Press 'q' or Escape to quit"
295
-
296
- # Check Metal availability
297
- unless window.metal_available?
298
- puts "Metal compute is NOT available"
299
- exit 1
300
- end
301
-
302
- puts "Metal compute is available!"
303
-
304
- start_time = Time.now
305
- frame_count = 0
306
- last_fps_time = start_time
307
- running = true
308
- mouse_x = 0.0
309
- mouse_y = 0.0
310
-
311
- while running && !window.should_close?
312
- time = Time.now - start_time
313
-
314
- begin
315
- shader.render_metal(window.native_handle, WIDTH, HEIGHT, { time: time, mouse: [mouse_x, mouse_y] })
316
- rescue => e
317
- puts "Error: #{e.message}"
318
- puts e.backtrace.first(10).join("\n")
319
- running = false
320
- break
321
- end
322
-
323
- events = window.poll_events_raw
324
- events.each do |e|
325
- case e[:type]
326
- when :key_press
327
- running = false if e[:key] == 12 || e[:key] == "q"
328
- when :mouse_move
329
- mouse_x = e[:x].to_f
330
- mouse_y = e[:y].to_f
331
- end
332
- end
333
-
334
- frame_count += 1
335
- now = Time.now
336
- if now - last_fps_time >= 1.0
337
- fps = frame_count / (now - last_fps_time)
338
- puts "FPS: #{fps.round(1)}"
339
- frame_count = 0
340
- last_fps_time = now
341
- end
342
- end
343
-
344
- window.close