diff --git a/src/consts.cu b/src/consts.cu index 7ed979a..30e201f 100644 --- a/src/consts.cu +++ b/src/consts.cu @@ -28,9 +28,9 @@ const ColorStop h_stopsBluePurleRed[] = { __device__ Point3 d_cameraPos; __device__ Vec3 d_cameraDir; -__device__ Vec3 d_cameraUp; __device__ Point3 d_lightPos; __device__ Color3 d_backgroundColor; +__device__ Vec3 d_cameraUp; Vec3 h_cameraUp = Vec3::init(0.0, 1.0, 0.0).normalize(); @@ -45,3 +45,9 @@ void copyConstantsToDevice() { // ----------------------- Camera and Light ----------------------- cudaMemcpyToSymbol(d_cameraUp, &h_cameraUp, sizeof(Vec3)); } + + +// ----------------------- TransferFunction ----------------------- +__device__ float d_opacityK; +__device__ float d_sigmoidOne; +__device__ float d_sigmoidTwo; diff --git a/src/consts.h b/src/consts.h index 70d6ccb..4790915 100644 --- a/src/consts.h +++ b/src/consts.h @@ -11,9 +11,8 @@ const int VOLUME_WIDTH = 97; // lon const int VOLUME_HEIGHT = 71; // lat const int VOLUME_DEPTH = 42; // lev -// TODO: replace with window->w and window->h -const int IMAGE_WIDTH = 800; -const int IMAGE_HEIGHT = 600; +const int INITIAL_WINDOW_WIDTH = 1200; +const int INITIAL_WINDOW_HEIGHT = 900; const double epsilon = 1e-10f; const double infty = 1e15f; // This value is used to represent missing values in data @@ -52,12 +51,19 @@ extern __device__ Point3 d_lightPos; // Background color extern __device__ Color3 d_backgroundColor; + // --------------------------- Transfer Function Constants --------------------------- struct ColorStop { float pos; // in [0,1] Color3 color; }; +// factor for the opacity function +extern __device__ float d_opacityK; +// sigmoid function variables +extern __device__ float d_sigmoidOne; +extern __device__ float d_sigmoidTwo; + const int lenStopsPythonLike = 5; const int lenStopsGrayscale = 2; const int lenStopsBluePurpleRed = 3; diff --git a/src/gui/input/Widget.cpp b/src/gui/input/Widget.cpp index 8684db3..e57d9a1 100644 --- a/src/gui/input/Widget.cpp +++ b/src/gui/input/Widget.cpp @@ -13,9 +13,9 @@ Widget::Widget(GLFWwindow* window) { ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL3_Init(); - // this->cameraPos = Point3::init(50.0f, -50.0f, -75.0f); // Camera for partially trimmed data set - this->cameraPos = Point3::init(300.0f, 200.0f, -700.0f); // Camera for full data set - // + this->cameraPos = Point3::init(50.0f, -50.0f, -75.0f); // Camera for partially trimmed data set + // this->cameraPos = Point3::init(300.0f, 200.0f, -700.0f); // Camera for full data set + Vec3 h_center = Vec3::init((float)VOLUME_WIDTH/2.0f, (float)VOLUME_HEIGHT/2.0f, (float)VOLUME_DEPTH/2.0f); this->cameraDir = (h_center - this->cameraPos).normalize(); @@ -25,8 +25,13 @@ Widget::Widget(GLFWwindow* window) { this->fps = (char*)malloc(512*sizeof(char)); this->paused = true; this->renderOnce = false; + + this->opacityK = 0; + this->sigmoidOne = 0.5f; + this->sigmoidTwo = -250.0f; }; +// TODO: can be marginally improvement by only copying changed values to device - however we're dealing with individual floats here so i dont think the benefit would be all that obvious. void Widget::tick(double fps) { if (this->renderOnce) { this->renderOnce = false; @@ -40,16 +45,22 @@ void Widget::tick(double fps) { // input widgets float min = -1, max = 1; + ImGui::Begin("Transfer Function Controls"); + ImGui::DragInt("k (log [1e-10, 1])", &this->opacityK, 1, 0, 100, "%d%%", ImGuiSliderFlags_AlwaysClamp); + ImGui::DragFloat("sigmoidOne", &this->sigmoidOne, 0.01f, 0.0f, 1.0f, "%.2f"); + ImGui::InputFloat("sigmoidTwo", &this->sigmoidTwo, 10.0f, 100.0f, "%.0f"); + ImGui::End(); + ImGui::Begin("Light Controls"); - ImGui::DragScalar("X coordinate", ImGuiDataType_Double, &this->lightPos.x, 0.005f, &min, &max, "%.3f"); - ImGui::DragScalar("Y coordinate", ImGuiDataType_Double, &this->lightPos.y, 0.005f, &min, &max, "%.3f"); - ImGui::DragScalar("Z coordinate", ImGuiDataType_Double, &this->lightPos.z, 0.005f, &min, &max, "%.3f"); + ImGui::DragScalar("X coordinate", ImGuiDataType_Double, &this->lightPos.x, 0.5f, &min, &max, "%.3f"); + ImGui::DragScalar("Y coordinate", ImGuiDataType_Double, &this->lightPos.z, 0.5f, &min, &max, "%.3f"); + ImGui::DragScalar("Z coordinate", ImGuiDataType_Double, &this->lightPos.y, 0.5f, &min, &max, "%.3f"); ImGui::End(); ImGui::Begin("Miscellaneous"); if (ImGui::Button(this->paused ? "Unpause" : "Pause")) this->paused = !this->paused; ImGui::SameLine(); - if (ImGui::Button("render Once")) { + if (ImGui::Button("Render once")) { this->paused = !this->paused; this->renderOnce = true; } @@ -58,12 +69,12 @@ void Widget::tick(double fps) { ImGui::End(); ImGui::Begin("Camera Controls"); - ImGui::DragScalar("X position", ImGuiDataType_Double, &this->cameraPos.x, 0.005f, &min, &max, "%.3f"); - ImGui::DragScalar("Y position", ImGuiDataType_Double, &this->cameraPos.y, 0.005f, &min, &max, "%.3f"); - ImGui::DragScalar("Z position", ImGuiDataType_Double, &this->cameraPos.z, 0.005f, &min, &max, "%.3f"); + ImGui::DragScalar("X position", ImGuiDataType_Double, &this->cameraPos.x, 0.5f, &min, &max, "%.3f"); + ImGui::DragScalar("Y position", ImGuiDataType_Double, &this->cameraPos.z, 0.5f, &min, &max, "%.3f"); + ImGui::DragScalar("Z position", ImGuiDataType_Double, &this->cameraPos.y, 0.5f, &min, &max, "%.3f"); ImGui::DragScalar("X direction", ImGuiDataType_Double, &this->cameraDir.x, 0.005f, &min, &max, "%.3f"); - ImGui::DragScalar("Y direction", ImGuiDataType_Double, &this->cameraDir.y, 0.005f, &min, &max, "%.3f"); - ImGui::DragScalar("Z direction", ImGuiDataType_Double, &this->cameraDir.z, 0.005f, &min, &max, "%.3f"); + ImGui::DragScalar("Y direction", ImGuiDataType_Double, &this->cameraDir.z, 0.005f, &min, &max, "%.3f"); + ImGui::DragScalar("Z direction", ImGuiDataType_Double, &this->cameraDir.y, 0.005f, &min, &max, "%.3f"); ImGui::End(); copyToDevice(); @@ -79,6 +90,13 @@ void Widget::copyToDevice() { cudaMemcpyToSymbol(&d_cameraDir, &this->cameraDir, sizeof(Vec3)); cudaMemcpyToSymbol(&d_lightPos, &this->lightPos, sizeof(Point3)); cudaMemcpyToSymbol(&d_backgroundColor, &this->bgColor, sizeof(Color3)); + + // cudaMemcpyToSymbol(&d_opacityK, &this->opacityK, sizeof(float)); + this->opacityKReal = std::pow(10.0f, (-10 + 0.1 * this->opacityK)); + cudaMemcpyToSymbol(&d_opacityK, &this->opacityKReal, sizeof(float)); + + cudaMemcpyToSymbol(&d_sigmoidOne, &this->sigmoidOne, sizeof(float)); + cudaMemcpyToSymbol(&d_sigmoidTwo, &this->sigmoidTwo, sizeof(float)); } Widget::~Widget() { diff --git a/src/gui/input/Widget.h b/src/gui/input/Widget.h index 2fb7813..21440f9 100644 --- a/src/gui/input/Widget.h +++ b/src/gui/input/Widget.h @@ -12,12 +12,17 @@ public: Point3 cameraDir; Vec3 cameraPos; Point3 lightPos; - Color3 bgColor; + Color3 bgColor; // TODO: widget bool paused; bool renderOnce; char* fps; + int opacityK; + float opacityKReal; + float sigmoidOne; + float sigmoidTwo; + ImGuiIO io; void tick(double fps); diff --git a/src/illumination/Raycaster.cu b/src/illumination/Raycaster.cu index 58837ad..400cce1 100644 --- a/src/illumination/Raycaster.cu +++ b/src/illumination/Raycaster.cu @@ -12,10 +12,10 @@ #include // TODO: instead of IMAGEWIDTH and IMAGEHEIGHT this should reflect the windowSize; -__global__ void raycastKernel(float* volumeData, FrameBuffer framebuffer) { +__global__ void raycastKernel(float* volumeData, FrameBuffer framebuffer, const int width, const int height) { int px = blockIdx.x * blockDim.x + threadIdx.x; int py = blockIdx.y * blockDim.y + threadIdx.y; - if (px >= IMAGE_WIDTH || py >= IMAGE_HEIGHT) return; + if (px >= width || py >= height) return; float accumR = 0.0f; float accumG = 0.0f; @@ -24,15 +24,15 @@ __global__ void raycastKernel(float* volumeData, FrameBuffer framebuffer) { // Initialize random state for ray scattering curandState randState; - curand_init(1234, px + py * IMAGE_WIDTH, 0, &randState); + curand_init(1234, px + py * width, 0, &randState); // Multiple samples per pixel for (int s = 0; s < SAMPLES_PER_PIXEL; s++) { // Map to [-1, 1] - float jitterU = (curand_uniform(&randState) - 0.5f) / IMAGE_WIDTH; - float jitterV = (curand_uniform(&randState) - 0.5f) / IMAGE_HEIGHT; - float u = ((px + 0.5f + jitterU) / IMAGE_WIDTH ) * 2.0f - 1.0f; - float v = ((py + 0.5f + jitterV) / IMAGE_HEIGHT) * 2.0f - 1.0f; + float jitterU = (curand_uniform(&randState) - 0.5f) / width; + float jitterV = (curand_uniform(&randState) - 0.5f) / height; + float u = ((px + 0.5f + jitterU) / width ) * 2.0f - 1.0f; + float v = ((py + 0.5f + jitterV) / height) * 2.0f - 1.0f; float tanHalfFov = tanf(fov * 0.5f); u *= tanHalfFov; @@ -167,7 +167,7 @@ void Raycaster::render() { // TODO: pass camera info at some point // frame buffer is implicitly copied to the device each frame - raycastKernel<<>> (this->data, *this->fb); + raycastKernel<<>> (this->data, *this->fb, this->w, this->h); check_cuda_errors(cudaGetLastError()); check_cuda_errors(cudaDeviceSynchronize()); diff --git a/src/illumination/transferFunction.cu b/src/illumination/transferFunction.cu index fd8db0c..c33e8e5 100644 --- a/src/illumination/transferFunction.cu +++ b/src/illumination/transferFunction.cu @@ -1,4 +1,7 @@ #include "transferFunction.h" +#include "consts.h" + +#include // Samples the voxel nearest to the given coordinates. @@ -53,13 +56,12 @@ __device__ float sampleVolumeTrilinear(float* volumeData, const int volW, const __device__ float opacityFromGradient(const Vec3 &grad) { float gradMag = grad.length(); - float k = 1e-6f; // tweak (the smaller the value, the less opacity) // TODO: Add a slider for this - float alpha = 1.0f - expf(-k * gradMag); + float alpha = 1.0f - expf(-d_opacityK * gradMag); return alpha; } __device__ float opacitySigmoid(float val) { - return 1.0f / (1.0f + expf(-250.f * (val - 0.5f))); // TODO: Parametrize and add sliders + return 1.0f / (1.0f + expf(d_sigmoidTwo * (val - d_sigmoidOne))); } __device__ Color3 colorMap(float normalizedValues, const ColorStop stops[], int N) { @@ -96,6 +98,7 @@ __device__ float4 transferFunction(float density, const Vec3& grad, const Point3 // TODO: Add a way to pick stops here Color3 baseColor = colorMap(normDensity, d_stopsPythonLike, lenStopsPythonLike); + // TODO: This is a Gui select element // TODO: Add a way to pick different function for alpha float alpha = opacityFromGradient(grad); // alpha = 0.1f; diff --git a/src/main.cu b/src/main.cu index d74f264..39e5c49 100644 --- a/src/main.cu +++ b/src/main.cu @@ -15,7 +15,7 @@ static float* d_volume = nullptr; -// FIXME: segfaults on window resize - the raycasting function should work with window->w and window-h instead of constants. +// FIXME: segfaults on window resize - the problem is _not_ in the raycasting function // TODO: general // * very similarly - actual code for loading new data as the simulation progresses - right now its effectively a static image loader * pause button once that dataloading is implemented @@ -116,7 +116,7 @@ int main() { copyConstantsToDevice(); // Create the GUI - Window window(IMAGE_WIDTH, IMAGE_HEIGHT); + Window window(INITIAL_WINDOW_WIDTH, INITIAL_WINDOW_HEIGHT); int out = window.init(d_volume); // memory management