180 lines
5.4 KiB
C++
180 lines
5.4 KiB
C++
|
//
|
||
|
// Created by LEI XU on 5/16/19.
|
||
|
//
|
||
|
|
||
|
#ifndef RAYTRACING_MATERIAL_H
|
||
|
#define RAYTRACING_MATERIAL_H
|
||
|
|
||
|
#include "Vector.hpp"
|
||
|
|
||
|
enum MaterialType { DIFFUSE};
|
||
|
|
||
|
class Material{
|
||
|
private:
|
||
|
|
||
|
// Compute reflection direction
|
||
|
Vector3f reflect(const Vector3f &I, const Vector3f &N) const
|
||
|
{
|
||
|
return I - 2 * dotProduct(I, N) * N;
|
||
|
}
|
||
|
|
||
|
// Compute refraction direction using Snell's law
|
||
|
//
|
||
|
// We need to handle with care the two possible situations:
|
||
|
//
|
||
|
// - When the ray is inside the object
|
||
|
//
|
||
|
// - When the ray is outside.
|
||
|
//
|
||
|
// If the ray is outside, you need to make cosi positive cosi = -N.I
|
||
|
//
|
||
|
// If the ray is inside, you need to invert the refractive indices and negate the normal N
|
||
|
Vector3f refract(const Vector3f &I, const Vector3f &N, const float &ior) const
|
||
|
{
|
||
|
float cosi = clamp(-1, 1, dotProduct(I, N));
|
||
|
float etai = 1, etat = ior;
|
||
|
Vector3f n = N;
|
||
|
if (cosi < 0) { cosi = -cosi; } else { std::swap(etai, etat); n= -N; }
|
||
|
float eta = etai / etat;
|
||
|
float k = 1 - eta * eta * (1 - cosi * cosi);
|
||
|
return k < 0 ? 0 : eta * I + (eta * cosi - sqrtf(k)) * n;
|
||
|
}
|
||
|
|
||
|
// Compute Fresnel equation
|
||
|
//
|
||
|
// \param I is the incident view direction
|
||
|
//
|
||
|
// \param N is the normal at the intersection point
|
||
|
//
|
||
|
// \param ior is the material refractive index
|
||
|
//
|
||
|
// \param[out] kr is the amount of light reflected
|
||
|
void fresnel(const Vector3f &I, const Vector3f &N, const float &ior, float &kr) const
|
||
|
{
|
||
|
float cosi = clamp(-1, 1, dotProduct(I, N));
|
||
|
float etai = 1, etat = ior;
|
||
|
if (cosi > 0) { std::swap(etai, etat); }
|
||
|
// Compute sini using Snell's law
|
||
|
float sint = etai / etat * sqrtf(std::max(0.f, 1 - cosi * cosi));
|
||
|
// Total internal reflection
|
||
|
if (sint >= 1) {
|
||
|
kr = 1;
|
||
|
}
|
||
|
else {
|
||
|
float cost = sqrtf(std::max(0.f, 1 - sint * sint));
|
||
|
cosi = fabsf(cosi);
|
||
|
float Rs = ((etat * cosi) - (etai * cost)) / ((etat * cosi) + (etai * cost));
|
||
|
float Rp = ((etai * cosi) - (etat * cost)) / ((etai * cosi) + (etat * cost));
|
||
|
kr = (Rs * Rs + Rp * Rp) / 2;
|
||
|
}
|
||
|
// As a consequence of the conservation of energy, transmittance is given by:
|
||
|
// kt = 1 - kr;
|
||
|
}
|
||
|
|
||
|
Vector3f toWorld(const Vector3f &a, const Vector3f &N){
|
||
|
Vector3f B, C;
|
||
|
if (std::fabs(N.x) > std::fabs(N.y)){
|
||
|
float invLen = 1.0f / std::sqrt(N.x * N.x + N.z * N.z);
|
||
|
C = Vector3f(N.z * invLen, 0.0f, -N.x *invLen);
|
||
|
}
|
||
|
else {
|
||
|
float invLen = 1.0f / std::sqrt(N.y * N.y + N.z * N.z);
|
||
|
C = Vector3f(0.0f, N.z * invLen, -N.y *invLen);
|
||
|
}
|
||
|
B = crossProduct(C, N);
|
||
|
return a.x * B + a.y * C + a.z * N;
|
||
|
}
|
||
|
|
||
|
public:
|
||
|
MaterialType m_type;
|
||
|
//Vector3f m_color;
|
||
|
Vector3f m_emission;
|
||
|
float ior;
|
||
|
Vector3f Kd, Ks;
|
||
|
float specularExponent;
|
||
|
//Texture tex;
|
||
|
|
||
|
inline Material(MaterialType t=DIFFUSE, Vector3f e=Vector3f(0,0,0));
|
||
|
inline MaterialType getType();
|
||
|
//inline Vector3f getColor();
|
||
|
inline Vector3f getColorAt(double u, double v);
|
||
|
inline Vector3f getEmission();
|
||
|
inline bool hasEmission();
|
||
|
|
||
|
// sample a ray by Material properties
|
||
|
inline Vector3f sample(const Vector3f &wi, const Vector3f &N);
|
||
|
// given a ray, calculate the PdF of this ray
|
||
|
inline float pdf(const Vector3f &wi, const Vector3f &wo, const Vector3f &N);
|
||
|
// given a ray, calculate the contribution of this ray
|
||
|
inline Vector3f eval(const Vector3f &wi, const Vector3f &wo, const Vector3f &N);
|
||
|
|
||
|
};
|
||
|
|
||
|
Material::Material(MaterialType t, Vector3f e){
|
||
|
m_type = t;
|
||
|
//m_color = c;
|
||
|
m_emission = e;
|
||
|
}
|
||
|
|
||
|
MaterialType Material::getType(){return m_type;}
|
||
|
///Vector3f Material::getColor(){return m_color;}
|
||
|
Vector3f Material::getEmission() {return m_emission;}
|
||
|
bool Material::hasEmission() {
|
||
|
if (m_emission.norm() > EPSILON) return true;
|
||
|
else return false;
|
||
|
}
|
||
|
|
||
|
Vector3f Material::getColorAt(double u, double v) {
|
||
|
return Vector3f();
|
||
|
}
|
||
|
|
||
|
|
||
|
Vector3f Material::sample(const Vector3f &wi, const Vector3f &N){
|
||
|
switch(m_type){
|
||
|
case DIFFUSE:
|
||
|
{
|
||
|
// uniform sample on the hemisphere
|
||
|
float x_1 = get_random_float(), x_2 = get_random_float();
|
||
|
float z = std::fabs(1.0f - 2.0f * x_1);
|
||
|
float r = std::sqrt(1.0f - z * z), phi = 2 * M_PI * x_2;
|
||
|
Vector3f localRay(r*std::cos(phi), r*std::sin(phi), z);
|
||
|
return toWorld(localRay, N);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
float Material::pdf(const Vector3f &wi, const Vector3f &wo, const Vector3f &N){
|
||
|
switch(m_type){
|
||
|
case DIFFUSE:
|
||
|
{
|
||
|
// uniform sample probability 1 / (2 * PI)
|
||
|
if (dotProduct(wo, N) > 0.0f)
|
||
|
return 0.5f / M_PI;
|
||
|
else
|
||
|
return 0.0f;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Vector3f Material::eval(const Vector3f &wi, const Vector3f &wo, const Vector3f &N){
|
||
|
switch(m_type){
|
||
|
case DIFFUSE:
|
||
|
{
|
||
|
// calculate the contribution of diffuse model
|
||
|
float cosalpha = dotProduct(N, wo);
|
||
|
if (cosalpha > 0.0f) {
|
||
|
Vector3f diffuse = Kd / M_PI;
|
||
|
return diffuse;
|
||
|
}
|
||
|
else
|
||
|
return Vector3f(0.0f);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif //RAYTRACING_MATERIAL_H
|