diff --git a/common/texture.cpp b/common/texture.cpp index bf529e9..f7f0b70 100644 --- a/common/texture.cpp +++ b/common/texture.cpp @@ -51,28 +51,33 @@ void Texture::setPixelAt(int x, int y, Color const &color) { } Color Texture::color(float u, float v, bool interpolate) const { - Color color; - if (!interpolate) { - color = this->getPixelAt(int(roundf(u * this->width())), int(roundf(v * this->height()))); - } else { - // IMPLEMENT bilinear interpolation - float x = u * this->width(); - float x1 = std::floor(x); - float x2 = std::ceil(x); + Color color; + if (!interpolate) { + color = this->getPixelAt(int(roundf(u * this->width())), int(roundf(v * this->height()))); + } else { + // bilinear interpolation + // adjacent pixel coordinates + int left = int(floorf(u * this->width())); + int right = int(ceilf(u * this->width())); + int top = int(floorf(v * this->height())); + int bottom = int(ceilf(v * this->height())); - float y = v * this->height(); - float y1 = std::floor(y); - float y2 = std::ceil(y); + // weights + float w[4]; + w[0] = right - u * this->width(); + w[1] = 1 - w[0]; + w[2] = bottom - v * this->height(); + w[3] = 1 - w[2]; - Color x1y1 = this->getPixelAt(static_cast(x1), static_cast(y1)); - Color x2y1 = this->getPixelAt(static_cast(x2), static_cast(y1)); - Color x1y2 = this->getPixelAt(static_cast(x1), static_cast(y2)); - Color x2y2 = this->getPixelAt(static_cast(x2), static_cast(y2)); - Color fxy1 = (x2 - x) * x1y1 + (x - x1) * x2y1; - Color fxy2 = (x2 - x) * x1y2 + (x - x1) * x2y2; - color = (y2 - y) * fxy1 + (y - y1) * fxy2; - } - return color; + // get color values and interpolate + Color val[4]; + val[0] = this->getPixelAt(left, top); + val[1] = this->getPixelAt(right, top); + val[2] = this->getPixelAt(left, bottom); + val[3] = this->getPixelAt(right, bottom); + color = w[2] * w[0] * val[0] + w[2] * w[1] * val[1] + w[3] * w[0] * val[2] + w[3] * w[1] * val[3]; + } + return color; } Color Texture::color(Vector2d const &surfacePosition, bool interpolate) const { diff --git a/renderer/superrenderer.cpp b/renderer/superrenderer.cpp index 6c17403..5605e76 100644 --- a/renderer/superrenderer.cpp +++ b/renderer/superrenderer.cpp @@ -1,110 +1,94 @@ -// -// Created by arvids on 13.12.22. -// - -#include "superrenderer.h" -#include "common/ray.h" -#include -#include -#include -#include +#include "renderer/superrenderer.h" #include "camera/camera.h" #include "scene/scene.h" +#include +#include +#include +#include -void SuperRenderer::renderThread(const Scene *scene, Camera const *camera, Texture *image, int width, int widthStep, - int widthOffset, int height, int heightStep, int heightOffset, std::atomic *k, - int const stepSize, int superSamplingFactor) { - float const aspectRatio = static_cast(height) / width; - for (int y = heightOffset; y < height; y += heightStep) { - for (int x = widthOffset; x < width; x += widthStep) { - Color fragmentColor = {}; - fragmentColor = calcSuperColor(scene, camera, width, height, superSamplingFactor, aspectRatio, y, x, - fragmentColor); - fragmentColor /= static_cast(superSamplingFactor * superSamplingFactor); - image->setPixelAt(x, y, clamped(fragmentColor)); - // Super hacky progress bar! - if (++*k % stepSize == 0) { - std::cout << "=" << std::flush; - } +void SuperRenderer::renderThread(const Scene *scene, Camera const *camera, Texture *image, int width, int widthStep, int widthOffset, int height, int heightStep, int heightOffset, std::atomic *k, int const stepSize, + int superSamplingFactor) { + int const sampleCount = superSamplingFactor * superSamplingFactor; + float const samplingStep = 1.0f / superSamplingFactor; + float const aspectRatio = static_cast(height) / width; + + for (int y = heightOffset; y < image->height(); y += heightStep) { + for (int x = widthOffset; x < image->width(); x += widthStep) { + // The fragment color is averaged over all sub-pixel rays + Color fragmentColor; + for (int xs = 0; xs < superSamplingFactor; ++xs) { + for (int ys = 0; ys < superSamplingFactor; ++ys) { + Ray ray = camera->createRay(((xs * samplingStep + x) / width * 2 - 1), -((ys * samplingStep + y) / height * 2 - 1) * aspectRatio); + fragmentColor += scene->traceRay(ray); } - } -} + } + image->setPixelAt(x, y, clamped(fragmentColor / float(sampleCount))); -Color & -SuperRenderer::calcSuperColor(const Scene *scene, const Camera *camera, int width, int height, int superSamplingFactor, - const float aspectRatio, int y, int x, Color &fragmentColor) { - for (int x1 = 0; x1 < superSamplingFactor; x1++) { - for (int y1 = 0; y1 < superSamplingFactor; y1++) { - float offsetX = (-0.5f + static_cast(x1) / static_cast(superSamplingFactor - 1)); - float offsetY = (-0.5f + static_cast(y1) / static_cast(superSamplingFactor - 1)); - - Ray ray = camera->createRay(((static_cast(x) + offsetX) / static_cast(width) * 2.0f - 1), - -((static_cast(y) + offsetY) / static_cast(height) * 2.0f - 1) * aspectRatio); - fragmentColor += scene->traceRay(ray); - } + // Super hacky progress bar! + if (++*k % stepSize == 0) { + std::cout << "=" << std::flush; + } } - return fragmentColor; + } } Texture SuperRenderer::renderImage(Scene const &scene, Camera const &camera, int width, int height) { - Texture image(width, height); + Texture image(width, height); - // Setup timer - std::chrono::steady_clock::time_point start, stop; + // Setup timer + std::chrono::steady_clock::time_point start, stop; - // Reset Ray counting - Ray::resetRayCount(); + // ICGVARIANT ray_counting + // Reset Ray counting + Ray::resetRayCount(); - // Super-hacky progress bar! - std::cout << "(SuperRenderer): Begin rendering..." << std::endl; - std::cout << "| 0%"; - int const barSize = 50; - int const stepSize = (width * height) / barSize; - for (int i = 0; i < barSize - 3 - 5; ++i) - std::cout << " "; - std::cout << "100% |" << std::endl << "|"; - std::atomic k(0); + // ENDVARIANT ray_counting + // Super-hacky progress bar! + std::cout << "(SuperRenderer): Begin rendering..." << std::endl; + std::cout << "| 0%"; + int const barSize = 50; + int const stepSize = (width * height) / barSize; + for (int i = 0; i < barSize - 3 - 5; ++i) + std::cout << " "; + std::cout << "100% |" << std::endl << "|"; + std::atomic k(0); - /* Start timer */ start = std::chrono::steady_clock::now(); + // Start timer + start = std::chrono::steady_clock::now(); - // Spawn a thread for every logical processor -1, calling the renderThread function - int const nThreads = std::thread::hardware_concurrency(); - std::vector threads; - for (int t = 0; t < nThreads - 1; ++t) { - threads.emplace_back(renderThread, &scene, &camera, &image, width, nThreads, t, height, 1, 0, &k, stepSize, - this->superSamplingFactor); - } + // Spawn a thread for every logical processor -1, calling the renderThread function + int const nThreads = std::thread::hardware_concurrency(); + std::vector threads; + for (int t = 0; t < nThreads - 1; ++t) { + threads.emplace_back(renderThread, &scene, &camera, &image, width, nThreads, t, height, 1, 0, &k, stepSize, this->superSamplingFactor_); + } - // Call the renderThread function yourself - renderThread(&scene, &camera, &image, width, nThreads, nThreads - 1, height, 1, 0, &k, stepSize, - this->superSamplingFactor); + // Call the renderThread function yourself + renderThread(&scene, &camera, &image, width, nThreads, nThreads - 1, height, 1, 0, &k, stepSize, this->superSamplingFactor_); - // Rejoin the threads - for (int t = 0; t < nThreads - 1; ++t) { - threads[t].join(); - } + // Rejoin the threads + for (int t = 0; t < nThreads - 1; ++t) { + threads[t].join(); + } - // Stop timer - stop = std::chrono::steady_clock::now(); + // Stop timer + stop = std::chrono::steady_clock::now(); - std::cout << "| Done!" << std::endl; + std::cout << "| Done!" << std::endl; - // Calculate the Time taken in seconds - double seconds = std::chrono::duration_cast>(stop - start).count(); + // Calculate the Time taken in seconds + double seconds = std::chrono::duration_cast>(stop - start).count(); - std::cout << "Time: " << seconds << "s" << std::endl; + std::cout << "Time: " << seconds << "s" << std::endl; - // Get the number of seconds per ray - int rays = Ray::getRayCount(); + // ICGVARIANT ray_counting + // Get the number of seconds per ray + int rays = Ray::getRayCount(); - std::cout << "Paths: " << rays << std::endl; - std::cout << "Paths per second: " << std::fixed << std::setprecision(0) << rays / seconds << std::endl; + std::cout << "Paths: " << rays << std::endl; + std::cout << "Paths per second: " << std::fixed << std::setprecision(0) << rays / seconds << std::endl; + // ENDVARIANT ray_counting - return image; -} - - -void SuperRenderer::setSuperSamplingFactor(int superSamplingFactor) { - SuperRenderer::superSamplingFactor = superSamplingFactor; + return image; } diff --git a/renderer/superrenderer.h b/renderer/superrenderer.h index 92c88c1..dd5b099 100644 --- a/renderer/superrenderer.h +++ b/renderer/superrenderer.h @@ -1,33 +1,30 @@ -// -// Created by arvids on 13.12.22. -// +#ifndef SUPERRENDERER_H +#define SUPERRENDERER_H -#ifndef CG1_TRACER_SUPERRENDERER_H -#define CG1_TRACER_SUPERRENDERER_H - -#include "renderer.h" -#include +#include "renderer/renderer.h" #include class SuperRenderer : public Renderer { + static void renderThread(const Scene *scene, Camera const *camera, Texture *image, int width, int widthStep, + int widthOffset, int height, int heightStep, int heightOffset, std::atomic *k, + int const stepSize, int superSamplingFactor); + public: - static void - renderThread(const Scene *scene, Camera const *camera, Texture *image, int width, int widthStep, - int widthOffset, int height, int heightStep, int heightOffset, std::atomic *k, - int const stepSize, int superSamplingFactor); + // Constructor / Destructor + SuperRenderer() = default; + ~SuperRenderer() override = default; - Texture renderImage(const Scene &scene, const Camera &camera, int width, int height) override; - void setSuperSamplingFactor(int superSamplingFactor); + // Get + int superSamplingFactor() { return this->superSamplingFactor_; } + + // Set + void setSuperSamplingFactor(int factor) { this->superSamplingFactor_ = factor; } + + // Render functions + Texture renderImage(Scene const &scene, Camera const &camera, int width, int height) override; private: -private: - - int superSamplingFactor; - - static Color & - calcSuperColor(const Scene *scene, const Camera *camera, int width, int height, int superSamplingFactor, - const float aspectRatio, int y, int x, Color &fragmentColor); + int superSamplingFactor_ = 2; }; - -#endif //CG1_TRACER_SUPERRENDERER_H +#endif diff --git a/shader/materialshader.cpp b/shader/materialshader.cpp index d3907c0..c06ddfa 100644 --- a/shader/materialshader.cpp +++ b/shader/materialshader.cpp @@ -6,64 +6,88 @@ Vector3d tangentToWorldSpace(const Vector3d &surfaceNormal, const Vector3d &surfaceTangent, const Vector3d &surfaceBitangent, - const Vector3d &textureNormal) -{ + const Vector3d &textureNormal) { return textureNormal.x * surfaceTangent + textureNormal.y * surfaceBitangent + textureNormal.z * surfaceNormal; } MaterialShader::MaterialShader() : opacity(1.0f), normalCoefficient(1.0f), diffuseCoefficient(0.5f), reflectance(0.0f), - specularCoefficient(0.5f), shininessExponent(8) -{} + specularCoefficient(0.5f), shininessExponent(8) {} -Color MaterialShader::shade(Scene const &scene, Ray const &ray) const -{ +Color MaterialShader::shade(Scene const &scene, Ray const &ray) const { Color fragmentColor; // IMPLEMENT ME - // (Normal Map) Calculate the new normal vector - Vector3d surfaceNormal = ray.normal; - if (this->normalMap != nullptr) - { - auto surfaceNormalMapColor = this->normalMap->color(ray.surface, true); - Vector3d textureNormal = {surfaceNormalMapColor.r, surfaceNormalMapColor.g, surfaceNormalMapColor.b}; - textureNormal = textureNormal * 2.0f - Vector3d{1, 1, 1}; - surfaceNormal = ray.normal * normalCoefficient + - (1 - normalCoefficient) * - tangentToWorldSpace(ray.normal, ray.tangent, ray.bitangent, textureNormal); + Vector3d normal = ray.normal; + if (this->normalMap) { + Color const normalColor = this->normalMap->color(ray.surface); + Vector3d const textureNormal = + Vector3d(2.0f * normalColor.r, 2.0f * normalColor.g, 2.0f * normalColor.b) - Vector3d(1, 1, 1); + normal = normalized(tangentToWorldSpace(normal, ray.tangent, ray.bitangent, normalized(textureNormal)) * + this->normalCoefficient + (1.0f - this->normalCoefficient) * normal); } + // Calculate the reflection vector + Vector3d const reflection = normalized(ray.direction - 2 * dotProduct(normal, ray.direction) * normal); + // (Diffuse-/Specular Map) Accumulate the light over all light sources - Color surfaceDiffuseColor(0, 0, 0); - if (this->diffuseMap != nullptr) - { - surfaceDiffuseColor = this->diffuseMap->color(ray.surface, true); + for (const auto &light: scene.lights()) { + + // Retrieve an illumination object + Light::Illumination illum = light->illuminate(scene, ray); + + // Diffuse term + Color const diffuse = + this->diffuseCoefficient * illum.color * std::max(dotProduct(-illum.direction, normal), 0.0f); + if (this->diffuseMap) + fragmentColor += diffuse * this->diffuseMap->color(ray.surface); + else + fragmentColor += diffuse; + + // Specular term + float const cosine = dotProduct(-illum.direction, reflection); + if (cosine > 0) { + Color const specular = this->specularCoefficient * illum.color * std::pow(cosine, shininessExponent); + if (this->specularMap) + fragmentColor += specular * this->specularMap->color(ray.surface); + else + fragmentColor += specular; + } } - Color surfaceSpecularColor(0, 0, 0); - if (this->specularMap != nullptr) - { - surfaceSpecularColor = this->specularMap->color(ray.surface, true); + // (Reflection Map) Calculate the reflectance + float reflectance = this->reflectance; + if (this->reflectionMap) + reflectance *= this->reflectionMap->color(ray.surface).r; + if (reflectance > 0.0f) { + // Create a new reflection ray + Ray reflectionRay = ray; + reflectionRay.origin = ray.origin + (ray.length - EPSILON) * ray.direction; + reflectionRay.direction = reflection; + reflectionRay.length = INFINITY; + reflectionRay.primitive = nullptr; + // Mix the object and the reflected image + Color const reflectionColor = scene.traceRay(reflectionRay); + fragmentColor = (1 - reflectance) * fragmentColor + reflectance * reflectionColor; } - - // (Reflection Map) Calculate the reflectance, create a reflection ray - Vector3d const reflection = ray.direction - 2 * dotProduct(surfaceNormal, ray.direction) * ray.normal; - float surfaceReflectanceCoefficient = this->reflectance; - if (this->reflectionMap != nullptr) - { - auto surfaceReflectiveMapColor = this->reflectionMap->color(ray.surface, true); - surfaceReflectanceCoefficient = surfaceReflectiveMapColor.r; + // (Alpha Map) Calculate the opacity + float alpha = this->opacity; + if (this->alphaMap) + alpha *= this->alphaMap->color(ray.surface).r; + if (alpha < 1) { + // Create a new alpha ray + Ray alphaRay = ray; + alphaRay.origin = ray.origin + (ray.length + EPSILON) * ray.direction; + alphaRay.length = INFINITY; + alphaRay.primitive = nullptr; + // Mix the foreground and background colors + Color const backgroundColor = scene.traceRay(alphaRay); + fragmentColor = alpha * fragmentColor + (1 - alpha) * backgroundColor; } - Ray reflectionRay = ray; - reflectionRay.origin = ray.origin + (ray.length - REFR_EPS) * ray.direction; - reflectionRay.direction = normalized(reflection); - reflectionRay.length = INFINITY; - reflectionRay.primitive = nullptr; // (Alpha Map) Calculate the opacity, create a background ray float surfaceAlphaCoefficient(1); - if (this->alphaMap != nullptr) - { + if (this->alphaMap != nullptr) { auto surfaceAlphaMapColor = this->alphaMap->color(ray.surface, true); surfaceAlphaCoefficient = surfaceAlphaMapColor.r; } @@ -73,37 +97,8 @@ Color MaterialShader::shade(Scene const &scene, Ray const &ray) const propagatedRay.length = INFINITY; propagatedRay.primitive = nullptr; - // Iterate over light sources - for (const auto &light: scene.lights()) - { - Light::Illumination const illum = light->illuminate(scene, ray); - - // Diffuse term (lambertian) - Color const diffuse = this->diffuseCoefficient * surfaceDiffuseColor * - std::max(dotProduct(-illum.direction, ray.normal), 0.0f); - fragmentColor += diffuse * illum.color; - - // Specular term (phong) - float const cosine = dotProduct(-illum.direction, reflection); - if (cosine > 0) - { - Color const specular = this->specularCoefficient * surfaceSpecularColor // highlight - * powf(cosine, this->shininessExponent); // shininess factor - fragmentColor += specular * illum.color; - } - - - } - // Reflected ray - if (surfaceReflectanceCoefficient > 0) - { - Color const reflectionColor = scene.traceRay(reflectionRay); - fragmentColor += surfaceReflectanceCoefficient * reflectionColor * reflectance; - } - // Opacity - if (surfaceAlphaCoefficient < 1) - { + if (surfaceAlphaCoefficient < 1) { Color const background = scene.traceRay(propagatedRay); fragmentColor = (1 - surfaceAlphaCoefficient) * background + surfaceAlphaCoefficient * fragmentColor; } @@ -111,5 +106,4 @@ Color MaterialShader::shade(Scene const &scene, Ray const &ray) const return fragmentColor; } -bool MaterialShader::isTransparent() const -{ return this->opacity < 1.0f || this->alphaMap; } +bool MaterialShader::isTransparent() const { return this->opacity < 1.0f || this->alphaMap; }