1 |
#!/usr/bin/perl |
2 |
|
3 |
use OpenGL ":all"; |
4 |
use OpenCL; |
5 |
|
6 |
my $S = $ARGV[0] || 256; # window/texture size, smaller is faster |
7 |
|
8 |
# open a window and create a gl texture |
9 |
OpenGL::glpOpenWindow width => $S, height => $S; |
10 |
my $texid = glGenTextures_p 1; |
11 |
glBindTexture GL_TEXTURE_2D, $texid; |
12 |
glTexImage2D_c GL_TEXTURE_2D, 0, GL_RGBA8, $S, $S, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0; |
13 |
|
14 |
# find and use the first opencl device that let's us get a shared opengl context |
15 |
my $platform; |
16 |
my $dev; |
17 |
my $ctx; |
18 |
|
19 |
for (OpenCL::platforms) { |
20 |
$platform = $_; |
21 |
for ($platform->devices) { |
22 |
$dev = $_; |
23 |
$ctx = $platform->context (undef, [$dev]) |
24 |
and last; |
25 |
} |
26 |
} |
27 |
|
28 |
$ctx |
29 |
or die "cannot find suitable OpenCL device\n"; |
30 |
|
31 |
my $queue = $ctx->queue ($dev); |
32 |
|
33 |
# now the boring opencl code |
34 |
my $src = <<EOF; |
35 |
kernel void |
36 |
juliatunnel (write_only image2d_t img, float time) |
37 |
{ |
38 |
int2 xy = (int2)(get_global_id (0), get_global_id (1)); |
39 |
float2 p = convert_float2 (xy) / $S.f * 2.f - 1.f; |
40 |
|
41 |
float2 m = (float2)(1.f, p.y) / fabs (p.x); // tunnel |
42 |
m.x = fabs (fmod (m.x + time * 0.05f, 4.f) - 2.f); |
43 |
|
44 |
float2 z = m; |
45 |
float2 c = (float2)(sin (time * 0.01133f), cos (time * 0.02521f)); |
46 |
|
47 |
for (int i = 0; i < 25 && dot (z, z) < 4.f; ++i) // standard julia |
48 |
z = (float2)(z.x * z.x - z.y * z.y, 2.f * z.x * z.y) + c; |
49 |
|
50 |
float3 colour = (float3)(z.x, z.y, atan2 (z.y, z.x)); |
51 |
write_imagef (img, xy, (float4)(colour * p.x * p.x, 1.)); |
52 |
} |
53 |
EOF |
54 |
|
55 |
my $prog = $ctx->build_program ($src); |
56 |
my $kernel = $prog->kernel ("juliatunnel"); |
57 |
|
58 |
my $tex = $ctx->image2d (OpenCL::MEM_WRITE_ONLY, OpenCL::RGBA, OpenCL::UNORM_INT8, $S, $S); |
59 |
|
60 |
# program compiled, kernel ready, now draw and loop |
61 |
|
62 |
for (my $time; ; ++$time) { |
63 |
# configure and run our kernel |
64 |
$kernel->setf ("mf", $tex, $time*2); # mf = memory object, float |
65 |
$queue->nd_range_kernel ($kernel, undef, [$S, $S], undef); |
66 |
|
67 |
# read image |
68 |
$queue->read_image ($tex, 0, 0, 0, 0, $S, $S, 1, 0, 0, my $data); |
69 |
|
70 |
# wait |
71 |
$queue->finish; |
72 |
|
73 |
# upload texture |
74 |
glTexSubImage2D_s GL_TEXTURE_2D, 0, 0, 0, $S, $S, GL_RGBA, GL_UNSIGNED_BYTE, $data; |
75 |
|
76 |
# now draw the texture, the defaults should be all right |
77 |
glTexParameterf GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST; |
78 |
|
79 |
glEnable GL_TEXTURE_2D; |
80 |
glBegin GL_QUADS; |
81 |
glTexCoord2f 0, 1; glVertex3i -1, -1, -1; |
82 |
glTexCoord2f 0, 0; glVertex3i 1, -1, -1; |
83 |
glTexCoord2f 1, 0; glVertex3i 1, 1, -1; |
84 |
glTexCoord2f 1, 1; glVertex3i -1, 1, -1; |
85 |
glEnd; |
86 |
|
87 |
# with glDrawPixels, you would simply do: |
88 |
# glDrawPixels_s $S, $S, GL_RGBA, GL_UNSIGNED_BYTE, $data; |
89 |
# and then start to worry a lot about the coordinate system |
90 |
# used in the opencl kernel |
91 |
|
92 |
glXSwapBuffers; |
93 |
|
94 |
select undef, undef, undef, 1/60; |
95 |
} |