1 #pragma once
2 
3 #ifndef Y_MATERIAL_H
4 #define Y_MATERIAL_H
5 
6 #include <yafray_constants.h>
7 #include "color.h"
8 #include "vector3d.h"
9 #include "surface.h"
10 #include "utilities/sample_utils.h"
11 #include "object3d.h"
12 
13 __BEGIN_YAFRAY
14 
15 #define FACE_FORWARD(Ng,N,I) ((((Ng)*(I))<0) ? (-N) : (N))
16 /*
17 class YAFRAYCORE_EXPORT BSDF_t
18 {
19 	public:
20 	BSDF_t(){};
21 	virtual ~BSDF_t(){};
22 	BSDF_t(const surfacePoint_t &sp, void* userdata);
23 	virtual color_t sample(const vector3d_t &wo, vector3d_t &wi, float s1, float s2, void* userdata) = 0;
24 	virtual color_t eval(const surfacePoint_t &sp, const vector3d_t &wo, const vector3d_t &wl, void* userdata) = 0;
25 };
26 */
27 
28 class surfacePoint_t;
29 struct renderState_t;
30 class volumeHandler_t;
31 
32 enum bsdfFlags_t
33 {
34 	BSDF_NONE		= 0x0000,
35 	BSDF_SPECULAR	= 0x0001,
36 	BSDF_GLOSSY		= 0x0002,
37 	BSDF_DIFFUSE	= 0x0004,
38 	BSDF_DISPERSIVE	= 0x0008,
39 	BSDF_REFLECT	= 0x0010,
40 	BSDF_TRANSMIT	= 0x0020,
41 	BSDF_FILTER		= 0x0040,
42 	BSDF_EMIT		= 0x0080,
43 	BSDF_VOLUMETRIC = 0x0100,
44 	BSDF_ALL_SPECULAR = BSDF_SPECULAR | BSDF_REFLECT | BSDF_TRANSMIT,
45 	BSDF_ALL_GLOSSY = BSDF_GLOSSY | BSDF_REFLECT | BSDF_TRANSMIT,
46 	BSDF_ALL = BSDF_SPECULAR | BSDF_GLOSSY | BSDF_DIFFUSE | BSDF_DISPERSIVE | BSDF_REFLECT | BSDF_TRANSMIT | BSDF_FILTER
47 };
48 
49 typedef unsigned int BSDF_t;
50 
51 struct sample_t
52 {
53 	sample_t(float s_1, float s_2, BSDF_t sflags=BSDF_ALL, bool revrs=false):
s1sample_t54 			s1(s_1), s2(s_2), pdf(0.f), flags(sflags), sampledFlags(BSDF_NONE), reverse(revrs){}
55 	float s1, s2;
56 	float pdf;
57 	BSDF_t flags, sampledFlags;
58 	bool reverse; //!< if true, the sample method shall return the probability/color for swapped incoming/outgoing dir
59 	float pdf_back;
60 	color_t col_back;
61 };
62 
63 struct pSample_t: public sample_t // << whats with the public?? structs inherit publicly by default *DarkTide
64 {
65 	pSample_t(float s_1, float s_2, float s_3, BSDF_t sflags, const color_t &l_col, const color_t& transm=color_t(1.f)):
sample_tpSample_t66 		sample_t(s_1, s_2, sflags), s3(s_3), lcol(l_col), alpha(transm){}
67 	float s3;
68 	const color_t lcol; //!< the photon color from last scattering
69 	const color_t alpha; //!< the filter color between last scattering and this hit (not pre-applied to lcol!)
70 	color_t color; //!< the new color after scattering, i.e. what will be lcol for next scatter.
71 };
72 
73 enum visibility_t
74 {
75 	NORMAL_VISIBLE			= 0,
76 	VISIBLE_NO_SHADOWS		= 1,
77 	INVISIBLE_SHADOWS_ONLY	= 2,
78 	INVISIBLE				= 3
79 };
80 
81 
82 class YAFRAYCORE_EXPORT material_t
83 {
84 	public:
material_t()85 		material_t(): bsdfFlags(BSDF_NONE), mVisibility(NORMAL_VISIBLE), mReceiveShadows(true), reqMem(0), volI(nullptr), volO(nullptr),additionalDepth(0)
86 		{
87 			materialIndexAuto++;
88 			srand(materialIndexAuto);
89 			float R,G,B;
90 			do
91 			{
92 				R = (float) (rand() % 8) / 8.f;
93 				G = (float) (rand() % 8) / 8.f;
94 				B = (float) (rand() % 8) / 8.f;
95 			}
96 			while (R+G+B < 0.5f);
97 			materialIndexAutoColor = color_t(R,G,B);
98             materialIndexAutoNumber = materialIndexAuto;
99 		}
~material_t()100 		virtual ~material_t() { resetMaterialIndex(); }
101 
102 		/*! Initialize the BSDF of a material. You must call this with the current surface point
103 			first before any other methods (except isTransparent/getTransparency)! The renderstate
104 			holds a pointer to preallocated userdata to save data that only depends on the current sp,
105 			like texture lookups etc.
106 			\param bsdfTypes returns flags for all bsdf components the material has
107 		 */
108         virtual void initBSDF(const renderState_t &state, surfacePoint_t &sp, BSDF_t &bsdfTypes)const = 0;
109 
110 		/*! evaluate the BSDF for the given components.
111 				@param types the types of BSDFs to be evaluated (e.g. diffuse only, or diffuse and glossy) */
112 		virtual color_t eval(const renderState_t &state, const surfacePoint_t &sp, const vector3d_t &wo, const vector3d_t &wl, BSDF_t types, bool force_eval = false)const = 0;
113 
114 		/*! take a sample from the BSDF, given a 2-dimensional sample value and the BSDF types to be sampled from
115 			\param s s1, s2 and flags members give necessary information for creating the sample, pdf and sampledFlags need to be returned
116 			\param W returns the weight for importance sampling
117 		*/
118 		virtual color_t sample(const renderState_t &state, const surfacePoint_t &sp, const vector3d_t &wo, vector3d_t &wi, sample_t &s, float &W)const = 0;// {return color_t(0.f);}
sample(const renderState_t & state,const surfacePoint_t & sp,const vector3d_t & wo,vector3d_t * const dir,color_t & tcol,sample_t & s,float * const W)119 		virtual color_t sample(const renderState_t &state, const surfacePoint_t &sp, const vector3d_t &wo, vector3d_t *const dir, color_t &tcol, sample_t &s, float *const W)const {return color_t(0.f);}
120 
sampleClay(const renderState_t & state,const surfacePoint_t & sp,const vector3d_t & wo,vector3d_t & wi,sample_t & s,float & W)121 		virtual color_t sampleClay(const renderState_t &state, const surfacePoint_t &sp, const vector3d_t &wo, vector3d_t &wi, sample_t &s, float &W)const
122 		{
123 			vector3d_t N = FACE_FORWARD(sp.Ng, sp.N, wo);
124 			wi = SampleCosHemisphere(N, sp.NU, sp.NV, s.s1, s.s2);
125 			s.pdf = std::fabs(wi*N);
126 			W = (std::fabs(wi*sp.N))/(s.pdf*0.99f + 0.01f);
127 			return color_t(1.0f);	//Clay color White 100%
128 		}
129 		/*! return the pdf for sampling the BSDF with wi and wo
130 		*/
pdf(const renderState_t & state,const surfacePoint_t & sp,const vector3d_t & wo,const vector3d_t & wi,BSDF_t bsdfs)131 		virtual float pdf(const renderState_t &state, const surfacePoint_t &sp, const vector3d_t &wo, const vector3d_t &wi, BSDF_t bsdfs)const {return 0.f;}
132 
133 
134 		/*! indicate whether light can (partially) pass the material without getting refracted,
135 			e.g. a curtain or even very thin foils approximated as single non-refractive layer.
136 			used to trace transparent shadows. Note that in this case, initBSDF was NOT called before!
137 		*/
isTransparent()138 		virtual bool isTransparent() const { return false; }
139 
140 		/*!	used for computing transparent shadows.	Default implementation returns black (i.e. solid shadow).
141 			This is only used for shadow calculations and may only be called when isTransparent returned true.	*/
getTransparency(const renderState_t & state,const surfacePoint_t & sp,const vector3d_t & wo)142 		virtual color_t getTransparency(const renderState_t &state, const surfacePoint_t &sp, const vector3d_t &wo)const { return color_t(0.0); }
143 		/*! evaluate the specular components for given direction. Somewhat a specialization of sample(),
144 			because neither sample values nor pdf values are necessary for this.
145 			Typical use: recursive raytracing of integrators.
146 			\param dir dir[0] returns reflected direction, dir[1] refracted direction
147 			\param col col[0] returns reflected spectrum, dir[1] refracted spectrum */
getSpecular(const renderState_t & state,const surfacePoint_t & sp,const vector3d_t & wo,bool & reflect,bool & refract,vector3d_t * const dir,color_t * const col)148 		virtual void getSpecular(const renderState_t &state, const surfacePoint_t &sp, const vector3d_t &wo,
149 								 bool &reflect, bool &refract, vector3d_t *const dir, color_t *const col)const
150 		{ reflect=false; refract=false; }
151 
152 		/*! get the overall reflectivity of the material (used to compute radiance map for example) */
153 		virtual color_t getReflectivity(const renderState_t &state, const surfacePoint_t &sp, BSDF_t flags)const;
154 
155 		/*!	allow light emitting materials, for realizing correctly visible area lights.
156 			default implementation returns black obviously.	*/
emit(const renderState_t & state,const surfacePoint_t & sp,const vector3d_t & wo)157 		virtual color_t emit(const renderState_t &state, const surfacePoint_t &sp, const vector3d_t &wo)const { return color_t(0.0); }
158 
159 		/*! get the volumetric handler for space at specified side of the surface
160 			\param inside true means space opposite of surface normal, which is considered "inside" */
getVolumeHandler(bool inside)161 		virtual const volumeHandler_t* getVolumeHandler(bool inside)const { return inside ? volI : volO; }
162 
163 		/*! special function, get the alpha-value of a material, used to calculate the alpha-channel */
getAlpha(const renderState_t & state,const surfacePoint_t & sp,const vector3d_t & wo)164 		virtual float getAlpha(const renderState_t &state, const surfacePoint_t &sp, const vector3d_t &wo)const { return 1.f; }
165 
166 		/*! specialized function for photon mapping. Default function uses the sample function, which will do fine for
167 			most materials unless there's a less expensive way or smarter scattering approach */
168 		virtual bool scatterPhoton(const renderState_t &state, const surfacePoint_t &sp, const vector3d_t &wi, vector3d_t &wo, pSample_t &s) const;
169 
getFlags()170 		BSDF_t getFlags() const { return bsdfFlags; }
171 		/*! Materials may have to do surface point specific (pre-)calculation that need extra storage.
172 			returns the required amount of "userdata" memory for all the functions that require a render state */
getReqMem()173 		size_t getReqMem() const { return reqMem; }
174 
175 		/*! Get materials IOR (for refracted photons) */
176 
getMatIOR()177 		virtual float getMatIOR() const { return 1.5f; }
getDiffuseColor(const renderState_t & state)178 	        virtual color_t getDiffuseColor(const renderState_t &state) const { return color_t(0.f); }
getGlossyColor(const renderState_t & state)179 	        virtual color_t getGlossyColor(const renderState_t &state) const { return color_t(0.f); }
getTransColor(const renderState_t & state)180 	        virtual color_t getTransColor(const renderState_t &state) const { return color_t(0.f); }
getMirrorColor(const renderState_t & state)181 	        virtual color_t getMirrorColor(const renderState_t &state) const { return color_t(0.f); }
getSubSurfaceColor(const renderState_t & state)182 	        virtual color_t getSubSurfaceColor(const renderState_t &state) const { return color_t(0.f); }
setMaterialIndex(const float & newMatIndex)183 	        void setMaterialIndex(const float &newMatIndex)
184 	        {
185 			materialIndex = newMatIndex;
186 			if(highestMaterialIndex < materialIndex) highestMaterialIndex = materialIndex;
187 		}
resetMaterialIndex()188 	        void resetMaterialIndex() { highestMaterialIndex = 1.f; materialIndexAuto = 0; }
setMaterialIndex(const int & newMatIndex)189 	        void setMaterialIndex(const int &newMatIndex) { setMaterialIndex((float) newMatIndex); }
getAbsMaterialIndex()190 	        float getAbsMaterialIndex() const { return materialIndex; }
getNormMaterialIndex()191 	        float getNormMaterialIndex() const { return (materialIndex / highestMaterialIndex); }
getAbsMaterialIndexColor()192 	        color_t getAbsMaterialIndexColor() const
193 		{
194 			return color_t(materialIndex);
195 		}
getNormMaterialIndexColor()196 		color_t getNormMaterialIndexColor() const
197 		{
198 			float normalizedMaterialIndex = getNormMaterialIndex();
199 			return color_t(normalizedMaterialIndex);
200 		}
getAutoMaterialIndexColor()201 		color_t getAutoMaterialIndexColor() const
202 		{
203 			return materialIndexAutoColor;
204 		}
getAutoMaterialIndexNumber()205 		color_t getAutoMaterialIndexNumber() const
206 		{
207 			return materialIndexAutoNumber;
208 		}
209 
getVisibility()210 		visibility_t getVisibility() const { return mVisibility; }
getReceiveShadows()211 		bool getReceiveShadows() const { return mReceiveShadows; }
212 
isFlat()213 		bool isFlat() const { return mFlatMaterial; }
214 
getAdditionalDepth()215 		int getAdditionalDepth() const { return additionalDepth; }
getTransparentBiasFactor()216 		float getTransparentBiasFactor() const { return transparentBiasFactor; }
getTransparentBiasMultiplyRayDepth()217 		bool getTransparentBiasMultiplyRayDepth() const { return transparentBiasMultiplyRayDepth; }
218 
219 		void applyWireFrame(float & value, float wireFrameAmount, const surfacePoint_t &sp) const;
220 		void applyWireFrame(color_t & col, float wireFrameAmount, const surfacePoint_t &sp) const;
221 		void applyWireFrame(color_t *const col, float wireFrameAmount, const surfacePoint_t &sp) const;
222 		void applyWireFrame(colorA_t & col, float wireFrameAmount, const surfacePoint_t &sp) const;
223 		void applyWireFrame(colorA_t *const col, float wireFrameAmount, const surfacePoint_t &sp) const;
224 
setSamplingFactor(const float & newSamplingFactor)225 		void setSamplingFactor(const float &newSamplingFactor)
226 		{
227 			mSamplingFactor = newSamplingFactor;
228 			if(mHighestSamplingFactor < mSamplingFactor) mHighestSamplingFactor = mSamplingFactor;
229 		}
getSamplingFactor()230 		float getSamplingFactor() const { return mSamplingFactor; }
231 
232 	protected:
233 		/* small function to apply bump mapping to a surface point
234 			you need to determine the partial derivatives for NU and NV first, e.g. from a shader node */
235         void applyBump(surfacePoint_t &sp, float dfdNU, float dfdNV) const;
236 
237 		BSDF_t bsdfFlags;
238 
239 		visibility_t mVisibility; //!< sets material visibility (Normal:visible, visible without shadows, invisible (shadows only) or totally invisible.
240 
241 		bool mReceiveShadows; //!< enables/disables material reception of shadows.
242 
243 		size_t reqMem; //!< the amount of "temporary" memory required to compute/store surface point specific data
244 		volumeHandler_t* volI; //!< volumetric handler for space inside material (opposed to surface normal)
245 		volumeHandler_t* volO; //!< volumetric handler for space outside ofmaterial (where surface normal points to)
246 	        float materialIndex;	//!< Material Index for the material-index render pass
247 		static unsigned int materialIndexAuto;	//!< Material Index automatically generated for the material-index-auto render pass
248 		color_t materialIndexAutoColor;	//!< Material Index color automatically generated for the material-index-auto (color) render pass
249         float materialIndexAutoNumber = 0.f;	//!< Material Index number automatically generated for the material-index-auto-abs (numeric) render pass
250 		static float highestMaterialIndex;	//!< Class shared variable containing the highest material index used for the Normalized Material Index pass.
251 		int additionalDepth;	//!< Per-material additional ray-depth
252 		float transparentBiasFactor = 0.f;	//!< Per-material additional ray-bias setting for transparency (trick to avoid black areas due to insufficient depth when many transparent surfaces stacked). If >0.f this function is enabled and the result will no longer be realistic and may have artifacts.
253 		bool transparentBiasMultiplyRayDepth = false;	//!< Per-material additional ray-bias setting for transparency (trick to avoid black areas due to insufficient depth when many transparent surfaces stacked). If enabled the bias will be multiplied by the current ray depth so the first transparent surfaces are rendered better and subsequent surfaces might be skipped.
254 
255 		float mWireFrameAmount = 0.f;           //!< Wireframe shading amount
256         float mWireFrameThickness = 0.01f;      //!< Wireframe thickness
257         float mWireFrameExponent = 0.f;         //!< Wireframe exponent (0.f = solid, 1.f=linearly gradual, etc)
258         color_t mWireFrameColor = color_t(1.f); //!< Wireframe shading color
259 
260         float mSamplingFactor = 1.f;	//!< Material sampling factor, to allow some materials to receive more samples than others
261 
262         bool mFlatMaterial = false;		//!< Flat Material is a special non-photorealistic material that does not multiply the surface color by the cosine of the angle with the light, as happens in real life. Also, if receive_shadows is disabled, this flat material does no longer self-shadow. For special applications only.
263 
264 		static float mHighestSamplingFactor;	//!< Class shared variable containing the highest material sampling factor. This is used to calculate the max. possible samples for the Sampling pass.
265 };
266 
267 
applyWireFrame(float & value,float wireFrameAmount,const surfacePoint_t & sp)268 inline void material_t::applyWireFrame(float & value, float wireFrameAmount, const surfacePoint_t &sp) const
269 {
270 	if(wireFrameAmount > 0.f && mWireFrameThickness > 0.f)
271 	{
272 		float dist = sp.getDistToNearestEdge();
273 		if(dist <= mWireFrameThickness)
274 		{
275 			if(mWireFrameExponent > 0.f)
276 			{
277 				wireFrameAmount *= std::pow((mWireFrameThickness - dist) / mWireFrameThickness, mWireFrameExponent);
278 			}
279 			value = value * (1.f - wireFrameAmount);
280 		}
281 	}
282 }
283 
applyWireFrame(color_t & col,float wireFrameAmount,const surfacePoint_t & sp)284 inline void material_t::applyWireFrame(color_t & col, float wireFrameAmount, const surfacePoint_t &sp) const
285 {
286 	if(wireFrameAmount > 0.f && mWireFrameThickness > 0.f)
287 	{
288 		float dist = sp.getDistToNearestEdge();
289 		if(dist <= mWireFrameThickness)
290 		{
291 			color_t wireFrameCol = mWireFrameColor * wireFrameAmount;
292 			if(mWireFrameExponent > 0.f)
293 			{
294 				wireFrameAmount *= std::pow((mWireFrameThickness - dist) / mWireFrameThickness, mWireFrameExponent);
295 			}
296 			col.blend(wireFrameCol, wireFrameAmount);
297 		}
298 	}
299 }
300 
applyWireFrame(color_t * const col,float wireFrameAmount,const surfacePoint_t & sp)301 inline void material_t::applyWireFrame(color_t *const col, float wireFrameAmount, const surfacePoint_t &sp) const
302 {
303 	if(wireFrameAmount > 0.f && mWireFrameThickness > 0.f)
304 	{
305 		float dist = sp.getDistToNearestEdge();
306 		if(dist <= mWireFrameThickness)
307 		{
308 			color_t wireFrameCol = mWireFrameColor * wireFrameAmount;
309 			if(mWireFrameExponent > 0.f)
310 			{
311 				wireFrameAmount *= std::pow((mWireFrameThickness - dist) / mWireFrameThickness, mWireFrameExponent);
312 			}
313             col[0].blend(wireFrameCol, wireFrameAmount);
314             col[1].blend(wireFrameCol, wireFrameAmount);
315 		}
316 	}
317 }
318 
applyWireFrame(colorA_t & col,float wireFrameAmount,const surfacePoint_t & sp)319 inline void material_t::applyWireFrame(colorA_t & col, float wireFrameAmount, const surfacePoint_t &sp) const
320 {
321 	if(wireFrameAmount > 0.f && mWireFrameThickness > 0.f)
322 	{
323 		float dist = sp.getDistToNearestEdge();
324 		if(dist <= mWireFrameThickness)
325 		{
326 			color_t wireFrameCol = mWireFrameColor * wireFrameAmount;
327 			if(mWireFrameExponent > 0.f)
328 			{
329 				wireFrameAmount *= std::pow((mWireFrameThickness - dist) / mWireFrameThickness, mWireFrameExponent);
330 			}
331 			col.blend(wireFrameCol, wireFrameAmount);
332 			col.A = wireFrameAmount;
333 		}
334 	}
335 }
336 
applyWireFrame(colorA_t * const col,float wireFrameAmount,const surfacePoint_t & sp)337 inline void material_t::applyWireFrame(colorA_t *const col, float wireFrameAmount, const surfacePoint_t &sp) const
338 {
339 	if(wireFrameAmount > 0.f && mWireFrameThickness > 0.f)
340 	{
341 		float dist = sp.getDistToNearestEdge();
342 		if(dist <= mWireFrameThickness)
343 		{
344 			color_t wireFrameCol = mWireFrameColor * wireFrameAmount;
345 			if(mWireFrameExponent > 0.f)
346 			{
347 				wireFrameAmount *= std::pow((mWireFrameThickness - dist) / mWireFrameThickness, mWireFrameExponent);
348 			}
349             col[0].blend(wireFrameCol, wireFrameAmount);
350 			col[0].A = wireFrameAmount;
351             col[1].blend(wireFrameCol, wireFrameAmount);
352 			col[1].A = wireFrameAmount;
353 		}
354 	}
355 }
356 
357 __END_YAFRAY
358 
359 #endif // Y_MATERIAL_H
360