rbgl 0.1.0
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 +7 -0
- data/LICENSE +21 -0
- data/README.md +123 -0
- data/Rakefile +12 -0
- data/examples/array_test.rb +99 -0
- data/examples/chemical_heartbeat.rb +166 -0
- data/examples/color_test.rb +61 -0
- data/examples/cube_spinning.rb +87 -0
- data/examples/dark_transit.rb +166 -0
- data/examples/fractured_orb.rb +428 -0
- data/examples/fractured_orb_rb.rb +598 -0
- data/examples/gradient.rb +84 -0
- data/examples/hexagonal_flow.rb +333 -0
- data/examples/multi_return_test.rb +98 -0
- data/examples/plasma.rb +78 -0
- data/examples/sphere_raymarch.rb +126 -0
- data/examples/teapot.rb +362 -0
- data/examples/teapot_mcu.rb +344 -0
- data/examples/triangle_basic.rb +36 -0
- data/examples/triangle_window.rb +62 -0
- data/examples/window_test.rb +36 -0
- data/lib/rbgl/engine/buffer.rb +160 -0
- data/lib/rbgl/engine/context.rb +157 -0
- data/lib/rbgl/engine/framebuffer.rb +115 -0
- data/lib/rbgl/engine/pipeline.rb +35 -0
- data/lib/rbgl/engine/rasterizer.rb +213 -0
- data/lib/rbgl/engine/shader.rb +324 -0
- data/lib/rbgl/engine/texture.rb +125 -0
- data/lib/rbgl/engine.rb +15 -0
- data/lib/rbgl/gui/backend.rb +76 -0
- data/lib/rbgl/gui/cocoa/backend.rb +121 -0
- data/lib/rbgl/gui/event.rb +34 -0
- data/lib/rbgl/gui/file_backend.rb +91 -0
- data/lib/rbgl/gui/wayland/backend.rb +126 -0
- data/lib/rbgl/gui/wayland/connection.rb +331 -0
- data/lib/rbgl/gui/window.rb +148 -0
- data/lib/rbgl/gui/x11/backend.rb +156 -0
- data/lib/rbgl/gui/x11/connection.rb +344 -0
- data/lib/rbgl/gui.rb +16 -0
- data/lib/rbgl/version.rb +5 -0
- data/lib/rbgl.rb +6 -0
- metadata +114 -0
|
@@ -0,0 +1,333 @@
|
|
|
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
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Multiple Return Value Test Shader
|
|
4
|
+
# Tests Ruby-mode multiple return value support in RLSL
|
|
5
|
+
|
|
6
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
|
|
7
|
+
|
|
8
|
+
require "rbgl"
|
|
9
|
+
require "rlsl"
|
|
10
|
+
|
|
11
|
+
WIDTH = 640
|
|
12
|
+
HEIGHT = 480
|
|
13
|
+
|
|
14
|
+
shader = RLSL.define(:multi_return_test) do
|
|
15
|
+
uniforms do
|
|
16
|
+
float :time
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
functions do
|
|
20
|
+
# Function that returns multiple values (tuple)
|
|
21
|
+
define :compute_basis, returns: [:vec3, :vec3], params: { n: :vec3 }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
helpers do
|
|
25
|
+
# Function returning multiple values as an array
|
|
26
|
+
def compute_basis(n)
|
|
27
|
+
# Orthonormal basis calculation
|
|
28
|
+
a = 1.0 / (1.0 + n.z + 0.0001)
|
|
29
|
+
b = n.y * a
|
|
30
|
+
c = 0.0 - n.x * a
|
|
31
|
+
xp = vec3(n.z + b, c, 0.0 - n.x)
|
|
32
|
+
yp = vec3(c, 1.0 - b, 0.0 - n.y)
|
|
33
|
+
[xp, yp]
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
fragment do |frag_coord, resolution, u|
|
|
38
|
+
uv = vec2(
|
|
39
|
+
(frag_coord.x - resolution.x * 0.5) / resolution.y,
|
|
40
|
+
(frag_coord.y - resolution.y * 0.5) / resolution.y
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# Create a normal from UV coordinates
|
|
44
|
+
t = u.time * 0.5
|
|
45
|
+
n = normalize(vec3(uv.x + sin(t), uv.y + cos(t), 1.0))
|
|
46
|
+
|
|
47
|
+
# Get basis vectors using multiple return values
|
|
48
|
+
tang, binorm = compute_basis(n)
|
|
49
|
+
|
|
50
|
+
# Visualize the basis vectors
|
|
51
|
+
r = abs(tang.x) * 0.5 + 0.5
|
|
52
|
+
g = abs(binorm.y) * 0.5 + 0.5
|
|
53
|
+
b = abs(n.z) * 0.5 + 0.5
|
|
54
|
+
|
|
55
|
+
vec3(r, g, b)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Initialize display
|
|
60
|
+
window = RBGL::GUI::Window.new(width: WIDTH, height: HEIGHT, title: "Multi Return Test")
|
|
61
|
+
|
|
62
|
+
puts "Multiple Return Value Test Shader"
|
|
63
|
+
puts "Tests Ruby-mode multiple return value support"
|
|
64
|
+
puts "Press 'q' or Escape to quit"
|
|
65
|
+
|
|
66
|
+
start_time = Time.now
|
|
67
|
+
frame_count = 0
|
|
68
|
+
last_fps_time = start_time
|
|
69
|
+
running = true
|
|
70
|
+
|
|
71
|
+
buffer = "\x00" * (WIDTH * HEIGHT * 4)
|
|
72
|
+
|
|
73
|
+
while running && !window.should_close?
|
|
74
|
+
time = Time.now - start_time
|
|
75
|
+
|
|
76
|
+
shader.render(buffer, WIDTH, HEIGHT, { time: time })
|
|
77
|
+
|
|
78
|
+
window.set_pixels(buffer)
|
|
79
|
+
|
|
80
|
+
events = window.poll_events_raw
|
|
81
|
+
events.each do |e|
|
|
82
|
+
case e[:type]
|
|
83
|
+
when :key_press
|
|
84
|
+
running = false if e[:key] == 12 || e[:key] == "q"
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
frame_count += 1
|
|
89
|
+
now = Time.now
|
|
90
|
+
if now - last_fps_time >= 1.0
|
|
91
|
+
fps = frame_count / (now - last_fps_time)
|
|
92
|
+
puts "FPS: #{fps.round(1)}"
|
|
93
|
+
frame_count = 0
|
|
94
|
+
last_fps_time = now
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
window.close
|
data/examples/plasma.rb
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Classic Plasma Effect using Native Shader DSL
|
|
4
|
+
|
|
5
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
|
|
6
|
+
|
|
7
|
+
require "rbgl"
|
|
8
|
+
require "rlsl"
|
|
9
|
+
|
|
10
|
+
WIDTH = 640
|
|
11
|
+
HEIGHT = 480
|
|
12
|
+
|
|
13
|
+
# Define plasma shader using pure Ruby DSL
|
|
14
|
+
shader = RLSL.define(:plasma) do
|
|
15
|
+
uniforms do
|
|
16
|
+
float :time
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
fragment do |frag_coord, resolution, u|
|
|
20
|
+
uv = frag_coord / resolution.y
|
|
21
|
+
cx = uv.x - 0.5
|
|
22
|
+
cy = uv.y - 0.5
|
|
23
|
+
|
|
24
|
+
v1 = sin(cx * 10.0 + u.time)
|
|
25
|
+
v2 = sin(10.0 * (cx * sin(u.time / 2.0) + cy * cos(u.time / 3.0)) + u.time)
|
|
26
|
+
|
|
27
|
+
cx2 = cx + 0.5 * sin(u.time / 5.0)
|
|
28
|
+
cy2 = cy + 0.5 * cos(u.time / 3.0)
|
|
29
|
+
v3 = sin(sqrt(100.0 * (cx2 * cx2 + cy2 * cy2) + 1.0) + u.time)
|
|
30
|
+
|
|
31
|
+
v = v1 + v2 + v3
|
|
32
|
+
|
|
33
|
+
r = sin(v * PI)
|
|
34
|
+
g = sin(v * PI + 2.0 * PI / 3.0)
|
|
35
|
+
b = sin(v * PI + 4.0 * PI / 3.0)
|
|
36
|
+
|
|
37
|
+
vec3((r + 1.0) * 0.5, (g + 1.0) * 0.5, (b + 1.0) * 0.5)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Initialize display
|
|
42
|
+
window = RBGL::GUI::Window.new(width: WIDTH, height: HEIGHT, title: "Plasma Effect")
|
|
43
|
+
|
|
44
|
+
puts "Plasma Effect - Native Shader DSL"
|
|
45
|
+
puts "Press 'q' or Escape to quit"
|
|
46
|
+
|
|
47
|
+
start_time = Time.now
|
|
48
|
+
frame_count = 0
|
|
49
|
+
last_fps_time = start_time
|
|
50
|
+
running = true
|
|
51
|
+
|
|
52
|
+
buffer = "\x00" * (WIDTH * HEIGHT * 4)
|
|
53
|
+
|
|
54
|
+
while running && !window.should_close?
|
|
55
|
+
time = Time.now - start_time
|
|
56
|
+
|
|
57
|
+
shader.render(buffer, WIDTH, HEIGHT, { time: time })
|
|
58
|
+
|
|
59
|
+
window.set_pixels(buffer)
|
|
60
|
+
|
|
61
|
+
events = window.poll_events_raw
|
|
62
|
+
events.each do |e|
|
|
63
|
+
if e[:type] == :key_press && (e[:key] == 12 || e[:key] == "q")
|
|
64
|
+
running = false
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
frame_count += 1
|
|
69
|
+
now = Time.now
|
|
70
|
+
if now - last_fps_time >= 1.0
|
|
71
|
+
fps = frame_count / (now - last_fps_time)
|
|
72
|
+
puts "FPS: #{fps.round(1)}"
|
|
73
|
+
frame_count = 0
|
|
74
|
+
last_fps_time = now
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
window.close
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Raymarching Sphere - Native Shader DSL
|
|
4
|
+
|
|
5
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
|
|
6
|
+
|
|
7
|
+
require "rbgl"
|
|
8
|
+
require "rlsl"
|
|
9
|
+
|
|
10
|
+
WIDTH = 640
|
|
11
|
+
HEIGHT = 480
|
|
12
|
+
|
|
13
|
+
shader = RLSL.define(:raymarch) do
|
|
14
|
+
uniforms do
|
|
15
|
+
float :time
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
fragment do |frag_coord, resolution, u|
|
|
19
|
+
uv = frag_coord / resolution.y
|
|
20
|
+
|
|
21
|
+
# Ray origin (camera position)
|
|
22
|
+
ro = vec3(0.0, 0.0, -3.0)
|
|
23
|
+
|
|
24
|
+
# Ray direction
|
|
25
|
+
centered = uv - vec2(0.5, 0.5)
|
|
26
|
+
rd = normalize(vec3(centered.x, centered.y, 1.0))
|
|
27
|
+
|
|
28
|
+
# Sphere center (animated)
|
|
29
|
+
sphere_center = vec3(
|
|
30
|
+
sin(u.time) * 0.5,
|
|
31
|
+
cos(u.time * 0.7) * 0.3,
|
|
32
|
+
0.0
|
|
33
|
+
)
|
|
34
|
+
sphere_radius = 0.8
|
|
35
|
+
|
|
36
|
+
# Ray-sphere intersection
|
|
37
|
+
oc = ro - sphere_center
|
|
38
|
+
a = dot(rd, rd)
|
|
39
|
+
b = 2.0 * dot(oc, rd)
|
|
40
|
+
c = dot(oc, oc) - sphere_radius * sphere_radius
|
|
41
|
+
discriminant = b * b - 4.0 * a * c
|
|
42
|
+
|
|
43
|
+
if discriminant > 0.0
|
|
44
|
+
t = (0.0 - b - sqrt(discriminant)) / (2.0 * a)
|
|
45
|
+
if t > 0.0
|
|
46
|
+
# Hit point
|
|
47
|
+
hit = ro + rd * t
|
|
48
|
+
|
|
49
|
+
# Normal at hit point
|
|
50
|
+
normal = normalize(hit - sphere_center)
|
|
51
|
+
|
|
52
|
+
# Light direction (animated)
|
|
53
|
+
light_dir = normalize(vec3(
|
|
54
|
+
sin(u.time * 0.5),
|
|
55
|
+
1.0,
|
|
56
|
+
cos(u.time * 0.3)
|
|
57
|
+
))
|
|
58
|
+
|
|
59
|
+
# Diffuse lighting
|
|
60
|
+
diff = clamp(dot(normal, light_dir), 0.0, 1.0)
|
|
61
|
+
|
|
62
|
+
# Specular
|
|
63
|
+
view_dir = rd * -1.0
|
|
64
|
+
reflect_dir = normal * 2.0 * dot(normal, light_dir) - light_dir
|
|
65
|
+
spec = pow(clamp(dot(view_dir, reflect_dir), 0.0, 1.0), 32.0)
|
|
66
|
+
|
|
67
|
+
# Base color (hue shifts with time)
|
|
68
|
+
base_color = vec3(
|
|
69
|
+
0.5 + 0.5 * sin(u.time),
|
|
70
|
+
0.5 + 0.5 * sin(u.time + 2.0),
|
|
71
|
+
0.5 + 0.5 * sin(u.time + 4.0)
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# Combine
|
|
75
|
+
ambient = 0.1
|
|
76
|
+
base_color * (ambient + diff * 0.7) + vec3(spec * 0.5, spec * 0.5, spec * 0.5)
|
|
77
|
+
else
|
|
78
|
+
# Behind camera
|
|
79
|
+
vec3(0.05, 0.05, 0.1)
|
|
80
|
+
end
|
|
81
|
+
else
|
|
82
|
+
# Background gradient
|
|
83
|
+
t = uv.y
|
|
84
|
+
mix(vec3(0.1, 0.1, 0.2), vec3(0.02, 0.02, 0.05), t)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Initialize display
|
|
90
|
+
window = RBGL::GUI::Window.new(width: WIDTH, height: HEIGHT, title: "Raymarched Sphere")
|
|
91
|
+
|
|
92
|
+
puts "Raymarching Sphere - Native Shader DSL"
|
|
93
|
+
puts "Press 'q' or Escape to quit"
|
|
94
|
+
|
|
95
|
+
start_time = Time.now
|
|
96
|
+
frame_count = 0
|
|
97
|
+
last_fps_time = start_time
|
|
98
|
+
running = true
|
|
99
|
+
|
|
100
|
+
buffer = "\x00" * (WIDTH * HEIGHT * 4)
|
|
101
|
+
|
|
102
|
+
while running && !window.should_close?
|
|
103
|
+
time = Time.now - start_time
|
|
104
|
+
|
|
105
|
+
shader.render(buffer, WIDTH, HEIGHT, { time: time })
|
|
106
|
+
|
|
107
|
+
window.set_pixels(buffer)
|
|
108
|
+
|
|
109
|
+
events = window.poll_events_raw
|
|
110
|
+
events.each do |e|
|
|
111
|
+
if e[:type] == :key_press && (e[:key] == 12 || e[:key] == "q")
|
|
112
|
+
running = false
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
frame_count += 1
|
|
117
|
+
now = Time.now
|
|
118
|
+
if now - last_fps_time >= 1.0
|
|
119
|
+
fps = frame_count / (now - last_fps_time)
|
|
120
|
+
puts "FPS: #{fps.round(1)}"
|
|
121
|
+
frame_count = 0
|
|
122
|
+
last_fps_time = now
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
window.close
|