-
Notifications
You must be signed in to change notification settings - Fork 3
/
SceneObjectSphere.c
122 lines (84 loc) · 3.78 KB
/
SceneObjectSphere.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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#include "SceneObjectSphere.h"
#include <math.h>
#include "randf.h"
#include "pi.h"
#include <stdio.h>
const SceneObjectVTable sceneObjectSphereVTable = (SceneObjectVTable) {
&sceneObjectSphereIntersectRay,
&sceneObjectSphereEmitPhotons,
&sceneObjectSphereRadiantFlux
};
SceneObjectSphere makeSceneObjectSphere (const Sphere sphere, const Material *material) {
return (SceneObjectSphere) {makeSceneObject(&sceneObjectSphereVTable), sphere, material};
}
defineAllocator(SceneObjectSphere)
Intersection sceneObjectSphereIntersectRay(const SceneObject *superobject, const Ray ray) {
const SceneObjectSphere *object = (SceneObjectSphere *) superobject;
Intersection intersection = sIntersect(object->sphere, ray);
intersection.material = object->material;
return intersection;
}
bool sceneObjectSphereEmitPhotons(const SceneObject *superobject, const int numPhotons, PhotonContainer *photons) {
const SceneObjectSphere *object = (SceneObjectSphere *) superobject;
Color flux = sceneObjectSphereRadiantFlux(superobject);
/*
I use stratified sampling to reduce noise. It means I divide the surface into a grid, then
sample randomly within each cell. This creates a more even spread without any clustering.
But if numPhotons isn't a power of 2, a simple n*n grid can't be used. I want to spawn the exact
number of photons requested, not just an approximation. To still spread the photons evenly,
the last row needs to be thinner than the others, like this:
+----+----+----+----+
| . | | .| . |
| | . | | |
+----+----+----+----+
| | | . | |
| . | . | | . |
+----+----+----+----+
| | . | |. |
| .| |. | |
+----+-+--+--+-+----+
| . |. | . |
+------+-----+------+
(Actually, for this sphere I use a n*2n grid, but still.)
*/
// Divide the photons onto a grid of n*m, most closely matching the wanted number.
int numPhotonsU = ceil(sqrt(numPhotons/2.0))*2;
int numPhotonsV = numPhotons/numPhotonsU;
// How many photons in the last row, and how tall it is.
int lastRowNumPhotonsU = numPhotons - numPhotonsU*numPhotonsV;
float lastRowFactor = lastRowNumPhotonsU / (float) numPhotons;
// printf("numPhotonsU: %i\n", numPhotonsU);
// printf("numPhotonsV: %i\n", numPhotonsV);
// printf("lastRowNumPhotonsU: %i\n", lastRowNumPhotonsU);
// printf("lastRowFactor: %f\n", lastRowFactor);
for (int iV = 0; iV < numPhotonsV; ++iV) {
for (int iU = 0; iU < numPhotonsU; ++iU) {
float u = (iU + randf()) / numPhotonsU;
float v = (iV + randf()) / numPhotonsV * (1-lastRowFactor);
Vector normal = vRotated(
vRotated(makeVector(object->sphere.radius, 0, 0), makeVector(0, 1, 0), acosf(v*2 - 1)),
makeVector(1, 0, 0),
u * 2*PI
);
Vector position = vAdd(object->sphere.position, vsMul(normal, 1+vEpsilon));
photonContainerAddValue(photons, makePhoton(makeRay(position, vSampleHemisphere(normal)), csMul(flux, 1.0 / numPhotons)));
}
}
for (int iU = 0; iU < lastRowNumPhotonsU; ++iU) {
float u = (iU + randf()) / lastRowNumPhotonsU;
float v = (1 - lastRowFactor) + randf() * lastRowFactor;
Vector normal = vRotated(
vRotated(makeVector(object->sphere.radius, 0, 0), makeVector(0, 1, 0), acosf(v*2 - 1)),
makeVector(1, 0, 0),
u * 2*PI
);
Vector position = vAdd(object->sphere.position, vsMul(normal, 1+vEpsilon));
photonContainerAddValue(photons, makePhoton(makeRay(position, vSampleHemisphere(normal)), csMul(materialIrradience(object->material), 1.0 / numPhotons)));
}
return true;
}
Color sceneObjectSphereRadiantFlux(const SceneObject *superobject) {
const SceneObjectSphere *object = (SceneObjectSphere *) superobject;
Color averageIrradiance = materialIrradience(object->material);
return csMul(averageIrradiance, sSurfaceArea(object->sphere));
}