#pragma once #include "Vector.hpp" /*! \brief Material class for ray tracing. A material that can be assigned to Intersectables. Implements [lambert + phong + ideal reflection + refraction + emission] model. Descendants may override this by texturing (TexturedMaterial) or procedural texturing. */ class Material { protected: char name[64]; //! diffuse coefficient Vector kd; //! specular energy reflected for perpendicular light Vector ks; //! specularity float ns; //! ideal Vector ki; //! refractive Vector kr; //! refraction ratio float rf; //! diffuse albedo float ad; //! specular albedo float as; //! ideal albedo float ai; //! refraction albedo float ar; //! diffuse emission Vector ed; //! directional (near surface normal) emission Vector es; //! directional (near surface normal) emission exponent float ens; //! diffuse emission albedo float aed; //! directional emission albedo float aes; public: static const Material DIFFUSEWHITE; static const Material DIFFUSEGRAY; static const Material SHINYBLACK; static const Material STEEL; static const Material DIFFUSERED; static const Material DIFFUSEBLUE; static const Material DIFFUSELIGHTBLUE; static const Material DIFFUSEGREEN; static const Material DIFFUSEYELLOW; static const Material DIFFUSEORANGE; static const Material YELLOWPLASTIC; static const Material LAMP; Material(std::istream& isc) { name[0] = '\0'; kd = ks = ki = kr = ed = es = Vector::RGBBLACK; ns = rf = ens = 0.0f; char key[200]; do { isc >> key; if(strcmp(key,"name") == 0) isc >> name; else if(strcmp(key,"diffuse") == 0) isc >> kd; else if(strcmp(key,"specular") == 0) { isc >> ks; isc >> ns; } else if(strcmp(key,"emissive") == 0) isc >> ed; else if(strcmp(key,"specularemissive") == 0) { isc >> es; isc >> ens; } else if(strcmp(key,"ideal") == 0) isc >> ki; else if(strcmp(key,"refactive") == 0) { isc >> kd; isc >> rf; } }while(strcmp(key,"end") != 0); ad = kd.sum() / 3.0f; as = ks.sum() / 3.0f; ai = ki.sum() / 3.0f; ar = kr.sum() / 3.0f; aed = ed.sum() / 3.0f; aes = es.sum() / 3.0f; } Material(const Vector& kd, const Vector& ks, float ns) { this->kd = kd; this->ks = ks; this->ns = ns; this->ki = Vector::RGBBLACK; this->kr = Vector::RGBBLACK; this->rf = 1.0f; this->ed = Vector::RGBBLACK; this->es = Vector::RGBBLACK; this->ens = 2.0f; ad = kd.sum() / 3.0f; as = ks.sum() / 3.0f; ai = ki.sum() / 3.0f; ar = kr.sum() / 3.0f; aed = ed.sum() / 3.0f; aes = es.sum() / 3.0f; } Material(const Vector& kd, const Vector& ks, float ns, const Vector& ed, const Vector& es, float ens) { this->kd = kd; this->ks = ks; this->ns = ns; this->ki = Vector::RGBBLACK; this->kr = Vector::RGBBLACK; this->rf = 1.0f; this->ed = ed; this->es = es; this->ens = ens; ad = kd.sum() / 3.0f; as = ks.sum() / 3.0f; ai = ki.sum() / 3.0f; ar = kr.sum() / 3.0f; aed = ed.sum() / 3.0f; aes = es.sum() / 3.0f; } void makeDiffuseAndIdeal(const Vector& kd, const Vector& ki) { this->kd = kd; this->ks = Vector::RGBBLACK; this->ns = 0.0f; this->ki = ki; this->kr = Vector::RGBBLACK; this->rf = 1.0f; ad = kd.sum() / 3.0f; as = ks.sum() / 3.0f; ai = ki.sum() / 3.0f; ar = kr.sum() / 3.0f; } void makeRefractiveAndIdeal(float rf, const Vector& kr, const Vector& ki) { this->kd = Vector::RGBBLACK; this->ks = Vector::RGBBLACK; this->ns = 0.0f; this->ki = ki; this->kr = kr; this->rf = rf; ad = kd.sum() / 3.0f; as = ks.sum() / 3.0f; ai = ki.sum() / 3.0f; ar = kr.sum() / 3.0f; } bool isEmissive() const { if(isHDRI()) return true; return aes + aed > 0.0f; } bool isMirror() const { return ai > 0.0f; } bool isGlass() const { return ar > 0.0f; } void getReflectivity(Vector& result) const { result = ki; } void getRefractivity(Vector& result) const { result = kr; } const Vector& getDiffuseEmission() const { return ed; } const Vector& getDiffuseBrdf() const { return kd; } virtual Vector getProceduralDiffuseBrdf(const Vector& atPoint) const { return kd; } virtual Vector getTextureDiffuseBrdf(const Vector& atPoint) const { return kd; } const Vector& getIdealBrdf() const { return ki; } float getDiffuseAlbedo() const { return ad; } float getTextureDiffuseAlbedo(const Vector& uv) const { return ad; } float getSpecularAlbedo() const { return as; } float getIdealAlbedo() const { return ai; } float getTextureIdealAlbedo(const Vector& uv) const { return ad; } float getRefractiveAlbedo() const { return ar; } float getRefractionDensity() const { return rf; } Vector getSurfaceRadiance() const { return ed; } void scatteringProbability (const Vector& in, Vector& out, const Vector& normal, Vector& result) const { float nDotOut = normal * out; float nDotIn = - (normal * in); if ( nDotOut < 0) { result = Vector::RGBBLACK; return; } // add diffuse part result = kd; // add specular using max Phong shading Vector idealReflected; idealReflected.setIdealReflectedDirection (in, normal); float dotProduct = idealReflected* out; if(dotProduct > 0) { float scale = (float)pow (dotProduct, ns) * (ns + 2.0f) * 0.5f / 3.14159265358979323846f ; if(nDotOut > nDotIn) scale /= nDotOut; else scale /= nDotIn; result += ks * scale; } result *= nDotOut; } virtual void proceduralScatteringProbability (const Vector& atPoint, const Vector& in, Vector& out, const Vector& normal, Vector& result) const { float nDotOut = normal * out; float nDotIn = - (normal * in); if ( nDotOut < 0) { result = Vector::RGBBLACK; return; } // add diffuse part result = kd; // add specular using max Phong shading Vector idealReflected; idealReflected.setIdealReflectedDirection (in, normal); float dotProduct = idealReflected* out; if(dotProduct > 0) { float scale = (float)pow (dotProduct, ns) * (ns + 2.0f) * 0.5f / 3.14159265358979323846f ; if(nDotOut > nDotIn) scale /= nDotOut; else scale /= nDotIn; result += ks * scale; } result *= nDotOut; } const char* getName() const { return name; } virtual bool isHDRI() const { return false; } virtual ~Material(){} };