aboutsummaryrefslogtreecommitdiffstats
path: root/src/com/mbien/opencl/demos/julia3d/rendering_kernel.cl
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/mbien/opencl/demos/julia3d/rendering_kernel.cl')
-rw-r--r--src/com/mbien/opencl/demos/julia3d/rendering_kernel.cl382
1 files changed, 382 insertions, 0 deletions
diff --git a/src/com/mbien/opencl/demos/julia3d/rendering_kernel.cl b/src/com/mbien/opencl/demos/julia3d/rendering_kernel.cl
new file mode 100644
index 0000000..9c25c1b
--- /dev/null
+++ b/src/com/mbien/opencl/demos/julia3d/rendering_kernel.cl
@@ -0,0 +1,382 @@
+/*
+Copyright (c) 2009 David Bucciarelli ([email protected])
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#define GPU_KERNEL
+
+
+#define BOUNDING_RADIUS_2 4.f
+#define ESCAPE_THRESHOLD 1e1f
+#define DELTA 1e-4f
+
+typedef struct {
+ float x, y, z; // position, also color (r,g,b)
+} Vec;
+
+typedef struct {
+ Vec orig, target;
+ Vec dir, x, y;
+} Camera;
+
+typedef struct {
+ unsigned int width, height;
+ int superSamplingSize;
+ int actvateFastRendering;
+ int enableShadow;
+
+ unsigned int maxIterations;
+ float epsilon;
+ float mu[4];
+ float light[3];
+ Camera camera;
+} RenderingConfig;
+
+
+static float4 QuatMult(const float4 q1, const float4 q2) {
+ float4 r;
+
+ // a1a2 - b1b2 - c1c2 - d1d2
+ r.x = q1.x * q2.x - q1.y * q2.y - q1.z * q2.z - q1.w * q2.w;
+ // a1b2 + b1a2 + c1d2 - d1c2
+ r.y = q1.x * q2.y + q1.y * q2.x + q1.z * q2.w - q1.w * q2.z;
+ // a1c2 - b1d2 + c1a2 + d1b2
+ r.z = q1.x * q2.z - q1.y * q2.w + q1.z * q2.x + q1.w * q2.y;
+ // a1d2 + b1c2 - c1b2 + d1a2
+ r.w = q1.x * q2.w + q1.y * q2.z - q1.z * q2.y + q1.w * q2.x;
+
+ return r;
+}
+
+static float4 QuatSqr(const float4 q) {
+ float4 r;
+
+ r.x = q.x * q.x - q.y * q.y - q.z * q.z - q.w * q.w;
+ r.y = 2.f * q.x * q.y;
+ r.z = 2.f * q.x * q.z;
+ r.w = 2.f * q.x * q.w;
+
+ return r;
+}
+
+static void IterateIntersect(float4 *q, float4 *qp, const float4 c, const uint maxIterations) {
+ float4 q0 = *q;
+ float4 qp0 = *qp;
+
+ for (uint i = 0; i < maxIterations; ++i) {
+ qp0 = 2.f * QuatMult(q0, qp0);
+ q0 = QuatSqr(q0) + c;
+
+ if (dot(q0, q0) > ESCAPE_THRESHOLD)
+ break;
+ }
+
+ *q = q0;
+ *qp = qp0;
+}
+
+static float IntersectJulia(const float4 eyeRayOrig, const float4 eyeRayDir,
+ const float4 c, const uint maxIterations, const float epsilon,
+ float4 *hitPoint, uint *steps) {
+ float dist;
+ float4 r0 = eyeRayOrig;
+
+ uint s = 0;
+ do {
+ float4 z = r0;
+ float4 zp = (float4) (1.f, 0.f, 0.f, 0.f);
+
+ IterateIntersect(&z, &zp, c, maxIterations);
+
+ const float normZP = length(zp);
+
+ // We are inside
+ if (normZP == 0.f)
+ break;
+
+ const float normZ = length(z);
+ dist = 0.5f * normZ * log(normZ) / normZP;
+
+ r0 += eyeRayDir * dist;
+ s++;
+ } while ((dist > epsilon) && (dot(r0, r0) < BOUNDING_RADIUS_2));
+
+ *hitPoint = r0;
+ *steps = s;
+ return dist;
+}
+
+#define WORLD_RADIUS 1000.f
+#define WORLD_CENTER ((float4)(0.f, -WORLD_RADIUS - 2.f, 0.f, 0.f))
+
+float IntersectFloorSphere(const float4 eyeRayOrig, const float4 eyeRayDir) {
+ const float4 op = WORLD_CENTER - eyeRayOrig;
+ const float b = dot(op, eyeRayDir);
+ float det = b * b - dot(op, op) + WORLD_RADIUS * WORLD_RADIUS;
+
+ if (det < 0.f)
+ return -1.f;
+ else
+ det = sqrt(det);
+
+ float t = b - det;
+ if (t > 0.f)
+ return t;
+ else {
+ // We are inside, avoid the hit
+ return -1.f;
+ }
+}
+
+float IntersectBoundingSphere(const float4 eyeRayOrig, const float4 eyeRayDir) {
+ const float4 op = -eyeRayOrig;
+ const float b = dot(op, eyeRayDir);
+ float det = b * b - dot(op, op) + BOUNDING_RADIUS_2;
+
+ if (det < 0.f)
+ return -1.f;
+ else
+ det = sqrt(det);
+
+ float t = b - det;
+ if (t > 0.f)
+ return t;
+ else {
+ t = b + det;
+
+ if (t > 0.f) {
+ // We are inside, start from the ray origin
+ return 0.0f;
+ } else
+ return -1.f;
+ }
+}
+
+static float4 NormEstimate(const float4 p, const float4 c,
+ const float delta, const uint maxIterations) {
+ float4 N;
+ float4 qP = p;
+ float gradX, gradY, gradZ;
+
+ float4 gx1 = qP - (float4) (DELTA, 0.f, 0.f, 0.f);
+ float4 gx2 = qP + (float4) (DELTA, 0.f, 0.f, 0.f);
+ float4 gy1 = qP - (float4) (0.f, DELTA, 0.f, 0.f);
+ float4 gy2 = qP + (float4) (0.f, DELTA, 0.f, 0.f);
+ float4 gz1 = qP - (float4) (0.f, 0.f, DELTA, 0.f);
+ float4 gz2 = qP + (float4) (0.f, 0.f, DELTA, 0.f);
+
+ for (uint i = 0; i < maxIterations; ++i) {
+ gx1 = QuatSqr(gx1) + c;
+ gx2 = QuatSqr(gx2) + c;
+ gy1 = QuatSqr(gy1) + c;
+ gy2 = QuatSqr(gy2) + c;
+ gz1 = QuatSqr(gz1) + c;
+ gz2 = QuatSqr(gz2) + c;
+ }
+
+ gradX = length(gx2) - length(gx1);
+ gradY = length(gy2) - length(gy1);
+ gradZ = length(gz2) - length(gz1);
+
+ N = normalize((float4) (gradX, gradY, gradZ, 0.f));
+
+ return N;
+}
+
+static float4 Phong(const float4 light, const float4 eye, const float4 pt, const float4 N, const float4 diffuse) {
+
+ const float4 ambient = (float4) (0.05f, 0.05f, 0.05f, 0.f);
+ float4 L = normalize(light - pt);
+ float NdotL = dot(N, L);
+ if (NdotL < 0.f)
+ return diffuse * ambient;
+
+ const float specularExponent = 30.f;
+ const float specularity = 0.65f;
+
+ float4 E = normalize(eye - pt);
+ float4 H = (L + E) * (float) 0.5f;
+
+ return diffuse * NdotL +
+ specularity * pow(dot(N, H), specularExponent) +
+ diffuse * ambient;
+}
+
+kernel void JuliaGPU( global float *pixels,
+ const global RenderingConfig *config,
+ int enableAccumulation,
+ float sampleX,
+ float sampleY ) {
+
+ const int gid = get_global_id(0);
+ unsigned width = config->width;
+ unsigned height = config->height;
+
+ const unsigned int x = gid % width;
+ const int y = gid / width;
+
+ // Check if we have to do something
+ if (y >= height)
+ return;
+
+ const float epsilon = config->actvateFastRendering ? (config->epsilon * (1.f / 0.75f)) : config->epsilon;
+ const uint maxIterations = max(1u, config->actvateFastRendering ? (config->maxIterations - 1) : config->maxIterations);
+
+ const float4 mu = (float4)(config->mu[0], config->mu[1], config->mu[2], config->mu[3]);
+ const float4 light = (float4) (config->light[0], config->light[1], config->light[2], 0.f);
+ const global Camera *camera = &config->camera;
+
+ //--------------------------------------------------------------------------
+ // Calculate eye ray
+ //--------------------------------------------------------------------------
+
+ const float invWidth = 1.f / width;
+ const float invHeight = 1.f / height;
+ const float kcx = (x + sampleX) * invWidth - .5f;
+ const float4 kcx4 = (float4) kcx;
+ const float kcy = (y + sampleY) * invHeight - .5f;
+ const float4 kcy4 = (float4) kcy;
+
+ const float4 cameraX = (float4) (camera->x.x, camera->x.y, camera->x.z, 0.f);
+ const float4 cameraY = (float4) (camera->y.x, camera->y.y, camera->y.z, 0.f);
+ const float4 cameraDir = (float4) (camera->dir.x, camera->dir.y, camera->dir.z, 0.f);
+ const float4 cameraOrig = (float4) (camera->orig.x, camera->orig.y, camera->orig.z, 0.f);
+
+ const float4 eyeRayDir = normalize(cameraX * kcx4 + cameraY * kcy4 + cameraDir);
+ const float4 eyeRayOrig = eyeRayDir * (float4) 0.1f + cameraOrig;
+
+ //--------------------------------------------------------------------------
+ // Check if we hit the bounding sphere
+ //--------------------------------------------------------------------------
+
+ float distSet = IntersectBoundingSphere(eyeRayOrig, eyeRayDir);
+ float4 hitPoint;
+ if (distSet >= 0.f) {
+ //--------------------------------------------------------------------------
+ // Find the intersection with the set
+ //--------------------------------------------------------------------------
+
+ uint steps;
+ float4 rayOrig = eyeRayOrig + eyeRayDir * (float4) distSet;
+ distSet = IntersectJulia(rayOrig, eyeRayDir, mu, maxIterations,
+ epsilon, &hitPoint, &steps);
+ if (distSet > epsilon)
+ distSet = -1.f;
+ }
+
+ //--------------------------------------------------------------------------
+ // Check if we hit the floor
+ //--------------------------------------------------------------------------
+
+ float distFloor = IntersectFloorSphere(eyeRayOrig, eyeRayDir);
+
+ //--------------------------------------------------------------------------
+ // Select the hit point
+ //--------------------------------------------------------------------------
+
+ int doShade = 0;
+ int useAO = 1;
+ float4 diffuse, n, color;
+ if ((distSet < 0.f) && (distFloor < 0.f)) {
+ // Sky hit
+ color = (float4) (0.f, 0.1f, 0.3f, 0.f);
+ } else if ((distSet >= 0.f) && ((distFloor < 0.f) || (distSet <= distFloor))) {
+ // Set hit
+ diffuse = (float4) (1.f, 0.35f, 0.15f, 0.f);
+ n = NormEstimate(hitPoint, mu, distSet, maxIterations);
+ doShade = 1;
+ } else if ((distFloor >= 0.f) && ((distSet < 0.f) || (distFloor <= distSet))) {
+ // Floor hit
+ hitPoint = eyeRayOrig + eyeRayDir * (float4) distFloor;
+ n = hitPoint - WORLD_CENTER;
+ n = normalize(n);
+ // The most important feature in a ray tracer: a checker texture !
+ const int ix = (hitPoint.x > 0.f) ? hitPoint.x : (1.f - hitPoint.x);
+ const int iz = (hitPoint.z > 0.f) ? hitPoint.z : (1.f - hitPoint.z);
+ if ((ix + iz) % 2)
+ diffuse = (float4) (0.75f, 0.75f, 0.75f, 0.f);
+ else
+ diffuse = (float4) (0.75f, 0.f, 0.f, 0.f);
+ doShade = 1;
+ useAO = 0;
+ }
+
+ //--------------------------------------------------------------------------
+ // Select the shadow pass
+ //--------------------------------------------------------------------------
+
+ if (doShade) {
+ float shadowFactor = 1.f;
+ if (config->enableShadow) {
+ float4 L = normalize(light - hitPoint);
+ float4 rO = hitPoint + n * 1e-2f;
+ float4 shadowHitPoint;
+
+ // Check bounding sphere
+ float shadowDistSet = IntersectBoundingSphere(rO, L);
+ if (shadowDistSet >= 0.f) {
+ uint steps;
+
+ rO = rO + L * (float4) shadowDistSet;
+ shadowDistSet = IntersectJulia(rO, L, mu, maxIterations, epsilon,
+ &shadowHitPoint, &steps);
+ if (shadowDistSet < epsilon) {
+ if (useAO) {
+ // Use steps count to simulate ambient occlusion
+ shadowFactor = 0.6f - min(steps / 255.f, 0.5f);
+ } else
+ shadowFactor = 0.6f;
+ }
+ } else
+ shadowDistSet = -1.f;
+ }
+
+ //--------------------------------------------------------------------------
+ // Direct lighting of hit point
+ //--------------------------------------------------------------------------
+
+ color = Phong(light, eyeRayOrig, hitPoint, n, diffuse) * shadowFactor;
+ }
+
+ //--------------------------------------------------------------------------
+ // Write pixel
+ //--------------------------------------------------------------------------
+
+ int offset = 3 * (x + y * width);
+ color = clamp(color, (float4) (0.f, 0.f, 0.f, 0.f), (float4) (1.f, 1.f, 1.f, 0.f));
+ if (enableAccumulation) {
+ pixels[offset++] += color.s0;
+ pixels[offset++] += color.s1;
+ pixels[offset ] += color.s2;
+ } else {
+ pixels[offset++] = color.s0;
+ pixels[offset++] = color.s1;
+ pixels[offset ] = color.s2;
+ }
+}
+
+kernel void multiply(global float *array, const int numElements, const float s) {
+ const int gid = get_global_id(0);
+ if (gid >= numElements) {
+ return;
+ }
+ array[gid] *= s;
+}