mirror of
https://github.com/MartinOpat/cuda-based-raytrace.git
synced 2025-06-07 02:13:10 +02:00
Moved transfer function functions to their own file
This commit is contained in:
@@ -5,214 +5,12 @@
|
||||
|
||||
#include "linalg/linalg.h"
|
||||
#include "consts.h"
|
||||
#include "transferFunction.h"
|
||||
#include "cuda_error.h"
|
||||
#include "shading.h"
|
||||
|
||||
#include <iostream>
|
||||
#include "objs/sphere.h"
|
||||
#include <curand_kernel.h>
|
||||
|
||||
// TODO: Probbably move this transfer function business into a different file
|
||||
// Samples the voxel nearest to the given coordinates. TODO: Can be re-used in other places so move
|
||||
__device__ float sampleVolumeNearest(float* volumeData, const int volW, const int volH, const int volD, int vx, int vy, int vz) {
|
||||
// x <-> height, y <-> width, z <-> depth <--- So far this is the best one
|
||||
if (vx < 0) vx = 0;
|
||||
if (vy < 0) vy = 0;
|
||||
if (vz < 0) vz = 0;
|
||||
if (vx >= volH) vx = volH - 1;
|
||||
if (vy >= volW) vy = volW - 1;
|
||||
if (vz >= volD) vz = volD - 1;
|
||||
|
||||
int idx = vz * volW * volH + vx * volW + vy;
|
||||
return volumeData[idx];
|
||||
}
|
||||
|
||||
// tri-linear interpolation - ready if necessary (but no visible improvement for full volume)
|
||||
__device__ float sampleVolumeTrilinear(float* volumeData, const int volW, const int volH, const int volD, float fx, float fy, float fz) {
|
||||
int ix = (int)floorf(fx);
|
||||
int iy = (int)floorf(fy);
|
||||
int iz = (int)floorf(fz);
|
||||
|
||||
// Clamp indices to valid range
|
||||
int ix1 = min(ix + 1, volW - 1);
|
||||
int iy1 = min(iy + 1, volH - 1);
|
||||
int iz1 = min(iz + 1, volD - 1);
|
||||
ix = max(ix, 0);
|
||||
iy = max(iy, 0);
|
||||
iz = max(iz, 0);
|
||||
|
||||
// Compute weights
|
||||
float dx = fx - ix;
|
||||
float dy = fy - iy;
|
||||
float dz = fz - iz;
|
||||
|
||||
// Sample values
|
||||
float c00 = sampleVolumeNearest(volumeData, volW, volH, volD, ix, iy, iz) * (1.0f - dx) +
|
||||
sampleVolumeNearest(volumeData, volW, volH, volD, ix1, iy, iz) * dx;
|
||||
float c10 = sampleVolumeNearest(volumeData, volW, volH, volD, ix, iy1, iz) * (1.0f - dx) +
|
||||
sampleVolumeNearest(volumeData, volW, volH, volD, ix1, iy1, iz) * dx;
|
||||
float c01 = sampleVolumeNearest(volumeData, volW, volH, volD, ix, iy, iz1) * (1.0f - dx) +
|
||||
sampleVolumeNearest(volumeData, volW, volH, volD, ix1, iy, iz1) * dx;
|
||||
float c11 = sampleVolumeNearest(volumeData, volW, volH, volD, ix, iy1, iz1) * (1.0f - dx) +
|
||||
sampleVolumeNearest(volumeData, volW, volH, volD, ix1, iy1, iz1) * dx;
|
||||
|
||||
float c0 = c00 * (1.0f - dy) + c10 * dy;
|
||||
float c1 = c01 * (1.0f - dy) + c11 * dy;
|
||||
|
||||
return c0 * (1.0f - dz) + c1 * dz;
|
||||
}
|
||||
|
||||
|
||||
__device__ float opacityFromGradient(const Vec3 &grad) {
|
||||
float gradMag = grad.length(); // magnitude
|
||||
float k = 1e-4f; // tweak (the smaller the value, the less opacity) // TODO: What should be the value of this?
|
||||
float alpha = 1.0f - expf(-k * gradMag);
|
||||
return alpha;
|
||||
}
|
||||
|
||||
struct ColorStop
|
||||
{
|
||||
float pos; // in [0,1]
|
||||
Color3 color; // R,G,B in [0,1]
|
||||
};
|
||||
|
||||
// TODO: Rename probably
|
||||
__device__ Color3 colorMapViridis(float normalizedT) {
|
||||
// Here we redefine the color stops to go from deep blue (0.0) to purple (0.5) to deep red (1.0)
|
||||
ColorStop tempStops[] = {
|
||||
{ 0.0f, Color3::init(0.0f, 0.0f, 1.0f) }, // deep blue
|
||||
{ 0.5f, Color3::init(0.5f, 0.0f, 0.5f) }, // purple
|
||||
{ 1.0f, Color3::init(1.0f, 0.0f, 0.0f) } // deep red
|
||||
};
|
||||
|
||||
// Clamp to [0,1]
|
||||
if (normalizedT < 0.0f) normalizedT = 0.0f;
|
||||
if (normalizedT > 1.0f) normalizedT = 1.0f;
|
||||
|
||||
// We have 3 stops => 2 intervals
|
||||
const int N = 3;
|
||||
for (int i = 0; i < N - 1; ++i)
|
||||
{
|
||||
float start = tempStops[i].pos;
|
||||
float end = tempStops[i + 1].pos;
|
||||
|
||||
if (normalizedT >= start && normalizedT <= end)
|
||||
{
|
||||
float localT = (normalizedT - start) / (end - start);
|
||||
return interpolate(tempStops[i].color, tempStops[i + 1].color, localT);
|
||||
}
|
||||
}
|
||||
// Fallback if something goes out of [0,1] or numerical issues
|
||||
return tempStops[N - 1].color;
|
||||
}
|
||||
|
||||
// Monochromatic colormap for speed
|
||||
__device__ Color3 colorMapMonochrome(float normalizedSpeed) {
|
||||
// Define the color stops: black (0.0) to white (1.0)
|
||||
ColorStop speedStops[] = {
|
||||
{ 0.0f, Color3::init(0.0f, 0.0f, 0.0f) }, // No colour
|
||||
{ 1.0f, Color3::init(1.0f, 1.0f, 1.0f) } // White
|
||||
};
|
||||
|
||||
// Clamp to [0,1]
|
||||
if (normalizedSpeed < 0.0f) normalizedSpeed = 0.0f;
|
||||
if (normalizedSpeed > 1.0f) normalizedSpeed = 1.0f;
|
||||
|
||||
// Single interval (N = 2 for black to white)
|
||||
const int N = 2;
|
||||
for (int i = 0; i < N - 1; ++i)
|
||||
{
|
||||
float start = speedStops[i].pos;
|
||||
float end = speedStops[i + 1].pos;
|
||||
|
||||
if (normalizedSpeed >= start && normalizedSpeed <= end)
|
||||
{
|
||||
float localT = (normalizedSpeed - start) / (end - start);
|
||||
return interpolate(speedStops[i].color, speedStops[i + 1].color, localT);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback
|
||||
return speedStops[N - 1].color;
|
||||
}
|
||||
|
||||
// Colormap function for gradient from blue to white to red
|
||||
__device__ Color3 colorMapPythonLike(float normalizedValue) {
|
||||
// Define control points for the colormap (approximation of coolwarm)
|
||||
ColorStop coolwarmStops[] = {
|
||||
{ 0.0f, Color3::init(0.2298057f, 0.29871797f, 0.75368315f) }, // Dark Blue
|
||||
{ 0.25f, Color3::init(0.23437708f, 0.30554173f, 0.75967953f) }, // Mid Blue
|
||||
{ 0.5f, Color3::init(0.27582712f, 0.36671692f, 0.81255294f) }, // White
|
||||
{ 0.75f, Color3::init(0.79606387f, 0.84869321f, 0.93347147f) }, // Light Orange
|
||||
{ 1.0f, Color3::init(0.70567316f, 0.01555616f, 0.15023281f) } // Red
|
||||
};
|
||||
|
||||
// Clamp the scalar value to [0, 1]
|
||||
normalizedValue = fminf(fmaxf(normalizedValue, 0.0f), 1.0f);
|
||||
|
||||
// Interpolate between the defined color stops
|
||||
const int N = 5; // Number of control points
|
||||
for (int i = 0; i < N - 1; ++i) {
|
||||
float start = coolwarmStops[i].pos;
|
||||
float end = coolwarmStops[i + 1].pos;
|
||||
|
||||
if (normalizedValue >= start && normalizedValue <= end) {
|
||||
float localT = (normalizedValue - start) / (end - start);
|
||||
return interpolate(coolwarmStops[i].color, coolwarmStops[i + 1].color, localT);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback (shouldn't reach here due to clamping)
|
||||
return coolwarmStops[N - 1].color;
|
||||
}
|
||||
|
||||
|
||||
// Transfer function
|
||||
__device__ float4 transferFunction(float density, const Vec3& grad, const Point3& pos, const Vec3& rayDir) {
|
||||
// Basic transfer function. TODO: Move to a separate file, and then improve
|
||||
|
||||
// Color3 baseColor = Color3::init(density, 0.1f*density, 1.f - density); // TODO: Implement a proper transfer function
|
||||
// Color3 baseColor = temperatureToRGB(density);
|
||||
|
||||
float normDensity = (density - MIN_TEMP) / (MAX_TEMP - MIN_TEMP);
|
||||
// float normDensity = (density - MIN_SPEED) / (MAX_SPEED - MIN_SPEED);
|
||||
|
||||
normDensity = clamp(normDensity, 0.0f, 1.0f);
|
||||
|
||||
// Color3 baseColor = colorMapViridis(normDensity);
|
||||
// Color3 baseColor = colorMapMonochrome(normDensity);
|
||||
Color3 baseColor = colorMapPythonLike(normDensity);
|
||||
|
||||
|
||||
float alpha = opacityFromGradient(grad);
|
||||
// alpha = 0.1f;
|
||||
alpha = 1.0f / (1.0f + expf(-250.f * (normDensity - 0.5f))); // This is also quite nice, but the exponent should be parameterized
|
||||
float alphaSample = density * alpha; // TODO: Decide whether to keep alpha here or not
|
||||
|
||||
Vec3 normal = -grad.normalize();
|
||||
|
||||
Vec3 lightDir = (d_lightPos - pos).normalize();
|
||||
Vec3 viewDir = -rayDir.normalize();
|
||||
|
||||
// Apply Phong
|
||||
Vec3 shadedColor = phongShading(normal, lightDir, viewDir, baseColor);
|
||||
|
||||
// Compose
|
||||
float4 result;
|
||||
result.x = shadedColor.x * alphaSample;
|
||||
result.y = shadedColor.y * alphaSample;
|
||||
result.z = shadedColor.z * alphaSample;
|
||||
result.w = alpha; // TODO: Again, decide if alpha here is correct or not
|
||||
|
||||
// TODO: This is the black silhouette, technically if we are doing alpha based on gradient then it's kind of redundant (?) ... but could also be used for even sharper edges
|
||||
if (grad.length() > epsilon && fabs(grad.normalize().dot(viewDir)) < 0.2f) {
|
||||
result.x = 0.0f;
|
||||
result.y = 0.0f;
|
||||
result.z = 0.0f;
|
||||
result.w = 1.0f;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user