From caa765bff49d54217b75aaf0e7acf4e5392a11e4 Mon Sep 17 00:00:00 2001 From: sotech117 Date: Thu, 7 Dec 2023 16:23:20 -0500 Subject: upload base code --- src/aliasing/filter.cpp | 114 +++++++++++++++++++++++++++++++++++++++++ src/aliasing/supersample.cpp | 119 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 233 insertions(+) create mode 100644 src/aliasing/filter.cpp create mode 100644 src/aliasing/supersample.cpp (limited to 'src/aliasing') diff --git a/src/aliasing/filter.cpp b/src/aliasing/filter.cpp new file mode 100644 index 0000000..1732dc4 --- /dev/null +++ b/src/aliasing/filter.cpp @@ -0,0 +1,114 @@ +#include "raytracer/raytracer.h" + +/** + * Extra credit. + * Code from filter project to offer antialiasing. + * FilterBlur at bottom of file used in raytracer. + */ + +enum KERNEL_CHANNEL { + RED, + GREEN, + BLUE, + NONE=-1, +}; + +struct Kernel1D { + std::function getWeight; + double radius; +}; + +enum CONVOLVE_DIRECTION { + HORIZONTAL, + VERTICAL +}; + +RGBA getPixelWrapped(std::vector &data, int width, int height, int x, int y) { + int newX = (x < 0) ? x + width : x % width; + int newY = (y < 0) ? y + height : y % height; + return data[width * newY + newX]; +} + +std::uint8_t floatToUint8(float x) { + x = std::min(255.f, x); + return round(x * 255.f); +} + +std::vector convolve1D(std::vector data, int width, int height, Kernel1D kernel, CONVOLVE_DIRECTION direction) { + // need to assign then set, since the direction could be either way + std::vector result; + result.assign(width*height, RGBA{0, 0, 0, 255}); + + // get the order of the for loop, based on the bound + int outerBound = direction == CONVOLVE_DIRECTION::HORIZONTAL ? height : width; + int innerBound = direction == CONVOLVE_DIRECTION::HORIZONTAL ? width : height; + + for (int i = 0; i < outerBound; i++) { + for (int j = 0; j < innerBound; j++) { + float redAcc = 0.f, greenAcc = 0.f, blueAcc = 0.f; + for (int k = -kernel.radius; k <= kernel.radius; k++) { + // get the weight for each channel, at this kernel index + double rWeight = kernel.getWeight(k, KERNEL_CHANNEL::RED); + double gWeight = kernel.getWeight(k, KERNEL_CHANNEL::GREEN); + double bWeight = kernel.getWeight(k, KERNEL_CHANNEL::BLUE); + + // determine the pixel location on the canvas + int pixelX = direction == CONVOLVE_DIRECTION::HORIZONTAL ? j + k : i; + int pixelY = direction == CONVOLVE_DIRECTION::HORIZONTAL ? i : j + k; + + // get the pixel to compute this inner index of convolution. + // if out of bounds, get the wrapped + RGBA pixel; + if (pixelX < 0 || pixelX >= width || pixelY < 0 || pixelY >= height) + pixel = getPixelWrapped(data, width, height, pixelX, pixelY); + else + pixel = data.at(width * pixelY + pixelX); + + // sum the weights on each channel + redAcc += rWeight * pixel.r/255.f; + greenAcc += gWeight * pixel.g/255.f; + blueAcc += bWeight * pixel.b/255.f; + } + + // get location then set the pixel into the result + int pixelOnCanvas = direction == CONVOLVE_DIRECTION::HORIZONTAL ? width * i + j : width * j + i; + result[pixelOnCanvas] = RGBA{floatToUint8(redAcc), floatToUint8(greenAcc), floatToUint8(blueAcc), 255}; + } + } + + return result; +} + +double triangleFilter(double x, double a) { + double radius; + if (a < 1) { + radius = 1/a; + } else { + radius = 1; + } + + if (x < -radius || x > radius) + return 0; + + return (1 - std::fabs(x)/radius) / radius; +} + +void RayTracer::filterBlur(RGBA *imageData, int width, int height, float blurRadius) { + // make triangle filter + // note: 1/blurRadius for the "radius" of the filter will normalize the area under it to 1 + Kernel1D triangleKernel; + triangleKernel.radius = blurRadius; + triangleKernel.getWeight = [blurRadius](double x, int c) { return triangleFilter(x, 1/blurRadius); }; + + std::vector data{}; + for (int i = 0; i < width*height; i++) { + data.push_back(imageData[i]); + } + + std::vector res = convolve1D(data, width, height, triangleKernel, HORIZONTAL); + res = convolve1D(res, width,height, triangleKernel, VERTICAL); + + for (int i = 0; i < res.size(); i++) { + imageData[i] = res[i]; + } +} diff --git a/src/aliasing/supersample.cpp b/src/aliasing/supersample.cpp new file mode 100644 index 0000000..aa8e9d3 --- /dev/null +++ b/src/aliasing/supersample.cpp @@ -0,0 +1,119 @@ +#include "raytracer/raytracer.h" + +/** + * Extra credit -> Super Sampling + */ + +const float SUPERSAMPLE_DISTANCE_FROM_CENTER = .25f; // note: max of .5f, unless overlapping with other pixels +bool SUPER_SAMPLE = false; +bool ADAPTIVE_SUPER_SAMPLING = false; + +RGBA RayTracer::superSample( + glm::vec4 eyeCamera, + glm::vec4 pixelDirCamera, + const RayTraceScene &scene) { + // get the color value at value between center and four corners + float x_delta = SUPERSAMPLE_DISTANCE_FROM_CENTER / (scene.width()); + float y_delta = SUPERSAMPLE_DISTANCE_FROM_CENTER / (scene.height()); + // TL == TOP LEFT + // BR = BOTTOM RIGHT, not Battle Royale :) + glm::vec4 pixelTL = getPixelFromRay( + eyeCamera, + glm::vec4(pixelDirCamera.x - x_delta, pixelDirCamera.y - y_delta, pixelDirCamera.z, 0.f), + scene); + glm::vec4 pixelTR = getPixelFromRay( + eyeCamera, + glm::vec4(pixelDirCamera.x + x_delta, pixelDirCamera.y - y_delta, pixelDirCamera.z, 0.f), + scene); + glm::vec4 pixelBL = getPixelFromRay( + eyeCamera, + glm::vec4(pixelDirCamera.x - x_delta, pixelDirCamera.y + y_delta, pixelDirCamera.z, 0.f), + scene); + glm::vec4 pixelBR = getPixelFromRay( + eyeCamera, + glm::vec4(pixelDirCamera.x + x_delta, pixelDirCamera.y + y_delta, pixelDirCamera.z, 0.f), + scene); + + if (!ADAPTIVE_SUPER_SAMPLING) { + return toRGBA((pixelTL + pixelTR + pixelBL + pixelBR) / 4.f); + } + + // ADAPTIVE SUPER SAMPLING + // make the region from the center of pixel smaller until we hit something + RGBA nohit = {0, 0, 0, 0}; // just here to say that a is 0 if no hit... + float TRAVERSE_DISTANCE = .025f; + float num_pixels = 4.f; + if (pixelTL.a == 0) { + num_pixels--; + float smallerDist = SUPERSAMPLE_DISTANCE_FROM_CENTER - TRAVERSE_DISTANCE; + while (smallerDist < TRAVERSE_DISTANCE) { + float x_delta = smallerDist / (scene.width()); + float y_delta = smallerDist / (scene.height()); + pixelTL = getPixelFromRay( + eyeCamera, + glm::vec4(pixelDirCamera.x - x_delta, pixelDirCamera.y - y_delta, pixelDirCamera.z, 0.f), + scene); + if (pixelTL.a != 0) { + num_pixels++; + break; + } + smallerDist -= TRAVERSE_DISTANCE; + } + } + if (pixelTR.a == 0) { + num_pixels--; + float smallerDist = SUPERSAMPLE_DISTANCE_FROM_CENTER - TRAVERSE_DISTANCE; + while (smallerDist < TRAVERSE_DISTANCE) { + float x_delta = smallerDist / (scene.width()); + float y_delta = smallerDist / (scene.height()); + pixelTR = getPixelFromRay( + eyeCamera, + glm::vec4(pixelDirCamera.x - x_delta, pixelDirCamera.y - y_delta, pixelDirCamera.z, 0.f), + scene); + if (pixelTR.a != 0) { + num_pixels += 1; + break; + } + smallerDist -= TRAVERSE_DISTANCE; + } + } + if (pixelBL.a == 0) { + num_pixels--; + float smallerDist = SUPERSAMPLE_DISTANCE_FROM_CENTER - TRAVERSE_DISTANCE; + while (smallerDist < TRAVERSE_DISTANCE) { + float x_delta = smallerDist / (scene.width()); + float y_delta = smallerDist / (scene.height()); + pixelBL = getPixelFromRay( + eyeCamera, + glm::vec4(pixelDirCamera.x - x_delta, pixelDirCamera.y - y_delta, pixelDirCamera.z, 0.f), + scene); + if (pixelBL.a != 0) { + num_pixels += 1; + break; + } + smallerDist -= TRAVERSE_DISTANCE; + } + } + if (pixelBR.a == 0) { + num_pixels--; + float smallerDist = SUPERSAMPLE_DISTANCE_FROM_CENTER - TRAVERSE_DISTANCE; + while (smallerDist < TRAVERSE_DISTANCE) { + float x_delta = smallerDist / (scene.width()); + float y_delta = smallerDist / (scene.height()); + pixelBR = getPixelFromRay( + eyeCamera, + glm::vec4(pixelDirCamera.x - x_delta, pixelDirCamera.y - y_delta, pixelDirCamera.z, 0.f), + scene); + if (pixelBR.a != 0) { + num_pixels += 1; + break; + } + smallerDist -= TRAVERSE_DISTANCE; + } + } + + if (num_pixels == 0.f) { + return nohit; + } + return toRGBA((pixelTL + pixelTR + pixelBL + pixelBR) / num_pixels); +} -- cgit v1.2.3-70-g09d2