Merged trunk up to rev41928
[blender.git] / intern / cycles / kernel / osl / bsdf_oren_nayar.cpp
1 /*
2  * Copyright 2011, Blender Foundation.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18
19 /*
20  *      An implementation of Oren-Nayar reflectance model, public domain
21  *              http://www1.cs.columbia.edu/CAVE/publications/pdfs/Oren_SIGGRAPH94.pdf
22  *
23  *      NOTE:
24  *              BSDF = A + B * cos() * sin() * tan()
25  *
26  *              The parameter sigma means different from original.
27  *              A and B are calculated by the following formula:
28  *                      0 <= sigma <= 1
29  *                      A =     1 / ((1 + sigma / 2) * pi);
30  *                      B = sigma / ((1 + sigma / 2) * pi);
31  *
32  *              This formula is derived as following:
33  *
34  *              0. Normalize A-term and B-term of BSDF *individually*.
35  *                 B-term is normalized at maximum point: dot(L, N) = 0.
36  *                      A = (1/pi) * A'
37  *                      B = (2/pi) * B'
38  *
39  *              1. Solve the following equation:
40  *                      A' + B' = 1
41  *                      B / A = sigma
42  */
43
44 #include <OpenImageIO/fmath.h>
45 #include <OSL/genclosure.h>
46 #include "osl_closures.h"
47
48 CCL_NAMESPACE_BEGIN
49
50 using namespace OSL;
51
52
53 class OrenNayarClosure: public BSDFClosure {
54 public:
55         Vec3 m_N;
56         float m_sigma;
57         float m_a, m_b;
58
59         OrenNayarClosure(): BSDFClosure(Labels::DIFFUSE) {}
60
61         void setup() {
62                 m_sigma = clamp(m_sigma, 0.0f, 1.0f);
63                 m_a =    1.0f / ((1.0f + 0.5f * m_sigma) * M_PI);
64                 m_b = m_sigma / ((1.0f + 0.5f * m_sigma) * M_PI);
65         }
66
67         bool mergeable(const ClosurePrimitive* other) const {
68                 const OrenNayarClosure* comp = static_cast<const OrenNayarClosure*>(other);
69                 return
70                         m_N == comp->m_N &&
71                         m_sigma == comp->m_sigma &&
72                         BSDFClosure::mergeable(other);
73         }
74
75         size_t memsize() const {
76                 return sizeof(*this);
77         }
78
79         const char* name() const {
80                 return "oren_nayar";
81         }
82
83         void print_on(std::ostream& out) const {
84                 out << name() << " (";
85                 out << "(" << m_N[0] << ", " << m_N[1] << ", " << m_N[2] << "), ";
86                 out << m_sigma;
87                 out << ")";
88         }
89
90         float albedo(const Vec3& omega_out) const {
91                 return 1.0f;
92         }
93
94         Color3 eval_reflect(const Vec3& omega_out, const Vec3& omega_in, float& pdf) const {
95                 if (m_N.dot(omega_in) > 0.0f) {
96                         pdf = float(0.5 * M_1_PI);
97                         float is = get_intensity(m_N, omega_out, omega_in);
98                         return Color3(is, is, is);
99                 }
100                 else {
101                         pdf = 0.0f;
102                         return Color3(0.0f, 0.0f, 0.0f);
103                 }
104         }
105
106         Color3 eval_transmit(const Vec3& omega_out, const Vec3& omega_in, float& pdf) const {
107                 return Color3(0.0f, 0.0f, 0.0f);
108         }
109
110         ustring sample(
111                 const Vec3& Ng,
112                 const Vec3& omega_out, const Vec3& domega_out_dx, const Vec3& domega_out_dy,
113                 float randu, float randv,
114                 Vec3& omega_in, Vec3& domega_in_dx, Vec3& domega_in_dy,
115                 float& pdf, Color3& eval
116         ) const {
117                 sample_uniform_hemisphere (m_N, omega_out, randu, randv, omega_in, pdf);
118
119                 if (Ng.dot(omega_in) > 0.0f) {
120                         float is = get_intensity(m_N, omega_out, omega_in);
121                         eval.setValue(is, is, is);
122
123                         // TODO: find a better approximation for the bounce
124                         domega_in_dx = (2.0f * m_N.dot(domega_out_dx)) * m_N - domega_out_dx;
125                         domega_in_dy = (2.0f * m_N.dot(domega_out_dy)) * m_N - domega_out_dy;
126                         domega_in_dx *= 125.0f;
127                         domega_in_dy *= 125.0f;
128                 }
129                 else {
130                         pdf = 0.0f;
131                 }
132
133                 return Labels::REFLECT;
134         }
135
136 private:
137         float get_intensity(Vec3 const& n, Vec3 const& v, Vec3 const& l) const {
138                 float nl = max(n.dot(l), 0.0f);
139                 float nv = max(n.dot(v), 0.0f);
140
141                 Vec3 al = l - nl * n;
142                 al.normalize();
143                 Vec3 av = v - nv * n;
144                 av.normalize();
145                 float t = max(al.dot(av), 0.0f);
146
147                 float cos_a, cos_b;
148                 if (nl < nv) {
149                         cos_a = nl;
150                         cos_b = nv;
151                 }
152                 else {
153                         cos_a = nv;
154                         cos_b = nl;
155                 }
156
157                 float sin_a = sqrtf(1.0f - cos_a * cos_a);
158                 float tan_b = sqrtf(1.0f - cos_b * cos_b) / (cos_b + FLT_MIN);
159
160                 return nl * (m_a + m_b * t * sin_a * tan_b);
161         }
162 };
163
164 ClosureParam bsdf_oren_nayar_params[] = {
165         CLOSURE_VECTOR_PARAM    (OrenNayarClosure, m_N),
166         CLOSURE_FLOAT_PARAM             (OrenNayarClosure, m_sigma),
167         CLOSURE_STRING_KEYPARAM ("label"),
168         CLOSURE_FINISH_PARAM    (OrenNayarClosure)
169 };
170
171 CLOSURE_PREPARE(bsdf_oren_nayar_prepare, OrenNayarClosure)
172
173
174 CCL_NAMESPACE_END