-
Notifications
You must be signed in to change notification settings - Fork 353
/
background.h
115 lines (104 loc) · 4.08 KB
/
background.h
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
#pragma once
#include "dual_vec.h"
#include "oslconfig.h"
#include <algorithm> // upper_bound
OSL_NAMESPACE_ENTER
struct Background {
Background() : values(0), rows(0), cols(0) {}
~Background() {
delete [] values;
delete [] rows;
delete [] cols;
}
template <typename F, typename T>
void prepare(int resolution, F cb, T* data) {
res = resolution;
if (res < 32) res = 32; // validate
invres = 1.0f / res;
jacobian = invres * invres * float(4 * M_PI);
values = new Vec3[res * res];
rows = new float[res];
cols = new float[res * res];
for (int y = 0, i = 0; y < res; y++) {
for (int x = 0; x < res; x++, i++) {
values[i] = cb(map(x + 0.5f, y + 0.5f), data);
cols[i] = (values[i].x + values[i].y + values[i].z) + ((x > 0) ? cols[i - 1] : 0.0f);
}
rows[y] = cols[i - 1] + ((y > 0) ? rows[y - 1] : 0.0f);
// normalize the pdf for this scanline (if it was non-zero)
if (cols[i - 1] > 0)
for (int x = 0; x < res; x++)
cols[i - res + x] /= cols[i - 1];
}
// normalize the pdf across all scanlines
for (int y = 0; y < res; y++)
rows[y] /= rows[res - 1];
#if 0 // DEBUG: visualize importance table
using namespace OIIO;
ImageOutput* out = ImageOutput::create("bg.exr");
ImageSpec spec(res, res, 3, TypeDesc::TypeFloat);
if (out && out->open("bg.exr", spec))
out->write_image(TypeDesc::TypeFloat, &values[0]);
delete out;
#endif
}
Vec3 eval(const Vec3& dir, float& pdf) const {
// map from sphere to unit-square
float u = atan2f(dir.y, dir.x) * float(M_1_PI * 0.5f);
if (u < 0) u++;
float v = (1 - dir.z) * 0.5f;
// retrieve nearest neighbor
int x = (int) (u * res); if (x < 0) x = 0; else if (x >= res) x = res - 1;
int y = (int) (v * res); if (y < 0) y = 0; else if (y >= res) y = res - 1;
int i = y * res + x;
float row_pdf = rows[y] - (y > 0 ? rows[y - 1] : 0.0f);
float col_pdf = cols[i] - (x > 0 ? cols[i - 1] : 0.0f);
pdf = (row_pdf * col_pdf) / jacobian;
return values[i];
}
Vec3 sample(float rx, float ry, Dual2<Vec3>& dir, float& invpdf) const {
float row_pdf, col_pdf;
unsigned x, y;
ry = sample_cdf(rows, res, ry, &y, &row_pdf);
rx = sample_cdf(cols + y * res, res, rx, &x, &col_pdf);
dir = map(x + rx, y + ry);
invpdf = jacobian / (row_pdf * col_pdf);
return values[y * res + x];
}
private:
Dual2<Vec3> map(float x, float y) const {
// pixel coordinates of entry (x,y)
Dual2<float> u = Dual2<float>(x, 1, 0) * invres;
Dual2<float> v = Dual2<float>(y, 0, 1) * invres;
Dual2<float> theta = u * float(2 * M_PI);
Dual2<float> cos_phi = 1.0f - 2.0f * v;
Dual2<float> sin_phi = sqrt(1.0f - cos_phi * cos_phi);
return make_Vec3(sin_phi * cos(theta),
sin_phi * sin(theta),
cos_phi);
}
static float sample_cdf(const float* data, unsigned int n, float x, unsigned int *idx, float* pdf) {
DASSERT(x >= 0);
DASSERT(x < 1);
*idx = std::upper_bound(data, data + n, x) - data;
DASSERT(*idx < n);
DASSERT(x < data[*idx]);
float scaled_sample;
if (*idx == 0) {
*pdf = data[0];
scaled_sample = x / data[0];
} else {
DASSERT(x >= data[*idx - 1]);
*pdf = data[*idx] - data[*idx - 1];
scaled_sample = (x - data[*idx - 1]) / (data[*idx] - data[*idx - 1]);
}
return scaled_sample;
}
Vec3* values; // actual map
float* rows; // probability of choosing a given row 'y'
float* cols; // probability of choosing a given column 'x', given that we've chosen row 'y'
int res; // resolution in pixels of the precomputed table
float invres; // 1 / resolution
float jacobian;
};
OSL_NAMESPACE_EXIT