-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathMaterialPhong.c
95 lines (64 loc) · 2.98 KB
/
MaterialPhong.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include "MaterialPhong.h"
#include "randf.h"
#include "pi.h"
#include <math.h>
const MaterialVTable materialPhongVTable = (MaterialVTable) {
&materialPhongSampleBRDF,
&materialPhongBRDF,
&materialPhongIrradience
};
MaterialPhong makeMaterialPhong(const Color reflectivity, const float specularity, const float exponent, const bool metallic) {
return (MaterialPhong) {{makeMaterial(&materialPhongVTable), reflectivity}, specularity, exponent, metallic};
}
defineAllocator(MaterialPhong)
Photon materialPhongSampleBRDF(const Material *superObject, const Intersection intersection, const Photon incoming) {
MaterialPhong *material = (MaterialPhong *) superObject;
// "Interpolate" diffuse and specular based on material specularity.
if(randf() > material->specularity){
// Diffuse component.
return materialDiffuseSampleBRDF(superObject, intersection, incoming);
}else{
// Specular component.
// Try n times to find a valid light direction. Fallback to the perfect reflection.
// I just wanted this to be a guaranteed finite loop.
Vector tangent = vTangent(intersection.normal);
Vector reflectedDirection;
for (int i=0; i<100; ++i) {
// The perfect reflection should be rotated randomly, weighted by the Phong specularity
// function. Instead, I rotate the normal the ray is reflected in, by half that angle.
Vector randomizedNormal = vRotated(
vRotated(intersection.normal, tangent, 0.5 * acosf(powf(randf(), 1/(material->exponent+1)))),
intersection.normal,
randf() * 2*PI
);
reflectedDirection = vReflected(incoming.heading.direction, randomizedNormal);
// When a valid direction is found, we're done.
if(vDot(reflectedDirection, intersection.normal) > 0) {
break;
}
}
Vector offset = vsMul(intersection.normal, vEpsilon); // Avoid hitting the same surface again.
return makePhoton(makeRay(vAdd(intersection.position, offset), reflectedDirection), (material->metallic ? cMul(incoming.energy, material->parent.reflectivity) : incoming.energy));
}
}
Color materialPhongBRDF(const Material *superObject, const Intersection intersection, const Vector incoming, const Vector outgoing) {
MaterialPhong *material = (MaterialPhong *) superObject;
Vector perfectReflection = vReflected(incoming, intersection.normal);
float diffuseSurfaceIllumination = fmax(0, vDot(intersection.normal, incoming));
float specularSurfaceIllumination = fmax(0, powf(vDot(outgoing, perfectReflection), material->exponent));
if (material->metallic) {
return csMul(material->parent.reflectivity,
diffuseSurfaceIllumination * (1 - material->specularity) +
specularSurfaceIllumination * material->specularity
);
} else {
return cAdd(
csMul(material->parent.reflectivity, diffuseSurfaceIllumination * (1 - material->specularity)),
makeColorLightness(specularSurfaceIllumination * material->specularity)
);
}
}
Color materialPhongIrradience(const Material *material) {
// No light source.
return makeColorBlack();
}