mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-01-01 09:10:18 -06:00
298 lines
9.5 KiB
Common Lisp
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);
|
|
|
|
}
|
|
|