games101-hw/07/Assignment7/Material.hpp

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