2 * Copyright 2011, Blender Foundation.
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.
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.
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.
20 * An implementation of Oren-Nayar reflectance model, public domain
21 * http://www1.cs.columbia.edu/CAVE/publications/pdfs/Oren_SIGGRAPH94.pdf
24 * BSDF = A + B * cos() * sin() * tan()
26 * The parameter sigma means different from original.
27 * A and B are calculated by the following formula:
29 * A = 1 / ((1 + sigma / 2) * pi);
30 * B = sigma / ((1 + sigma / 2) * pi);
32 * This formula is derived as following:
34 * 0. Normalize A-term and B-term of BSDF *individually*.
35 * B-term is normalized at maximum point: dot(L, N) = 0.
39 * 1. Solve the following equation:
44 #include <OpenImageIO/fmath.h>
45 #include <OSL/genclosure.h>
46 #include "osl_closures.h"
53 class OrenNayarClosure: public BSDFClosure {
59 OrenNayarClosure(): BSDFClosure(Labels::DIFFUSE) {}
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);
67 bool mergeable(const ClosurePrimitive* other) const {
68 const OrenNayarClosure* comp = static_cast<const OrenNayarClosure*>(other);
71 m_sigma == comp->m_sigma &&
72 BSDFClosure::mergeable(other);
75 size_t memsize() const {
79 const char* name() const {
83 void print_on(std::ostream& out) const {
84 out << name() << " (";
85 out << "(" << m_N[0] << ", " << m_N[1] << ", " << m_N[2] << "), ";
90 float albedo(const Vec3& omega_out) const {
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);
102 return Color3(0.0f, 0.0f, 0.0f);
106 Color3 eval_transmit(const Vec3& omega_out, const Vec3& omega_in, float& pdf) const {
107 return Color3(0.0f, 0.0f, 0.0f);
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
117 sample_uniform_hemisphere (m_N, omega_out, randu, randv, omega_in, pdf);
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);
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;
133 return Labels::REFLECT;
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);
141 Vec3 al = l - nl * n;
143 Vec3 av = v - nv * n;
145 float t = max(al.dot(av), 0.0f);
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);
160 return nl * (m_a + m_b * t * sin_a * tan_b);
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)
171 CLOSURE_PREPARE(bsdf_oren_nayar_prepare, OrenNayarClosure)