Files
OpenSpace/kernels/RaycasterBricks.cl
Hans-Christian Helltegen 658ae13d96 Added flare to OpenSpace
2014-03-19 16:41:12 -04:00

298 lines
9.5 KiB
Common Lisp

// Needs to mirror struct on host
struct KernelConstants {
float stepSize;
float intensity;
int numBoxesPerAxis;
};
// Linearly interpolate between two values. Distance
// is assumed to be normalized.
float Lerp(float _v0, float _v1, float _d) {
return _v0*(1.0 - _d) + _v1*_d;
}
// Turn normalized [0..1] cartesian coordinates
// to normalized spherical [0..1] coordinates
float3 CartesianToSpherical(float3 _cartesian) {
// Put cartesian in [-1..1] range first
_cartesian = (float3)(-1.0) + 2.0* _cartesian;
float r = length(_cartesian);
float theta, phi;
if (r == 0.0) {
theta = phi = 0.0;
} else {
theta = acospi(_cartesian.z/r);
phi = (M_PI + atan2(_cartesian.y, _cartesian.x)) / (2.0*M_PI);
}
r = r / native_sqrt(3.0);
// Sampler ignores w component
return (float3)(r, theta, phi);
}
float4 TransferFunction(__global __read_only float *_tf, float _i) {
// TODO remove hard-coded value and change to 1D texture
int i0 = (int)floor(1023.0*_i);
int i1 = (i0 < 1023) ? i0+1 : i0;
float di = _i - floor(_i);
float tfr0 = _tf[i0*4+0];
float tfr1 = _tf[i1*4+0];
float tfg0 = _tf[i0*4+1];
float tfg1 = _tf[i1*4+1];
float tfb0 = _tf[i0*4+2];
float tfb1 = _tf[i1*4+2];
float tfa0 = _tf[i0*4+3];
float tfa1 = _tf[i1*4+3];
float tfr = Lerp(tfr0, tfr1, di);
float tfg = Lerp(tfg0, tfg1, di);
float tfb = Lerp(tfb0, tfb1, di);
float tfa = Lerp(tfa0, tfa1, di);
return (float4)(tfr, tfg, tfb, tfa);
}
// Translates a global volume coordinate [0..1] to a box coordinate,
// given a number of boxes that fit along each axis
int3 BoxCoords(float3 _globalCoords, int _boxesPerAxis) {
int3 boxCoords = convert_int3(floor(_globalCoords * (float)_boxesPerAxis));
return clamp(boxCoords, (int3)(0, 0, 0), (int3)(_boxesPerAxis-1));
}
// Calculate global volume coordinates for the corners of a box, given
// the box's coordinates [0..NumBoxesPerAxis] and the number of boxes
// that fit along each axis
void BoxCorners(int3 _boxCoords, float3 *_minCorner, float3 *_maxCorner,
int _boxesPerAxis) {
// TODO figure out a better way to handle offsets
*_minCorner = convert_float3(_boxCoords) / (float)_boxesPerAxis +
(float3)(0.00001);
*_maxCorner = convert_float3((_boxCoords+1)) / (float)_boxesPerAxis -
(float3)(0.00001);
}
// Intersect a ray specifiec by origin and direction
// with a box specified by opposing corners.
// Returns intersect/no intersect along with t values
// for intersections points.
bool IntersectBox(float3 _boundsMin, float3 _boundsMax,
float3 _rayO, float3 _rayD,
float *_tMinOut, float *_tMaxOut) {
float _tMin, _tMax, tYMin, tYMax, tZMin, tZMax;
float divx = 1.0/_rayD.x;
if (divx >= 0.0) {
_tMin = (_boundsMin.x - _rayO.x) * divx;
_tMax = (_boundsMax.x - _rayO.x) * divx;
} else {
_tMin = (_boundsMax.x - _rayO.x) * divx;
_tMax = (_boundsMin.x - _rayO.x) * divx;
}
float divy = 1.0/_rayD.y;
if (divy >= 0.0) {
tYMin = (_boundsMin.y - _rayO.y) * divy;
tYMax = (_boundsMax.y - _rayO.y) * divy;
} else {
tYMin = (_boundsMax.y - _rayO.y) * divy;
tYMax = (_boundsMin.y - _rayO.y) * divy;
}
if ( (_tMin > tYMax || tYMin > _tMax) ) return false;
if (tYMin > _tMin) _tMin = tYMin;
if (tYMax < _tMax) _tMax = tYMax;
float divz = 1.0/_rayD.z;
if (divz >= 0.0) {
tZMin = (_boundsMin.z - _rayO.z) * divz;
tZMax = (_boundsMax.z - _rayO.z) * divz;
} else {
tZMin = (_boundsMax.z - _rayO.z) * divz;
tZMax = (_boundsMin.z - _rayO.z) * divz;
}
if ( (_tMin > tZMax || tZMin > _tMax) ) return false;
if (tZMin > _tMin) _tMin = tZMin;
if (tZMax < _tMax) _tMax = tZMax;
*_tMinOut = _tMin;
*_tMaxOut = _tMax;
return ( (_tMin < 1e20 && _tMax > -1e20 ) );
}
int3 BrickAtlasCoords(int3 _boxCoords,
__global int *_boxList,
int _numBoxesPerAxis) {
int boxIndex = _boxCoords.x +
_boxCoords.y*_numBoxesPerAxis +
_boxCoords.z*_numBoxesPerAxis*_numBoxesPerAxis;
int x = _boxList[5*boxIndex+1];
int y = _boxList[5*boxIndex+2];
int z = _boxList[5*boxIndex+3];
return (int3)(x, y, z);
}
int BrickSize(int3 _boxCoords,
__global int * _brickList,
int _numBoxesPerAxis) {
int boxIndex = _boxCoords.x +
_boxCoords.y*_numBoxesPerAxis +
_boxCoords.z*_numBoxesPerAxis*_numBoxesPerAxis;
return _brickList[5*boxIndex+4];
}
// Traverse one brick, sampling individual voxels
float4 TraverseBrick(__global __read_only image3d_t _textureAtlas,
__global __read_only float *_tf,
int3 _brickAtlasCoords, int _numBoxesPerAxis,
int _brickSize, float _globalStepSize,
float3 _brickEntry, float3 _brickExit) {
// Number of bricks of this size for each axix
int numBricksPerAxis = _numBoxesPerAxis/_brickSize;
// Find brick coordinates [0 .. NumBricksPerAxis]
// Also taking brick size into account
int3 brickCoords = BoxCoords(_brickEntry, numBricksPerAxis);
float numBoxesPerAxisf = (float)_numBoxesPerAxis;
float numBricksPerAxisf = (float)numBricksPerAxis;
// Calculate local brick entry and exit coordinates [0..1]
float3 localEntryCoords = numBricksPerAxisf *
(_brickEntry - convert_float3(brickCoords)/numBricksPerAxisf);
float3 localExitCoords = numBricksPerAxisf *
(_brickExit - convert_float3(brickCoords)/numBricksPerAxisf);
// Calculate offset into texture atlas
float3 offset = convert_float3(_brickAtlasCoords)/numBoxesPerAxisf;
float3 atlasEntry = localEntryCoords/numBoxesPerAxisf + offset;
float3 atlasExit = localExitCoords/numBoxesPerAxisf + offset;
float3 direction = atlasExit - atlasEntry;
float maxDistance = length(atlasExit - atlasEntry);
direction = normalize(direction);
float localStepSize = _globalStepSize * (float)_brickSize;
float4 color = (float4)(0.0, 0.0, 0.0, 0.0);
float traversed = 0.0;
float3 samplePoint = atlasEntry;
float4 spherical;
const sampler_t atlasSampler = CLK_FILTER_LINEAR |
CLK_NORMALIZED_COORDS_TRUE |
CLK_ADDRESS_CLAMP_TO_EDGE;
while (traversed < maxDistance) {
spherical.xyz = CartesianToSpherical(samplePoint);
spherical.w = 1.0;
// Texture stores intensity in R channel
float i = read_imagef(_textureAtlas, atlasSampler, spherical).x;
// Front-to-back compositing
float4 tf = TransferFunction(_tf, i);
color += (1.0 - color.w)*tf;
traversed += localStepSize;
samplePoint += localStepSize * direction;
}
return color;
}
__kernel void
Raycaster(__global __read_only image2d_t _cubeFront,
__global __read_only image2d_t _cubeBack,
__global __write_only image2d_t _output,
__global __read_only image3d_t _textureAtlas,
__constant struct KernelConstants *_constants,
__global __read_only float *_tf,
__global int *_brickList) {
// Kernel should be launched in 2D with one work item per pixel
int idx = get_global_id(0);
int idy = get_global_id(1);
int2 intCoords = (int2)(idx, idy);
// Sampler for texture reading
const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE;
// Read from textures
float4 cubeFrontColor = read_imagef(_cubeFront, sampler, intCoords);
float4 cubeBackColor = read_imagef(_cubeBack, sampler, intCoords);
// Figure out the direction
float3 direction = (cubeBackColor-cubeFrontColor).xyz;
float maxDistance = length(direction);
direction = normalize(direction);
float3 origin = cubeFrontColor.xyz - 0.001 * direction;
// Keep track of distance traversed
float traversed = 0.0;
// Sum colors
float stepSize = _constants->stepSize;
float3 samplePoint = cubeFrontColor.xyz;
float4 color = (float4)(0.0, 0.0, 0.0, 0.0);
// TODO temp
float a, b;
if (IntersectBox((float3)(0.0), (float3)(1.0), origin, direction, &a, &b)) {
color += (float4)(0.0, 0.0, 0.08, 1.0);
}
while (traversed < maxDistance) {
int numBoxesPerAxis = _constants->numBoxesPerAxis;
// Convert the sample point to coords
int3 boxCoords = BoxCoords(samplePoint, numBoxesPerAxis);
// Lookup brick coords and size
int3 brickAtlasCoords = BrickAtlasCoords(boxCoords,
_brickList,
numBoxesPerAxis);
int brickSize = BrickSize(boxCoords, _brickList, numBoxesPerAxis);
// Calculate the box's corners
float3 minCorner, maxCorner;
BoxCorners(boxCoords, &minCorner, &maxCorner, numBoxesPerAxis);
// Intersect ray with box
float tMin, tMax;
IntersectBox(minCorner, maxCorner, origin, direction, &tMin, &tMax);
float3 brickEntry = origin + tMin*direction;
// TODO calculate BRICK exit
float3 brickExit = origin + tMax*direction;
float brickDist = length(brickExit - brickEntry);
// Traverse brick
float4 brickColor = TraverseBrick(_textureAtlas, _tf,
brickAtlasCoords, numBoxesPerAxis,
brickSize, stepSize,
brickEntry, brickExit);
// Compositing
color += (1.0 - brickColor.w)*brickColor;
// Advance ray
samplePoint = brickExit;
traversed += brickDist;
// color += (float4)(i, i, i, 1.0);
samplePoint += direction * stepSize;
traversed += stepSize;
}
// Output
float intensity = _constants->intensity;
color *= intensity*stepSize;
// Write to image
write_imagef(_output, intCoords, color);
}