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