From 56e48628613ad1c693ae0dba9ffb2458924f4ad3 Mon Sep 17 00:00:00 2001 From: hal8174 Date: Thu, 13 Jun 2024 13:54:34 +0200 Subject: [PATCH] Add Assignment 3 --- Assignments/Assignment3/Application3.cpp | 147 ++++++ Assignments/Assignment3/Application3.h | 26 + Assignments/Assignment3/CMakeLists.txt | 8 +- .../Assignment3/application_integrator.cpp | 108 +++++ .../Assignment3/application_integrator.h | 31 ++ Assignments/Assignment3/assignment3.cpp | 5 + Assignments/Assignment3/helper.hpp | 444 ++++++++++++++++++ CMakeLists.txt | 2 +- 8 files changed, 768 insertions(+), 3 deletions(-) create mode 100644 Assignments/Assignment3/Application3.cpp create mode 100644 Assignments/Assignment3/Application3.h create mode 100644 Assignments/Assignment3/application_integrator.cpp create mode 100644 Assignments/Assignment3/application_integrator.h create mode 100644 Assignments/Assignment3/helper.hpp diff --git a/Assignments/Assignment3/Application3.cpp b/Assignments/Assignment3/Application3.cpp new file mode 100644 index 0000000..1e069cb --- /dev/null +++ b/Assignments/Assignment3/Application3.cpp @@ -0,0 +1,147 @@ +#include "Application3.h" + +void Application3::initScene() { + Data_Constructor(&data, 1, 8); + + /* select scene here */ + //standardScene(); + causticScene(); +} + +void Application3::standardScene() { + FileName file = workingDir + FileName("Framework/scenes/cornell_box.obj"); + + /* set default camera */ + camera.from = Vec3fa(278, 273, -800); + camera.to = Vec3fa(278, 273, 0); + + Ref sceneGraph = loadOBJ(file, false).cast(); + + auto light = new SceneGraph::QuadLightMesh(Vec3fa(343.0, 548.0, 227.0), Vec3fa(213.0, 548.0, 332.0), + Vec3fa(343.0, 548.0, 332.0), + Vec3fa(213.0, 548.0, 227.0), Vec3fa(25, 25, 25)); + sceneGraph->add(light); + + Ref flattened_scene = SceneGraph::flatten(sceneGraph, SceneGraph::INSTANCING_NONE); + Scene* scene = new Scene; + scene->add(flattened_scene); + sceneGraph = nullptr; + flattened_scene = nullptr; + + auto renderScene = new RenderScene(g_device, scene); + g_render_scene = renderScene; + data.scene = renderScene; + scene = nullptr; +} + +void Application3::causticScene() { + FileName file = workingDir + FileName("Framework/scenes/caustics/ring.obj"); + + /* set default camera */ + camera.from = Vec3fa(1, 2, 1); + camera.to = Vec3fa(0, 0, 0); + camera.fov = 60; + + Ref sceneGraph = loadOBJ(file, false).cast(); + + auto light = new SceneGraph::QuadLightMesh(Vec3fa(0.1, 1.0, 2.0), Vec3fa(-0.1, 1.2, 2.0), + Vec3fa(-0.1, 1.0, 2.0), Vec3fa(0.1, 1.2, 2.0), + Vec3fa(50, 50, 50)); + + sceneGraph->add(light); + + + Ref flattened_scene = SceneGraph::flatten(sceneGraph, SceneGraph::INSTANCING_NONE); + Scene* scene = new Scene; + scene->add(flattened_scene); + sceneGraph = nullptr; + flattened_scene = nullptr; + + auto renderScene = new RenderScene(g_device, scene); + g_render_scene = renderScene; + data.scene = renderScene; + scene = nullptr; +} + + +/* + IMPORTANT: use your own path tracing implementation from the 1st assignment!! + the only change that we made was introduction of the RandomSamplerWrapper. It wrappes the sampling routines, so you could introduce a new sampler for Metropolis Light Transport and by overwriting RandomSamplerWrapper methods: get1D(), get2D()... + also the drawGUI() function now invokes ApplicationIntegrator::drawGUI(); +*/ + +/* task that renders a single screen tile */ +Vec3fa Application3::renderPixel(float x, float y, const ISPCCamera& camera, RayStats& stats, RandomSamplerWrapper& sampler) { + /* radiance accumulator and weight */ + Vec3fa L = Vec3fa(0.0f); + Vec3fa Lw = Vec3fa(1.0f); + + /* initialize ray */ + Ray ray(Vec3fa(camera.xfm.p), Vec3fa(normalize(x * camera.xfm.l.vx + y * camera.xfm.l.vy + camera.xfm.l.vz)), 0.0f, + inf); + + /* intersect ray with scene */ + RTCIntersectArguments iargs; + rtcInitIntersectArguments(&iargs); + iargs.feature_mask = RTC_FEATURE_FLAG_TRIANGLE; + rtcIntersect1(data.g_scene, RTCRayHit_(ray), &iargs); + RayStats_addRay(stats); + + const Vec3fa wo = neg(ray.dir); + + /* shade pixels */ + if (ray.geomID != RTC_INVALID_GEOMETRY_ID) { + Vec3fa Ns = normalize(ray.Ng); + Sample sample; + sample.P = ray.org + ray.tfar * ray.dir; + sample.Ng = ray.Ng; + sample.Ns = Ns; + unsigned matId = data.scene->geometries[ray.geomID]->materialID; + unsigned lightID = data.scene->geometries[ray.geomID]->lightID; + + if (lightID != unsigned(-1)) { + const Light* l = data.scene->lights[lightID]; + Light_EvalRes evalRes = Lights_eval(l, sample, -wo); + + L += evalRes.value; + } else { + sample.Ng = face_forward(ray.dir, normalize(sample.Ng)); + sample.Ns = face_forward(ray.dir, normalize(sample.Ns)); + + /* calculate BRDF */ + BRDF brdf; + std::vector material_array = data.scene->materials; + Material__preprocess(material_array, matId, brdf, wo, sample); + + /* sample BRDF at hit point */ + Sample3f wi1; + Material__sample(material_array, matId, brdf, Lw, wo, sample, wi1, sampler.get2D()); + + int id = (int)(sampler.get1D()* data.scene->lights.size()); + if (id == data.scene->lights.size()) + id = data.scene->lights.size() - 1; + const Light* l = data.scene->lights[id]; + + Light_SampleRes ls = Lights_sample(l, sample, sampler.get2D()); + + Vec3fa diffuse = Material__eval(material_array, matId, brdf, wo, sample, ls.dir); + + /* initialize shadow ray */ + Ray shadow(sample.P, ls.dir, 0.001f, ls.dist-0.001f, 0.0f); + + /* trace shadow ray */ + RTCOccludedArguments sargs; + rtcInitOccludedArguments(&sargs); + sargs.feature_mask = RTC_FEATURE_FLAG_TRIANGLE; + rtcOccluded1(data.g_scene, RTCRay_(shadow), &sargs); + RayStats_addShadowRay(stats); + + /* add light contribution if not occluded */ + if (shadow.tfar >= 0.0f) { + L += diffuse * ls.weight; + } + } + } + + return L; +} diff --git a/Assignments/Assignment3/Application3.h b/Assignments/Assignment3/Application3.h new file mode 100644 index 0000000..896d62b --- /dev/null +++ b/Assignments/Assignment3/Application3.h @@ -0,0 +1,26 @@ +#pragma once +#include "helper.hpp" +#include "application_integrator.h" + + +class Application3 : public ApplicationIntegrator { +public: + Application3(int argc, char** argv) : ApplicationIntegrator(argc, argv, "Assignment 3") { + } + +private: + Vec3fa renderPixel(float x, float y, const ISPCCamera& camera, RayStats& stats, RandomSamplerWrapper& sampler) override; + + + void drawGUI() override { + ApplicationIntegrator::drawGUI(); // NEW! + } + + void initScene() override; + + void standardScene(); + + void causticScene(); + + float colorLight[3] = {1.0f, 1.0f, 1.0f}; +}; diff --git a/Assignments/Assignment3/CMakeLists.txt b/Assignments/Assignment3/CMakeLists.txt index 8823e0e..fa8d869 100644 --- a/Assignments/Assignment3/CMakeLists.txt +++ b/Assignments/Assignment3/CMakeLists.txt @@ -1,5 +1,9 @@ cmake_minimum_required(VERSION 3.16.0 FATAL_ERROR) project(Assignment3) - -add_executable(${PROJECT_NAME} "assignment3.cpp") +add_executable(${PROJECT_NAME} "assignment3.cpp" + Application3.cpp + Application3.h + application_integrator.h + application_integrator.cpp + helper.hpp) target_link_libraries(${PROJECT_NAME} PUBLIC CGI-framework) diff --git a/Assignments/Assignment3/application_integrator.cpp b/Assignments/Assignment3/application_integrator.cpp new file mode 100644 index 0000000..9796304 --- /dev/null +++ b/Assignments/Assignment3/application_integrator.cpp @@ -0,0 +1,108 @@ +#include "application_integrator.h" + + + + + +ApplicationIntegrator::ApplicationIntegrator(int argc, char** argv, const std::string& name): +Application(argc, argv, name) +{ + resetRender(); +} + +void ApplicationIntegrator::drawGUI() { + + bool bDirty = false; + + if (ImGui::Checkbox("Metropolis", &bMetropolis)) { + resetRender(); + } + + if (bDirty) { + resetRender(); + } +} + + +inline float luminance(Vec3fa v) { + return 0.2126f * v.x + 0.7152f * v.y + 0.0722f * v.z; +} + +void ApplicationIntegrator::resetRender() { + Application::resetRender(); + + if (bMetropolis) { + data.film.count = false; + } + else { + data.film.count = true; + data.film.scalar = 1.0; + } +} + +void ApplicationIntegrator::render(int* pixels, int width, int height, float time, const ISPCCamera& camera) { + deviceRender(camera); + + if (!bMetropolis) { + mcRender(pixels, width, height, time, camera); + } + else { + mltRender(pixels, width, height, time, camera); + } +} + + +void ApplicationIntegrator::mltRender(int* pixels, int width, int height, float time, const ISPCCamera& camera) { + + // data.film.scalar = ... use it for setting up the correct normalization coefficient + // + // + // you may want to use Distribution1D for the bootstrap + // d = Distribution1D(float* bis_values, num_bins) + // float integral = d.funcInt; + // int index_of_the_sampled_bin = d.SampleDiscrete(rng.get1D()); + assert(0); +} + +void ApplicationIntegrator::mcRender(int* pixels, int width, int height, float time, const ISPCCamera& camera) { + const int numTilesX = (width + TILE_SIZE_X - 1) / TILE_SIZE_X; + const int numTilesY = (height + TILE_SIZE_Y - 1) / TILE_SIZE_Y; + parallel_for(size_t(0), size_t(numTilesX * numTilesY), [&](const range& range) { + const int threadIndex = (int)TaskScheduler::threadIndex(); + for (size_t i = range.begin(); i < range.end(); i++) + renderTile((int)i, threadIndex, pixels, width, height, time, camera, numTilesX, numTilesY); + }); +} + + +/* renders a single screen tile */ +void ApplicationIntegrator::mcRenderTile(int taskIndex, int threadIndex, int* pixels, const unsigned int width, + const unsigned int height, const float time, const ISPCCamera& camera, const int numTilesX, + const int numTilesY) { + const unsigned int tileY = taskIndex / numTilesX; + const unsigned int tileX = taskIndex - tileY * numTilesX; + const unsigned int x0 = tileX * TILE_SIZE_X; + const unsigned int x1 = min(x0 + TILE_SIZE_X, width); + const unsigned int y0 = tileY * TILE_SIZE_Y; + const unsigned int y1 = min(y0 + TILE_SIZE_Y, height); + + for (unsigned int y = y0; y < y1; y++) + for (unsigned int x = x0; x < x1; x++) { + RandomSamplerWrapper sampler; + Vec3fa L = Vec3fa(0.0f); + + for (int i = 0; i < data.spp; i++) + { + sampler.init(x, y, (data.frame_count) * data.spp + i); + + /* calculate pixel color */ + float fx = x + sampler.get1D(); + float fy = y + sampler.get1D(); + L = L + renderPixel(fx, fy, camera, g_stats[threadIndex], sampler); + } + L = L / (float)data.spp; + + /* write color to framebuffer */ + data.film.addSplat(x, y, L); + } +} \ No newline at end of file diff --git a/Assignments/Assignment3/application_integrator.h b/Assignments/Assignment3/application_integrator.h new file mode 100644 index 0000000..1c9e84c --- /dev/null +++ b/Assignments/Assignment3/application_integrator.h @@ -0,0 +1,31 @@ +#pragma once +#include "helper.hpp" +#include "distribution.hpp" + + + +class ApplicationIntegrator: public Application { +public: + ApplicationIntegrator(int argc, char** argv, const std::string& name); + + virtual ~ApplicationIntegrator() = default; + + +protected: + virtual void render(int* pixels, int width, int height, float time, const ISPCCamera& camera) override; + virtual void drawGUI() override; + virtual void resetRender() override; + + + bool bMetropolis = false; + + + void mltRender(int* pixels, int width, int height, float time, const ISPCCamera& camera); + + void mcRender(int* pixels, int width, int height, float time, const ISPCCamera& camera); + + /* renders a single screen tile */ + void mcRenderTile(int taskIndex, int threadIndex, int* pixels, const unsigned int width, + const unsigned int height, const float time, const ISPCCamera& camera, const int numTilesX, + const int numTilesY); +}; diff --git a/Assignments/Assignment3/assignment3.cpp b/Assignments/Assignment3/assignment3.cpp index 1a91ec0..e6878b6 100644 --- a/Assignments/Assignment3/assignment3.cpp +++ b/Assignments/Assignment3/assignment3.cpp @@ -1,4 +1,9 @@ +#include "Application3.h" int main(int argc, char** argv) { + auto app = new Application3(argc, argv); + + app->run(); + return 0; } diff --git a/Assignments/Assignment3/helper.hpp b/Assignments/Assignment3/helper.hpp new file mode 100644 index 0000000..7b556a3 --- /dev/null +++ b/Assignments/Assignment3/helper.hpp @@ -0,0 +1,444 @@ +#pragma once +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace embree; + +inline Vec3fa reflect(const Vec3fa& V, const Vec3fa& N) { return 2.0f * dot(V, N) * N - V; } + +inline Vec3fa face_forward(const Vec3fa& dir, const Vec3fa& _Ng) { + const Vec3fa Ng = _Ng; + return dot(dir, Ng) < 0.0f ? Ng : neg(Ng); +} + +inline Light_SampleRes Lights_sample(const Light* self, + const Sample& sp, /*! point to generate the sample for >*/ + const Vec2f s) /*! random numbers to generate the sample >*/ +{ + LightType ty = self->type; + switch (ty) { + case LIGHT_AMBIENT: return AmbientLight_sample(self, sp, s); + case LIGHT_POINT: return PointLight_sample(self, sp, s); + case LIGHT_DIRECTIONAL: return DirectionalLight_sample(self, sp, s); + case LIGHT_SPOT: return SpotLight_sample(self, sp, s); + case LIGHT_QUAD: return QuadLight_sample(self, sp, s); + default: { + Light_SampleRes res; + res.weight = Vec3fa(0, 0, 0); + res.dir = Vec3fa(0, 0, 0); + res.dist = 0; + res.pdf = inf; + return res; + } + } +} + +inline Light_EvalRes Lights_eval(const Light* self, + const Sample& sp, + const Vec3fa& dir) { + LightType ty = self->type; + switch (ty) { + case LIGHT_AMBIENT: return AmbientLight_eval(self, sp, dir); + case LIGHT_POINT: return PointLight_eval(self, sp, dir); + case LIGHT_DIRECTIONAL: return DirectionalLight_eval(self, sp, dir); + case LIGHT_SPOT: return SpotLight_eval(self, sp, dir); + case LIGHT_QUAD: return QuadLight_eval(self, sp, dir); + default: { + Light_EvalRes res; + res.value = Vec3fa(0, 0, 0); + res.dist = inf; + res.pdf = 0.f; + return res; + } + } +} + +struct BRDF { + float Ns; /*< specular exponent */ + float Ni; /*< optical density for the surface (index of refraction) */ + Vec3fa Ka; /*< ambient reflectivity */ + Vec3fa Kd; /*< diffuse reflectivity */ + Vec3fa Ks; /*< specular reflectivity */ + Vec3fa Kt; /*< transmission filter */ + float dummy[30]; +}; + +//////////////////////////////////////////////////////////////////////////////// +// Lambertian BRDF // +//////////////////////////////////////////////////////////////////////////////// + +struct Lambertian { + Vec3fa R; +}; + +inline Vec3fa Lambertian__eval(const Lambertian* This, + const Vec3fa& wo, const Sample& dg, const Vec3fa& wi) { + return This->R * (1.0f / (float) (float(M_PI))) * clamp(dot(wi, dg.Ns)); +} + +inline Vec3fa Lambertian__sample(const Lambertian* This, + const Vec3fa& wo, + const Sample& dg, + Sample3f& wi, + const Vec2f& s) { + wi = cosineSampleHemisphere(s.x, s.y, dg.Ns); + return Lambertian__eval(This, wo, dg, wi.v); +} + +inline void Lambertian__Constructor(Lambertian* This, const Vec3fa& R) { + This->R = R; +} + +inline Lambertian make_Lambertian(const Vec3fa& R) { + Lambertian v; + Lambertian__Constructor(&v, R); + return v; +} + +//////////////////////////////////////////////////////////////////////////////// +// Matte Material // +//////////////////////////////////////////////////////////////////////////////// + +inline void MatteMaterial__preprocess(MatteMaterial* material, BRDF& brdf, const Vec3fa& wo, const Sample& sp) { +} + +inline Vec3fa MatteMaterial__eval(MatteMaterial* This, const BRDF& brdf, const Vec3fa& wo, const Sample& sp, + const Vec3fa& wi) { + Lambertian lambertian = make_Lambertian(Vec3fa((Vec3fa) This->reflectance)); + return Lambertian__eval(&lambertian, wo, sp, wi); +} + +inline Vec3fa MatteMaterial__sample(MatteMaterial* This, const BRDF& brdf, const Vec3fa& Lw, const Vec3fa& wo, + const Sample& sp, Sample3f& wi_o, const Vec2f& s) { + Lambertian lambertian = make_Lambertian(Vec3fa((Vec3fa) This->reflectance)); + return Lambertian__sample(&lambertian, wo, sp, wi_o, s); +} + + +//////////////////////////////////////////////////////////////////////////////// +// OBJ Material // +//////////////////////////////////////////////////////////////////////////////// + +inline void OBJMaterial__preprocess(OBJMaterial* material, BRDF& brdf, const Vec3fa& wo, const Sample& sp) { + float d = material->d; + // if (material->map_d) d *= getTextureTexel1f(material->map_d, dg.u, dg.v); + brdf.Ka = Vec3fa(material->Ka); + //if (material->map_Ka) { brdf.Ka *= material->map_Ka->get(dg.st); } + brdf.Kd = d * Vec3fa(material->Kd); + // if (material->map_Kd) brdf.Kd = brdf.Kd * getTextureTexel3f(material->map_Kd, dg.u, dg.v); + brdf.Ks = d * Vec3fa(material->Ks); + //if (material->map_Ks) brdf.Ks *= material->map_Ks->get(dg.st); + brdf.Ns = material->Ns; + //if (material->map_Ns) { brdf.Ns *= material->map_Ns.get(dg.st); } + brdf.Kt = (1.0f - d) * Vec3fa(material->Kt); + brdf.Ni = material->Ni; +} + +inline Vec3fa OBJMaterial__eval(OBJMaterial* material, const BRDF& brdf, const Vec3fa& wo, const Sample& sp, + const Vec3fa& wi) { + Vec3fa R = Vec3fa(0.0f); + const float Md = max(max(brdf.Kd.x, brdf.Kd.y), brdf.Kd.z); + const float Ms = max(max(brdf.Ks.x, brdf.Ks.y), brdf.Ks.z); + const float Mt = max(max(brdf.Kt.x, brdf.Kt.y), brdf.Kt.z); + if (Md > 0.0f) { + R = R + (1.0f / float(M_PI)) * clamp(dot(wi, sp.Ns)) * brdf.Kd; + } + if (Ms > 0.0f) { + const Sample3f refl = make_Sample3f(reflect(wo, sp.Ns), 1.0f); + if (dot(refl.v, wi) > 0.0f) { + R = R + (brdf.Ns + 2) * float(one_over_two_pi) * powf(max(1e-10f, dot(refl.v, wi)), brdf.Ns) * + clamp(dot(wi, sp.Ns)) * brdf.Ks; + } + } + if (Mt > 0.0f) { + } + return R; +} + +inline Vec3fa OBJMaterial__sample(OBJMaterial* material, const BRDF& brdf, const Vec3fa& Lw, const Vec3fa& wo, + const Sample& sp, Sample3f& wi_o, const Vec2f& s) { + Vec3fa cd = Vec3fa(0.0f); + Sample3f wid = make_Sample3f(Vec3fa(0.0f), 0.0f); + if (max(max(brdf.Kd.x, brdf.Kd.y), brdf.Kd.z) > 0.0f) { + wid = cosineSampleHemisphere(s.x, s.y, sp.Ns); + cd = float(one_over_pi) * clamp(dot(wid.v, sp.Ns)) * brdf.Kd; + } + + Vec3fa cs = Vec3fa(0.0f); + Sample3f wis = make_Sample3f(Vec3fa(0.0f), 0.0f); + if (max(max(brdf.Ks.x, brdf.Ks.y), brdf.Ks.z) > 0.0f) { + const Sample3f refl = make_Sample3f(reflect(wo, sp.Ns), 1.0f); + wis.v = powerCosineSampleHemisphere(brdf.Ns, s); + wis.pdf = powerCosineSampleHemispherePDF(wis.v, brdf.Ns); + wis.v = frame(refl.v) * wis.v; + cs = (brdf.Ns + 2) * float(one_over_two_pi) * powf(max(dot(refl.v, wis.v), 1e-10f), brdf.Ns) * + clamp(dot(wis.v, sp.Ns)) * brdf.Ks; + } + + Vec3fa ct = Vec3fa(0.0f); + Sample3f wit = make_Sample3f(Vec3fa(0.0f), 0.0f); + if (max(max(brdf.Kt.x, brdf.Kt.y), brdf.Kt.z) > 0.0f) { + wit = make_Sample3f(neg(wo), 1.0f); + ct = brdf.Kt; + } + + const Vec3fa md = Lw * cd / wid.pdf; + const Vec3fa ms = Lw * cs / wis.pdf; + const Vec3fa mt = Lw * ct / wit.pdf; + + const float Cd = wid.pdf == 0.0f ? 0.0f : max(max(md.x, md.y), md.z); + const float Cs = wis.pdf == 0.0f ? 0.0f : max(max(ms.x, ms.y), ms.z); + const float Ct = wit.pdf == 0.0f ? 0.0f : max(max(mt.x, mt.y), mt.z); + const float C = Cd + Cs + Ct; + + if (C == 0.0f) { + wi_o = make_Sample3f(Vec3fa(0, 0, 0), 0); + return Vec3fa(0, 0, 0); + } + + const float CPd = Cd / C; + const float CPs = Cs / C; + const float CPt = Ct / C; + + if (s.x < CPd) { + wi_o = make_Sample3f(wid.v, wid.pdf * CPd); + return cd; + } else if (s.x < CPd + CPs) { + wi_o = make_Sample3f(wis.v, wis.pdf * CPs); + return cs; + } else { + wi_o = make_Sample3f(wit.v, wit.pdf * CPt); + return ct; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Metal Material // +//////////////////////////////////////////////////////////////////////////////// + +// ======================================================= +struct FresnelConductor +{ + Vec3fa eta; //!< Real part of refraction index + Vec3fa k; //!< Imaginary part of refraction index +}; + +inline Vec3fa fresnelConductor(const float cosi, const Vec3fa &eta, const Vec3fa &k) +{ + const Vec3fa tmp = eta * eta + k * k; + const Vec3fa Rpar = (tmp * (cosi * cosi) - 2.0f * eta * cosi + Vec3fa(1.0f)) * + rcp(tmp * (cosi * cosi) + 2.0f * eta * cosi + Vec3fa(1.0f)); + const Vec3fa Rper = (tmp - 2.0f * eta * cosi + Vec3fa(cosi * cosi)) * + rcp(tmp + 2.0f * eta * cosi + Vec3fa(cosi * cosi)); + return 0.5f * (Rpar + Rper); +} + +inline Vec3fa eval(const FresnelConductor &This, const float cosTheta) +{ + return fresnelConductor(cosTheta, This.eta, This.k); +} + +inline FresnelConductor make_FresnelConductor(const Vec3fa &eta, const Vec3fa &k) +{ + FresnelConductor m; + m.eta = eta; + m.k = k; + return m; +} + +// ======================================================= +struct PowerCosineDistribution +{ + float exp; +}; + +inline float eval(const PowerCosineDistribution &This, const float cosThetaH) +{ + return (This.exp + 2) * (1.0f / (2.0f * (float(M_PI)))) * powf(fabs(cosThetaH), This.exp); +} + +inline void sample(const PowerCosineDistribution &This, const Vec3fa &wo, const Vec3fa &N, Sample3f &wi, const Vec2f s) +{ + Vec3fa dir = powerCosineSampleHemisphere(This.exp, s); + Sample3f wh; + wh.v = frame(N) * dir; + wh.pdf = powerCosineSampleHemispherePDF(dir, This.exp); + Sample3f r = make_Sample3f(reflect(wo, wh.v), 1.0f); + wi = make_Sample3f(r.v, wh.pdf / (4.0f * fabs(dot(wo, wh.v)))); +} + +inline PowerCosineDistribution make_PowerCosineDistribution(const float _exp) +{ + PowerCosineDistribution m; + m.exp = _exp; + return m; +} + +inline void MetalMaterial__preprocess(MetalMaterial *material, BRDF &brdf, const Vec3fa &wo, const Sample &sp) +{ +} + +inline Vec3fa MetalMaterial__eval(MetalMaterial *material, const BRDF &brdf, const Vec3fa &wo, const Sample &sp, const Vec3fa &wi) +{ + const FresnelConductor fresnel = make_FresnelConductor(Vec3fa(material->eta), Vec3fa(material->k)); + const PowerCosineDistribution distribution = make_PowerCosineDistribution(rcp(material->roughness)); + + const float cosThetaO = dot(wo, sp.Ns); + const float cosThetaI = dot(wi, sp.Ns); + if (cosThetaI <= 0.0f || cosThetaO <= 0.0f) + return Vec3fa(0.f); + const Vec3fa wh = normalize(wi + wo); + const float cosThetaH = dot(wh, sp.Ns); + const float cosTheta = dot(wi, wh); // = dot(wo, wh); + const Vec3fa F = eval(fresnel, cosTheta); + const float D = eval(distribution, cosThetaH); + const float G = min(1.0f, min(2.0f * cosThetaH * cosThetaO / cosTheta, + 2.0f * cosThetaH * cosThetaI / cosTheta)); + return (Vec3fa(material->reflectance) * F) * D * G * rcp(4.0f * cosThetaO); +} + +inline Vec3fa MetalMaterial__sample(MetalMaterial *material, const BRDF &brdf, const Vec3fa &Lw, const Vec3fa &wo, const Sample &sp, Sample3f &wi_o, const Vec2f &s) +{ + const PowerCosineDistribution distribution = make_PowerCosineDistribution(rcp(material->roughness)); + + if (dot(wo, sp.Ns) <= 0.0f) + { + wi_o = make_Sample3f(Vec3fa(0.0f), 0.0f); + return Vec3fa(0.f); + } + sample(distribution, wo, sp.Ns, wi_o, s); + if (dot(wi_o.v, sp.Ns) <= 0.0f) + { + wi_o = make_Sample3f(Vec3fa(0.0f), 0.0f); + return Vec3fa(0.f); + } + return MetalMaterial__eval(material, brdf, wo, sp, wi_o.v); +} + +//////////////////////////////////////////////////////////////////////////////// +// ReflectiveMetal Material // +//////////////////////////////////////////////////////////////////////////////// + +inline void ReflectiveMetalMaterial__preprocess(ReflectiveMetalMaterial *material, BRDF &brdf, const Vec3fa &wo, const Sample &sp) +{ +} + +inline Vec3fa ReflectiveMetalMaterial__eval(ReflectiveMetalMaterial *material, const BRDF &brdf, const Vec3fa &wo, const Sample &sp, const Vec3fa &wi) +{ + return Vec3fa(0.0f); +} + +inline Vec3fa ReflectiveMetalMaterial__sample(ReflectiveMetalMaterial *material, const BRDF &brdf, const Vec3fa &Lw, const Vec3fa &wo, const Sample &sp, Sample3f &wi_o, const Vec2f &s) +{ + wi_o = make_Sample3f(reflect(wo,sp.Ns),1.0f); + return Vec3fa(material->reflectance) * fresnelConductor(dot(wo,sp.Ns),Vec3fa((Vec3fa)material->eta),Vec3fa((Vec3fa)material->k)); +} + + +//////////////////////////////////////////////////////////////////////////////// +// Material // +//////////////////////////////////////////////////////////////////////////////// + +inline void Material__preprocess(std::vector materials, unsigned int materialID, + BRDF &brdf, const Vec3fa &wo, const Sample &sp) +{ + auto id = materialID; + { + if (id < materials.size()) // FIXME: workaround for ISPC bug, location reached with empty execution mask + { + Material *material = materials[id]; + + switch (material->type) + { + case MATERIAL_OBJ: + OBJMaterial__preprocess((OBJMaterial *)material, brdf, wo, sp); + break; + case MATERIAL_MATTE: + MatteMaterial__preprocess((MatteMaterial *)material, brdf, wo, sp); + break; + case MATERIAL_METAL: + MetalMaterial__preprocess((MetalMaterial *)material, brdf, wo, sp); + break; + case MATERIAL_REFLECTIVE_METAL: + ReflectiveMetalMaterial__preprocess((ReflectiveMetalMaterial *)material, brdf, wo, sp); + break; + default: + break; + } + } + } +} + +inline Vec3fa Material__eval(std::vector materials, unsigned int materialID, + const BRDF &brdf, const Vec3fa &wo, const Sample &sp, const Vec3fa &wi) +{ + Vec3fa c = Vec3fa(0.0f); + auto id = materialID; + { + if (id < materials.size()) // FIXME: workaround for ISPC bug, location reached with empty execution mask + { + Material *material = materials[id]; + switch (material->type) + { + case MATERIAL_OBJ: + c = OBJMaterial__eval((OBJMaterial *)material, brdf, wo, sp, wi); + break; + case MATERIAL_MATTE: + c = MatteMaterial__eval((MatteMaterial *)material, brdf, wo, sp, wi); + break; + case MATERIAL_METAL: + c = MetalMaterial__eval((MetalMaterial *)material, brdf, wo, sp, wi); + break; + case MATERIAL_REFLECTIVE_METAL: + c = ReflectiveMetalMaterial__eval((ReflectiveMetalMaterial *)material, brdf, wo, sp, wi); + break; + default: + std::cout << "No Material found" << std::endl; + c = Vec3fa(0.0f); + } + } + } + return c; +} + +inline Vec3fa Material__sample(std::vector materials, unsigned int materialID, + const BRDF &brdf, const Vec3fa &Lw, const Vec3fa &wo, const Sample &sp, Sample3f &wi_o, + const Vec2f &s) +{ + Vec3fa c = Vec3fa(0.0f); + auto id = materialID; + { + if (id < materials.size()) // FIXME: workaround for ISPC bug, location reached with empty execution mask + { + Material *material = materials[id]; + switch (material->type) + { + case MATERIAL_OBJ: + c = OBJMaterial__sample((OBJMaterial *)material, brdf, Lw, wo, sp, wi_o, s); + break; + case MATERIAL_MATTE: + c = MatteMaterial__sample((MatteMaterial *)material, brdf, Lw, wo, sp, wi_o, s); + break; + case MATERIAL_METAL: + c = MetalMaterial__sample((MetalMaterial *)material, brdf, Lw, wo, sp, wi_o, s); + break; + case MATERIAL_REFLECTIVE_METAL: + c = ReflectiveMetalMaterial__sample((ReflectiveMetalMaterial *)material, brdf, Lw, wo, sp, wi_o, s); + break; + default: + wi_o = make_Sample3f(Vec3fa(0.0f), 0.0f); + c = Vec3fa(0.0f); + break; + } + } + } + return c; +} diff --git a/CMakeLists.txt b/CMakeLists.txt index 4b2364c..8ad77d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,6 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Framework) ### Assignments ### -foreach (num RANGE 1 2) +foreach (num RANGE 1 3) add_subdirectory(${CMAKE_SOURCE_DIR}/Assignments/Assignment${num}) endforeach ()