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.
- checksums.yaml +4 -4
- data/.codespellignore +4 -0
- data/.rubocop.yml +3 -0
- data/.yamllint +16 -0
- data/CHANGELOG.md +11 -1
- data/README.md +130 -74
- data/Rakefile +7 -0
- data/bench/renderer_bench.rb +283 -0
- data/docs/.nojekyll +1 -0
- data/docs/index.html +38 -0
- data/examples/array_test.rb +1 -1
- data/examples/color_test.rb +1 -1
- data/examples/cube_spinning.rb +1 -1
- data/examples/gradient.rb +1 -1
- data/examples/multi_return_test.rb +1 -1
- data/examples/plasma.rb +1 -1
- data/examples/sphere_raymarch.rb +1 -1
- data/examples/triangle_window.rb +1 -1
- data/exe/rbgl +42 -0
- data/lib/rbgl/cli/doctor.rb +155 -0
- data/lib/rbgl/engine/buffer.rb +55 -9
- data/lib/rbgl/engine/clip_space_clipper.rb +23 -3
- data/lib/rbgl/engine/context.rb +23 -6
- data/lib/rbgl/engine/framebuffer.rb +146 -41
- data/lib/rbgl/engine/immutable_color.rb +32 -0
- data/lib/rbgl/engine/rasterizer/attribute_interpolator.rb +100 -10
- data/lib/rbgl/engine/rasterizer/line_renderer.rb +30 -14
- data/lib/rbgl/engine/rasterizer/point_renderer.rb +3 -3
- data/lib/rbgl/engine/rasterizer/triangle_renderer.rb +72 -23
- data/lib/rbgl/engine/rasterizer.rb +3 -20
- data/lib/rbgl/engine/shader/base_shader.rb +3 -7
- data/lib/rbgl/engine/shader/builtins.rb +13 -10
- data/lib/rbgl/engine/shader/dynamic_data.rb +18 -10
- data/lib/rbgl/engine/texture.rb +74 -50
- data/lib/rbgl/engine.rb +3 -1
- data/lib/rbgl/gui/backend.rb +12 -7
- data/lib/rbgl/gui/backend_factory.rb +7 -3
- data/lib/rbgl/gui/cocoa/backend.rb +50 -17
- data/lib/rbgl/gui/cocoa/key_mapper.rb +48 -0
- data/lib/rbgl/gui/event.rb +4 -2
- data/lib/rbgl/gui/file_backend/bmp_writer.rb +2 -2
- data/lib/rbgl/gui/file_backend/frame_writer.rb +7 -0
- data/lib/rbgl/gui/file_backend/png_writer.rb +18 -0
- data/lib/rbgl/gui/file_backend/ppm_writer.rb +1 -0
- data/lib/rbgl/gui/file_backend.rb +24 -6
- data/lib/rbgl/gui/wayland/backend.rb +127 -55
- data/lib/rbgl/gui/wayland/codec.rb +7 -11
- data/lib/rbgl/gui/wayland/connection.rb +137 -20
- data/lib/rbgl/gui/wayland/event_dispatcher.rb +103 -12
- data/lib/rbgl/gui/wayland/global_binder.rb +6 -4
- data/lib/rbgl/gui/wayland/input_mapper.rb +61 -0
- data/lib/rbgl/gui/wayland/protocol_objects/arguments.rb +14 -2
- data/lib/rbgl/gui/wayland/protocol_objects/seat.rb +115 -0
- data/lib/rbgl/gui/wayland/protocol_objects/shm.rb +23 -12
- data/lib/rbgl/gui/wayland/protocol_objects/xdg_shell.rb +15 -0
- data/lib/rbgl/gui/wayland/protocol_objects.rb +1 -0
- data/lib/rbgl/gui/window/render_loop.rb +71 -18
- data/lib/rbgl/gui/window.rb +39 -8
- data/lib/rbgl/gui/x11/atom_cache.rb +4 -1
- data/lib/rbgl/gui/x11/backend.rb +66 -28
- data/lib/rbgl/gui/x11/connection.rb +216 -86
- data/lib/rbgl/gui/x11/event_parser.rb +13 -2
- data/lib/rbgl/gui/x11/key_mapper.rb +48 -0
- data/lib/rbgl/gui/x11/pixel_encoder.rb +164 -0
- data/lib/rbgl/gui/x11/request_encoder.rb +29 -28
- data/lib/rbgl/gui/x11/setup_parser.rb +138 -0
- data/lib/rbgl/gui/x11/transport.rb +52 -2
- data/lib/rbgl/gui/x11/x_authority.rb +107 -0
- data/lib/rbgl/gui.rb +2 -5
- data/lib/rbgl/version.rb +1 -1
- metadata +38 -24
- data/examples/chemical_heartbeat.rb +0 -166
- data/examples/dark_transit.rb +0 -166
- data/examples/fractured_orb.rb +0 -428
- data/examples/fractured_orb_rb.rb +0 -598
- data/examples/hexagonal_flow.rb +0 -333
- data/examples/teapot.rb +0 -362
- data/examples/teapot_mcu.rb +0 -344
data/examples/hexagonal_flow.rb
DELETED
|
@@ -1,333 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Hexagon X5 - Hexagonal Flow Pattern
|
|
4
|
-
# Original: https://www.shadertoy.com/view/4cVfWG
|
|
5
|
-
# Created by @byt3_m3chanic - 12/17/2024
|
|
6
|
-
# License: Creative Commons Attribution-NonCommercial-ShareAlike 3.0
|
|
7
|
-
|
|
8
|
-
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
|
|
9
|
-
|
|
10
|
-
require "rbgl"
|
|
11
|
-
require "rlsl"
|
|
12
|
-
|
|
13
|
-
WIDTH = 640
|
|
14
|
-
HEIGHT = 480
|
|
15
|
-
|
|
16
|
-
# Hexagonal grid constants
|
|
17
|
-
module HexConstants
|
|
18
|
-
N = 3.0
|
|
19
|
-
S4 = 0.577350 # 1/sqrt(3)
|
|
20
|
-
S3 = 0.288683 # 1/(2*sqrt(3))
|
|
21
|
-
S2 = 0.866025 # sqrt(3)/2
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
shader = RLSL.define(:hexflow) do
|
|
25
|
-
uniforms do
|
|
26
|
-
float :time
|
|
27
|
-
vec2 :mouse
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
helpers(:c) do
|
|
31
|
-
n = HexConstants::N
|
|
32
|
-
s4 = HexConstants::S4
|
|
33
|
-
s3 = HexConstants::S3
|
|
34
|
-
s2 = HexConstants::S2
|
|
35
|
-
|
|
36
|
-
<<~C
|
|
37
|
-
// Constants (PI and TAU already defined in code_generator.rb)
|
|
38
|
-
#define PI2 6.283185307f
|
|
39
|
-
|
|
40
|
-
static const float N = #{n}f;
|
|
41
|
-
static const float s4 = #{s4}f;
|
|
42
|
-
static const float s3 = #{s3}f;
|
|
43
|
-
static const float s2 = #{s2}f;
|
|
44
|
-
|
|
45
|
-
// Global state
|
|
46
|
-
static vec3 clr, trm;
|
|
47
|
-
static float tk, ln;
|
|
48
|
-
static float r2_cos, r2_sin, r3_cos, r3_sin;
|
|
49
|
-
|
|
50
|
-
// 2x2 matrix multiply
|
|
51
|
-
static inline vec2 mat2_mul(float c, float s, vec2 v) {
|
|
52
|
-
return vec2_new(c * v.x + s * v.y, -s * v.x + c * v.y);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Hash function (custom version for hex grid)
|
|
56
|
-
static float hex_hash21(vec2 p) {
|
|
57
|
-
p.x = fmodf(p.x, 3.0f * N);
|
|
58
|
-
if (p.x < 0.0f) p.x += 3.0f * N;
|
|
59
|
-
float d = p.x * 26.37f + p.y * 45.93f;
|
|
60
|
-
return fract(sinf(d) * 4374.23f);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Hexagon grid system
|
|
64
|
-
static vec4 hexgrid(vec2 uv) {
|
|
65
|
-
vec2 p1 = vec2_new(floorf(uv.x / 1.732f) + 0.5f, floorf(uv.y) + 0.5f);
|
|
66
|
-
vec2 p2 = vec2_new(floorf((uv.x - 1.0f) / 1.732f) + 0.5f, floorf(uv.y - 0.5f) + 0.5f);
|
|
67
|
-
|
|
68
|
-
vec2 h1 = vec2_new(uv.x - p1.x * 1.732f, uv.y - p1.y);
|
|
69
|
-
vec2 h2 = vec2_new(uv.x - (p2.x + 0.5f) * 1.732f, uv.y - (p2.y + 0.5f));
|
|
70
|
-
|
|
71
|
-
if (vec2_dot(h1, h1) < vec2_dot(h2, h2)) {
|
|
72
|
-
return vec4_new(h1.x, h1.y, p1.x, p1.y);
|
|
73
|
-
} else {
|
|
74
|
-
return vec4_new(h2.x, h2.y, p2.x + 0.5f, p2.y + 0.5f);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Draw function with anti-aliasing
|
|
79
|
-
static void draw(float d, float px, vec3 *C) {
|
|
80
|
-
float b = fabsf(d) - tk;
|
|
81
|
-
|
|
82
|
-
// Shadow
|
|
83
|
-
float t1 = smoothstep(0.1f + px, -px, b - 0.01f);
|
|
84
|
-
C->x = mix_f(C->x, C->x * 0.25f, t1);
|
|
85
|
-
C->y = mix_f(C->y, C->y * 0.25f, t1);
|
|
86
|
-
C->z = mix_f(C->z, C->z * 0.25f, t1);
|
|
87
|
-
|
|
88
|
-
// Fill
|
|
89
|
-
float t2 = smoothstep(px, -px, b);
|
|
90
|
-
C->x = mix_f(C->x, clr.x, t2);
|
|
91
|
-
C->y = mix_f(C->y, clr.y, t2);
|
|
92
|
-
C->z = mix_f(C->z, clr.z, t2);
|
|
93
|
-
|
|
94
|
-
// Highlight
|
|
95
|
-
float t3 = smoothstep(0.01f + px, -px, b + 0.1f);
|
|
96
|
-
C->x = mix_f(C->x, clamp_f(C->x + 0.2f, C->x, 0.95f), t3);
|
|
97
|
-
C->y = mix_f(C->y, clamp_f(C->y + 0.2f, C->y, 0.95f), t3);
|
|
98
|
-
C->z = mix_f(C->z, clamp_f(C->z + 0.2f, C->z, 0.95f), t3);
|
|
99
|
-
|
|
100
|
-
// Trim
|
|
101
|
-
float t4 = smoothstep(px, -px, fabsf(b) - ln);
|
|
102
|
-
C->x = mix_f(C->x, trm.x, t4);
|
|
103
|
-
C->y = mix_f(C->y, trm.y, t4);
|
|
104
|
-
C->z = mix_f(C->z, trm.z, t4);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Procedural texture replacement (since we don't have iChannel0)
|
|
108
|
-
static vec3 proc_texture(vec2 p) {
|
|
109
|
-
float n = sinf(p.x * 10.0f) * sinf(p.y * 10.0f);
|
|
110
|
-
n = n * 0.5f + 0.5f;
|
|
111
|
-
return vec3_new(0.906f * n, 0.282f * n, 0.075f * n);
|
|
112
|
-
}
|
|
113
|
-
C
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
fragment do
|
|
117
|
-
<<~C
|
|
118
|
-
// Initialize rotation matrices (1.047 radians = 60 degrees)
|
|
119
|
-
r2_cos = cosf(1.047f);
|
|
120
|
-
r2_sin = sinf(1.047f);
|
|
121
|
-
r3_cos = cosf(-1.047f);
|
|
122
|
-
r3_sin = sinf(-1.047f);
|
|
123
|
-
|
|
124
|
-
// Normalized coordinates
|
|
125
|
-
vec2 uv_screen = vec2_new(
|
|
126
|
-
(2.0f * frag_coord.x - resolution.x) / fmaxf(resolution.x, resolution.y),
|
|
127
|
-
(2.0f * frag_coord.y - resolution.y) / fmaxf(resolution.x, resolution.y)
|
|
128
|
-
);
|
|
129
|
-
|
|
130
|
-
// Mouse offset
|
|
131
|
-
vec2 mouse_norm = vec2_new(
|
|
132
|
-
(2.0f * u.mouse.x - resolution.x) / resolution.x,
|
|
133
|
-
(2.0f * u.mouse.y - resolution.y) / resolution.y
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
// Log-polar transformation
|
|
137
|
-
float len = sqrtf(uv_screen.x * uv_screen.x + uv_screen.y * uv_screen.y);
|
|
138
|
-
if (len < 0.001f) len = 0.001f;
|
|
139
|
-
|
|
140
|
-
vec2 uv_polar = vec2_new(
|
|
141
|
-
-logf(len) - mouse_norm.x,
|
|
142
|
-
-atan2f(uv_screen.y, uv_screen.x) - mouse_norm.y
|
|
143
|
-
);
|
|
144
|
-
|
|
145
|
-
uv_polar = vec2_div(uv_polar, 3.628f);
|
|
146
|
-
uv_polar = vec2_mul(uv_polar, N);
|
|
147
|
-
|
|
148
|
-
// Animation
|
|
149
|
-
uv_polar.y += u.time * 0.05f;
|
|
150
|
-
uv_polar.x += u.time * 0.15f;
|
|
151
|
-
|
|
152
|
-
float sc = 3.0f;
|
|
153
|
-
float px = 0.01f; // Approximate fwidth
|
|
154
|
-
|
|
155
|
-
// Hexgrid with swapped coordinates
|
|
156
|
-
vec4 H = hexgrid(vec2_new(uv_polar.y * sc, uv_polar.x * sc));
|
|
157
|
-
vec2 p = vec2_new(H.x, H.y);
|
|
158
|
-
vec2 id = vec2_new(H.z, H.w);
|
|
159
|
-
|
|
160
|
-
float hs = hex_hash21(id);
|
|
161
|
-
|
|
162
|
-
// Random rotation
|
|
163
|
-
if (hs < 0.5f) {
|
|
164
|
-
if (hs < 0.25f) {
|
|
165
|
-
p = mat2_mul(r3_cos, r3_sin, p);
|
|
166
|
-
} else {
|
|
167
|
-
p = mat2_mul(r2_cos, r2_sin, p);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// Triangle vertices
|
|
172
|
-
vec2 p0 = vec2_new(p.x - (-s3), p.y - 0.5f);
|
|
173
|
-
vec2 p1 = vec2_new(p.x - s4, p.y);
|
|
174
|
-
vec2 p2 = vec2_new(p.x - (-s3), p.y - (-0.5f));
|
|
175
|
-
|
|
176
|
-
vec3 d3 = vec3_new(vec2_length(p0), vec2_length(p1), vec2_length(p2));
|
|
177
|
-
vec2 pp = vec2_new(0.0f, 0.0f);
|
|
178
|
-
|
|
179
|
-
if (d3.x > d3.y) pp = p1;
|
|
180
|
-
if (d3.y > d3.z) pp = p2;
|
|
181
|
-
if (d3.z > d3.x && d3.y > d3.x) pp = p0;
|
|
182
|
-
|
|
183
|
-
ln = 0.015f;
|
|
184
|
-
tk = 0.14f + 0.1f * sinf(uv_polar.x * 5.0f + u.time);
|
|
185
|
-
|
|
186
|
-
vec3 C = vec3_new(0.0f, 0.0f, 0.0f);
|
|
187
|
-
|
|
188
|
-
// Tile background (hexagon SDF)
|
|
189
|
-
float d = fmaxf(fabsf(p.x) * s2 + fabsf(p.y) * 0.5f, fabsf(p.y)) - (0.5f - ln);
|
|
190
|
-
vec3 tex_col = proc_texture(vec2_mul(p, 2.0f));
|
|
191
|
-
|
|
192
|
-
float t_bg = smoothstep(px, -px, d);
|
|
193
|
-
C.x = mix_f(0.0125f, tex_col.x, t_bg);
|
|
194
|
-
C.y = mix_f(0.0125f, tex_col.y, t_bg);
|
|
195
|
-
C.z = mix_f(0.0125f, tex_col.z, t_bg);
|
|
196
|
-
|
|
197
|
-
// Shading
|
|
198
|
-
float shade1 = clamp_f(1.0f - (H.y + 0.15f), 0.0f, 1.0f);
|
|
199
|
-
float t_s1 = mix_f(smoothstep(px, -px, d + 0.035f), 0.0f, shade1);
|
|
200
|
-
C.x = mix_f(C.x, C.x + 0.1f, t_s1);
|
|
201
|
-
C.y = mix_f(C.y, C.y + 0.1f, t_s1);
|
|
202
|
-
C.z = mix_f(C.z, C.z + 0.1f, t_s1);
|
|
203
|
-
|
|
204
|
-
float shade2 = clamp_f(1.0f - (H.x + 0.5f), 0.0f, 1.0f);
|
|
205
|
-
float t_s2 = mix_f(smoothstep(px, -px, d + 0.025f), 0.0f, shade2);
|
|
206
|
-
C.x = mix_f(C.x, C.x * 0.1f, t_s2);
|
|
207
|
-
C.y = mix_f(C.y, C.y * 0.1f, t_s2);
|
|
208
|
-
C.z = mix_f(C.z, C.z * 0.1f, t_s2);
|
|
209
|
-
|
|
210
|
-
// Base tile distance
|
|
211
|
-
float b = vec2_length(pp) - s3;
|
|
212
|
-
float t_val = 1e5f, g = 1e5f;
|
|
213
|
-
float tg = 1.0f;
|
|
214
|
-
|
|
215
|
-
hs = fract(hs * 53.71f);
|
|
216
|
-
|
|
217
|
-
// Alternate tile patterns
|
|
218
|
-
if (hs > 0.95f) {
|
|
219
|
-
vec2 p4 = mat2_mul(r3_cos, r3_sin, p);
|
|
220
|
-
vec2 p5 = mat2_mul(r2_cos, r2_sin, p);
|
|
221
|
-
|
|
222
|
-
b = vec2_length(vec2_new(p.x, fabsf(p.y) - 0.5f));
|
|
223
|
-
g = fabsf(p5.x);
|
|
224
|
-
t_val = fabsf(p4.x);
|
|
225
|
-
tg = 0.0f;
|
|
226
|
-
} else if (hs > 0.65f) {
|
|
227
|
-
b = fabsf(p.x);
|
|
228
|
-
g = fminf(vec2_length(p1) - s3, vec2_length(vec2_new(p1.x + 1.155f, p1.y)) - s3);
|
|
229
|
-
tg = 0.0f;
|
|
230
|
-
} else if (hs < 0.15f) {
|
|
231
|
-
vec2 p4 = mat2_mul(r3_cos, r3_sin, p);
|
|
232
|
-
vec2 p5 = mat2_mul(r2_cos, r2_sin, p);
|
|
233
|
-
|
|
234
|
-
t_val = fabsf(p.x);
|
|
235
|
-
b = fabsf(p5.x);
|
|
236
|
-
g = fabsf(p4.x);
|
|
237
|
-
tg = 0.0f;
|
|
238
|
-
} else if (hs < 0.22f) {
|
|
239
|
-
b = vec2_length(vec2_new(p.x, fabsf(p.y) - 0.5f));
|
|
240
|
-
g = fminf(vec2_length(p1) - s3, vec2_length(vec2_new(p1.x + 1.155f, p1.y)) - s3);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
clr = vec3_new(0.420f, 0.278f, 0.043f);
|
|
244
|
-
trm = vec3_new(0.0f, 0.0f, 0.0f);
|
|
245
|
-
|
|
246
|
-
// Draw segments
|
|
247
|
-
draw(t_val, px, &C);
|
|
248
|
-
draw(g, px, &C);
|
|
249
|
-
draw(b, px, &C);
|
|
250
|
-
|
|
251
|
-
// Solid balls
|
|
252
|
-
if (tg > 0.0f) {
|
|
253
|
-
float v = vec2_length(p) - 0.25f;
|
|
254
|
-
|
|
255
|
-
float t1 = smoothstep(0.1f + px, -px, v - 0.01f);
|
|
256
|
-
C.x = mix_f(C.x, C.x * 0.25f, t1);
|
|
257
|
-
C.y = mix_f(C.y, C.y * 0.25f, t1);
|
|
258
|
-
C.z = mix_f(C.z, C.z * 0.25f, t1);
|
|
259
|
-
|
|
260
|
-
float t2 = smoothstep(px, -px, v);
|
|
261
|
-
C.x = mix_f(C.x, clr.x, t2);
|
|
262
|
-
C.y = mix_f(C.y, clr.y, t2);
|
|
263
|
-
C.z = mix_f(C.z, clr.z, t2);
|
|
264
|
-
|
|
265
|
-
float t3 = smoothstep(0.01f + px, -px, v + 0.1f);
|
|
266
|
-
C.x = mix_f(C.x, clamp_f(C.x + 0.2f, C.x, 0.95f), t3);
|
|
267
|
-
C.y = mix_f(C.y, clamp_f(C.y + 0.2f, C.y, 0.95f), t3);
|
|
268
|
-
C.z = mix_f(C.z, clamp_f(C.z + 0.2f, C.z, 0.95f), t3);
|
|
269
|
-
|
|
270
|
-
float t4 = smoothstep(px, -px, fabsf(v) - ln);
|
|
271
|
-
C.x = mix_f(C.x, trm.x, t4);
|
|
272
|
-
C.y = mix_f(C.y, trm.y, t4);
|
|
273
|
-
C.z = mix_f(C.z, trm.z, t4);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// Gamma correction
|
|
277
|
-
C.x = powf(C.x, 0.4545f);
|
|
278
|
-
C.y = powf(C.y, 0.4545f);
|
|
279
|
-
C.z = powf(C.z, 0.4545f);
|
|
280
|
-
|
|
281
|
-
return C;
|
|
282
|
-
C
|
|
283
|
-
end
|
|
284
|
-
end
|
|
285
|
-
|
|
286
|
-
# Initialize display
|
|
287
|
-
window = RBGL::GUI::Window.new(width: WIDTH, height: HEIGHT, title: "Hexagon X5")
|
|
288
|
-
|
|
289
|
-
puts "Hexagon X5 - Hexagonal Flow Pattern"
|
|
290
|
-
puts "Original: https://www.shadertoy.com/view/4cVfWG"
|
|
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 pan"
|
|
294
|
-
puts "Press 'q' or Escape to quit"
|
|
295
|
-
|
|
296
|
-
start_time = Time.now
|
|
297
|
-
frame_count = 0
|
|
298
|
-
last_fps_time = start_time
|
|
299
|
-
running = true
|
|
300
|
-
mouse_x = WIDTH / 2.0
|
|
301
|
-
mouse_y = HEIGHT / 2.0
|
|
302
|
-
|
|
303
|
-
buffer = "\x00" * (WIDTH * HEIGHT * 4)
|
|
304
|
-
|
|
305
|
-
while running && !window.should_close?
|
|
306
|
-
time = Time.now - start_time
|
|
307
|
-
|
|
308
|
-
shader.render(buffer, WIDTH, HEIGHT, { time: time, mouse: [mouse_x, mouse_y] })
|
|
309
|
-
|
|
310
|
-
window.set_pixels(buffer)
|
|
311
|
-
|
|
312
|
-
events = window.poll_events_raw
|
|
313
|
-
events.each do |e|
|
|
314
|
-
case e[:type]
|
|
315
|
-
when :key_press
|
|
316
|
-
running = false if e[:key] == 12 || e[:key] == "q"
|
|
317
|
-
when :mouse_move
|
|
318
|
-
mouse_x = e[:x].to_f
|
|
319
|
-
mouse_y = e[:y].to_f
|
|
320
|
-
end
|
|
321
|
-
end
|
|
322
|
-
|
|
323
|
-
frame_count += 1
|
|
324
|
-
now = Time.now
|
|
325
|
-
if now - last_fps_time >= 1.0
|
|
326
|
-
fps = frame_count / (now - last_fps_time)
|
|
327
|
-
puts "FPS: #{fps.round(1)}"
|
|
328
|
-
frame_count = 0
|
|
329
|
-
last_fps_time = now
|
|
330
|
-
end
|
|
331
|
-
end
|
|
332
|
-
|
|
333
|
-
window.close
|
data/examples/teapot.rb
DELETED
|
@@ -1,362 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Teapot - Raymarched Bezier Curves (Ruby Mode)
|
|
4
|
-
# Original: https://www.shadertoy.com/view/MdKcDw
|
|
5
|
-
# Created by Sebastien Durand - 2014
|
|
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(:teapot_ruby) do
|
|
18
|
-
uniforms do
|
|
19
|
-
float :time
|
|
20
|
-
vec2 :mouse
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
functions do
|
|
24
|
-
# Helper functions
|
|
25
|
-
define :cross2d, returns: :float, params: { a: :vec2, b: :vec2 }
|
|
26
|
-
define :smin, returns: :float, params: { a: :float, b: :float, k: :float }
|
|
27
|
-
define :bezier_dist, returns: :vec2, params: { m: :vec2, n: :vec2, o: :vec2, p: :vec3 }
|
|
28
|
-
define :scene_dist, returns: :float, params: { p: :vec3 }
|
|
29
|
-
define :basis, returns: [:vec3, :vec3], params: { n: :vec3 }
|
|
30
|
-
define :calc_normal, returns: :vec3, params: { p: :vec3, ray: :vec3, t: :float, res_x: :float }
|
|
31
|
-
define :compute_brdf, returns: :vec3, params: { n: :vec3, l: :vec3, h: :vec3, r: :vec3, tang: :vec3, binorm: :vec3 }
|
|
32
|
-
define :hsv2rgb_smooth, returns: :vec3, params: { h: :float, s: :float, v: :float }
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
helpers do
|
|
36
|
-
# Control points - Body profile (15 points)
|
|
37
|
-
A = [
|
|
38
|
-
vec2(0.0, 0.0), vec2(0.64, 0.0), vec2(0.64, 0.03),
|
|
39
|
-
vec2(0.8, 0.12), vec2(0.8, 0.3), vec2(0.8, 0.48),
|
|
40
|
-
vec2(0.64, 0.9), vec2(0.6, 0.93), vec2(0.56, 0.9),
|
|
41
|
-
vec2(0.56, 0.96), vec2(0.12, 1.02), vec2(0.0, 1.05),
|
|
42
|
-
vec2(0.16, 1.14), vec2(0.2, 1.2), vec2(0.0, 1.2)
|
|
43
|
-
]
|
|
44
|
-
|
|
45
|
-
# Control points - Spout (5 points)
|
|
46
|
-
T1 = [
|
|
47
|
-
vec2(1.16, 0.96), vec2(1.04, 0.9), vec2(1.0, 0.72),
|
|
48
|
-
vec2(0.92, 0.48), vec2(0.72, 0.42)
|
|
49
|
-
]
|
|
50
|
-
|
|
51
|
-
# Control points - Handle (5 points)
|
|
52
|
-
T2 = [
|
|
53
|
-
vec2(-0.6, 0.78), vec2(-1.16, 0.84), vec2(-1.16, 0.63),
|
|
54
|
-
vec2(-1.2, 0.42), vec2(-0.72, 0.24)
|
|
55
|
-
]
|
|
56
|
-
|
|
57
|
-
# Material properties
|
|
58
|
-
LO = vec2(0.450, 0.048)
|
|
59
|
-
ALPHA_M = vec2(0.045, 0.068)
|
|
60
|
-
SCALE = vec3(1.0, 20.0, 10.0)
|
|
61
|
-
SURFACE_COLOR = vec3(0.45, 0.54, 1.0)
|
|
62
|
-
|
|
63
|
-
# Light direction (pre-normalized: normalize(vec3(1.0, 0.72, 1.0)))
|
|
64
|
-
L = vec3(0.6286, 0.4526, 0.6286)
|
|
65
|
-
|
|
66
|
-
# Up vector
|
|
67
|
-
Y = vec3(0.0, 1.0, 0.0)
|
|
68
|
-
|
|
69
|
-
# Constants
|
|
70
|
-
ONE_OVER_PI = 0.31830988618
|
|
71
|
-
|
|
72
|
-
# Cross product 2D
|
|
73
|
-
def cross2d(a, b)
|
|
74
|
-
a.x * b.y - b.x * a.y
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
# Smooth minimum for blending
|
|
78
|
-
def smin(a, b, k)
|
|
79
|
-
h = clamp(0.5 + 0.5 * (b - a) / k, 0.0, 1.0)
|
|
80
|
-
mix(b, a, h) - k * h * (1.0 - h)
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
# Distance to quadratic Bezier curve
|
|
84
|
-
def bezier_dist(m, n, o, p)
|
|
85
|
-
q = vec2(p.x, p.y)
|
|
86
|
-
m = vec2(m.x - q.x, m.y - q.y)
|
|
87
|
-
n = vec2(n.x - q.x, n.y - q.y)
|
|
88
|
-
o = vec2(o.x - q.x, o.y - q.y)
|
|
89
|
-
|
|
90
|
-
x = cross2d(m, o)
|
|
91
|
-
y = 2.0 * cross2d(n, m)
|
|
92
|
-
z = 2.0 * cross2d(o, n)
|
|
93
|
-
|
|
94
|
-
i = vec2(o.x - m.x, o.y - m.y)
|
|
95
|
-
j = vec2(o.x - n.x, o.y - n.y)
|
|
96
|
-
k = vec2(n.x - m.x, n.y - m.y)
|
|
97
|
-
|
|
98
|
-
s = vec2(
|
|
99
|
-
2.0 * (x * i.x + y * j.x + z * k.x),
|
|
100
|
-
2.0 * (x * i.y + y * j.y + z * k.y)
|
|
101
|
-
)
|
|
102
|
-
|
|
103
|
-
dot_s = s.x * s.x + s.y * s.y
|
|
104
|
-
if dot_s < 0.0000000001
|
|
105
|
-
dot_s = 0.0000000001
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
r = vec2(
|
|
109
|
-
m.x + (y * z - x * x) * s.y / dot_s,
|
|
110
|
-
m.y - (y * z - x * x) * s.x / dot_s
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
denom = x + x + y + z
|
|
114
|
-
if abs(denom) < 0.0000000001
|
|
115
|
-
denom = 0.0000000001
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
t = clamp((cross2d(r, i) + 2.0 * cross2d(k, r)) / denom, 0.0, 1.0)
|
|
119
|
-
|
|
120
|
-
jk = vec2(j.x - k.x, j.y - k.y)
|
|
121
|
-
r = vec2(
|
|
122
|
-
m.x + t * (k.x + k.x + t * jk.x),
|
|
123
|
-
m.y + t * (k.y + k.y + t * jk.y)
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
vec2(sqrt(r.x * r.x + r.y * r.y + p.z * p.z), t)
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
# Scene distance function
|
|
130
|
-
def scene_dist(p)
|
|
131
|
-
# Spout curve
|
|
132
|
-
h = bezier_dist(T1[2], T1[3], T1[4], p)
|
|
133
|
-
|
|
134
|
-
# Handle distance
|
|
135
|
-
handle_d = min(
|
|
136
|
-
bezier_dist(T2[0], T2[1], T2[2], p).x,
|
|
137
|
-
bezier_dist(T2[2], T2[3], T2[4], p).x
|
|
138
|
-
) - 0.06
|
|
139
|
-
|
|
140
|
-
# Spout distance
|
|
141
|
-
spout_hole = abs(bezier_dist(T1[0], T1[1], T1[2], p).x - 0.07) - 0.01
|
|
142
|
-
spout_body = h.x * (1.0 - 0.75 * h.y) - 0.08
|
|
143
|
-
spout_d = max(p.y - 0.9, min(spout_hole, spout_body))
|
|
144
|
-
|
|
145
|
-
b = min(handle_d, spout_d)
|
|
146
|
-
|
|
147
|
-
# Body distance (rotation symmetry)
|
|
148
|
-
r_xz = sqrt(p.x * p.x + p.z * p.z)
|
|
149
|
-
qq = vec3(r_xz, p.y, 0.0)
|
|
150
|
-
|
|
151
|
-
# Body curves (step by 2)
|
|
152
|
-
a0 = bezier_dist(A[0], A[1], A[2], qq).x - 0.015
|
|
153
|
-
a1 = bezier_dist(A[2], A[3], A[4], qq).x - 0.015
|
|
154
|
-
a2 = bezier_dist(A[4], A[5], A[6], qq).x - 0.015
|
|
155
|
-
a3 = bezier_dist(A[6], A[7], A[8], qq).x - 0.015
|
|
156
|
-
a4 = bezier_dist(A[8], A[9], A[10], qq).x - 0.015
|
|
157
|
-
a5 = bezier_dist(A[10], A[11], A[12], qq).x - 0.015
|
|
158
|
-
a6 = bezier_dist(A[12], A[13], A[14], qq).x - 0.015
|
|
159
|
-
|
|
160
|
-
a = min(a0, min(a1, min(a2, min(a3, min(a4, min(a5, a6)))))) * 0.7
|
|
161
|
-
|
|
162
|
-
smin(a, b, 0.02)
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
# Build orthonormal basis from normal
|
|
166
|
-
def basis(n)
|
|
167
|
-
a = n.y / (1.0 + n.z + 0.0000000001)
|
|
168
|
-
b = n.y * a
|
|
169
|
-
c = 0.0 - n.x * a
|
|
170
|
-
xp = vec3(n.z + b, c, 0.0 - n.x)
|
|
171
|
-
yp = vec3(c, 1.0 - b, 0.0 - n.y)
|
|
172
|
-
[xp, yp]
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
# Normal calculation
|
|
176
|
-
def calc_normal(p, ray, t, res_x)
|
|
177
|
-
eps = 0.4 * t / res_x
|
|
178
|
-
|
|
179
|
-
d0 = scene_dist(p)
|
|
180
|
-
dx = scene_dist(vec3(p.x + eps, p.y, p.z)) - d0
|
|
181
|
-
dy = scene_dist(vec3(p.x, p.y + eps, p.z)) - d0
|
|
182
|
-
dz = scene_dist(vec3(p.x, p.y, p.z + eps)) - d0
|
|
183
|
-
|
|
184
|
-
grad = vec3(dx, dy, dz)
|
|
185
|
-
|
|
186
|
-
# Prevent normals pointing away from camera
|
|
187
|
-
d = grad.x * ray.x + grad.y * ray.y + grad.z * ray.z
|
|
188
|
-
if d > 0.0
|
|
189
|
-
grad = vec3(grad.x - ray.x * d, grad.y - ray.y * d, grad.z - ray.z * d)
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
normalize(grad)
|
|
193
|
-
end
|
|
194
|
-
|
|
195
|
-
# BRDF computation
|
|
196
|
-
def compute_brdf(n, l, h, r, tang, binorm)
|
|
197
|
-
e1 = (h.x * tang.x + h.y * tang.y + h.z * tang.z) / ALPHA_M.x
|
|
198
|
-
e2 = (h.x * binorm.x + h.y * binorm.y + h.z * binorm.z) / ALPHA_M.y
|
|
199
|
-
hn = h.x * n.x + h.y * n.y + h.z * n.z
|
|
200
|
-
big_e = 0.0 - 2.0 * ((e1 * e1 + e2 * e2) / (1.0 + hn))
|
|
201
|
-
|
|
202
|
-
cos_i = n.x * l.x + n.y * l.y + n.z * l.z
|
|
203
|
-
cos_r = n.x * r.x + n.y * r.y + n.z * r.z
|
|
204
|
-
denom = sqrt(abs(cos_i * cos_r) + 0.000001)
|
|
205
|
-
|
|
206
|
-
brdf = LO.x * ONE_OVER_PI + LO.y * (1.0 / denom) * (1.0 / (4.0 * PI * ALPHA_M.x * ALPHA_M.y)) * exp(big_e)
|
|
207
|
-
|
|
208
|
-
intensity = SCALE.x * LO.x * ONE_OVER_PI + SCALE.y * LO.y * cos_i * brdf + SCALE.z * hn * LO.y
|
|
209
|
-
|
|
210
|
-
vec3(SURFACE_COLOR.x * intensity, SURFACE_COLOR.y * intensity, SURFACE_COLOR.z * intensity)
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
# HSV to RGB with cubic smoothing
|
|
214
|
-
def hsv2rgb_smooth(h, s, v)
|
|
215
|
-
rx = abs(mod(h * 6.0 + 0.0, 6.0) - 3.0) - 1.0
|
|
216
|
-
gx = abs(mod(h * 6.0 + 4.0, 6.0) - 3.0) - 1.0
|
|
217
|
-
bx = abs(mod(h * 6.0 + 2.0, 6.0) - 3.0) - 1.0
|
|
218
|
-
|
|
219
|
-
rx = clamp(rx, 0.0, 1.0)
|
|
220
|
-
gx = clamp(gx, 0.0, 1.0)
|
|
221
|
-
bx = clamp(bx, 0.0, 1.0)
|
|
222
|
-
|
|
223
|
-
# Cubic smoothing
|
|
224
|
-
rx = rx * rx * (3.0 - 2.0 * rx)
|
|
225
|
-
gx = gx * gx * (3.0 - 2.0 * gx)
|
|
226
|
-
bx = bx * bx * (3.0 - 2.0 * bx)
|
|
227
|
-
|
|
228
|
-
vec3(
|
|
229
|
-
v * (1.0 + s * (rx - 1.0)),
|
|
230
|
-
v * (1.0 + s * (gx - 1.0)),
|
|
231
|
-
v * (1.0 + s * (bx - 1.0))
|
|
232
|
-
)
|
|
233
|
-
end
|
|
234
|
-
end
|
|
235
|
-
|
|
236
|
-
fragment do |frag_coord, resolution, u|
|
|
237
|
-
# UV coordinates (aspect-ratio preserving, same as Metal)
|
|
238
|
-
uv = vec2(frag_coord.x / resolution.y, frag_coord.y / resolution.y)
|
|
239
|
-
|
|
240
|
-
# Normalized screen coordinates
|
|
241
|
-
q = vec2(uv.x * resolution.y / resolution.x, uv.y)
|
|
242
|
-
|
|
243
|
-
# Centered coordinates for camera ray
|
|
244
|
-
p = vec2(q.x * 2.0 - 1.0, q.y * 2.0 - 1.0)
|
|
245
|
-
p = vec2(p.x * resolution.x / resolution.y, p.y)
|
|
246
|
-
|
|
247
|
-
# Camera with mouse control
|
|
248
|
-
mouse_x = u.mouse.x / resolution.x
|
|
249
|
-
mouse_y = u.mouse.y / resolution.y
|
|
250
|
-
cam_angle = 5.0 + 0.2 * u.time + 4.0 * mouse_x
|
|
251
|
-
|
|
252
|
-
origin = vec3(cos(cam_angle) * 3.5, (0.7 - mouse_y) * 3.5, sin(cam_angle) * 3.5)
|
|
253
|
-
target = vec3(Y.x * 0.4, Y.y * 0.4, Y.z * 0.4)
|
|
254
|
-
w = normalize(vec3(target.x - origin.x, target.y - origin.y, target.z - origin.z))
|
|
255
|
-
|
|
256
|
-
# Camera basis
|
|
257
|
-
cam_u = normalize(vec3(
|
|
258
|
-
w.y * Y.z - w.z * Y.y,
|
|
259
|
-
w.z * Y.x - w.x * Y.z,
|
|
260
|
-
w.x * Y.y - w.y * Y.x
|
|
261
|
-
))
|
|
262
|
-
cam_v = vec3(
|
|
263
|
-
cam_u.y * w.z - cam_u.z * w.y,
|
|
264
|
-
cam_u.z * w.x - cam_u.x * w.z,
|
|
265
|
-
cam_u.x * w.y - cam_u.y * w.x
|
|
266
|
-
)
|
|
267
|
-
|
|
268
|
-
ray = normalize(vec3(
|
|
269
|
-
cam_u.x * p.x + cam_v.x * p.y + w.x * 2.0,
|
|
270
|
-
cam_u.y * p.x + cam_v.y * p.y + w.y * 2.0,
|
|
271
|
-
cam_u.z * p.x + cam_v.z * p.y + w.z * 2.0
|
|
272
|
-
))
|
|
273
|
-
|
|
274
|
-
# Raymarching
|
|
275
|
-
t = 0.0
|
|
276
|
-
h = 0.1
|
|
277
|
-
i = 0.0
|
|
278
|
-
while i < 48.0 && h > 0.0001 && t < 4.7
|
|
279
|
-
pos = vec3(origin.x + ray.x * t, origin.y + ray.y * t, origin.z + ray.z * t)
|
|
280
|
-
h = scene_dist(pos)
|
|
281
|
-
t = t + h
|
|
282
|
-
i = i + 1.0
|
|
283
|
-
end
|
|
284
|
-
|
|
285
|
-
# Background gradient
|
|
286
|
-
color = mix(
|
|
287
|
-
hsv2rgb_smooth(0.5 + u.time * 0.02, 0.35, 0.4),
|
|
288
|
-
hsv2rgb_smooth(0.0 - 0.5 + u.time * 0.02, 0.35, 0.7),
|
|
289
|
-
q.y
|
|
290
|
-
)
|
|
291
|
-
|
|
292
|
-
if h < 0.001
|
|
293
|
-
hit = vec3(origin.x + ray.x * t, origin.y + ray.y * t, origin.z + ray.z * t)
|
|
294
|
-
n = calc_normal(hit, ray, t, resolution.x)
|
|
295
|
-
|
|
296
|
-
big_v = normalize(vec3(origin.x - hit.x, origin.y - hit.y, origin.z - hit.z))
|
|
297
|
-
big_h = normalize(vec3(L.x + big_v.x, L.y + big_v.y, L.z + big_v.z))
|
|
298
|
-
dot_nl = n.x * L.x + n.y * L.y + n.z * L.z
|
|
299
|
-
big_r = normalize(vec3(
|
|
300
|
-
n.x * 2.0 * dot_nl - L.x,
|
|
301
|
-
n.y * 2.0 * dot_nl - L.y,
|
|
302
|
-
n.z * 2.0 * dot_nl - L.z
|
|
303
|
-
))
|
|
304
|
-
|
|
305
|
-
tang, binorm = basis(n)
|
|
306
|
-
color = compute_brdf(n, L, big_h, big_r, tang, binorm)
|
|
307
|
-
end
|
|
308
|
-
|
|
309
|
-
# Vignette
|
|
310
|
-
vignette = pow(16.0 * q.x * q.y * (1.0 - q.x) * (1.0 - q.y), 0.16)
|
|
311
|
-
vec3(color.x * vignette, color.y * vignette, color.z * vignette)
|
|
312
|
-
end
|
|
313
|
-
end
|
|
314
|
-
|
|
315
|
-
# Initialize display
|
|
316
|
-
window = RBGL::GUI::Window.new(width: WIDTH, height: HEIGHT, title: "Teapot Ruby (Raymarched Bezier)")
|
|
317
|
-
|
|
318
|
-
puts "Teapot - Raymarched Bezier Curves (Ruby Mode)"
|
|
319
|
-
puts "Original: https://www.shadertoy.com/view/MdKcDw"
|
|
320
|
-
puts "License: Creative Commons Attribution-NonCommercial-ShareAlike 3.0"
|
|
321
|
-
puts "https://creativecommons.org/licenses/by-nc-sa/3.0/deed.en"
|
|
322
|
-
puts "Move mouse to rotate camera"
|
|
323
|
-
puts "Press 'q' or Escape to quit"
|
|
324
|
-
|
|
325
|
-
start_time = Time.now
|
|
326
|
-
frame_count = 0
|
|
327
|
-
last_fps_time = start_time
|
|
328
|
-
running = true
|
|
329
|
-
mouse_x = 0.0
|
|
330
|
-
mouse_y = 0.0
|
|
331
|
-
|
|
332
|
-
buffer = "\x00" * (WIDTH * HEIGHT * 4)
|
|
333
|
-
|
|
334
|
-
while running && !window.should_close?
|
|
335
|
-
time = Time.now - start_time
|
|
336
|
-
|
|
337
|
-
shader.render(buffer, WIDTH, HEIGHT, { time: time, mouse: [mouse_x, mouse_y] })
|
|
338
|
-
|
|
339
|
-
window.set_pixels(buffer)
|
|
340
|
-
|
|
341
|
-
events = window.poll_events_raw
|
|
342
|
-
events.each do |e|
|
|
343
|
-
case e[:type]
|
|
344
|
-
when :key_press
|
|
345
|
-
running = false if e[:key] == 12 || e[:key] == "q"
|
|
346
|
-
when :mouse_move
|
|
347
|
-
mouse_x = e[:x].to_f
|
|
348
|
-
mouse_y = e[:y].to_f
|
|
349
|
-
end
|
|
350
|
-
end
|
|
351
|
-
|
|
352
|
-
frame_count += 1
|
|
353
|
-
now = Time.now
|
|
354
|
-
if now - last_fps_time >= 1.0
|
|
355
|
-
fps = frame_count / (now - last_fps_time)
|
|
356
|
-
puts "FPS: #{fps.round(1)}"
|
|
357
|
-
frame_count = 0
|
|
358
|
-
last_fps_time = now
|
|
359
|
-
end
|
|
360
|
-
end
|
|
361
|
-
|
|
362
|
-
window.close
|