358 lines
14 KiB
C++
358 lines
14 KiB
C++
#pragma once
|
|
#include <application.h>
|
|
#include <sampling.hpp>
|
|
#include <scenegraph/obj_loader.h>
|
|
#include <scenegraph/grid.h>
|
|
|
|
#include <lights/ambient_light.h>
|
|
#include <lights/directional_light.h>
|
|
#include <lights/point_light.h>
|
|
#include <lights/quad_light.h>
|
|
#include <lights/spot_light.h>
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
// Henyey-Greenstein phase function
|
|
inline float phase(float g, float cos_theta) {
|
|
float denom = 1 + g * g - 2 * g * cos_theta;
|
|
return 1 / (4 * M_PI) * (1 - g * g) / (denom * sqrtf(denom));
|
|
}
|
|
|
|
|
|
inline void build_basis(Vec3fa normal, Vec3fa& tangent, Vec3fa& binormal) {
|
|
float sign_value = normal.z >= 0.0 ? 1.0 : -1.0;
|
|
float a = -1.0 / (sign_value + normal.z);
|
|
float b = normal.x * normal.y * a;
|
|
tangent = Vec3fa(1.0 + sign_value * normal.x * normal.x * a, sign_value * b, -sign_value * normal.x);
|
|
binormal = Vec3fa(b, sign_value + normal.y * normal.y * a, -normal.y);
|
|
}
|
|
|
|
// wo = outgoing direction, g = HG scattering parameter, u = random vector. it outputs the sampled direction + fill pdf in
|
|
inline Vec3fa sample_phase_function(Vec3fa wo, float g, Vec2fa u, float& pdf) {
|
|
float cos_theta;
|
|
if (std::abs(g) < 1e-3)
|
|
cos_theta = 1.0 - 2.0 * u[0];
|
|
else {
|
|
float sqr = (1.0 - g * g) / (1.0 - g + 2.0 * g * u[0]);
|
|
cos_theta = (1.0 + g * g - sqr * sqr) / (2.0 * g);
|
|
}
|
|
|
|
float sin_theta = std::sqrt(max(0.0, 1.0 - cos_theta * cos_theta));
|
|
float phi = 2.0 * M_PI * u[1];
|
|
|
|
Vec3fa tvec, bvec;
|
|
build_basis(wo, tvec, bvec);
|
|
|
|
float sin_phi = std::sin(phi);
|
|
float cos_phi = std::cos(phi);
|
|
|
|
Vec3fa inc_light = sin_theta * cos_phi * tvec +
|
|
sin_theta * sin_phi * bvec +
|
|
-cos_theta * wo;
|
|
|
|
pdf = phase(g, cos_theta);
|
|
return inc_light;
|
|
}
|
|
|
|
// blackbody radiance calculation for a given tempretature
|
|
inline float blackbody_radiance(float lambda, float temp) {
|
|
const float c = 299792458; // Speed of light in vacuum (m/s)
|
|
const float h = 6.62606957e-34; // Planck's constant (J*s)
|
|
const float kb = 1.3806488e-23; // Boltzmann constant (J/K)
|
|
|
|
// Convert lambda from nanometers to meters
|
|
float l = lambda * 1e-9;
|
|
|
|
// Calculate lambda to the fifth power
|
|
float lambda5 = l * l * l * l * l;
|
|
|
|
// Calculate emitted radiance using Planck's Law
|
|
float Le = (2 * h * c * c) / (lambda5 * (std::exp((h * c) / (l * kb * temp)) - 1));
|
|
|
|
return Le;
|
|
}
|
|
|
|
inline float blackbody_radiance_normalized(float lambda, float tmp) {
|
|
float radiance = blackbody_radiance(lambda, tmp);
|
|
float lambdaMax = 2.8977721e-3 / tmp * 1e9;
|
|
float maxL = blackbody_radiance(lambdaMax, tmp);
|
|
return radiance / maxL;
|
|
}
|
|
|
|
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];
|
|
std::string name;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// 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;
|
|
brdf.name = material->name;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Material //
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
inline void Material__preprocess(std::vector<Material *> 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;
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
inline Vec3fa Material__eval(std::vector<Material *> 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;
|
|
default:
|
|
std::cout << "No Material found" << std::endl;
|
|
c = Vec3fa(0.0f);
|
|
}
|
|
}
|
|
}
|
|
return c;
|
|
}
|
|
|
|
inline Vec3fa Material__sample(std::vector<Material *> 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;
|
|
default: wi_o = make_Sample3f(Vec3fa(0.0f), 0.0f);
|
|
c = Vec3fa(0.0f);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return c;
|
|
}
|