COSC-3P93-Project/Step 3/resources/opencl/sphereray.cl

202 lines
6.9 KiB
Common Lisp
Raw Normal View History

2022-12-10 14:25:09 -05:00
// these values get dynamically defined by the preprocessor
#define maxTriangleCount 336
#define objectCount 6
#define imageWidth 800
#define imageHeight 600
2022-12-12 02:07:59 -05:00
/*#define imageOrigin (float3)(0,0,0)
#define horizontalAxis (float3)(0,0,0)
#define verticalAxis (float3)(0,0,0)
#define cameraPosition (float3)(0,0,0) */
2022-12-10 14:25:09 -05:00
2022-12-12 02:07:59 -05:00
#define MAX_DEPTH 50
#define MAX_PER_PIXEL 50
2022-12-10 14:25:09 -05:00
#define PI 3.1415926535897
2022-12-12 02:07:59 -05:00
#include "randoms_git.cl"
2022-12-10 14:25:09 -05:00
struct Ray {
// the starting point for our ray
2022-12-12 02:07:59 -05:00
float3 start;
2022-12-10 14:25:09 -05:00
// and the direction it is currently traveling
2022-12-12 02:07:59 -05:00
float3 direction;
float3 inverseDirection;
2022-12-10 14:25:09 -05:00
};
struct HitData {
// the hit point on the object
2022-12-12 02:07:59 -05:00
float3 hitPoint;
2022-12-10 14:25:09 -05:00
// the normal of that hit point
2022-12-12 02:07:59 -05:00
float3 normal;
2022-12-10 14:25:09 -05:00
// the length of the vector from its origin in its direction.
float length;
};
2022-12-12 02:07:59 -05:00
// required because for some reason OpenCL stores vectors in a really weird byte order??
// this prevents all of the graphical issues + allows us to assume the order *no platform dependance*
struct Vec {
float x, y, z;
2022-12-10 14:25:09 -05:00
};
2022-12-12 02:07:59 -05:00
float3 randomVector(unsigned long seed){
pcg6432_state state;
pcg6432_seed(&state, seed);
return ((float3)(pcg6432_float(state), pcg6432_float(state), pcg6432_float(state)) * 2) - 1;
2022-12-10 14:25:09 -05:00
}
2022-12-12 02:07:59 -05:00
float3 along(struct Ray ray, float length) {
return ray.start + length * ray.direction;
2022-12-10 14:25:09 -05:00
}
2022-12-12 02:07:59 -05:00
float lengthSquared(float3 vec){
return vec.x * vec.x + vec.y * vec.y + vec.z * vec.z;
2022-12-10 14:25:09 -05:00
}
2022-12-12 02:07:59 -05:00
float magnitude(float3 vec){
return sqrt(lengthSquared(vec));
2022-12-10 14:25:09 -05:00
}
2022-12-12 02:07:59 -05:00
struct Ray projectRay(__global struct Vec* cameraData, float x, float y){
2022-12-10 14:25:09 -05:00
float transformedX = (x / (imageWidth - 1));
float transformedY = (y / (imageHeight - 1));
2022-12-12 02:07:59 -05:00
float3 cameraPosition = (float3)(cameraData[0].x, cameraData[0].y, cameraData[0].z);
float3 verticalAxis = (float3)(cameraData[1].x, cameraData[1].y, cameraData[1].z);
float3 horizontalAxis = (float3)(cameraData[2].x, cameraData[2].y, cameraData[2].z);
float3 imageOrigin = (float3)(cameraData[3].x, cameraData[3].y, cameraData[3].z);
2022-12-10 14:25:09 -05:00
struct Ray ray;
ray.start = cameraPosition;
2022-12-12 02:07:59 -05:00
ray.direction = (imageOrigin + (transformedX * horizontalAxis) + (transformedY * verticalAxis)) - cameraPosition;
2022-12-10 14:25:09 -05:00
ray.inverseDirection = 1.0f / ray.direction;
return ray;
}
2022-12-12 02:07:59 -05:00
bool checkIfHit(struct HitData* data, struct Ray ray, float3 position, float radius, float min, float max){
2022-12-10 14:25:09 -05:00
float radiusSquared = radius * radius;
2022-12-12 02:07:59 -05:00
float3 rayWRTSphere = ray.start - position;
2022-12-10 14:25:09 -05:00
// now determine the discriminant for the quadratic formula for the function of line sphere intercept
float a = lengthSquared(ray.direction);
float b = dot(rayWRTSphere, ray.direction);
float c = lengthSquared(rayWRTSphere) - radiusSquared;
// > 0: the hit has two roots, meaning we hit both sides of the sphere
// = 0: the ray has one root, we hit the edge of the sphere
// < 0: ray isn't inside the sphere.
float discriminant = b * b - (a * c);
// < 0: ray isn't inside the sphere. Don't need to bother calculating the roots.
if (discriminant < 0) {
return false;
}
// now we have to find the root which exists inside our range [min,max]
float root = (-b - sqrt(discriminant)) / a;
// if the first root isn't in our range
if (root < min || root > max) {
// check the second root
root = (-b + sqrt(discriminant)) / a;
if (root < min || root > max) {
// if the second isn't in the range then we also must return false.
return false;
}
}
// the hit point is where the ray is when extended to the root
2022-12-12 02:07:59 -05:00
float3 RayAtRoot = along(ray,root);
2022-12-10 14:25:09 -05:00
// The normal of a sphere is just the point of the hit minus the center position
2022-12-12 02:07:59 -05:00
float3 normal = (RayAtRoot - position) / radius;
2022-12-10 14:25:09 -05:00
// have to invert the v since we have to invert the v again later due to triangles
data->hitPoint = RayAtRoot;
data->normal = normal;
data->length = root;
return true;
}
2022-12-12 02:07:59 -05:00
bool scatter(struct Ray* ray, struct HitData data, int currentDepth){
const float EPSILON = 0.0000001f;
int x = get_global_id(0);
int y = get_global_id(1);
2022-12-12 02:07:59 -05:00
unsigned long seed = x * y * currentDepth;
float3 newRay = data.normal + normalize(randomVector(seed));
2022-12-12 02:07:59 -05:00
// rays that are close to zero are liable to floating point precision errors
if (newRay.x < EPSILON && newRay.y < EPSILON && newRay.z < EPSILON)
newRay = data.normal;
ray->start = data.hitPoint;
ray->direction = newRay;
ray->inverseDirection = 1/ray->direction;
return true;
}
int checkWorldForIntersection(struct HitData* hit, struct Ray ray){
const float4 positions[] = {
(float4)(0, 1, -2, 1.0f),
(float4)(0, -100.0f, 0, 100.0f),
(float4)(0, 1, 0, 1.0f),
(float4)(0, 1, 5, 1.0f),
(float4)(10, 5, 5, 1.0f),
};
hit->length = 100000000.0f;
int hasHit = 0;
for (int i = 0; i < 5; i++){
if (checkIfHit( hit, ray, (float3)(positions[i].x, positions[i].y, positions[i].z), positions[i].w, 0.001f, hit->length )){
hasHit = i+1;
}
}
return hasHit;
}
2022-12-10 14:25:09 -05:00
2022-12-12 02:07:59 -05:00
float4 raycastI(struct Ray ray){
const float4 colors[] = {
(float4)(1.0f, 0.0f, 0.0f, 1.0f),
(float4)(0.0f,1.0f,0.0f, 1.0f),
(float4)(0.0f,0.0f,1.0f, 1.0f),
(float4)(0.0f,1.0f,1.0f, 1.0f),
(float4)(1.0f,0.0f,1.0f, 1.0f)
};
struct Ray localRay = ray;
float4 localColor = (float4)(1.0f);
for (int _ = 0; _ < MAX_DEPTH; _++){
struct HitData hit;
int hitIndex = checkWorldForIntersection(&hit, localRay);
if ( hitIndex ){
if (scatter(&localRay, hit, _)){
localColor = localColor * colors[hitIndex-1];
} else {
localColor = (float4)(0.0,0.0,0.0,0.0);
break;
}
} else {
// since we didn't hit, we hit the sky.
localColor = localColor * (float4)(0.5, 0.7, 1.0, 1.0);
// if we don't hit we cannot keep looping.
break;
}
localColor += localColor;
}
return localColor;
}
__kernel void raycast(__write_only image2d_t outputImage, __global unsigned char* objects, __global struct Vec* cameraData) {
2022-12-10 14:25:09 -05:00
unsigned long currentByte = 0;
int x = get_global_id(0);
int y = get_global_id(1);
float4 color = (float4)(0.0);
2022-12-12 02:07:59 -05:00
for (int i = 0; i < MAX_PER_PIXEL; i++){
pcg6432_state state;
unsigned long seed = x * y * i;
pcg6432_seed(&state, seed);
color = color + raycastI(projectRay(cameraData, x + pcg6432_float(state), y + pcg6432_float(state)));
2022-12-10 14:25:09 -05:00
}
2022-12-12 02:07:59 -05:00
float scaleFactor = 1.0 / MAX_PER_PIXEL;
write_imagef(outputImage, (int2)(x, y), (float4)(sqrt(color.x * scaleFactor), sqrt(color.y * scaleFactor), sqrt(color.z * scaleFactor), 1.0f));
//pcg6432_state state;
//unsigned long seed = x * y;
//pcg6432_seed(&state, seed);
//write_imagef(outputImage, (int2)(x, y), (float4)(randomVector(state), 1.0f));
2022-12-10 14:25:09 -05:00
}