From 6b47a3c78a33f3623a78878be14dafee712be1e8 Mon Sep 17 00:00:00 2001 From: Alexander Chan Date: Wed, 17 Oct 2018 22:43:31 -0400 Subject: [PATCH] submit --- README.md | 46 ++- img/checkerboard-persp.PNG | Bin 0 -> 17430 bytes img/duck.PNG | Bin 0 -> 44623 bytes img/fxaa_debug_horzvert.PNG | Bin 0 -> 16540 bytes img/fxaa_debug_pair.PNG | Bin 0 -> 17516 bytes img/fxaa_debug_passthrough.PNG | Bin 0 -> 16329 bytes src/CMakeLists.txt | 2 +- src/main.cpp | 2 +- src/rasterize.cu | 574 +++++++++++++++++++++++++++++++-- src/rasterize.h | 23 ++ 10 files changed, 618 insertions(+), 29 deletions(-) create mode 100644 img/checkerboard-persp.PNG create mode 100644 img/duck.PNG create mode 100644 img/fxaa_debug_horzvert.PNG create mode 100644 img/fxaa_debug_pair.PNG create mode 100644 img/fxaa_debug_passthrough.PNG diff --git a/README.md b/README.md index 41b91f0..e8574c9 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,48 @@ CUDA Rasterizer =============== -[CLICK ME FOR INSTRUCTION OF THIS PROJECT](./INSTRUCTION.md) - **University of Pennsylvania, CIS 565: GPU Programming and Architecture, Project 4** -* (TODO) YOUR NAME HERE - * (TODO) [LinkedIn](), [personal website](), [twitter](), etc. -* Tested on: (TODO) Windows 22, i7-2222 @ 2.22GHz 22GB, GTX 222 222MB (Moore 2222 Lab) +* Alexander Chan +* Tested on: Windows 10 Version 1803, i7-5820k @ 3.70 GHz 16GB, GTX 1080 @ 1620 MHz 8GB (Personal Computer) + +### Features +- Lambertian shading +- Perspective correct UV interpolation and textures + + ![](img/checkerboard-persp.PNG) + + ![](img/duck.PNG) + +- FXAA + + FXAA is a fast screen space antialiasing method. It detects edges and blurs them. + + Step 1: + + Detect which pixels to apply antialiasing on. + + ![](img/fxaa_debug_passthrough.PNG) + + Step 2: + + Determine if the edge is oriented horizontal or vertical. Blue is vertical, yellow is horizontal. + + ![](img/fxaa_debug_horzvert.PNG) + + Step 3: + + Determine highest contrast pixel pair orthogonal to the edge. A pair consists of a blue and green pixel in the debug image. + + ![](img/fxaa_debug_pair.PNG) + + Step 4: + + Determine the length of the edge by traversing along the orientation of the edge. -### (TODO: Your README) + Step 5: -*DO NOT* leave the README to the last minute! It is a crucial part of the -project, and we will not be able to grade you without a good README. + Offset the pixel by its distance to the end of the edge. The closer it is to the edge, the more it is blurred. ### Credits diff --git a/img/checkerboard-persp.PNG b/img/checkerboard-persp.PNG new file mode 100644 index 0000000000000000000000000000000000000000..e32ba68f1a03fc03cbb4ae895310c05a54b2cb08 GIT binary patch literal 17430 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 P5Qvq9-I+==uY3Os((5b` literal 0 HcmV?d00001 diff --git a/img/duck.PNG b/img/duck.PNG new file mode 100644 index 0000000000000000000000000000000000000000..0f143ef1c47a3c97f14a15318c65a5cbfd690723 GIT binary patch literal 44623 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 V(;cbc-F(P7CwIrPA20p(e*w8e9bNzc literal 0 HcmV?d00001 diff --git a/img/fxaa_debug_pair.PNG b/img/fxaa_debug_pair.PNG new file mode 100644 index 0000000000000000000000000000000000000000..8e706a7ed615b2cfde456301a71003987cd75457 GIT binary patch literal 17516 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%0D@x#?}x*gX! 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 literal 0 HcmV?d00001 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a57f69f..00edee0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,5 +6,5 @@ set(SOURCE_FILES cuda_add_library(src ${SOURCE_FILES} - OPTIONS -arch=sm_20 + OPTIONS -arch=sm_61 ) diff --git a/src/main.cpp b/src/main.cpp index 7986959..afcf882 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -97,7 +97,7 @@ void mainLoop() { //---------RUNTIME STUFF--------- //------------------------------- float scale = 1.0f; -float x_trans = 0.0f, y_trans = 0.0f, z_trans = -10.0f; +float x_trans = 0.0f, y_trans = 0.0f, z_trans = -3.0f; float x_angle = 0.0f, y_angle = 0.0f; void runCuda() { // Map OpenGL buffer object for writing from CUDA on a single GPU diff --git a/src/rasterize.cu b/src/rasterize.cu index 1262a09..755a4bd 100644 --- a/src/rasterize.cu +++ b/src/rasterize.cu @@ -46,7 +46,7 @@ namespace { // glm::vec3 col; glm::vec2 texcoord0; TextureData* dev_diffuseTex = NULL; - // int texWidth, texHeight; + int texWidth, texHeight; // ... }; @@ -62,10 +62,11 @@ namespace { // The attributes listed below might be useful, // but always feel free to modify on your own - // glm::vec3 eyePos; // eye space position used for shading - // glm::vec3 eyeNor; - // VertexAttributeTexcoord texcoord0; - // TextureData* dev_diffuseTex; + glm::vec3 eyePos; // eye space position used for shading + glm::vec3 eyeNor; + VertexAttributeTexcoord texcoord0; + TextureData* dev_diffuseTex; + int texWidth, texHeight; // ... }; @@ -108,8 +109,10 @@ static int totalNumPrimitives = 0; static Primitive *dev_primitives = NULL; static Fragment *dev_fragmentBuffer = NULL; static glm::vec3 *dev_framebuffer = NULL; +static glm::vec3 *dev_framebuffer_2 = NULL; -static int * dev_depth = NULL; // you might need this buffer when doing depth test +static float * dev_depth = NULL; // you might need this buffer when doing depth test +static int *dev_mutex = NULL; /** * Kernel that writes the image to the OpenGL PBO directly. @@ -133,6 +136,22 @@ void sendImageToPBO(uchar4 *pbo, int w, int h, glm::vec3 *image) { } } +__forceinline__ +__device__ +glm::vec3 fetchColor(glm::vec3 *textureData, int x, int y, int w, int h) { + int pix = y * w + x; + return textureData[pix]; +} + +__device__ +glm::vec3 colorAt(TextureData* texture, int textureWidth, float u, float v) { + int flatIndex = u + v * textureWidth; + float r = (float) texture[flatIndex * 3] / 255.0f; + float g = (float) texture[flatIndex * 3 + 1] / 255.0f; + float b = (float) texture[flatIndex * 3 + 2] / 255.0f; + return glm::vec3(r, g, b); +} + /** * Writes fragment colors to the framebuffer */ @@ -143,7 +162,18 @@ void render(int w, int h, Fragment *fragmentBuffer, glm::vec3 *framebuffer) { int index = x + (y * w); if (x < w && y < h) { - framebuffer[index] = fragmentBuffer[index].color; + glm::vec3 lightPos = glm::vec3(1, 1, 1); + Fragment frag = fragmentBuffer[index]; + int u = frag.texcoord0.x * frag.texWidth; + int v = frag.texcoord0.y * frag.texHeight; + glm::vec3 col; + if (frag.dev_diffuseTex != NULL) { + col = colorAt(frag.dev_diffuseTex, frag.texWidth, u, v); + } else { + col = frag.color; + } + glm::vec3 lightDir = (lightPos - frag.eyePos); + framebuffer[index] = col * glm::max(0.f, glm::dot(frag.eyeNor, lightDir)); // TODO: add your fragment shader code here @@ -154,23 +184,33 @@ void render(int w, int h, Fragment *fragmentBuffer, glm::vec3 *framebuffer) { * Called once at the beginning of the program to allocate memory. */ void rasterizeInit(int w, int h) { + cudaError_t stat = cudaDeviceSetLimit(cudaLimitStackSize, 8192); + checkCUDAError("set stack limit"); width = w; height = h; cudaFree(dev_fragmentBuffer); cudaMalloc(&dev_fragmentBuffer, width * height * sizeof(Fragment)); cudaMemset(dev_fragmentBuffer, 0, width * height * sizeof(Fragment)); + cudaFree(dev_framebuffer); cudaMalloc(&dev_framebuffer, width * height * sizeof(glm::vec3)); cudaMemset(dev_framebuffer, 0, width * height * sizeof(glm::vec3)); + + cudaFree(dev_framebuffer_2); + cudaMalloc(&dev_framebuffer_2, width * height * sizeof(glm::vec3)); + cudaMemset(dev_framebuffer_2, 0, width * height * sizeof(glm::vec3)); cudaFree(dev_depth); - cudaMalloc(&dev_depth, width * height * sizeof(int)); + cudaMalloc(&dev_depth, width * height * sizeof(float)); + + cudaFree(dev_mutex); + cudaMalloc(&dev_mutex, width * height * sizeof(int)); checkCUDAError("rasterizeInit"); } __global__ -void initDepth(int w, int h, int * depth) +void initDepth(int w, int h, float * depth) { int x = (blockIdx.x * blockDim.x) + threadIdx.x; int y = (blockIdx.y * blockDim.y) + threadIdx.y; @@ -178,7 +218,7 @@ void initDepth(int w, int h, int * depth) if (x < w && y < h) { int index = x + (y * w); - depth[index] = INT_MAX; + depth[index] = FLT_MAX; } } @@ -638,10 +678,25 @@ void _vertexTransformAndAssembly( // Multiply the MVP matrix for each vertex position, this will transform everything into clipping space // Then divide the pos by its w element to transform into NDC space // Finally transform x and y to viewport space + glm::vec4 mPos(primitive.dev_position[vid], 1.f); + // clip space + glm::vec4 camPos = MVP * mPos; + // NDC + glm::vec4 ndcPos = camPos / camPos.w; + // viewport + float x = (ndcPos.x + 1.f) * ((float) width) * 0.5f; + float y = (1.f - ndcPos.y) * ((float) height) * 0.5f; + float z = -ndcPos.z; // TODO: Apply vertex assembly here // Assemble all attribute arraies into the primitive array - + primitive.dev_verticesOut[vid].pos = { x, y, z, 1.f }; + primitive.dev_verticesOut[vid].eyePos = glm::vec3(MV * mPos); + primitive.dev_verticesOut[vid].eyeNor = glm::normalize(MV_normal * primitive.dev_normal[vid]); + primitive.dev_verticesOut[vid].texcoord0 = primitive.dev_texcoord0[vid]; + primitive.dev_verticesOut[vid].dev_diffuseTex = primitive.dev_diffuseTex; + primitive.dev_verticesOut[vid].texHeight = primitive.diffuseTexHeight; + primitive.dev_verticesOut[vid].texWidth = primitive.diffuseTexWidth; } } @@ -660,12 +715,12 @@ void _primitiveAssembly(int numIndices, int curPrimitiveBeginId, Primitive* dev_ // TODO: uncomment the following code for a start // This is primitive assembly for triangles - //int pid; // id for cur primitives vector - //if (primitive.primitiveMode == TINYGLTF_MODE_TRIANGLES) { - // pid = iid / (int)primitive.primitiveType; - // dev_primitives[pid + curPrimitiveBeginId].v[iid % (int)primitive.primitiveType] - // = primitive.dev_verticesOut[primitive.dev_indices[iid]]; - //} + int pid; // id for cur primitives vector + if (primitive.primitiveMode == TINYGLTF_MODE_TRIANGLES) { + pid = iid / (int)primitive.primitiveType; + dev_primitives[pid + curPrimitiveBeginId].v[iid % (int)primitive.primitiveType] + = primitive.dev_verticesOut[primitive.dev_indices[iid]]; + } // TODO: other primitive types (point, line) @@ -673,7 +728,463 @@ void _primitiveAssembly(int numIndices, int curPrimitiveBeginId, Primitive* dev_ } +__global__ +void _rasterize(int numPrimitives, Primitive *dev_primitives, Fragment *dev_fragmentBuffer, int width, int height, float *dev_depthBuffer, int *mutexes) { + int idx = (blockIdx.x * blockDim.x) + threadIdx.x; + if (idx < numPrimitives) { + Primitive prim = dev_primitives[idx]; + VertexOut vs[3]; + vs[0] = prim.v[0]; + vs[1] = prim.v[1]; + vs[2] = prim.v[2]; + glm::vec3 pos[3]; + pos[0] = glm::vec3(vs[0].pos); + pos[1] = glm::vec3(vs[1].pos); + pos[2] = glm::vec3(vs[2].pos); + + // Get bounds of this primitive + glm::vec2 min, max; + AABB bounds = getAABBForTriangle(pos); + min.x = glm::clamp(bounds.min.x, 0.f, (float) (width - 1)); + min.y = glm::clamp(bounds.min.y, 0.f, (float) (height - 1)); + max.x = glm::clamp(bounds.max.x, 0.f, (float) (width - 1)); + max.y = glm::clamp(bounds.max.y, 0.f, (float) (height - 1)); + + // Generate fragments for each pixel this primitive overlaps + for (int x = min.x; x <= max.x; ++x) { + for (int y = min.y; y <= max.y; ++y) { + glm::vec3 bary = calculateBarycentricCoordinate(pos, { x, y }); + if (isBarycentricCoordInBounds(bary)) { + int pixIdx = x + width * y; + float depth = getZAtCoordinate(bary, pos); + glm::vec3 nor = bary.x * vs[0].eyeNor + + bary.y * vs[1].eyeNor + + bary.z * vs[2].eyeNor; + + Fragment frag; + frag.color = glm::vec3(.95f, .95, .15); + frag.eyeNor = nor; + + + glm::vec2 cord = bary.x * vs[0].texcoord0 / vs[0].eyePos.z + + bary.y * vs[1].texcoord0 / vs[1].eyePos.z + + bary.z * vs[2].texcoord0 / vs[2].eyePos.z; + + float z = bary.x * (1.f / vs[0].eyePos.z) + + bary.y * (1.f / vs[1].eyePos.z) + + bary.z * (1.f / vs[2].eyePos.z); + + frag.texcoord0 = cord / z; + + frag.texHeight = vs[0].texHeight; + frag.texWidth = vs[0].texWidth; + frag.dev_diffuseTex = vs[0].dev_diffuseTex; + + int *mutex = &mutexes[pixIdx]; + bool isSet; + do { + isSet = (atomicCAS(mutex, 0, 1) == 0); + if (isSet) { + if (depth < dev_depthBuffer[pixIdx]) { + dev_depthBuffer[pixIdx] = depth; + dev_fragmentBuffer[pixIdx] = frag; + } + } + if (isSet) { + mutexes[pixIdx] = 0; + } + } while (!isSet); + + } + } + } + } +} + +__forceinline__ +__device__ +float rgb2luma(glm::vec3 rgb) { + return glm::dot(rgb, glm::vec3(0.299, 0.587, 0.114)); +} + +__forceinline__ +__device__ +int flatIdx(int w, int h, glm::vec2 pos) { + pos.x = w - pos.x; + pos = glm::clamp(pos, glm::vec2(0, 0), glm::vec2(w - 1, h - 1)); + return pos.x + (pos.y * w); +} + +// bilinear filtering +__forceinline__ +__device__ +float getAlpha(float y, float py, float qy) { + return (y - py) / (qy - py); +} + +__forceinline__ +__device__ +glm::vec3 slerp(float alpha, glm::vec3 az, glm::vec3 bz) { + return glm::vec3((1 - alpha) * az.r + alpha * bz.r, + (1 - alpha) * az.g + alpha * bz.g, + (1 - alpha) * az.b + alpha * bz.b); +} + +__forceinline__ +__device__ +float fract(float t) { + return t - glm::floor(t); +} + +__forceinline__ +__device__ +glm::vec3 textureFetch(glm::vec3 *t, glm::vec2 pix, int w, int h) { + pix.x = w - pix.x; + pix = glm::clamp(pix, glm::vec2(0.f, 0.f), glm::vec2(w - 1, h - 1)); + glm::vec3 f = slerp(getAlpha(pix.x, glm::ceil(pix.x), glm::floor(pix.y)), + fetchColor(t, glm::ceil(pix.x), glm::ceil(pix.y), w, h), + fetchColor(t, glm::floor(pix.x), glm::ceil(pix.y), w, h)); + glm::vec3 s = slerp(getAlpha(pix.x, glm::ceil(pix.x), glm::floor(pix.y)), + fetchColor(t, glm::ceil(pix.x), glm::floor(pix.y), w, h), + fetchColor(t, glm::floor(pix.x), glm::floor(pix.y), w, h)); + return slerp(getAlpha(pix.y, glm::ceil(pix.y), glm::floor(pix.y)), f, s); +} + +__forceinline__ +__device__ int pow2(int e) { + int r = 1; + for (int i = 0; i < e; ++i) { + r *= 2; + } + return r; +} + +__forceinline__ +__device__ +float fxaaQualityStep(int i) { + return i < 5 ? 2.f : pow2(i - 3); +} + +#define EDGE_THRESHOLD_MIN 0.0312 +#define EDGE_THRESHOLD_MAX 0.125 +#define FXAA_ITERATIONS 12 +#define SUBPIXEL_QUALITY 0.75 +#define FXAA_REDUCE_MIN 1.0 / 128.0 +#define FXAA_REDUCE_MUL 1.0 / 8.0 +#define FXAA_SPAN_MAX 8.0 +__global__ +void _fxaa_post(int w, int h, glm::vec3 *i_framebuffer, glm::vec3 *o_framebuffer) { + int x = (blockIdx.x * blockDim.x) + threadIdx.x; + int y = (blockIdx.y * blockDim.y) + threadIdx.y; + int idx = (w - x) + (y * w); + if (x < w && y < h) { + glm::vec3 rgbN = i_framebuffer[flatIdx(w, h, { x, y - 1 })]; + glm::vec3 rgbW = i_framebuffer[flatIdx(w, h, { x - 1, y })]; + glm::vec3 rgbE = i_framebuffer[flatIdx(w, h, { x + 1, y })]; + glm::vec3 rgbS = i_framebuffer[flatIdx(w, h, { x, y + 1 })]; + + glm::vec3 rgbM = i_framebuffer[flatIdx(w, h, { x, y })]; + + float lumaN = rgb2luma(rgbN); + float lumaW = rgb2luma(rgbW); + float lumaE = rgb2luma(rgbE); + float lumaS = rgb2luma(rgbS); + float lumaM = rgb2luma(rgbM); + + float rangeMin = glm::min(lumaM, glm::min(glm::min(lumaN, lumaW), glm::min(lumaS, lumaE))); + float rangeMax = glm::max(lumaM, glm::max(glm::max(lumaN, lumaW), glm::max(lumaS, lumaE))); + + // Check local contrast to avoid processing non edges + float range = rangeMax - rangeMin; + if (range < glm::max(FXAA_EDGE_THRESHOLD_MIN, rangeMax * FXAA_EDGE_THRESHOLD)) { + o_framebuffer[idx] = i_framebuffer[idx]; + return; + } + + #if FXAA_DEBUG_PASSTHROUGH + // Set edges to red + o_framebuffer[idx] = COLOR_RED; + return; + #endif + + float lumaL = (lumaN + lumaW + lumaE + lumaS) * 0.25f; + float rangeL = glm::abs(lumaL - lumaM); + float blendL = glm::max(0.f, (rangeL / range) - FXAA_SUBPIX_TRIM) * FXAA_SUBPIX_TRIM_SCALE; + blendL = glm::min(FXAA_SUBPIX_CAP, blendL); + glm::vec3 rgbL = rgbN + rgbW + rgbM + rgbE + rgbS; + glm::vec3 rgbNW = i_framebuffer[flatIdx(w, h, { x - 1, y - 1 })]; + glm::vec3 rgbNE = i_framebuffer[flatIdx(w, h, { x + 1, y - 1 })]; + glm::vec3 rgbSW = i_framebuffer[flatIdx(w, h, { x - 1, y + 1 })]; + glm::vec3 rgbSE = i_framebuffer[flatIdx(w, h, { x + 1, y + 1 })]; + float lumaNW = rgb2luma(rgbNW); + float lumaNE = rgb2luma(rgbNE); + float lumaSW = rgb2luma(rgbSW); + float lumaSE = rgb2luma(rgbSE); + rgbL += (rgbNW + rgbNE + rgbSW + rgbSE); + rgbL *= (1.f / 9.f); + + float edgeVert = + glm::abs((0.25f * lumaNW) + (-0.5f * lumaN) + (0.25f * lumaNE)) + + glm::abs((0.50f * lumaW ) + (-1.0f * lumaM) + (0.50f * lumaE )) + + glm::abs((0.25f * lumaSW) + (-0.5f * lumaS) + (0.25f * lumaSE)); + + float edgeHorz = + glm::abs((0.25f * lumaNW) + (-0.5f * lumaW) + (0.25f * lumaSW)) + + glm::abs((0.50f * lumaN ) + (-1.0f * lumaM) + (0.50f * lumaS )) + + glm::abs((0.25f * lumaNE) + (-0.5f * lumaE) + (0.25f * lumaSE)); + + bool isHor = edgeHorz >= edgeVert; + + #if FXAA_DEBUG_HORZVERT + // Set horizontal edges to yellow, vertical edges to blue + o_framebuffer[idx] = isHor ? COLOR_YELLOW : COLOR_BLUE; + return; + #endif + + // Select highest contrast pixel pair orthogonal to the edge + + // If horizontal edge, check pair of M with S and N + // If vertical edge, check pair of M with W and E + float luma1 = isHor ? lumaS : lumaE; + float luma2 = isHor ? lumaN : lumaW; + + float grad1 = luma1 - lumaM; + float grad2 = luma2 - lumaM; + + bool is1Steepest = glm::abs(grad1) >= glm::abs(grad2); + float gradScaled = 0.25f * glm::max(glm::abs(grad1), glm::abs(grad2)); + + float stepLen = 1.f; + float lumaLocalAvg = 0.f; + + if (is1Steepest) { + lumaLocalAvg = 0.5f * (luma1 + lumaM); + } else { + stepLen = -stepLen; + lumaLocalAvg = 0.5f * (luma2 + lumaM); + } + + glm::vec2 currUV = { x, y }; + if (isHor) { + currUV.y += stepLen * 0.5f; + } else { + currUV.x += stepLen * 0.5f; + } + + #if FXAA_DEBUG_PAIR + // Set pixel up or left to BLUE + // Set pixel down or right to GREEN + glm::vec2 secondCoord = { x + (isHor ? stepLen : 0), y + (isHor ? 0 : stepLen) }; + int secondIdx = flatIdx(w, h, secondCoord); + if (secondCoord.x < x || secondCoord.y < y) { + o_framebuffer[idx] = COLOR_GREEN; + } else { + o_framebuffer[idx] = COLOR_BLUE; + } + return; + #endif + + // Search for end of edge in both - and + directions + glm::vec2 offset = isHor ? glm::vec2(1.f, 0.f) : glm::vec2(0.f, 1.f); + glm::vec2 uv1 = currUV; + glm::vec2 uv2 = currUV; + float lumaEnd1, lumaEnd2; + + bool reached1 = false; + bool reached2 = false; + bool reachedBoth = reached1 && reached2; + + for (int i = 0; i < FXAA_SEARCH_STEPS; ++i) { + if (!reached1) { + uv1 -= offset * fxaaQualityStep(i); + lumaEnd1 = rgb2luma(textureFetch(i_framebuffer, uv1, w, h)); + //lumaEnd1 -= lumaLocalAvg; + } + + if (!reached2) { + uv2 += offset * fxaaQualityStep(i); + lumaEnd2 = rgb2luma(textureFetch(i_framebuffer, uv2, w, h)); + //lumaEnd2 -= lumaLocalAvg; + } + + reached1 = (glm::abs(lumaEnd1 - lumaN) >= gradScaled); + reached2 = (glm::abs(lumaEnd2 - lumaN) >= gradScaled); + reachedBoth = (reached1 && reached2); + + if (reachedBoth) { break; } + } + + // Compute subpixel offset based on distance to end of edge + float dist1 = glm::abs(isHor ? (x - uv1.x) : (y - uv1.y)); + float dist2 = glm::abs(isHor ? (uv2.x - x) : (uv2.y - y)); + bool isDir1 = dist1 < dist2; + float distFinal = glm::min(dist1, dist2); + float edgeLength = dist1 + dist2; + + #if FXAA_DEBUG_EDGEPOS + float alpha = distFinal / 12.f; + o_framebuffer[idx] = alpha * COLOR_YELLOW + (1 - alpha) * COLOR_GREEN; + return; + #endif + + float pixelOffset = -distFinal / edgeLength + 0.5; + //printf("pixelOffset: %f\n", pixelOffset); + + bool isLumaCenterSmaller = lumaM < lumaLocalAvg; + + bool correctVariation = ((isDir1 ? lumaEnd1 : lumaEnd2) < 0.0) != isLumaCenterSmaller; + + pixelOffset = correctVariation ? pixelOffset : 0.f; + glm::vec2 finalUV = isHor ? glm::vec2(x, y + pixelOffset) : glm::vec2(x + pixelOffset, y); + + o_framebuffer[idx] = textureFetch(i_framebuffer, finalUV, w, h); + + /* + float lumaC = rgb2luma(i_framebuffer[idx]); + + float lumaD = rgb2luma(i_framebuffer[flatIdx(w, h, { x, y + 1 })]); + float lumaU = rgb2luma(i_framebuffer[flatIdx(w, h, { x, y - 1 })]); + float lumaL = rgb2luma(i_framebuffer[flatIdx(w, h, { x - 1, y })]); + float lumaR = rgb2luma(i_framebuffer[flatIdx(w, h, { x + 1, y })]); + + float lumaMin = glm::min(lumaC, glm::min(glm::min(lumaD, lumaU), glm::min(lumaL, lumaR))); + float lumaMax = glm::max(lumaC, glm::max(glm::max(lumaD, lumaU), glm::max(lumaL, lumaR))); + + float lumaDelta = lumaMax - lumaMin; + + if (glm::isnan(lumaDelta)) { + lumaDelta = 0.f; + } + + if (lumaDelta < glm::max(EDGE_THRESHOLD_MIN, lumaMax * EDGE_THRESHOLD_MAX)) { + o_framebuffer[idx] = i_framebuffer[idx]; + return; + } + + float lumaDL = rgb2luma(i_framebuffer[flatIdx(w, h, { x - 1, y + 1 })]); + float lumaUL = rgb2luma(i_framebuffer[flatIdx(w, h, { x - 1, y - 1 })]); + float lumaDR = rgb2luma(i_framebuffer[flatIdx(w, h, { x + 1, y + 1 })]); + float lumaUR = rgb2luma(i_framebuffer[flatIdx(w, h, { x + 1, y - 1 })]); + + float lumaDU = lumaD + lumaU; + float lumaLR = lumaL + lumaR; + float lumaLCorn = lumaDL + lumaUL; + float lumaDCorn = lumaDL + lumaDR; + float lumaRCorn = lumaDR + lumaUR; + float lumaUCorn = lumaUL + lumaUR; + + float edgeHor = glm::abs(-2.f * lumaL + lumaLCorn) + + glm::abs(-2.f * lumaC + lumaDU) * 2.f + + glm::abs(-2.f * lumaR + lumaRCorn); + + float edgeVer = glm::abs(-2.f * lumaU + lumaUCorn) + + glm::abs(-2.f * lumaC + lumaLR) * 2.f + + glm::abs(-2.f * lumaD + lumaDCorn); + + bool isHor = (edgeHor >= edgeVer); + + float luma1 = isHor ? lumaD : lumaL; + float luma2 = isHor ? lumaU : lumaR; + + float grad1 = luma1 - lumaC; + float grad2 = luma2 - lumaC; + + bool is1Steepest = glm::abs(grad1) >= glm::abs(grad2); + + float gradScale = 0.25f * glm::max(glm::abs(grad1), glm::abs(grad2)); + + float stepLen = 1.f; + float lumaLocalAvg = 0.f; + if (is1Steepest) { + stepLen = -stepLen; + lumaLocalAvg = 0.5f * (luma1 + lumaC); + } else { + lumaLocalAvg = 0.5f * (luma2 + lumaC); + } + + glm::vec2 currPos(x, y); + if (isHor) { + currPos.y += stepLen * 0.5f; + } else { + currPos.x += stepLen * 0.5f; + } + + glm::vec2 offset = isHor ? glm::vec2(1.f, 0.f) : glm::vec2(0.f, 1.f); + glm::vec2 p1 = currPos - offset; + glm::vec2 p2 = currPos + offset; + + float lumaEnd1 = rgb2luma(textureFetch(i_framebuffer, p1, w, h)); + float lumaEnd2 = rgb2luma(textureFetch(i_framebuffer, p2, w, h)); + lumaEnd1 -= lumaLocalAvg; + lumaEnd2 -= lumaLocalAvg; + bool reached1 = glm::abs(lumaEnd1) >= gradScale; + bool reached2 = glm::abs(lumaEnd2) >= gradScale; + bool reachedBoth = reached1 && reached2; + + if (!reached1) { + p1 -= offset; + } + if (!reached2) { + p2 += offset; + } + + if (!reachedBoth) { + for (int i = 2; i < FXAA_ITERATIONS; ++i) { + if (!reached1) { + lumaEnd1 = rgb2luma(textureFetch(i_framebuffer, p1, w, h)); + lumaEnd1 -= lumaLocalAvg; + } + if (!reached2) { + lumaEnd2 = rgb2luma(textureFetch(i_framebuffer, p2, w, h)); + lumaEnd2 -= lumaLocalAvg; + } + reached1 = glm::abs(lumaEnd1) >= gradScale; + reached2 = glm::abs(lumaEnd2) >= gradScale; + reachedBoth = reached1 && reached2; + if (!reached1) { + p1 -= offset * fxaaQualityStep(i); + } + if (!reached2) { + p2 += offset * fxaaQualityStep(i); + } + if (reachedBoth) { break; } + } + } + + float dist1 = isHor ? ((float) x - p1.x) : ((float) y - p1.y); + float dist2 = isHor ? (p2.x - (float) x) : (p2.y - (float) y); + + bool isDir1 = dist1 < dist2; + float distFinal = glm::min(dist1, dist2); + + float edgeThickness = (dist1 + dist2); + float pixOffset = -distFinal / edgeThickness + 0.5f; + + bool isLumaCSmaller = lumaC < lumaLocalAvg; + bool correctVar = ((isDir1 ? lumaEnd1 : lumaEnd2) < 0.f) != isLumaCSmaller; + float finalOffset = correctVar ? pixOffset : 0.f; + + float lumaAvg = (1.f / 12.f) * (2.f * (lumaDU + lumaLR) + lumaLCorn + lumaRCorn); + float subPixOffset1 = glm::clamp(glm::abs(lumaAvg - lumaC) / lumaDelta, 0.f, 1.f); + float subPixOffset2 = (-2.f * subPixOffset1 + 3.f) * subPixOffset1 * subPixOffset1; + + float subPixOffsetFinal = subPixOffset2 * subPixOffset2 * SUBPIXEL_QUALITY; + + finalOffset = glm::max(finalOffset, subPixOffsetFinal); + glm::vec2 finalPixPos = glm::vec2(x, y); + if (isHor) { + finalPixPos.y += finalOffset * stepLen; + } else { + finalPixPos.x += finalOffset * stepLen; + } + + o_framebuffer[idx] = textureFetch(i_framebuffer, finalPixPos, w, h); + o_framebuffer[idx] = isHor ? glm::vec3(1, 0, 0) : glm::vec3(0, 1, 0); + */ + + } +} /** * Perform rasterization. @@ -723,12 +1234,31 @@ void rasterize(uchar4 *pbo, const glm::mat4 & MVP, const glm::mat4 & MV, const g initDepth << > >(width, height, dev_depth); // TODO: rasterize - - + cudaMemset(dev_mutex, 0, sizeof(int)); + dim3 numThreadsPerBlock(128); + dim3 numBlocksPerPrimitive = (totalNumPrimitives + numThreadsPerBlock.x - 1) / numThreadsPerBlock.x; + _rasterize << > > (totalNumPrimitives, dev_primitives, dev_fragmentBuffer, width, height, dev_depth, dev_mutex); // Copy depthbuffer colors into framebuffer render << > >(width, height, dev_fragmentBuffer, dev_framebuffer); checkCUDAError("fragment shader"); + + // Do post process effects here: + // FXAA, SSAO +#if FXAA + { + _fxaa_post << < blockCount2d , blockSize2d >> > (width, height, dev_framebuffer, dev_framebuffer_2); + checkCUDAError("FXAA postprocess"); + + std::swap(dev_framebuffer, dev_framebuffer_2); + } +#endif + +#if SSAO + _ssao_post << > > (width, height, dev_framebuffer, dev_framebuffer_2); +#endif + + // Copy framebuffer into OpenGL buffer for OpenGL previewing sendImageToPBO<<>>(pbo, width, height, dev_framebuffer); checkCUDAError("copy render result to pbo"); @@ -769,8 +1299,14 @@ void rasterizeFree() { cudaFree(dev_framebuffer); dev_framebuffer = NULL; + cudaFree(dev_framebuffer_2); + dev_framebuffer_2 = NULL; + cudaFree(dev_depth); dev_depth = NULL; + cudaFree(dev_mutex); + dev_mutex = NULL; + checkCUDAError("rasterize Free"); } diff --git a/src/rasterize.h b/src/rasterize.h index 560aae9..0fbd765 100644 --- a/src/rasterize.h +++ b/src/rasterize.h @@ -12,6 +12,29 @@ #include #include +#define FXAA 0 +#define FXAA_DEBUG_PASSTHROUGH 0 +#define FXAA_DEBUG_HORZVERT 0 +#define FXAA_DEBUG_PAIR 0 +#define FXAA_DEBUG_EDGEPOS 0 +#define FXAA_DEBUG_OFFSET 0 + +#define FXAA_EDGE_THRESHOLD_MIN 0.0625 +#define FXAA_EDGE_THRESHOLD 0.375 + +#define FXAA_SUBPIX_TRIM .333f +#define FXAA_SUBPIX_TRIM_SCALE 1.f +#define FXAA_SUBPIX_CAP .75f + +#define FXAA_SEARCH_STEPS 12 +#define FXAA_SEARCH_ACCELERATION 2 +#define FXAA_SEARCH_THRESHOLD 0.25f + +#define COLOR_RED (glm::vec3(1, 0, 0)) +#define COLOR_BLUE (glm::vec3(0, 0, 1)) +#define COLOR_YELLOW (glm::vec3(1, 1, 0)) +#define COLOR_GREEN (glm::vec3(0, 1, 0)) + namespace tinygltf{ class Scene; }