diff --git a/apps/ysamples.cpp b/apps/ysamples.cpp index 52ce1c0e5..0ae921ae7 100644 --- a/apps/ysamples.cpp +++ b/apps/ysamples.cpp @@ -30,7 +30,6 @@ #include #include #include -#include #include #include diff --git a/libs/yocto/CMakeLists.txt b/libs/yocto/CMakeLists.txt index 1bf3440ef..e20e81c87 100644 --- a/libs/yocto/CMakeLists.txt +++ b/libs/yocto/CMakeLists.txt @@ -3,7 +3,7 @@ add_library(yocto STATIC yocto_image.h yocto_scene.h yocto_scene.cpp yocto_raytracing.h yocto_raytracing.cpp - yocto_sampling.h yocto_shading.h + yocto_shading.h yocto_modeling.h yocto_animation.h yocto_trace.h yocto_trace.cpp yocto_modelio.h yocto_modelio.cpp diff --git a/libs/yocto/yocto_diagram.cpp b/libs/yocto/yocto_diagram.cpp index 564dc652d..7661879d8 100644 --- a/libs/yocto/yocto_diagram.cpp +++ b/libs/yocto/yocto_diagram.cpp @@ -35,7 +35,6 @@ #include #include -#include "yocto_sampling.h" #include "yocto_sceneio.h" // ----------------------------------------------------------------------------- diff --git a/libs/yocto/yocto_math.h b/libs/yocto/yocto_math.h index 0946a5938..789df05e8 100644 --- a/libs/yocto/yocto_math.h +++ b/libs/yocto/yocto_math.h @@ -1384,6 +1384,102 @@ inline void shuffle(vector& vals, rng_state& rng); } // namespace yocto +// ----------------------------------------------------------------------------- +// MONTECARLO SAMPLING FUNCTIONS +// ----------------------------------------------------------------------------- +namespace yocto { + +// Sample an hemispherical direction with uniform distribution. +inline vec3f sample_hemisphere(vec2f ruv); +inline float sample_hemisphere_pdf(vec3f direction); + +// Sample an hemispherical direction with uniform distribution. +inline vec3f sample_hemisphere(vec3f normal, vec2f ruv); +inline float sample_hemisphere_pdf(vec3f normal, vec3f direction); + +// Sample a spherical direction with uniform distribution. +inline vec3f sample_sphere(vec2f ruv); +inline float sample_sphere_pdf(vec3f w); + +// Sample an hemispherical direction with cosine distribution. +inline vec3f sample_hemisphere_cos(vec2f ruv); +inline float sample_hemisphere_cos_pdf(vec3f direction); + +// Sample an hemispherical direction with cosine distribution. +inline vec3f sample_hemisphere_cos(vec3f normal, vec2f ruv); +inline float sample_hemisphere_cos_pdf(vec3f normal, vec3f direction); + +// Sample an hemispherical direction with cosine power distribution. +inline vec3f sample_hemisphere_cospower(float exponent, vec2f ruv); +inline float sample_hemisphere_cospower_pdf(float exponent, vec3f direction); + +// Sample an hemispherical direction with cosine power distribution. +inline vec3f sample_hemisphere_cospower( + float exponent, vec3f normal, vec2f ruv); +inline float sample_hemisphere_cospower_pdf( + float exponent, vec3f normal, vec3f direction); + +// Sample a point uniformly on a disk. +inline vec2f sample_disk(vec2f ruv); +inline float sample_disk_pdf(vec2f point); + +// Sample a point uniformly on a cylinder, without caps. +inline vec3f sample_cylinder(vec2f ruv); +inline float sample_cylinder_pdf(vec3f point); + +// Sample a point uniformly on a triangle returning the baricentric coordinates. +inline vec2f sample_triangle(vec2f ruv); + +// Sample a point uniformly on a triangle. +inline vec3f sample_triangle(vec3f p0, vec3f p1, vec3f p2, vec2f ruv); +// Pdf for uniform triangle sampling, i.e. triangle area. +inline float sample_triangle_pdf(vec3f p0, vec3f p1, vec3f p2); + +// Sample an index with uniform distribution. +inline int sample_uniform(int size, float r); +inline float sample_uniform_pdf(int size); + +// Sample an index with uniform distribution. +inline float sample_uniform(const vector& elements, float r); +inline float sample_uniform_pdf(const vector& elements); + +// Sample a discrete distribution represented by its cdf. +inline int sample_discrete(const vector& cdf, float r); +// Pdf for uniform discrete distribution sampling. +inline float sample_discrete_pdf(const vector& cdf, int idx); + +} // namespace yocto + +// ----------------------------------------------------------------------------- +// SHAPE SAMPLING +// ----------------------------------------------------------------------------- +namespace yocto { + +// Pick a point in a point set uniformly. +inline int sample_points(int npoints, float re); +inline int sample_points(const vector& cdf, float re); +inline vector sample_points_cdf(int npoints); + +// Pick a point on lines uniformly. +inline pair sample_lines( + const vector& cdf, float re, float ru); +inline vector sample_lines_cdf( + const vector& lines, const vector& positions); + +// Pick a point on a triangle mesh uniformly. +inline pair sample_triangles( + const vector& cdf, float re, vec2f ruv); +inline vector sample_triangles_cdf( + const vector& triangles, const vector& positions); + +// Pick a point on a quad mesh uniformly. +inline pair sample_quads( + const vector& cdf, float re, vec2f ruv); +inline vector sample_quads_cdf( + const vector& quads, const vector& positions); + +} // namespace yocto + // ----------------------------------------------------------------------------- // PYTHON-LIKE ITERATORS // ----------------------------------------------------------------------------- @@ -3509,6 +3605,235 @@ inline void shuffle(vector& vals, rng_state& rng) { } // namespace yocto +// ----------------------------------------------------------------------------- +// IMPLEMENTATION OF MONTECARLO SAMPLING FUNCTIONS +// ----------------------------------------------------------------------------- +namespace yocto { + +// Sample an hemispherical direction with uniform distribution. +inline vec3f sample_hemisphere(vec2f ruv) { + auto z = ruv.y; + auto r = sqrt(clamp(1 - z * z, 0.0f, 1.0f)); + auto phi = 2 * pif * ruv.x; + return {r * cos(phi), r * sin(phi), z}; +} +inline float sample_hemisphere_pdf(vec3f direction) { + return (direction.z <= 0) ? 0 : 1 / (2 * pif); +} + +// Sample an hemispherical direction with uniform distribution. +inline vec3f sample_hemisphere(vec3f normal, vec2f ruv) { + auto z = ruv.y; + auto r = sqrt(clamp(1 - z * z, 0.0f, 1.0f)); + auto phi = 2 * pif * ruv.x; + auto local_direction = vec3f{r * cos(phi), r * sin(phi), z}; + return transform_direction(basis_fromz(normal), local_direction); +} +inline float sample_hemisphere_pdf(vec3f normal, vec3f direction) { + return (dot(normal, direction) <= 0) ? 0 : 1 / (2 * pif); +} + +// Sample a spherical direction with uniform distribution. +inline vec3f sample_sphere(vec2f ruv) { + auto z = 2 * ruv.y - 1; + auto r = sqrt(clamp(1 - z * z, 0.0f, 1.0f)); + auto phi = 2 * pif * ruv.x; + return {r * cos(phi), r * sin(phi), z}; +} +inline float sample_sphere_pdf(vec3f w) { return 1 / (4 * pif); } + +// Sample an hemispherical direction with cosine distribution. +inline vec3f sample_hemisphere_cos(vec2f ruv) { + auto z = sqrt(ruv.y); + auto r = sqrt(1 - z * z); + auto phi = 2 * pif * ruv.x; + return {r * cos(phi), r * sin(phi), z}; +} +inline float sample_hemisphere_cos_pdf(vec3f direction) { + return (direction.z <= 0) ? 0 : direction.z / pif; +} + +// Sample an hemispherical direction with cosine distribution. +inline vec3f sample_hemisphere_cos(vec3f normal, vec2f ruv) { + auto z = sqrt(ruv.y); + auto r = sqrt(1 - z * z); + auto phi = 2 * pif * ruv.x; + auto local_direction = vec3f{r * cos(phi), r * sin(phi), z}; + return transform_direction(basis_fromz(normal), local_direction); +} +inline float sample_hemisphere_cos_pdf(vec3f normal, vec3f direction) { + auto cosw = dot(normal, direction); + return (cosw <= 0) ? 0 : cosw / pif; +} + +// Sample an hemispherical direction with cosine power distribution. +inline vec3f sample_hemisphere_cospower(float exponent, vec2f ruv) { + auto z = pow(ruv.y, 1 / (exponent + 1)); + auto r = sqrt(1 - z * z); + auto phi = 2 * pif * ruv.x; + return {r * cos(phi), r * sin(phi), z}; +} +inline float sample_hemisphere_cospower_pdf(float exponent, vec3f direction) { + return (direction.z <= 0) + ? 0 + : pow(direction.z, exponent) * (exponent + 1) / (2 * pif); +} + +// Sample an hemispherical direction with cosine power distribution. +inline vec3f sample_hemisphere_cospower( + float exponent, vec3f normal, vec2f ruv) { + auto z = pow(ruv.y, 1 / (exponent + 1)); + auto r = sqrt(1 - z * z); + auto phi = 2 * pif * ruv.x; + auto local_direction = vec3f{r * cos(phi), r * sin(phi), z}; + return transform_direction(basis_fromz(normal), local_direction); +} +inline float sample_hemisphere_cospower_pdf( + float exponent, vec3f normal, vec3f direction) { + auto cosw = dot(normal, direction); + return (cosw <= 0) ? 0 : pow(cosw, exponent) * (exponent + 1) / (2 * pif); +} + +// Sample a point uniformly on a disk. +inline vec2f sample_disk(vec2f ruv) { + auto r = sqrt(ruv.y); + auto phi = 2 * pif * ruv.x; + return {cos(phi) * r, sin(phi) * r}; +} +inline float sample_disk_pdf() { return 1 / pif; } + +// Sample a point uniformly on a cylinder, without caps. +inline vec3f sample_cylinder(vec2f ruv) { + auto phi = 2 * pif * ruv.x; + return {sin(phi), cos(phi), ruv.y * 2 - 1}; +} +inline float sample_cylinder_pdf(vec3f point) { return 1 / pif; } + +// Sample a point uniformly on a triangle returning the baricentric coordinates. +inline vec2f sample_triangle(vec2f ruv) { + return {1 - sqrt(ruv.x), ruv.y * sqrt(ruv.x)}; +} + +// Sample a point uniformly on a triangle. +inline vec3f sample_triangle(vec3f p0, vec3f p1, vec3f p2, vec2f ruv) { + auto uv = sample_triangle(ruv); + return p0 * (1 - uv.x - uv.y) + p1 * uv.x + p2 * uv.y; +} +// Pdf for uniform triangle sampling, i.e. triangle area. +inline float sample_triangle_pdf(vec3f p0, vec3f p1, vec3f p2) { + return 2 / length(cross(p1 - p0, p2 - p0)); +} + +// Sample an index with uniform distribution. +inline int sample_uniform(int size, float r) { + return clamp((int)(r * size), 0, size - 1); +} +inline float sample_uniform_pdf(int size) { return (float)1 / (float)size; } + +// Sample an index with uniform distribution. +inline float sample_uniform(const vector& elements, float r) { + if (elements.empty()) return {}; + auto size = (int)elements.size(); + return elements[clamp((int)(r * size), 0, size - 1)]; +} +inline float sample_uniform_pdf(const vector& elements) { + if (elements.empty()) return 0; + return 1.0f / (int)elements.size(); +} + +// Sample a discrete distribution represented by its cdf. +inline int sample_discrete(const vector& cdf, float r) { + r = clamp(r * cdf.back(), (float)0, cdf.back() - (float)0.00001); + auto idx = (int)(std::upper_bound(cdf.data(), cdf.data() + cdf.size(), r) - + cdf.data()); + return clamp(idx, 0, (int)cdf.size() - 1); +} +// Pdf for uniform discrete distribution sampling. +inline float sample_discrete_pdf(const vector& cdf, int idx) { + if (idx == 0) return cdf[0]; + return cdf[idx] - cdf[idx - 1]; +} + +} // namespace yocto + +// ----------------------------------------------------------------------------- +// IMPLEMENTATION OF SHAPE SAMPLING +// ----------------------------------------------------------------------------- +namespace yocto { + +// Pick a point in a point set uniformly. +inline int sample_points(int npoints, float re) { + return sample_uniform(npoints, re); +} +inline int sample_points(const vector& cdf, float re) { + return sample_discrete(cdf, re); +} +inline vector sample_points_cdf(int npoints) { + auto cdf = vector(npoints); + for (auto i = 0; i < cdf.size(); i++) cdf[i] = 1 + (i != 0 ? cdf[i - 1] : 0); + return cdf; +} + +// Pick a point on lines uniformly. +inline pair sample_lines( + const vector& cdf, float re, float ru) { + return {sample_discrete(cdf, re), ru}; +} +inline vector sample_lines_cdf( + const vector& lines, const vector& positions) { + auto cdf = vector(lines.size()); + for (auto i = 0; i < cdf.size(); i++) { + auto& l = lines[i]; + auto w = line_length(positions[l.x], positions[l.y]); + cdf[i] = w + (i != 0 ? cdf[i - 1] : 0); + } + return cdf; +} + +// Pick a point on a triangle mesh uniformly. +inline pair sample_triangles( + const vector& cdf, float re, vec2f ruv) { + return {sample_discrete(cdf, re), sample_triangle(ruv)}; +} +inline vector sample_triangles_cdf( + const vector& triangles, const vector& positions) { + auto cdf = vector(triangles.size()); + for (auto i = 0; i < cdf.size(); i++) { + auto& t = triangles[i]; + auto w = triangle_area(positions[t.x], positions[t.y], positions[t.z]); + cdf[i] = w + (i != 0 ? cdf[i - 1] : 0); + } + return cdf; +} + +// Pick a point on a quad mesh uniformly. +inline pair sample_quads( + const vector& cdf, float re, vec2f ruv) { + return {sample_discrete(cdf, re), ruv}; +} +inline pair sample_quads( + const vector& quads, const vector& cdf, float re, vec2f ruv) { + auto element = sample_discrete(cdf, re); + if (quads[element].z == quads[element].w) { + return {element, sample_triangle(ruv)}; + } else { + return {element, ruv}; + } +} +inline vector sample_quads_cdf( + const vector& quads, const vector& positions) { + auto cdf = vector(quads.size()); + for (auto i = 0; i < cdf.size(); i++) { + auto& q = quads[i]; + auto w = quad_area( + positions[q.x], positions[q.y], positions[q.z], positions[q.w]); + cdf[i] = w + (i ? cdf[i - 1] : 0); + } + return cdf; +} + +} // namespace yocto + // ----------------------------------------------------------------------------- // PYTHON-LIKE ITERATORS // ----------------------------------------------------------------------------- diff --git a/libs/yocto/yocto_raytracing.cpp b/libs/yocto/yocto_raytracing.cpp index 591837eb3..e178d9230 100644 --- a/libs/yocto/yocto_raytracing.cpp +++ b/libs/yocto/yocto_raytracing.cpp @@ -987,25 +987,6 @@ static inline vec3f fresnel_schlick( (1 - specular) * pow(clamp(1 - abs(cosine), 0.0f, 1.0f), 5.0f); } -// Sample an hemispherical direction with cosine distribution. -static inline vec3f sample_hemisphere_cos(vec3f normal, vec2f ruv) { - auto z = sqrt(ruv.y); - auto r = sqrt(1 - z * z); - auto phi = 2 * pif * ruv.x; - auto local_direction = vec3f{r * cos(phi), r * sin(phi), z}; - return transform_direction(basis_fromz(normal), local_direction); -} - -// Sample an hemispherical direction with cosine power distribution. -inline vec3f sample_hemisphere_cospower( - float exponent, vec3f normal, vec2f ruv) { - auto z = pow(ruv.y, 1 / (exponent + 1)); - auto r = sqrt(1 - z * z); - auto phi = 2 * pif * ruv.x; - auto local_direction = vec3f{r * cos(phi), r * sin(phi), z}; - return transform_direction(basis_fromz(normal), local_direction); -} - } // namespace yocto // ----------------------------------------------------------------------------- diff --git a/libs/yocto/yocto_sampling.h b/libs/yocto/yocto_sampling.h deleted file mode 100644 index 2e26215a1..000000000 --- a/libs/yocto/yocto_sampling.h +++ /dev/null @@ -1,413 +0,0 @@ -// -// # Yocto/Sampling: Sampling routines -// -// Yocto/Sampling provides many functions to generate points and directions -// useful in path tracing and procedural generation. We also include a random -// number generator suitable for ray tracing. -// - -// -// LICENSE: -// -// Copyright (c) 2016 -- 2022 Fabio Pellacini -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// -// LICENSE OF INCLUDED SOFTWARE for Pcg random number generator -// -// This code also includes a small exerpt from http://www.pcg-random.org/ -// licensed as follows -// *Really* minimal PCG32 code / (c) 2014 M.E. O'Neill / pcg-random.org -// Licensed under Apache License 2.0 (NO WARRANTY, etc. see website) -// - -#ifndef _YOCTO_SAMPLING_H_ -#define _YOCTO_SAMPLING_H_ - -// ----------------------------------------------------------------------------- -// INCLUDES -// ----------------------------------------------------------------------------- - -#include // std::upper_bound -#include -#include -#include - -#include "yocto_math.h" - -// ----------------------------------------------------------------------------- -// USING DIRECTIVES -// ----------------------------------------------------------------------------- -namespace yocto { - -// using directives -using std::array; -using std::vector; - -} // namespace yocto - -// ----------------------------------------------------------------------------- -// CUDA SUPPORT -// ----------------------------------------------------------------------------- -#ifdef __CUDACC__ -#define inline inline __device__ __forceinline__ -#endif - -// ----------------------------------------------------------------------------- -// MONETACARLO SAMPLING FUNCTIONS -// ----------------------------------------------------------------------------- -namespace yocto { - -// Sample an hemispherical direction with uniform distribution. -inline vec3f sample_hemisphere(vec2f ruv); -inline float sample_hemisphere_pdf(vec3f direction); - -// Sample an hemispherical direction with uniform distribution. -inline vec3f sample_hemisphere(vec3f normal, vec2f ruv); -inline float sample_hemisphere_pdf(vec3f normal, vec3f direction); - -// Sample a spherical direction with uniform distribution. -inline vec3f sample_sphere(vec2f ruv); -inline float sample_sphere_pdf(vec3f w); - -// Sample an hemispherical direction with cosine distribution. -inline vec3f sample_hemisphere_cos(vec2f ruv); -inline float sample_hemisphere_cos_pdf(vec3f direction); - -// Sample an hemispherical direction with cosine distribution. -inline vec3f sample_hemisphere_cos(vec3f normal, vec2f ruv); -inline float sample_hemisphere_cos_pdf(vec3f normal, vec3f direction); - -// Sample an hemispherical direction with cosine power distribution. -inline vec3f sample_hemisphere_cospower(float exponent, vec2f ruv); -inline float sample_hemisphere_cospower_pdf(float exponent, vec3f direction); - -// Sample an hemispherical direction with cosine power distribution. -inline vec3f sample_hemisphere_cospower( - float exponent, vec3f normal, vec2f ruv); -inline float sample_hemisphere_cospower_pdf( - float exponent, vec3f normal, vec3f direction); - -// Sample a point uniformly on a disk. -inline vec2f sample_disk(vec2f ruv); -inline float sample_disk_pdf(vec2f point); - -// Sample a point uniformly on a cylinder, without caps. -inline vec3f sample_cylinder(vec2f ruv); -inline float sample_cylinder_pdf(vec3f point); - -// Sample a point uniformly on a triangle returning the baricentric coordinates. -inline vec2f sample_triangle(vec2f ruv); - -// Sample a point uniformly on a triangle. -inline vec3f sample_triangle(vec3f p0, vec3f p1, vec3f p2, vec2f ruv); -// Pdf for uniform triangle sampling, i.e. triangle area. -inline float sample_triangle_pdf(vec3f p0, vec3f p1, vec3f p2); - -// Sample an index with uniform distribution. -inline int sample_uniform(int size, float r); -inline float sample_uniform_pdf(int size); - -// Sample an index with uniform distribution. -inline float sample_uniform(const vector& elements, float r); -inline float sample_uniform_pdf(const vector& elements); - -// Sample a discrete distribution represented by its cdf. -inline int sample_discrete(const vector& cdf, float r); -// Pdf for uniform discrete distribution sampling. -inline float sample_discrete_pdf(const vector& cdf, int idx); - -} // namespace yocto - -// ----------------------------------------------------------------------------- -// SHAPE SAMPLING -// ----------------------------------------------------------------------------- -namespace yocto { - -// Pick a point in a point set uniformly. -inline int sample_points(int npoints, float re); -inline int sample_points(const vector& cdf, float re); -inline vector sample_points_cdf(int npoints); - -// Pick a point on lines uniformly. -inline pair sample_lines( - const vector& cdf, float re, float ru); -inline vector sample_lines_cdf( - const vector& lines, const vector& positions); - -// Pick a point on a triangle mesh uniformly. -inline pair sample_triangles( - const vector& cdf, float re, vec2f ruv); -inline vector sample_triangles_cdf( - const vector& triangles, const vector& positions); - -// Pick a point on a quad mesh uniformly. -inline pair sample_quads( - const vector& cdf, float re, vec2f ruv); -inline vector sample_quads_cdf( - const vector& quads, const vector& positions); - -} // namespace yocto - -// ----------------------------------------------------------------------------- -// -// -// IMPLEMENTATION -// -// -// ----------------------------------------------------------------------------- - -// ----------------------------------------------------------------------------- -// IMPLEMENTATION OF MONTECARLO SAMPLING FUNCTIONS -// ----------------------------------------------------------------------------- -namespace yocto { - -// Sample an hemispherical direction with uniform distribution. -inline vec3f sample_hemisphere(vec2f ruv) { - auto z = ruv.y; - auto r = sqrt(clamp(1 - z * z, 0.0f, 1.0f)); - auto phi = 2 * pif * ruv.x; - return {r * cos(phi), r * sin(phi), z}; -} -inline float sample_hemisphere_pdf(vec3f direction) { - return (direction.z <= 0) ? 0 : 1 / (2 * pif); -} - -// Sample an hemispherical direction with uniform distribution. -inline vec3f sample_hemisphere(vec3f normal, vec2f ruv) { - auto z = ruv.y; - auto r = sqrt(clamp(1 - z * z, 0.0f, 1.0f)); - auto phi = 2 * pif * ruv.x; - auto local_direction = vec3f{r * cos(phi), r * sin(phi), z}; - return transform_direction(basis_fromz(normal), local_direction); -} -inline float sample_hemisphere_pdf(vec3f normal, vec3f direction) { - return (dot(normal, direction) <= 0) ? 0 : 1 / (2 * pif); -} - -// Sample a spherical direction with uniform distribution. -inline vec3f sample_sphere(vec2f ruv) { - auto z = 2 * ruv.y - 1; - auto r = sqrt(clamp(1 - z * z, 0.0f, 1.0f)); - auto phi = 2 * pif * ruv.x; - return {r * cos(phi), r * sin(phi), z}; -} -inline float sample_sphere_pdf(vec3f w) { return 1 / (4 * pif); } - -// Sample an hemispherical direction with cosine distribution. -inline vec3f sample_hemisphere_cos(vec2f ruv) { - auto z = sqrt(ruv.y); - auto r = sqrt(1 - z * z); - auto phi = 2 * pif * ruv.x; - return {r * cos(phi), r * sin(phi), z}; -} -inline float sample_hemisphere_cos_pdf(vec3f direction) { - return (direction.z <= 0) ? 0 : direction.z / pif; -} - -// Sample an hemispherical direction with cosine distribution. -inline vec3f sample_hemisphere_cos(vec3f normal, vec2f ruv) { - auto z = sqrt(ruv.y); - auto r = sqrt(1 - z * z); - auto phi = 2 * pif * ruv.x; - auto local_direction = vec3f{r * cos(phi), r * sin(phi), z}; - return transform_direction(basis_fromz(normal), local_direction); -} -inline float sample_hemisphere_cos_pdf(vec3f normal, vec3f direction) { - auto cosw = dot(normal, direction); - return (cosw <= 0) ? 0 : cosw / pif; -} - -// Sample an hemispherical direction with cosine power distribution. -inline vec3f sample_hemisphere_cospower(float exponent, vec2f ruv) { - auto z = pow(ruv.y, 1 / (exponent + 1)); - auto r = sqrt(1 - z * z); - auto phi = 2 * pif * ruv.x; - return {r * cos(phi), r * sin(phi), z}; -} -inline float sample_hemisphere_cospower_pdf(float exponent, vec3f direction) { - return (direction.z <= 0) - ? 0 - : pow(direction.z, exponent) * (exponent + 1) / (2 * pif); -} - -// Sample an hemispherical direction with cosine power distribution. -inline vec3f sample_hemisphere_cospower( - float exponent, vec3f normal, vec2f ruv) { - auto z = pow(ruv.y, 1 / (exponent + 1)); - auto r = sqrt(1 - z * z); - auto phi = 2 * pif * ruv.x; - auto local_direction = vec3f{r * cos(phi), r * sin(phi), z}; - return transform_direction(basis_fromz(normal), local_direction); -} -inline float sample_hemisphere_cospower_pdf( - float exponent, vec3f normal, vec3f direction) { - auto cosw = dot(normal, direction); - return (cosw <= 0) ? 0 : pow(cosw, exponent) * (exponent + 1) / (2 * pif); -} - -// Sample a point uniformly on a disk. -inline vec2f sample_disk(vec2f ruv) { - auto r = sqrt(ruv.y); - auto phi = 2 * pif * ruv.x; - return {cos(phi) * r, sin(phi) * r}; -} -inline float sample_disk_pdf() { return 1 / pif; } - -// Sample a point uniformly on a cylinder, without caps. -inline vec3f sample_cylinder(vec2f ruv) { - auto phi = 2 * pif * ruv.x; - return {sin(phi), cos(phi), ruv.y * 2 - 1}; -} -inline float sample_cylinder_pdf(vec3f point) { return 1 / pif; } - -// Sample a point uniformly on a triangle returning the baricentric coordinates. -inline vec2f sample_triangle(vec2f ruv) { - return {1 - sqrt(ruv.x), ruv.y * sqrt(ruv.x)}; -} - -// Sample a point uniformly on a triangle. -inline vec3f sample_triangle(vec3f p0, vec3f p1, vec3f p2, vec2f ruv) { - auto uv = sample_triangle(ruv); - return p0 * (1 - uv.x - uv.y) + p1 * uv.x + p2 * uv.y; -} -// Pdf for uniform triangle sampling, i.e. triangle area. -inline float sample_triangle_pdf(vec3f p0, vec3f p1, vec3f p2) { - return 2 / length(cross(p1 - p0, p2 - p0)); -} - -// Sample an index with uniform distribution. -inline int sample_uniform(int size, float r) { - return clamp((int)(r * size), 0, size - 1); -} -inline float sample_uniform_pdf(int size) { return (float)1 / (float)size; } - -// Sample an index with uniform distribution. -inline float sample_uniform(const vector& elements, float r) { - if (elements.empty()) return {}; - auto size = (int)elements.size(); - return elements[clamp((int)(r * size), 0, size - 1)]; -} -inline float sample_uniform_pdf(const vector& elements) { - if (elements.empty()) return 0; - return 1.0f / (int)elements.size(); -} - -// Sample a discrete distribution represented by its cdf. -inline int sample_discrete(const vector& cdf, float r) { - r = clamp(r * cdf.back(), (float)0, cdf.back() - (float)0.00001); - auto idx = (int)(std::upper_bound(cdf.data(), cdf.data() + cdf.size(), r) - - cdf.data()); - return clamp(idx, 0, (int)cdf.size() - 1); -} -// Pdf for uniform discrete distribution sampling. -inline float sample_discrete_pdf(const vector& cdf, int idx) { - if (idx == 0) return cdf[0]; - return cdf[idx] - cdf[idx - 1]; -} - -} // namespace yocto - -// ----------------------------------------------------------------------------- -// IMPLEMENTATION OF SHAPE SAMPLING -// ----------------------------------------------------------------------------- -namespace yocto { - -// Pick a point in a point set uniformly. -inline int sample_points(int npoints, float re) { - return sample_uniform(npoints, re); -} -inline int sample_points(const vector& cdf, float re) { - return sample_discrete(cdf, re); -} -inline vector sample_points_cdf(int npoints) { - auto cdf = vector(npoints); - for (auto i : range(cdf.size())) cdf[i] = 1 + (i != 0 ? cdf[i - 1] : 0); - return cdf; -} - -// Pick a point on lines uniformly. -inline pair sample_lines( - const vector& cdf, float re, float ru) { - return {sample_discrete(cdf, re), ru}; -} -inline vector sample_lines_cdf( - const vector& lines, const vector& positions) { - auto cdf = vector(lines.size()); - for (auto i : range(cdf.size())) { - auto& l = lines[i]; - auto w = line_length(positions[l.x], positions[l.y]); - cdf[i] = w + (i != 0 ? cdf[i - 1] : 0); - } - return cdf; -} - -// Pick a point on a triangle mesh uniformly. -inline pair sample_triangles( - const vector& cdf, float re, vec2f ruv) { - return {sample_discrete(cdf, re), sample_triangle(ruv)}; -} -inline vector sample_triangles_cdf( - const vector& triangles, const vector& positions) { - auto cdf = vector(triangles.size()); - for (auto i : range(cdf.size())) { - auto& t = triangles[i]; - auto w = triangle_area(positions[t.x], positions[t.y], positions[t.z]); - cdf[i] = w + (i != 0 ? cdf[i - 1] : 0); - } - return cdf; -} - -// Pick a point on a quad mesh uniformly. -inline pair sample_quads( - const vector& cdf, float re, vec2f ruv) { - return {sample_discrete(cdf, re), ruv}; -} -inline pair sample_quads( - const vector& quads, const vector& cdf, float re, vec2f ruv) { - auto element = sample_discrete(cdf, re); - if (quads[element].z == quads[element].w) { - return {element, sample_triangle(ruv)}; - } else { - return {element, ruv}; - } -} -inline vector sample_quads_cdf( - const vector& quads, const vector& positions) { - auto cdf = vector(quads.size()); - for (auto i : range(cdf.size())) { - auto& q = quads[i]; - auto w = quad_area( - positions[q.x], positions[q.y], positions[q.z], positions[q.w]); - cdf[i] = w + (i ? cdf[i - 1] : 0); - } - return cdf; -} - -} // namespace yocto - -// ----------------------------------------------------------------------------- -// CUDA SUPPORT -// ----------------------------------------------------------------------------- -#ifdef __CUDACC__ -#undef inline -#endif - -#endif diff --git a/libs/yocto/yocto_scene.cpp b/libs/yocto/yocto_scene.cpp index 385e72807..861221a88 100644 --- a/libs/yocto/yocto_scene.cpp +++ b/libs/yocto/yocto_scene.cpp @@ -42,7 +42,6 @@ #include "yocto_image.h" #include "yocto_modeling.h" -#include "yocto_shading.h" // ----------------------------------------------------------------------------- // USING DIRECTIVES diff --git a/libs/yocto/yocto_sceneio.cpp b/libs/yocto/yocto_sceneio.cpp index 083dd369d..68c537921 100644 --- a/libs/yocto/yocto_sceneio.cpp +++ b/libs/yocto/yocto_sceneio.cpp @@ -52,7 +52,6 @@ #include "yocto_image.h" #include "yocto_modelio.h" #include "yocto_pbrtio.h" -#include "yocto_shading.h" // ----------------------------------------------------------------------------- // USING DIRECTIVES @@ -4575,6 +4574,12 @@ static void save_mitsuba_scene( } }; + // Convert reflectivity to eta. + auto reflectivity_to_eta = [](vec3f reflectivity_) -> vec3f { + auto reflectivity = clamp(reflectivity_, 0.0f, 0.99f); + return (1 + sqrt(reflectivity)) / (1 - sqrt(reflectivity)); + }; + // materials auto mid = 0; for (auto& material : scene.materials) { diff --git a/libs/yocto/yocto_shading.h b/libs/yocto/yocto_shading.h deleted file mode 100644 index fcbcc9192..000000000 --- a/libs/yocto/yocto_shading.h +++ /dev/null @@ -1,1240 +0,0 @@ -// -// # Yocto/Shading: Shading routines -// -// Yocto/Shading defines shading and sampling functions useful to write path -// tracing algorithms. Yocto/Shading is implemented in `yocto_shading.h`. -// - -// -// LICENSE: -// -// Copyright (c) 2016 -- 2022 Fabio Pellacini -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// - -#ifndef _YOCTO_SHADING_H_ -#define _YOCTO_SHADING_H_ - -// ----------------------------------------------------------------------------- -// INCLUDES -// ----------------------------------------------------------------------------- - -#include -#include -#include -#include - -#include "yocto_math.h" -#include "yocto_sampling.h" - -// ----------------------------------------------------------------------------- -// USING DIRECTIVES -// ----------------------------------------------------------------------------- -namespace yocto { - -// using directives -using std::pair; -using std::string; -using std::vector; - -} // namespace yocto - -// ----------------------------------------------------------------------------- -// CUDA SUPPORT -// ----------------------------------------------------------------------------- -#ifdef __CUDACC__ -#define inline inline __device__ __forceinline__ -#endif - -// ----------------------------------------------------------------------------- -// SHADING FUNCTIONS -// ----------------------------------------------------------------------------- -namespace yocto { - -// Check if on the same side of the hemisphere -inline bool same_hemisphere(vec3f normal, vec3f outgoing, vec3f incoming); - -// Schlick approximation of the Fresnel term. -inline vec3f fresnel_schlick(vec3f specular, vec3f normal, vec3f outgoing); -// Compute the fresnel term for dielectrics. -inline float fresnel_dielectric(float eta, vec3f normal, vec3f outgoing); -// Compute the fresnel term for metals. -inline vec3f fresnel_conductor( - vec3f eta, vec3f etak, vec3f normal, vec3f outgoing); - -// Convert eta to reflectivity -inline vec3f eta_to_reflectivity(vec3f eta); -// Convert reflectivity to eta. -inline vec3f reflectivity_to_eta(vec3f reflectivity); -// Convert conductor eta to reflectivity. -inline vec3f eta_to_reflectivity(vec3f eta, vec3f etak); -// Convert eta to edge tint parametrization. -inline pair eta_to_edgetint(vec3f eta, vec3f etak); -// Convert reflectivity and edge tint to eta. -inline pair edgetint_to_eta(vec3f reflectivity, vec3f edgetint); - -// Get tabulated ior for conductors -inline pair conductor_eta(const string& name); - -// Evaluates the microfacet distribution. -inline float microfacet_distribution( - float roughness, vec3f normal, vec3f halfway, bool ggx = true); -// Evaluates the microfacet shadowing. -inline float microfacet_shadowing(float roughness, vec3f normal, vec3f halfway, - vec3f outgoing, vec3f incoming, bool ggx = true); - -// Samples a microfacet distribution. -inline vec3f sample_microfacet( - float roughness, vec3f normal, vec2f rn, bool ggx = true); -// Pdf for microfacet distribution sampling. -inline float sample_microfacet_pdf( - float roughness, vec3f normal, vec3f halfway, bool ggx = true); - -// Samples a microfacet distribution with the distribution of visible normals. -inline vec3f sample_microfacet( - float roughness, vec3f normal, vec3f outgoing, vec2f rn, bool ggx = true); -// Pdf for microfacet distribution sampling with the distribution of visible -// normals. -inline float sample_microfacet_pdf(float roughness, vec3f normal, vec3f halfway, - vec3f outgoing, bool ggx = true); - -// Microfacet energy compensation (E(cos(w))) -inline float microfacet_cosintegral( - float roughness, vec3f normal, vec3f outgoing); -// Approximate microfacet compensation for metals with Schlick's Fresnel -inline vec3f microfacet_compensation( - vec3f color, float roughness, vec3f normal, vec3f outgoing); - -// Evaluates a diffuse BRDF lobe. -inline vec3f eval_matte( - vec3f color, vec3f normal, vec3f outgoing, vec3f incoming); -// Sample a diffuse BRDF lobe. -inline vec3f sample_matte(vec3f color, vec3f normal, vec3f outgoing, vec2f rn); -// Pdf for diffuse BRDF lobe sampling. -inline float sample_matte_pdf( - vec3f color, vec3f normal, vec3f outgoing, vec3f incoming); - -// Evaluates a specular BRDF lobe. -inline vec3f eval_glossy(vec3f color, float ior, float roughness, vec3f normal, - vec3f outgoing, vec3f incoming); -// Sample a specular BRDF lobe. -inline vec3f sample_glossy(vec3f color, float ior, float roughness, - vec3f normal, vec3f outgoing, vec2f rn); -// Pdf for specular BRDF lobe sampling. -inline float sample_glossy_pdf(vec3f color, float ior, float roughness, - vec3f normal, vec3f outgoing, vec3f incoming); - -// Evaluates a metal BRDF lobe. -inline vec3f eval_reflective( - vec3f color, float roughness, vec3f normal, vec3f outgoing, vec3f incoming); -// Sample a metal BRDF lobe. -inline vec3f sample_reflective( - vec3f color, float roughness, vec3f normal, vec3f outgoing, vec2f rn); -// Pdf for metal BRDF lobe sampling. -inline float sample_reflective_pdf( - vec3f color, float roughness, vec3f normal, vec3f outgoing, vec3f incoming); - -// Evaluate a delta metal BRDF lobe. -inline vec3f eval_reflective( - vec3f color, vec3f normal, vec3f outgoing, vec3f incoming); -// Sample a delta metal BRDF lobe. -inline vec3f sample_reflective(vec3f color, vec3f normal, vec3f outgoing); -// Pdf for delta metal BRDF lobe sampling. -inline float sample_reflective_pdf( - vec3f color, vec3f normal, vec3f outgoing, vec3f incoming); - -// Evaluate a delta metal BRDF lobe. -inline vec3f eval_reflective( - vec3f eta, vec3f etak, vec3f normal, vec3f outgoing, vec3f incoming); -// Sample a delta metal BRDF lobe. -inline vec3f sample_reflective( - vec3f eta, vec3f etak, vec3f normal, vec3f outgoing); -// Pdf for delta metal BRDF lobe sampling. -inline float sample_reflective_pdf( - vec3f eta, vec3f etak, vec3f normal, vec3f outgoing, vec3f incoming); - -// Evaluate a delta metal BRDF lobe. -inline vec3f eval_reflective( - vec3f eta, vec3f etak, vec3f normal, vec3f outgoing, vec3f incoming); -// Sample a delta metal BRDF lobe. -inline vec3f sample_reflective( - vec3f eta, vec3f etak, vec3f normal, vec3f outgoing); -// Pdf for delta metal BRDF lobe sampling. -inline float sample_reflective_pdf( - vec3f eta, vec3f etak, vec3f normal, vec3f outgoing, vec3f incoming); - -// Evaluates a specular BRDF lobe. -inline vec3f eval_gltfpbr(vec3f color, float ior, float roughness, - float metallic, vec3f normal, vec3f outgoing, vec3f incoming); -// Sample a specular BRDF lobe. -inline vec3f sample_gltfpbr(vec3f color, float ior, float roughness, - float metallic, vec3f normal, vec3f outgoing, float rnl, vec2f rn); -// Pdf for specular BRDF lobe sampling. -inline float sample_gltfpbr_pdf(vec3f color, float ior, float roughness, - float metallic, vec3f normal, vec3f outgoing, vec3f incoming); - -// Evaluates a transmission BRDF lobe. -inline vec3f eval_transparent(vec3f color, float ior, float roughness, - vec3f normal, vec3f outgoing, vec3f incoming); -// Sample a transmission BRDF lobe. -inline vec3f sample_transparent(float ior, float roughness, vec3f normal, - vec3f outgoing, float rnl, vec2f rn); -// Pdf for transmission BRDF lobe sampling. -inline float sample_tranparent_pdf(vec3f color, float ior, float roughness, - vec3f normal, vec3f outgoing, vec3f incoming); - -// Evaluate a delta transmission BRDF lobe. -inline vec3f eval_transparent( - vec3f color, float ior, vec3f normal, vec3f outgoing, vec3f incoming); -// Sample a delta transmission BRDF lobe. -inline vec3f sample_transparent( - vec3f color, float ior, vec3f normal, vec3f outgoing, float rnl); -// Pdf for delta transmission BRDF lobe sampling. -inline float sample_tranparent_pdf( - vec3f color, float ior, vec3f normal, vec3f outgoing, vec3f incoming); - -// Evaluates a refraction BRDF lobe. -inline vec3f eval_refractive(vec3f color, float ior, float roughness, - vec3f normal, vec3f outgoing, vec3f incoming); -// Sample a refraction BRDF lobe. -inline vec3f sample_refractive(float ior, float roughness, vec3f normal, - vec3f outgoing, float rnl, vec2f rn); -// Pdf for refraction BRDF lobe sampling. -inline float sample_refractive_pdf(vec3f color, float ior, float roughness, - vec3f normal, vec3f outgoing, vec3f incoming); - -// Evaluate a delta refraction BRDF lobe. -inline vec3f eval_refractive( - vec3f color, float ior, vec3f normal, vec3f outgoing, vec3f incoming); -// Sample a delta refraction BRDF lobe. -inline vec3f sample_refractive( - vec3f color, float ior, vec3f normal, vec3f outgoing, float rnl); -// Pdf for delta refraction BRDF lobe sampling. -inline float sample_refractive_pdf( - vec3f color, float ior, vec3f normal, vec3f outgoing, vec3f incoming); - -// Evaluate a translucent BRDF lobe. -inline vec3f eval_translucent( - vec3f color, vec3f normal, vec3f outgoing, vec3f incoming); -// Pdf for translucency BRDF lobe sampling. -inline float sample_translucent_pdf( - vec3f color, vec3f normal, vec3f outgoing, vec3f incoming); -// Sample a translucency BRDF lobe. -inline vec3f sample_translucent( - vec3f color, vec3f normal, vec3f outgoing, vec2f rn); - -// Evaluate a passthrough BRDF lobe. -inline vec3f eval_passthrough( - vec3f color, vec3f normal, vec3f outgoing, vec3f incoming); -// Sample a passthrough BRDF lobe. -inline vec3f sample_passthrough(vec3f color, vec3f normal, vec3f outgoing); -// Pdf for passthrough BRDF lobe sampling. -inline float sample_passthrough_pdf( - vec3f color, vec3f normal, vec3f outgoing, vec3f incoming); - -// Convert mean-free-path to transmission -inline vec3f mfp_to_transmission(vec3f mfp, float depth); - -// Evaluate transmittance -inline vec3f eval_transmittance(vec3f density, float distance); -// Sample a distance proportionally to transmittance -inline float sample_transmittance( - vec3f density, float max_distance, float rl, float rd); -// Pdf for distance sampling -inline float sample_transmittance_pdf( - vec3f density, float distance, float max_distance); - -// Evaluate phase function -inline float eval_phasefunction( - float anisotropy, vec3f outgoing, vec3f incoming); -// Sample phase function -inline vec3f sample_phasefunction(float anisotropy, vec3f outgoing, vec2f rn); -// Pdf for phase function sampling -inline float sample_phasefunction_pdf( - float anisotropy, vec3f outgoing, vec3f incoming); - -} // namespace yocto - -// ----------------------------------------------------------------------------- -// -// -// IMPLEMENTATION -// -// -// ----------------------------------------------------------------------------- - -// ----------------------------------------------------------------------------- -// IMPLEMENTATION OF SHADING FUNCTIONS -// ----------------------------------------------------------------------------- -namespace yocto { - -// Check if on the same side of the hemisphere -inline bool same_hemisphere(vec3f normal, vec3f outgoing, vec3f incoming) { - return dot(normal, outgoing) * dot(normal, incoming) >= 0; -} - -// Schlick approximation of the Fresnel term -inline vec3f fresnel_schlick(vec3f specular, vec3f normal, vec3f outgoing) { - if (specular == vec3f{0, 0, 0}) return {0, 0, 0}; - auto cosine = dot(normal, outgoing); - return specular + - (1 - specular) * pow(clamp(1 - abs(cosine), 0.0f, 1.0f), 5.0f); -} - -// Compute the fresnel term for dielectrics. -inline float fresnel_dielectric(float eta, vec3f normal, vec3f outgoing) { - // Implementation from - // https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/ - auto cosw = abs(dot(normal, outgoing)); - - auto sin2 = 1 - cosw * cosw; - auto eta2 = eta * eta; - - auto cos2t = 1 - sin2 / eta2; - if (cos2t < 0) return 1; // tir - - auto t0 = sqrt(cos2t); - auto t1 = eta * t0; - auto t2 = eta * cosw; - - auto rs = (cosw - t1) / (cosw + t1); - auto rp = (t0 - t2) / (t0 + t2); - - return (rs * rs + rp * rp) / 2; -} - -// Compute the fresnel term for metals. -inline vec3f fresnel_conductor( - vec3f eta, vec3f etak, vec3f normal, vec3f outgoing) { - // Implementation from - // https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/ - auto cosw = dot(normal, outgoing); - if (cosw <= 0) return {0, 0, 0}; - - cosw = clamp(cosw, (float)-1, (float)1); - auto cos2 = cosw * cosw; - auto sin2 = clamp(1 - cos2, (float)0, (float)1); - auto eta2 = eta * eta; - auto etak2 = etak * etak; - - auto t0 = eta2 - etak2 - sin2; - auto a2plusb2 = sqrt(t0 * t0 + 4 * eta2 * etak2); - auto t1 = a2plusb2 + cos2; - auto a = sqrt((a2plusb2 + t0) / 2); - auto t2 = 2 * a * cosw; - auto rs = (t1 - t2) / (t1 + t2); - - auto t3 = cos2 * a2plusb2 + sin2 * sin2; - auto t4 = t2 * sin2; - auto rp = rs * (t3 - t4) / (t3 + t4); - - return (rp + rs) / 2; -} - -// Convert eta to reflectivity -inline vec3f eta_to_reflectivity(vec3f eta) { - return ((eta - 1) * (eta - 1)) / ((eta + 1) * (eta + 1)); -} -// Convert reflectivity to eta. -inline vec3f reflectivity_to_eta(vec3f reflectivity_) { - auto reflectivity = clamp(reflectivity_, 0.0f, 0.99f); - return (1 + sqrt(reflectivity)) / (1 - sqrt(reflectivity)); -} -// Convert conductor eta to reflectivity -inline vec3f eta_to_reflectivity(vec3f eta, vec3f etak) { - return ((eta - 1) * (eta - 1) + etak * etak) / - ((eta + 1) * (eta + 1) + etak * etak); -} -// Convert eta to edge tint parametrization -inline pair eta_to_edgetint(vec3f eta, vec3f etak) { - auto reflectivity = eta_to_reflectivity(eta, etak); - auto numer = (1 + sqrt(reflectivity)) / (1 - sqrt(reflectivity)) - eta; - auto denom = (1 + sqrt(reflectivity)) / (1 - sqrt(reflectivity)) - - (1 - reflectivity) / (1 + reflectivity); - auto edgetint = numer / denom; - return {reflectivity, edgetint}; -} -// Convert reflectivity and edge tint to eta. -inline pair edgetint_to_eta(vec3f reflectivity, vec3f edgetint) { - auto r = clamp(reflectivity, 0.0f, 0.99f); - auto g = edgetint; - - auto r_sqrt = sqrt(r); - auto n_min = (1 - r) / (1 + r); - auto n_max = (1 + r_sqrt) / (1 - r_sqrt); - - auto n = lerp(n_max, n_min, g); - auto k2 = ((n + 1) * (n + 1) * r - (n - 1) * (n - 1)) / (1 - r); - k2 = max(k2, 0.0f); - auto k = sqrt(k2); - return {n, k}; -} - -// Evaluate microfacet distribution -inline float microfacet_distribution( - float roughness, vec3f normal, vec3f halfway, bool ggx) { - // https://google.github.io/filament/Filament.html#materialsystem/specularbrdf - // http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html - auto cosine = dot(normal, halfway); - if (cosine <= 0) return 0; - auto roughness2 = roughness * roughness; - auto cosine2 = cosine * cosine; - if (ggx) { - return roughness2 / (pif * (cosine2 * roughness2 + 1 - cosine2) * - (cosine2 * roughness2 + 1 - cosine2)); - } else { - return exp((cosine2 - 1) / (roughness2 * cosine2)) / - (pif * roughness2 * cosine2 * cosine2); - } -} - -// Evaluate the microfacet shadowing1 -inline float microfacet_shadowing1( - float roughness, vec3f normal, vec3f halfway, vec3f direction, bool ggx) { - // https://google.github.io/filament/Filament.html#materialsystem/specularbrdf - // http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html - // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#appendix-b-brdf-implementation - auto cosine = dot(normal, direction); - auto cosineh = dot(halfway, direction); - if (cosine * cosineh <= 0) return 0; - auto roughness2 = roughness * roughness; - auto cosine2 = cosine * cosine; - if (ggx) { - return 2 * abs(cosine) / - (abs(cosine) + sqrt(cosine2 - roughness2 * cosine2 + roughness2)); - } else { - auto ci = abs(cosine) / (roughness * sqrt(1 - cosine2)); - return ci < 1.6f ? (3.535f * ci + 2.181f * ci * ci) / - (1.0f + 2.276f * ci + 2.577f * ci * ci) - : 1.0f; - } -} - -// Evaluate microfacet shadowing -inline float microfacet_shadowing(float roughness, vec3f normal, vec3f halfway, - vec3f outgoing, vec3f incoming, bool ggx) { - return microfacet_shadowing1(roughness, normal, halfway, outgoing, ggx) * - microfacet_shadowing1(roughness, normal, halfway, incoming, ggx); -} - -// Sample a microfacet distribution. -inline vec3f sample_microfacet( - float roughness, vec3f normal, vec2f rn, bool ggx) { - auto phi = 2 * pif * rn.x; - auto theta = 0.0f; - if (ggx) { - theta = atan(roughness * sqrt(rn.y / (1 - rn.y))); - } else { - auto roughness2 = roughness * roughness; - theta = atan(sqrt(-roughness2 * log(1 - rn.y))); - } - auto local_half_vector = vec3f{ - cos(phi) * sin(theta), sin(phi) * sin(theta), cos(theta)}; - return transform_direction(basis_fromz(normal), local_half_vector); -} - -// Pdf for microfacet distribution sampling. -inline float sample_microfacet_pdf( - float roughness, vec3f normal, vec3f halfway, bool ggx) { - auto cosine = dot(normal, halfway); - if (cosine < 0) return 0; - return microfacet_distribution(roughness, normal, halfway, ggx) * cosine; -} - -// Sample a microfacet distribution with the distribution of visible normals. -inline vec3f sample_microfacet( - float roughness, vec3f normal, vec3f outgoing, vec2f rn, bool ggx) { - // http://jcgt.org/published/0007/04/01/ - if (ggx) { - // move to local coordinate system - auto basis = basis_fromz(normal); - auto Ve = transform_direction(transpose(basis), outgoing); - auto alpha_x = roughness, alpha_y = roughness; - // Section 3.2: transforming the view direction to the hemisphere - // configuration - auto Vh = normalize(vec3f{alpha_x * Ve.x, alpha_y * Ve.y, Ve.z}); - // Section 4.1: orthonormal basis (with special case if cross product is - // zero) - auto lensq = Vh.x * Vh.x + Vh.y * Vh.y; - auto T1 = lensq > 0 ? vec3f{-Vh.y, Vh.x, 0} * (1 / sqrt(lensq)) - : vec3f{1, 0, 0}; - auto T2 = cross(Vh, T1); - // Section 4.2: parameterization of the projected area - auto r = sqrt(rn.y), phi = 2 * pif * rn.x; - auto t1 = r * cos(phi), t2 = r * sin(phi); - auto s = 0.5f * (1 + Vh.z); - t2 = (1 - s) * sqrt(1 - t1 * t1) + s * t2; - // Section 4.3: reprojection onto hemisphere - auto Nh = t1 * T1 + t2 * T2 + sqrt(max(0.0f, 1 - t1 * t1 - t2 * t2)) * Vh; - // Section 3.4: transforming the normal back to the ellipsoid configuration - auto Ne = normalize(vec3f{alpha_x * Nh.x, alpha_y * Nh.y, max(0.0f, Nh.z)}); - // move to world coordinate - auto local_halfway = Ne; - return transform_direction(basis, local_halfway); - } else { -#ifndef __CUDACC__ - throw std::invalid_argument{"not implemented yet"}; -#else - return {0, 0, 0}; -#endif - } -} - -// Pdf for microfacet distribution sampling with the distribution of visible -// normals. -inline float sample_microfacet_pdf( - float roughness, vec3f normal, vec3f halfway, vec3f outgoing, bool ggx) { - // http://jcgt.org/published/0007/04/01/ - if (dot(normal, halfway) < 0) return 0; - if (dot(halfway, outgoing) < 0) return 0; - return microfacet_distribution(roughness, normal, halfway, ggx) * - microfacet_shadowing1(roughness, normal, halfway, outgoing, ggx) * - max(0.0f, dot(halfway, outgoing)) / abs(dot(normal, outgoing)); -} - -// Microfacet energy compensation (E(cos(w))) -inline float microfacet_cosintegral( - float roughness, vec3f normal, vec3f outgoing) { - // https://blog.selfshadow.com/publications/s2017-shading-course/imageworks/s2017_pbs_imageworks_slides_v2.pdf - const float S[5] = {-0.170718f, 4.07985f, -11.5295f, 18.4961f, -9.23618f}; - const float T[5] = {0.0632331f, 3.1434f, -7.47567f, 13.0482f, -7.0401f}; - auto m = abs(dot(normal, outgoing)); - auto r = roughness; - auto s = S[0] * sqrt(m) + S[1] * r + S[2] * r * r + S[3] * r * r * r + - S[4] * r * r * r * r; - auto t = T[0] * m + T[1] * r + T[2] * r * r + T[3] * r * r * r + - T[4] * r * r * r * r; - return 1 - pow(s, 6.0f) * pow(m, 3.0f / 4.0f) / (pow(t, 6.0f) + pow(m, 2.0f)); -} -// Approximate microfacet compensation for metals with Schlick's Fresnel -inline vec3f microfacet_compensation( - vec3f color, float roughness, vec3f normal, vec3f outgoing) { - // https://blog.selfshadow.com/publications/turquin/ms_comp_final.pdf - auto E = microfacet_cosintegral(sqrt(roughness), normal, outgoing); - return 1 + color * (1 - E) / E; -} - -// Evaluate a diffuse BRDF lobe. -inline vec3f eval_matte( - vec3f color, vec3f normal, vec3f outgoing, vec3f incoming) { - if (dot(normal, incoming) * dot(normal, outgoing) <= 0) return {0, 0, 0}; - return color / pif * abs(dot(normal, incoming)); -} - -// Sample a diffuse BRDF lobe. -inline vec3f sample_matte(vec3f color, vec3f normal, vec3f outgoing, vec2f rn) { - auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; - return sample_hemisphere_cos(up_normal, rn); -} - -// Pdf for diffuse BRDF lobe sampling. -inline float sample_matte_pdf( - vec3f color, vec3f normal, vec3f outgoing, vec3f incoming) { - if (dot(normal, incoming) * dot(normal, outgoing) <= 0) return 0; - auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; - return sample_hemisphere_cos_pdf(up_normal, incoming); -} - -// Evaluate a specular BRDF lobe. -inline vec3f eval_glossy(vec3f color, float ior, float roughness, vec3f normal, - vec3f outgoing, vec3f incoming) { - if (dot(normal, incoming) * dot(normal, outgoing) <= 0) return {0, 0, 0}; - auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; - auto F1 = fresnel_dielectric(ior, up_normal, outgoing); - auto halfway = normalize(incoming + outgoing); - auto F = fresnel_dielectric(ior, halfway, incoming); - auto D = microfacet_distribution(roughness, up_normal, halfway); - auto G = microfacet_shadowing( - roughness, up_normal, halfway, outgoing, incoming); - return color * (1 - F1) / pif * abs(dot(up_normal, incoming)) + - vec3f{1, 1, 1} * F * D * G / - (4 * dot(up_normal, outgoing) * dot(up_normal, incoming)) * - abs(dot(up_normal, incoming)); -} - -// Sample a specular BRDF lobe. -inline vec3f sample_glossy(vec3f color, float ior, float roughness, - vec3f normal, vec3f outgoing, float rnl, vec2f rn) { - auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; - if (rnl < fresnel_dielectric(ior, up_normal, outgoing)) { - auto halfway = sample_microfacet(roughness, up_normal, rn); - auto incoming = reflect(outgoing, halfway); - if (!same_hemisphere(up_normal, outgoing, incoming)) return {0, 0, 0}; - return incoming; - } else { - return sample_hemisphere_cos(up_normal, rn); - } -} - -// Pdf for specular BRDF lobe sampling. -inline float sample_glossy_pdf(vec3f color, float ior, float roughness, - vec3f normal, vec3f outgoing, vec3f incoming) { - if (dot(normal, incoming) * dot(normal, outgoing) <= 0) return 0; - auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; - auto halfway = normalize(outgoing + incoming); - auto F = fresnel_dielectric(ior, up_normal, outgoing); - return F * sample_microfacet_pdf(roughness, up_normal, halfway) / - (4 * abs(dot(outgoing, halfway))) + - (1 - F) * sample_hemisphere_cos_pdf(up_normal, incoming); -} - -// Evaluate a metal BRDF lobe. -inline vec3f eval_reflective(vec3f color, float roughness, vec3f normal, - vec3f outgoing, vec3f incoming) { - if (dot(normal, incoming) * dot(normal, outgoing) <= 0) return {0, 0, 0}; - auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; - auto halfway = normalize(incoming + outgoing); - auto F = fresnel_conductor( - reflectivity_to_eta(color), {0, 0, 0}, halfway, incoming); - auto D = microfacet_distribution(roughness, up_normal, halfway); - auto G = microfacet_shadowing( - roughness, up_normal, halfway, outgoing, incoming); - return F * D * G / (4 * dot(up_normal, outgoing) * dot(up_normal, incoming)) * - abs(dot(up_normal, incoming)); -} - -// Sample a metal BRDF lobe. -inline vec3f sample_reflective( - vec3f color, float roughness, vec3f normal, vec3f outgoing, vec2f rn) { - auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; - auto halfway = sample_microfacet(roughness, up_normal, rn); - auto incoming = reflect(outgoing, halfway); - if (!same_hemisphere(up_normal, outgoing, incoming)) return {0, 0, 0}; - return incoming; -} - -// Pdf for metal BRDF lobe sampling. -inline float sample_reflective_pdf(vec3f color, float roughness, vec3f normal, - vec3f outgoing, vec3f incoming) { - if (dot(normal, incoming) * dot(normal, outgoing) <= 0) return 0; - auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; - auto halfway = normalize(outgoing + incoming); - return sample_microfacet_pdf(roughness, up_normal, halfway) / - (4 * abs(dot(outgoing, halfway))); -} - -// Evaluate a metal BRDF lobe. -inline vec3f eval_reflective(vec3f eta, vec3f etak, float roughness, - vec3f normal, vec3f outgoing, vec3f incoming) { - if (dot(normal, incoming) * dot(normal, outgoing) <= 0) return {0, 0, 0}; - auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; - auto halfway = normalize(incoming + outgoing); - auto F = fresnel_conductor(eta, etak, halfway, incoming); - auto D = microfacet_distribution(roughness, up_normal, halfway); - auto G = microfacet_shadowing( - roughness, up_normal, halfway, outgoing, incoming); - return F * D * G / (4 * dot(up_normal, outgoing) * dot(up_normal, incoming)) * - abs(dot(up_normal, incoming)); -} - -// Sample a metal BRDF lobe. -inline vec3f sample_reflective(vec3f eta, vec3f etak, float roughness, - vec3f normal, vec3f outgoing, vec2f rn) { - auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; - auto halfway = sample_microfacet(roughness, up_normal, rn); - return reflect(outgoing, halfway); -} - -// Pdf for metal BRDF lobe sampling. -inline float sample_reflective_pdf(vec3f eta, vec3f etak, float roughness, - vec3f normal, vec3f outgoing, vec3f incoming) { - if (dot(normal, incoming) * dot(normal, outgoing) <= 0) return 0; - auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; - auto halfway = normalize(outgoing + incoming); - return sample_microfacet_pdf(roughness, up_normal, halfway) / - (4 * abs(dot(outgoing, halfway))); -} - -// Evaluate a delta metal BRDF lobe. -inline vec3f eval_reflective( - vec3f color, vec3f normal, vec3f outgoing, vec3f incoming) { - if (dot(normal, incoming) * dot(normal, outgoing) <= 0) return {0, 0, 0}; - auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; - return fresnel_conductor( - reflectivity_to_eta(color), {0, 0, 0}, up_normal, outgoing); -} - -// Sample a delta metal BRDF lobe. -inline vec3f sample_reflective(vec3f color, vec3f normal, vec3f outgoing) { - auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; - return reflect(outgoing, up_normal); -} - -// Pdf for delta metal BRDF lobe sampling. -inline float sample_reflective_pdf( - vec3f color, vec3f normal, vec3f outgoing, vec3f incoming) { - if (dot(normal, incoming) * dot(normal, outgoing) <= 0) return 0; - return 1; -} - -// Evaluate a delta metal BRDF lobe. -inline vec3f eval_reflective( - vec3f eta, vec3f etak, vec3f normal, vec3f outgoing, vec3f incoming) { - if (dot(normal, incoming) * dot(normal, outgoing) <= 0) return {0, 0, 0}; - auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; - return fresnel_conductor(eta, etak, up_normal, outgoing); -} - -// Sample a delta metal BRDF lobe. -inline vec3f sample_reflective( - vec3f eta, vec3f etak, vec3f normal, vec3f outgoing) { - auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; - return reflect(outgoing, up_normal); -} - -// Pdf for delta metal BRDF lobe sampling. -inline float sample_reflective_pdf( - vec3f eta, vec3f etak, vec3f normal, vec3f outgoing, vec3f incoming) { - if (dot(normal, incoming) * dot(normal, outgoing) <= 0) return 0; - return 1; -} - -// Evaluate a specular BRDF lobe. -inline vec3f eval_gltfpbr(vec3f color, float ior, float roughness, - float metallic, vec3f normal, vec3f outgoing, vec3f incoming) { - if (dot(normal, incoming) * dot(normal, outgoing) <= 0) return {0, 0, 0}; - auto reflectivity = lerp( - eta_to_reflectivity(vec3f{ior, ior, ior}), color, metallic); - auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; - auto F1 = fresnel_schlick(reflectivity, up_normal, outgoing); - auto halfway = normalize(incoming + outgoing); - auto F = fresnel_schlick(reflectivity, halfway, incoming); - auto D = microfacet_distribution(roughness, up_normal, halfway); - auto G = microfacet_shadowing( - roughness, up_normal, halfway, outgoing, incoming); - return color * (1 - metallic) * (1 - F1) / pif * - abs(dot(up_normal, incoming)) + - F * D * G / (4 * dot(up_normal, outgoing) * dot(up_normal, incoming)) * - abs(dot(up_normal, incoming)); -} - -// Sample a specular BRDF lobe. -inline vec3f sample_gltfpbr(vec3f color, float ior, float roughness, - float metallic, vec3f normal, vec3f outgoing, float rnl, vec2f rn) { - auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; - auto reflectivity = lerp( - eta_to_reflectivity(vec3f{ior, ior, ior}), color, metallic); - if (rnl < mean(fresnel_schlick(reflectivity, up_normal, outgoing))) { - auto halfway = sample_microfacet(roughness, up_normal, rn); - auto incoming = reflect(outgoing, halfway); - if (!same_hemisphere(up_normal, outgoing, incoming)) return {0, 0, 0}; - return incoming; - } else { - return sample_hemisphere_cos(up_normal, rn); - } -} - -// Pdf for specular BRDF lobe sampling. -inline float sample_gltfpbr_pdf(vec3f color, float ior, float roughness, - float metallic, vec3f normal, vec3f outgoing, vec3f incoming) { - if (dot(normal, incoming) * dot(normal, outgoing) <= 0) return 0; - auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; - auto halfway = normalize(outgoing + incoming); - auto reflectivity = lerp( - eta_to_reflectivity(vec3f{ior, ior, ior}), color, metallic); - auto F = mean(fresnel_schlick(reflectivity, up_normal, outgoing)); - return F * sample_microfacet_pdf(roughness, up_normal, halfway) / - (4 * abs(dot(outgoing, halfway))) + - (1 - F) * sample_hemisphere_cos_pdf(up_normal, incoming); -} - -// Evaluate a transmission BRDF lobe. -inline vec3f eval_transparent(vec3f color, float ior, float roughness, - vec3f normal, vec3f outgoing, vec3f incoming) { - auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; - if (dot(normal, incoming) * dot(normal, outgoing) >= 0) { - auto halfway = normalize(incoming + outgoing); - auto F = fresnel_dielectric(ior, halfway, outgoing); - auto D = microfacet_distribution(roughness, up_normal, halfway); - auto G = microfacet_shadowing( - roughness, up_normal, halfway, outgoing, incoming); - return vec3f{1, 1, 1} * F * D * G / - (4 * dot(up_normal, outgoing) * dot(up_normal, incoming)) * - abs(dot(up_normal, incoming)); - } else { - auto reflected = reflect(-incoming, up_normal); - auto halfway = normalize(reflected + outgoing); - auto F = fresnel_dielectric(ior, halfway, outgoing); - auto D = microfacet_distribution(roughness, up_normal, halfway); - auto G = microfacet_shadowing( - roughness, up_normal, halfway, outgoing, reflected); - return color * (1 - F) * D * G / - (4 * dot(up_normal, outgoing) * dot(up_normal, reflected)) * - (abs(dot(up_normal, reflected))); - } -} - -// Sample a transmission BRDF lobe. -inline vec3f sample_transparent(vec3f color, float ior, float roughness, - vec3f normal, vec3f outgoing, float rnl, vec2f rn) { - auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; - auto halfway = sample_microfacet(roughness, up_normal, rn); - if (rnl < fresnel_dielectric(ior, halfway, outgoing)) { - auto incoming = reflect(outgoing, halfway); - if (!same_hemisphere(up_normal, outgoing, incoming)) return {0, 0, 0}; - return incoming; - } else { - auto reflected = reflect(outgoing, halfway); - auto incoming = -reflect(reflected, up_normal); - if (same_hemisphere(up_normal, outgoing, incoming)) return {0, 0, 0}; - return incoming; - } -} - -// Pdf for transmission BRDF lobe sampling. -inline float sample_tranparent_pdf(vec3f color, float ior, float roughness, - vec3f normal, vec3f outgoing, vec3f incoming) { - auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; - if (dot(normal, incoming) * dot(normal, outgoing) >= 0) { - auto halfway = normalize(incoming + outgoing); - return fresnel_dielectric(ior, halfway, outgoing) * - sample_microfacet_pdf(roughness, up_normal, halfway) / - (4 * abs(dot(outgoing, halfway))); - } else { - auto reflected = reflect(-incoming, up_normal); - auto halfway = normalize(reflected + outgoing); - auto d = (1 - fresnel_dielectric(ior, halfway, outgoing)) * - sample_microfacet_pdf(roughness, up_normal, halfway); - return d / (4 * abs(dot(outgoing, halfway))); - } -} - -// Evaluate a delta transmission BRDF lobe. -inline vec3f eval_transparent( - vec3f color, float ior, vec3f normal, vec3f outgoing, vec3f incoming) { - auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; - if (dot(normal, incoming) * dot(normal, outgoing) >= 0) { - return vec3f{1, 1, 1} * fresnel_dielectric(ior, up_normal, outgoing); - } else { - return color * (1 - fresnel_dielectric(ior, up_normal, outgoing)); - } -} - -// Sample a delta transmission BRDF lobe. -inline vec3f sample_transparent( - vec3f color, float ior, vec3f normal, vec3f outgoing, float rnl) { - auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; - if (rnl < fresnel_dielectric(ior, up_normal, outgoing)) { - return reflect(outgoing, up_normal); - } else { - return -outgoing; - } -} - -// Pdf for delta transmission BRDF lobe sampling. -inline float sample_tranparent_pdf( - vec3f color, float ior, vec3f normal, vec3f outgoing, vec3f incoming) { - auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; - if (dot(normal, incoming) * dot(normal, outgoing) >= 0) { - return fresnel_dielectric(ior, up_normal, outgoing); - } else { - return 1 - fresnel_dielectric(ior, up_normal, outgoing); - } -} - -// Evaluate a refraction BRDF lobe. -inline vec3f eval_refractive(vec3f color, float ior, float roughness, - vec3f normal, vec3f outgoing, vec3f incoming) { - auto entering = dot(normal, outgoing) >= 0; - auto up_normal = entering ? normal : -normal; - auto rel_ior = entering ? ior : (1 / ior); - if (dot(normal, incoming) * dot(normal, outgoing) >= 0) { - auto halfway = normalize(incoming + outgoing); - auto F = fresnel_dielectric(rel_ior, halfway, outgoing); - auto D = microfacet_distribution(roughness, up_normal, halfway); - auto G = microfacet_shadowing( - roughness, up_normal, halfway, outgoing, incoming); - return vec3f{1, 1, 1} * F * D * G / - abs(4 * dot(normal, outgoing) * dot(normal, incoming)) * - abs(dot(normal, incoming)); - } else { - auto halfway = -normalize(rel_ior * incoming + outgoing) * - (entering ? 1.0f : -1.0f); - auto F = fresnel_dielectric(rel_ior, halfway, outgoing); - auto D = microfacet_distribution(roughness, up_normal, halfway); - auto G = microfacet_shadowing( - roughness, up_normal, halfway, outgoing, incoming); - // [Walter 2007] equation 21 - return vec3f{1, 1, 1} * - abs((dot(outgoing, halfway) * dot(incoming, halfway)) / - (dot(outgoing, normal) * dot(incoming, normal))) * - (1 - F) * D * G / - pow(rel_ior * dot(halfway, incoming) + dot(halfway, outgoing), - 2.0f) * - abs(dot(normal, incoming)); - } -} - -// Sample a refraction BRDF lobe. -inline vec3f sample_refractive(vec3f color, float ior, float roughness, - vec3f normal, vec3f outgoing, float rnl, vec2f rn) { - auto entering = dot(normal, outgoing) >= 0; - auto up_normal = entering ? normal : -normal; - auto halfway = sample_microfacet(roughness, up_normal, rn); - // auto halfway = sample_microfacet(roughness, up_normal, outgoing, rn); - if (rnl < fresnel_dielectric(entering ? ior : (1 / ior), halfway, outgoing)) { - auto incoming = reflect(outgoing, halfway); - if (!same_hemisphere(up_normal, outgoing, incoming)) return {0, 0, 0}; - return incoming; - } else { - auto incoming = refract(outgoing, halfway, entering ? (1 / ior) : ior); - if (same_hemisphere(up_normal, outgoing, incoming)) return {0, 0, 0}; - return incoming; - } -} - -// Pdf for refraction BRDF lobe sampling. -inline float sample_refractive_pdf(vec3f color, float ior, float roughness, - vec3f normal, vec3f outgoing, vec3f incoming) { - auto entering = dot(normal, outgoing) >= 0; - auto up_normal = entering ? normal : -normal; - auto rel_ior = entering ? ior : (1 / ior); - if (dot(normal, incoming) * dot(normal, outgoing) >= 0) { - auto halfway = normalize(incoming + outgoing); - return fresnel_dielectric(rel_ior, halfway, outgoing) * - sample_microfacet_pdf(roughness, up_normal, halfway) / - // sample_microfacet_pdf(roughness, up_normal, halfway, outgoing) / - (4 * abs(dot(outgoing, halfway))); - } else { - auto halfway = -normalize(rel_ior * incoming + outgoing) * - (entering ? 1.0f : -1.0f); - // [Walter 2007] equation 17 - return (1 - fresnel_dielectric(rel_ior, halfway, outgoing)) * - sample_microfacet_pdf(roughness, up_normal, halfway) * - // sample_microfacet_pdf(roughness, up_normal, halfway, outgoing) / - abs(dot(halfway, incoming)) / // here we use incoming as from pbrt - pow(rel_ior * dot(halfway, incoming) + dot(halfway, outgoing), 2.0f); - } -} - -// Evaluate a delta refraction BRDF lobe. -inline vec3f eval_refractive( - vec3f color, float ior, vec3f normal, vec3f outgoing, vec3f incoming) { - if (abs(ior - 1) < 1e-3) - return dot(normal, incoming) * dot(normal, outgoing) <= 0 ? vec3f{1, 1, 1} - : vec3f{0, 0, 0}; - auto entering = dot(normal, outgoing) >= 0; - auto up_normal = entering ? normal : -normal; - auto rel_ior = entering ? ior : (1 / ior); - if (dot(normal, incoming) * dot(normal, outgoing) >= 0) { - return vec3f{1, 1, 1} * fresnel_dielectric(rel_ior, up_normal, outgoing); - } else { - return vec3f{1, 1, 1} * (1 / (rel_ior * rel_ior)) * - (1 - fresnel_dielectric(rel_ior, up_normal, outgoing)); - } -} - -// Sample a delta refraction BRDF lobe. -inline vec3f sample_refractive( - vec3f color, float ior, vec3f normal, vec3f outgoing, float rnl) { - if (abs(ior - 1) < 1e-3) return -outgoing; - auto entering = dot(normal, outgoing) >= 0; - auto up_normal = entering ? normal : -normal; - auto rel_ior = entering ? ior : (1 / ior); - if (rnl < fresnel_dielectric(rel_ior, up_normal, outgoing)) { - return reflect(outgoing, up_normal); - } else { - return refract(outgoing, up_normal, 1 / rel_ior); - } -} - -// Pdf for delta refraction BRDF lobe sampling. -inline float sample_refractive_pdf( - vec3f color, float ior, vec3f normal, vec3f outgoing, vec3f incoming) { - if (abs(ior - 1) < 1e-3) - return dot(normal, incoming) * dot(normal, outgoing) < 0 ? 1.0f : 0.0f; - auto entering = dot(normal, outgoing) >= 0; - auto up_normal = entering ? normal : -normal; - auto rel_ior = entering ? ior : (1 / ior); - if (dot(normal, incoming) * dot(normal, outgoing) >= 0) { - return fresnel_dielectric(rel_ior, up_normal, outgoing); - } else { - return (1 - fresnel_dielectric(rel_ior, up_normal, outgoing)); - } -} - -// Evaluate a translucent BRDF lobe. -inline vec3f eval_translucent( - vec3f color, vec3f normal, vec3f outgoing, vec3f incoming) { - if (dot(normal, incoming) * dot(normal, outgoing) >= 0) return {0, 0, 0}; - return color / pif * abs(dot(normal, incoming)); -} - -// Sample a translucency BRDF lobe. -inline vec3f sample_translucent( - vec3f color, vec3f normal, vec3f outgoing, vec2f rn) { - auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; - return sample_hemisphere_cos(-up_normal, rn); -} - -// Pdf for translucency BRDF lobe sampling. -inline float sample_translucent_pdf( - vec3f color, vec3f normal, vec3f outgoing, vec3f incoming) { - if (dot(normal, incoming) * dot(normal, outgoing) >= 0) return 0; - auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; - return sample_hemisphere_cos_pdf(-up_normal, incoming); -} - -// Evaluate a passthrough BRDF lobe. -inline vec3f eval_passthrough( - vec3f color, vec3f normal, vec3f outgoing, vec3f incoming) { - if (dot(normal, incoming) * dot(normal, outgoing) >= 0) { - return vec3f{0, 0, 0}; - } else { - return vec3f{1, 1, 1}; - } -} - -// Sample a passthrough BRDF lobe. -inline vec3f sample_passthrough(vec3f color, vec3f normal, vec3f outgoing) { - return -outgoing; -} - -// Pdf for passthrough BRDF lobe sampling. -inline float sample_passthrough_pdf( - vec3f color, vec3f normal, vec3f outgoing, vec3f incoming) { - if (dot(normal, incoming) * dot(normal, outgoing) >= 0) { - return 0; - } else { - return 1; - } -} - -// Convert mean-free-path to transmission -inline vec3f mfp_to_transmission(vec3f mfp, float depth) { - return exp(-depth / mfp); -} - -// Evaluate transmittance -inline vec3f eval_transmittance(vec3f density, float distance) { - return exp(-density * distance); -} - -// Sample a distance proportionally to transmittance -inline float sample_transmittance( - vec3f density, float max_distance, float rl, float rd) { - auto channel = clamp((int)(rl * 3), 0, 2); - auto distance = (density[channel] == 0) ? flt_max - : -log(1 - rd) / density[channel]; - return min(distance, max_distance); -} - -// Pdf for distance sampling -inline float sample_transmittance_pdf( - vec3f density, float distance, float max_distance) { - if (distance < max_distance) { - return sum(density * exp(-density * distance)) / 3; - } else { - return sum(exp(-density * max_distance)) / 3; - } -} - -// Evaluate phase function -inline float eval_phasefunction( - float anisotropy, vec3f outgoing, vec3f incoming) { - auto cosine = -dot(outgoing, incoming); - auto denom = 1 + anisotropy * anisotropy - 2 * anisotropy * cosine; - return (1 - anisotropy * anisotropy) / (4 * pif * denom * sqrt(denom)); -} - -// Sample phase function -inline vec3f sample_phasefunction(float anisotropy, vec3f outgoing, vec2f rn) { - auto cos_theta = 0.0f; - if (abs(anisotropy) < 1e-3f) { - cos_theta = 1 - 2 * rn.y; - } else { - auto square = (1 - anisotropy * anisotropy) / - (1 + anisotropy - 2 * anisotropy * rn.y); - cos_theta = (1 + anisotropy * anisotropy - square * square) / - (2 * anisotropy); - } - - auto sin_theta = sqrt(max(0.0f, 1 - cos_theta * cos_theta)); - auto phi = 2 * pif * rn.x; - auto local_incoming = vec3f{ - sin_theta * cos(phi), sin_theta * sin(phi), cos_theta}; - return basis_fromz(-outgoing) * local_incoming; -} - -// Pdf for phase function sampling -inline float sample_phasefunction_pdf( - float anisotropy, vec3f outgoing, vec3f incoming) { - return eval_phasefunction(anisotropy, outgoing, incoming); -} - -#ifndef __CUDACC__ - -// Conductor etas -inline pair conductor_eta(const string& name) { - static const vector>> metal_ior_table = { - {"a-C", {{2.9440999183f, 2.2271502925f, 1.9681668794f}, - {0.8874329109f, 0.7993216383f, 0.8152862927f}}}, - {"Ag", {{0.1552646489f, 0.1167232965f, 0.1383806959f}, - {4.8283433224f, 3.1222459278f, 2.1469504455f}}}, - {"Al", {{1.6574599595f, 0.8803689579f, 0.5212287346f}, - {9.2238691996f, 6.2695232477f, 4.8370012281f}}}, - {"AlAs", {{3.6051023902f, 3.2329365777f, 2.2175611545f}, - {0.0006670247f, -0.0004999400f, 0.0074261204f}}}, - {"AlSb", {{-0.0485225705f, 4.1427547893f, 4.6697691348f}, - {-0.0363741915f, 0.0937665154f, 1.3007390124f}}}, - {"Au", {{0.1431189557f, 0.3749570432f, 1.4424785571f}, - {3.9831604247f, 2.3857207478f, 1.6032152899f}}}, - {"Be", {{4.1850592788f, 3.1850604423f, 2.7840913457f}, - {3.8354398268f, 3.0101260162f, 2.8690088743f}}}, - {"Cr", {{4.3696828663f, 2.9167024892f, 1.6547005413f}, - {5.2064337956f, 4.2313645277f, 3.7549467933f}}}, - {"CsI", {{2.1449030413f, 1.7023164587f, 1.6624194173f}, - {0.0000000000f, 0.0000000000f, 0.0000000000f}}}, - {"Cu", {{0.2004376970f, 0.9240334304f, 1.1022119527f}, - {3.9129485033f, 2.4528477015f, 2.1421879552f}}}, - {"Cu2O", {{3.5492833755f, 2.9520622449f, 2.7369202137f}, - {0.1132179294f, 0.1946659670f, 0.6001681264f}}}, - {"CuO", {{3.2453822204f, 2.4496293965f, 2.1974114493f}, - {0.5202739621f, 0.5707372756f, 0.7172250613f}}}, - {"d-C", {{2.7112524747f, 2.3185812849f, 2.2288565009f}, - {0.0000000000f, 0.0000000000f, 0.0000000000f}}}, - {"Hg", {{2.3989314904f, 1.4400254917f, 0.9095512090f}, - {6.3276269444f, 4.3719414152f, 3.4217899270f}}}, - {"HgTe", {{4.7795267752f, 3.2309984581f, 2.6600252401f}, - {1.6319827058f, 1.5808189339f, 1.7295753852f}}}, - {"Ir", {{3.0864098394f, 2.0821938440f, 1.6178866805f}, - {5.5921510077f, 4.0671757150f, 3.2672611269f}}}, - {"K", {{0.0640493070f, 0.0464100621f, 0.0381842017f}, - {2.1042155920f, 1.3489364357f, 0.9132113889f}}}, - {"Li", {{0.2657871942f, 0.1956102432f, 0.2209198538f}, - {3.5401743407f, 2.3111306542f, 1.6685930000f}}}, - {"MgO", {{2.0895885542f, 1.6507224525f, 1.5948759692f}, - {0.0000000000f, -0.0000000000f, 0.0000000000f}}}, - {"Mo", {{4.4837010280f, 3.5254578255f, 2.7760769438f}, - {4.1111307988f, 3.4208716252f, 3.1506031404f}}}, - {"Na", {{0.0602665320f, 0.0561412435f, 0.0619909494f}, - {3.1792906496f, 2.1124800781f, 1.5790940266f}}}, - {"Nb", {{3.4201353595f, 2.7901921379f, 2.3955856658f}, - {3.4413817900f, 2.7376437930f, 2.5799132708f}}}, - {"Ni", {{2.3672753521f, 1.6633583302f, 1.4670554172f}, - {4.4988329911f, 3.0501643957f, 2.3454274399f}}}, - {"Rh", {{2.5857954933f, 1.8601866068f, 1.5544279524f}, - {6.7822927110f, 4.7029501026f, 3.9760892461f}}}, - {"Se-e", {{5.7242724833f, 4.1653992967f, 4.0816099264f}, - {0.8713747439f, 1.1052845009f, 1.5647788766f}}}, - {"Se", {{4.0592611085f, 2.8426947380f, 2.8207582835f}, - {0.7543791750f, 0.6385150558f, 0.5215872029f}}}, - {"SiC", {{3.1723450205f, 2.5259677964f, 2.4793623897f}, - {0.0000007284f, -0.0000006859f, 0.0000100150f}}}, - {"SnTe", {{4.5251865890f, 1.9811525984f, 1.2816819226f}, - {0.0000000000f, 0.0000000000f, 0.0000000000f}}}, - {"Ta", {{2.0625846607f, 2.3930915569f, 2.6280684948f}, - {2.4080467973f, 1.7413705864f, 1.9470377016f}}}, - {"Te-e", {{7.5090397678f, 4.2964603080f, 2.3698732430f}, - {5.5842076830f, 4.9476231084f, 3.9975145063f}}}, - {"Te", {{7.3908396088f, 4.4821028985f, 2.6370708478f}, - {3.2561412892f, 3.5273908133f, 3.2921683116f}}}, - {"ThF4", {{1.8307187117f, 1.4422274283f, 1.3876488528f}, - {0.0000000000f, 0.0000000000f, 0.0000000000f}}}, - {"TiC", {{3.7004673762f, 2.8374356509f, 2.5823030278f}, - {3.2656905818f, 2.3515586388f, 2.1727857800f}}}, - {"TiN", {{1.6484691607f, 1.1504482522f, 1.3797795097f}, - {3.3684596226f, 1.9434888540f, 1.1020123347f}}}, - {"TiO2-e", {{3.1065574823f, 2.5131551146f, 2.5823844157f}, - {0.0000289537f, -0.0000251484f, 0.0001775555f}}}, - {"TiO2", {{3.4566203131f, 2.8017076558f, 2.9051485020f}, - {0.0001026662f, -0.0000897534f, 0.0006356902f}}}, - {"VC", {{3.6575665991f, 2.7527298065f, 2.5326814570f}, - {3.0683516659f, 2.1986687713f, 1.9631816252f}}}, - {"VN", {{2.8656011588f, 2.1191817791f, 1.9400767149f}, - {3.0323264950f, 2.0561075580f, 1.6162930914f}}}, - {"V", {{4.2775126218f, 3.5131538236f, 2.7611257461f}, - {3.4911844504f, 2.8893580874f, 3.1116965117f}}}, - {"W", {{4.3707029924f, 3.3002972445f, 2.9982666528f}, - {3.5006778591f, 2.6048652781f, 2.2731930614f}}}, - }; - for (auto& [ename, eta] : metal_ior_table) { - if (ename == name) return eta; - } - return {{0, 0, 0}, {0, 0, 0}}; -} - -#endif - -} // namespace yocto - -// ----------------------------------------------------------------------------- -// IMPLEMENTATION OF DEPRECATED SHADING FUNCTIONS -// ----------------------------------------------------------------------------- -namespace yocto { - -// Evaluates a metal BRDF lobe. -[[deprecated]] inline vec3f eval_metallic(vec3f color, float roughness, - vec3f normal, vec3f outgoing, vec3f incoming) { - return eval_reflective(color, roughness, normal, outgoing, incoming); -} -// Sample a metal BRDF lobe. -[[deprecated]] inline vec3f sample_metallic( - vec3f color, float roughness, vec3f normal, vec3f outgoing, vec2f rn) { - return sample_reflective(color, roughness, normal, outgoing, rn); -} -// Pdf for metal BRDF lobe sampling. -[[deprecated]] inline float sample_metallic_pdf(vec3f color, float roughness, - vec3f normal, vec3f outgoing, vec3f incoming) { - return sample_reflective_pdf(color, roughness, normal, outgoing, incoming); -} - -// Evaluate a delta metal BRDF lobe. -[[deprecated]] inline vec3f eval_metallic( - vec3f color, vec3f normal, vec3f outgoing, vec3f incoming) { - return eval_reflective(color, normal, outgoing, incoming); -} -// Sample a delta metal BRDF lobe. -[[deprecated]] inline vec3f sample_metallic( - vec3f color, vec3f normal, vec3f outgoing) { - return sample_reflective(color, normal, outgoing); -} -// Pdf for delta metal BRDF lobe sampling. -[[deprecated]] inline float sample_metallic_pdf( - vec3f color, vec3f normal, vec3f outgoing, vec3f incoming) { - return sample_reflective_pdf(color, normal, outgoing, incoming); -} - -// Evaluate a delta metal BRDF lobe. -[[deprecated]] inline vec3f eval_metallic( - vec3f eta, vec3f etak, vec3f normal, vec3f outgoing, vec3f incoming) { - return eval_reflective(eta, etak, normal, outgoing, incoming); -} -// Sample a delta metal BRDF lobe. -[[deprecated]] inline vec3f sample_metallic( - vec3f eta, vec3f etak, vec3f normal, vec3f outgoing) { - return sample_reflective(eta, etak, normal, outgoing); -} -// Pdf for delta metal BRDF lobe sampling. -[[deprecated]] inline float sample_metallic_pdf( - vec3f eta, vec3f etak, vec3f normal, vec3f outgoing, vec3f incoming) { - return sample_reflective_pdf(eta, etak, normal, outgoing, incoming); -} - -} // namespace yocto - -// ----------------------------------------------------------------------------- -// CUDA SUPPORT -// ----------------------------------------------------------------------------- -#ifdef __CUDACC__ -#undef inline -#endif - -#endif diff --git a/libs/yocto/yocto_trace.cpp b/libs/yocto/yocto_trace.cpp index b857377db..b6594bfe0 100644 --- a/libs/yocto/yocto_trace.cpp +++ b/libs/yocto/yocto_trace.cpp @@ -35,9 +35,6 @@ #include #include -#include "yocto_sampling.h" -#include "yocto_shading.h" - #ifdef YOCTO_DENOISE #include #endif diff --git a/libs/yocto/yocto_trace.h b/libs/yocto/yocto_trace.h index f47817795..2cd7a858b 100644 --- a/libs/yocto/yocto_trace.h +++ b/libs/yocto/yocto_trace.h @@ -47,7 +47,6 @@ #include "yocto_image.h" #include "yocto_math.h" #include "yocto_raytracing.h" -#include "yocto_sampling.h" #include "yocto_scene.h" // ----------------------------------------------------------------------------- @@ -238,6 +237,1132 @@ void trace_preview(image_t& image, trace_context& context, } // namespace yocto +// ----------------------------------------------------------------------------- +// CUDA SUPPORT +// ----------------------------------------------------------------------------- +#ifdef __CUDACC__ +#define inline inline __device__ __forceinline__ +#endif + +// ----------------------------------------------------------------------------- +// SHADING FUNCTIONS +// ----------------------------------------------------------------------------- +namespace yocto { + +// Check if on the same side of the hemisphere +inline bool same_hemisphere(vec3f normal, vec3f outgoing, vec3f incoming); + +// Schlick approximation of the Fresnel term. +inline vec3f fresnel_schlick(vec3f specular, vec3f normal, vec3f outgoing); +// Compute the fresnel term for dielectrics. +inline float fresnel_dielectric(float eta, vec3f normal, vec3f outgoing); +// Compute the fresnel term for metals. +inline vec3f fresnel_conductor( + vec3f eta, vec3f etak, vec3f normal, vec3f outgoing); + +// Convert eta to reflectivity +inline vec3f eta_to_reflectivity(vec3f eta); +// Convert reflectivity to eta. +inline vec3f reflectivity_to_eta(vec3f reflectivity); +// Convert conductor eta to reflectivity. +inline vec3f eta_to_reflectivity(vec3f eta, vec3f etak); +// Convert eta to edge tint parametrization. +inline pair eta_to_edgetint(vec3f eta, vec3f etak); +// Convert reflectivity and edge tint to eta. +inline pair edgetint_to_eta(vec3f reflectivity, vec3f edgetint); + +// Get tabulated ior for conductors +inline pair conductor_eta(const string& name); + +// Evaluates the microfacet distribution. +inline float microfacet_distribution( + float roughness, vec3f normal, vec3f halfway, bool ggx = true); +// Evaluates the microfacet shadowing. +inline float microfacet_shadowing(float roughness, vec3f normal, vec3f halfway, + vec3f outgoing, vec3f incoming, bool ggx = true); + +// Samples a microfacet distribution. +inline vec3f sample_microfacet( + float roughness, vec3f normal, vec2f rn, bool ggx = true); +// Pdf for microfacet distribution sampling. +inline float sample_microfacet_pdf( + float roughness, vec3f normal, vec3f halfway, bool ggx = true); + +// Samples a microfacet distribution with the distribution of visible normals. +inline vec3f sample_microfacet( + float roughness, vec3f normal, vec3f outgoing, vec2f rn, bool ggx = true); +// Pdf for microfacet distribution sampling with the distribution of visible +// normals. +inline float sample_microfacet_pdf(float roughness, vec3f normal, vec3f halfway, + vec3f outgoing, bool ggx = true); + +// Microfacet energy compensation (E(cos(w))) +inline float microfacet_cosintegral( + float roughness, vec3f normal, vec3f outgoing); +// Approximate microfacet compensation for metals with Schlick's Fresnel +inline vec3f microfacet_compensation( + vec3f color, float roughness, vec3f normal, vec3f outgoing); + +// Evaluates a diffuse BRDF lobe. +inline vec3f eval_matte( + vec3f color, vec3f normal, vec3f outgoing, vec3f incoming); +// Sample a diffuse BRDF lobe. +inline vec3f sample_matte(vec3f color, vec3f normal, vec3f outgoing, vec2f rn); +// Pdf for diffuse BRDF lobe sampling. +inline float sample_matte_pdf( + vec3f color, vec3f normal, vec3f outgoing, vec3f incoming); + +// Evaluates a specular BRDF lobe. +inline vec3f eval_glossy(vec3f color, float ior, float roughness, vec3f normal, + vec3f outgoing, vec3f incoming); +// Sample a specular BRDF lobe. +inline vec3f sample_glossy(vec3f color, float ior, float roughness, + vec3f normal, vec3f outgoing, vec2f rn); +// Pdf for specular BRDF lobe sampling. +inline float sample_glossy_pdf(vec3f color, float ior, float roughness, + vec3f normal, vec3f outgoing, vec3f incoming); + +// Evaluates a metal BRDF lobe. +inline vec3f eval_reflective( + vec3f color, float roughness, vec3f normal, vec3f outgoing, vec3f incoming); +// Sample a metal BRDF lobe. +inline vec3f sample_reflective( + vec3f color, float roughness, vec3f normal, vec3f outgoing, vec2f rn); +// Pdf for metal BRDF lobe sampling. +inline float sample_reflective_pdf( + vec3f color, float roughness, vec3f normal, vec3f outgoing, vec3f incoming); + +// Evaluate a delta metal BRDF lobe. +inline vec3f eval_reflective( + vec3f color, vec3f normal, vec3f outgoing, vec3f incoming); +// Sample a delta metal BRDF lobe. +inline vec3f sample_reflective(vec3f color, vec3f normal, vec3f outgoing); +// Pdf for delta metal BRDF lobe sampling. +inline float sample_reflective_pdf( + vec3f color, vec3f normal, vec3f outgoing, vec3f incoming); + +// Evaluate a delta metal BRDF lobe. +inline vec3f eval_reflective( + vec3f eta, vec3f etak, vec3f normal, vec3f outgoing, vec3f incoming); +// Sample a delta metal BRDF lobe. +inline vec3f sample_reflective( + vec3f eta, vec3f etak, vec3f normal, vec3f outgoing); +// Pdf for delta metal BRDF lobe sampling. +inline float sample_reflective_pdf( + vec3f eta, vec3f etak, vec3f normal, vec3f outgoing, vec3f incoming); + +// Evaluate a delta metal BRDF lobe. +inline vec3f eval_reflective( + vec3f eta, vec3f etak, vec3f normal, vec3f outgoing, vec3f incoming); +// Sample a delta metal BRDF lobe. +inline vec3f sample_reflective( + vec3f eta, vec3f etak, vec3f normal, vec3f outgoing); +// Pdf for delta metal BRDF lobe sampling. +inline float sample_reflective_pdf( + vec3f eta, vec3f etak, vec3f normal, vec3f outgoing, vec3f incoming); + +// Evaluates a specular BRDF lobe. +inline vec3f eval_gltfpbr(vec3f color, float ior, float roughness, + float metallic, vec3f normal, vec3f outgoing, vec3f incoming); +// Sample a specular BRDF lobe. +inline vec3f sample_gltfpbr(vec3f color, float ior, float roughness, + float metallic, vec3f normal, vec3f outgoing, float rnl, vec2f rn); +// Pdf for specular BRDF lobe sampling. +inline float sample_gltfpbr_pdf(vec3f color, float ior, float roughness, + float metallic, vec3f normal, vec3f outgoing, vec3f incoming); + +// Evaluates a transmission BRDF lobe. +inline vec3f eval_transparent(vec3f color, float ior, float roughness, + vec3f normal, vec3f outgoing, vec3f incoming); +// Sample a transmission BRDF lobe. +inline vec3f sample_transparent(float ior, float roughness, vec3f normal, + vec3f outgoing, float rnl, vec2f rn); +// Pdf for transmission BRDF lobe sampling. +inline float sample_tranparent_pdf(vec3f color, float ior, float roughness, + vec3f normal, vec3f outgoing, vec3f incoming); + +// Evaluate a delta transmission BRDF lobe. +inline vec3f eval_transparent( + vec3f color, float ior, vec3f normal, vec3f outgoing, vec3f incoming); +// Sample a delta transmission BRDF lobe. +inline vec3f sample_transparent( + vec3f color, float ior, vec3f normal, vec3f outgoing, float rnl); +// Pdf for delta transmission BRDF lobe sampling. +inline float sample_tranparent_pdf( + vec3f color, float ior, vec3f normal, vec3f outgoing, vec3f incoming); + +// Evaluates a refraction BRDF lobe. +inline vec3f eval_refractive(vec3f color, float ior, float roughness, + vec3f normal, vec3f outgoing, vec3f incoming); +// Sample a refraction BRDF lobe. +inline vec3f sample_refractive(float ior, float roughness, vec3f normal, + vec3f outgoing, float rnl, vec2f rn); +// Pdf for refraction BRDF lobe sampling. +inline float sample_refractive_pdf(vec3f color, float ior, float roughness, + vec3f normal, vec3f outgoing, vec3f incoming); + +// Evaluate a delta refraction BRDF lobe. +inline vec3f eval_refractive( + vec3f color, float ior, vec3f normal, vec3f outgoing, vec3f incoming); +// Sample a delta refraction BRDF lobe. +inline vec3f sample_refractive( + vec3f color, float ior, vec3f normal, vec3f outgoing, float rnl); +// Pdf for delta refraction BRDF lobe sampling. +inline float sample_refractive_pdf( + vec3f color, float ior, vec3f normal, vec3f outgoing, vec3f incoming); + +// Evaluate a translucent BRDF lobe. +inline vec3f eval_translucent( + vec3f color, vec3f normal, vec3f outgoing, vec3f incoming); +// Pdf for translucency BRDF lobe sampling. +inline float sample_translucent_pdf( + vec3f color, vec3f normal, vec3f outgoing, vec3f incoming); +// Sample a translucency BRDF lobe. +inline vec3f sample_translucent( + vec3f color, vec3f normal, vec3f outgoing, vec2f rn); + +// Evaluate a passthrough BRDF lobe. +inline vec3f eval_passthrough( + vec3f color, vec3f normal, vec3f outgoing, vec3f incoming); +// Sample a passthrough BRDF lobe. +inline vec3f sample_passthrough(vec3f color, vec3f normal, vec3f outgoing); +// Pdf for passthrough BRDF lobe sampling. +inline float sample_passthrough_pdf( + vec3f color, vec3f normal, vec3f outgoing, vec3f incoming); + +// Convert mean-free-path to transmission +inline vec3f mfp_to_transmission(vec3f mfp, float depth); + +// Evaluate transmittance +inline vec3f eval_transmittance(vec3f density, float distance); +// Sample a distance proportionally to transmittance +inline float sample_transmittance( + vec3f density, float max_distance, float rl, float rd); +// Pdf for distance sampling +inline float sample_transmittance_pdf( + vec3f density, float distance, float max_distance); + +// Evaluate phase function +inline float eval_phasefunction( + float anisotropy, vec3f outgoing, vec3f incoming); +// Sample phase function +inline vec3f sample_phasefunction(float anisotropy, vec3f outgoing, vec2f rn); +// Pdf for phase function sampling +inline float sample_phasefunction_pdf( + float anisotropy, vec3f outgoing, vec3f incoming); + +} // namespace yocto + +// ----------------------------------------------------------------------------- +// +// +// IMPLEMENTATION +// +// +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// IMPLEMENTATION OF SHADING FUNCTIONS +// ----------------------------------------------------------------------------- +namespace yocto { + +// Check if on the same side of the hemisphere +inline bool same_hemisphere(vec3f normal, vec3f outgoing, vec3f incoming) { + return dot(normal, outgoing) * dot(normal, incoming) >= 0; +} + +// Schlick approximation of the Fresnel term +inline vec3f fresnel_schlick(vec3f specular, vec3f normal, vec3f outgoing) { + if (specular == vec3f{0, 0, 0}) return {0, 0, 0}; + auto cosine = dot(normal, outgoing); + return specular + + (1 - specular) * pow(clamp(1 - abs(cosine), 0.0f, 1.0f), 5.0f); +} + +// Compute the fresnel term for dielectrics. +inline float fresnel_dielectric(float eta, vec3f normal, vec3f outgoing) { + // Implementation from + // https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/ + auto cosw = abs(dot(normal, outgoing)); + + auto sin2 = 1 - cosw * cosw; + auto eta2 = eta * eta; + + auto cos2t = 1 - sin2 / eta2; + if (cos2t < 0) return 1; // tir + + auto t0 = sqrt(cos2t); + auto t1 = eta * t0; + auto t2 = eta * cosw; + + auto rs = (cosw - t1) / (cosw + t1); + auto rp = (t0 - t2) / (t0 + t2); + + return (rs * rs + rp * rp) / 2; +} + +// Compute the fresnel term for metals. +inline vec3f fresnel_conductor( + vec3f eta, vec3f etak, vec3f normal, vec3f outgoing) { + // Implementation from + // https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/ + auto cosw = dot(normal, outgoing); + if (cosw <= 0) return {0, 0, 0}; + + cosw = clamp(cosw, (float)-1, (float)1); + auto cos2 = cosw * cosw; + auto sin2 = clamp(1 - cos2, (float)0, (float)1); + auto eta2 = eta * eta; + auto etak2 = etak * etak; + + auto t0 = eta2 - etak2 - sin2; + auto a2plusb2 = sqrt(t0 * t0 + 4 * eta2 * etak2); + auto t1 = a2plusb2 + cos2; + auto a = sqrt((a2plusb2 + t0) / 2); + auto t2 = 2 * a * cosw; + auto rs = (t1 - t2) / (t1 + t2); + + auto t3 = cos2 * a2plusb2 + sin2 * sin2; + auto t4 = t2 * sin2; + auto rp = rs * (t3 - t4) / (t3 + t4); + + return (rp + rs) / 2; +} + +// Convert eta to reflectivity +inline vec3f eta_to_reflectivity(vec3f eta) { + return ((eta - 1) * (eta - 1)) / ((eta + 1) * (eta + 1)); +} +// Convert reflectivity to eta. +inline vec3f reflectivity_to_eta(vec3f reflectivity_) { + auto reflectivity = clamp(reflectivity_, 0.0f, 0.99f); + return (1 + sqrt(reflectivity)) / (1 - sqrt(reflectivity)); +} +// Convert conductor eta to reflectivity +inline vec3f eta_to_reflectivity(vec3f eta, vec3f etak) { + return ((eta - 1) * (eta - 1) + etak * etak) / + ((eta + 1) * (eta + 1) + etak * etak); +} +// Convert eta to edge tint parametrization +inline pair eta_to_edgetint(vec3f eta, vec3f etak) { + auto reflectivity = eta_to_reflectivity(eta, etak); + auto numer = (1 + sqrt(reflectivity)) / (1 - sqrt(reflectivity)) - eta; + auto denom = (1 + sqrt(reflectivity)) / (1 - sqrt(reflectivity)) - + (1 - reflectivity) / (1 + reflectivity); + auto edgetint = numer / denom; + return {reflectivity, edgetint}; +} +// Convert reflectivity and edge tint to eta. +inline pair edgetint_to_eta(vec3f reflectivity, vec3f edgetint) { + auto r = clamp(reflectivity, 0.0f, 0.99f); + auto g = edgetint; + + auto r_sqrt = sqrt(r); + auto n_min = (1 - r) / (1 + r); + auto n_max = (1 + r_sqrt) / (1 - r_sqrt); + + auto n = lerp(n_max, n_min, g); + auto k2 = ((n + 1) * (n + 1) * r - (n - 1) * (n - 1)) / (1 - r); + k2 = max(k2, 0.0f); + auto k = sqrt(k2); + return {n, k}; +} + +// Evaluate microfacet distribution +inline float microfacet_distribution( + float roughness, vec3f normal, vec3f halfway, bool ggx) { + // https://google.github.io/filament/Filament.html#materialsystem/specularbrdf + // http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html + auto cosine = dot(normal, halfway); + if (cosine <= 0) return 0; + auto roughness2 = roughness * roughness; + auto cosine2 = cosine * cosine; + if (ggx) { + return roughness2 / (pif * (cosine2 * roughness2 + 1 - cosine2) * + (cosine2 * roughness2 + 1 - cosine2)); + } else { + return exp((cosine2 - 1) / (roughness2 * cosine2)) / + (pif * roughness2 * cosine2 * cosine2); + } +} + +// Evaluate the microfacet shadowing1 +inline float microfacet_shadowing1( + float roughness, vec3f normal, vec3f halfway, vec3f direction, bool ggx) { + // https://google.github.io/filament/Filament.html#materialsystem/specularbrdf + // http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html + // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#appendix-b-brdf-implementation + auto cosine = dot(normal, direction); + auto cosineh = dot(halfway, direction); + if (cosine * cosineh <= 0) return 0; + auto roughness2 = roughness * roughness; + auto cosine2 = cosine * cosine; + if (ggx) { + return 2 * abs(cosine) / + (abs(cosine) + sqrt(cosine2 - roughness2 * cosine2 + roughness2)); + } else { + auto ci = abs(cosine) / (roughness * sqrt(1 - cosine2)); + return ci < 1.6f ? (3.535f * ci + 2.181f * ci * ci) / + (1.0f + 2.276f * ci + 2.577f * ci * ci) + : 1.0f; + } +} + +// Evaluate microfacet shadowing +inline float microfacet_shadowing(float roughness, vec3f normal, vec3f halfway, + vec3f outgoing, vec3f incoming, bool ggx) { + return microfacet_shadowing1(roughness, normal, halfway, outgoing, ggx) * + microfacet_shadowing1(roughness, normal, halfway, incoming, ggx); +} + +// Sample a microfacet distribution. +inline vec3f sample_microfacet( + float roughness, vec3f normal, vec2f rn, bool ggx) { + auto phi = 2 * pif * rn.x; + auto theta = 0.0f; + if (ggx) { + theta = atan(roughness * sqrt(rn.y / (1 - rn.y))); + } else { + auto roughness2 = roughness * roughness; + theta = atan(sqrt(-roughness2 * log(1 - rn.y))); + } + auto local_half_vector = vec3f{ + cos(phi) * sin(theta), sin(phi) * sin(theta), cos(theta)}; + return transform_direction(basis_fromz(normal), local_half_vector); +} + +// Pdf for microfacet distribution sampling. +inline float sample_microfacet_pdf( + float roughness, vec3f normal, vec3f halfway, bool ggx) { + auto cosine = dot(normal, halfway); + if (cosine < 0) return 0; + return microfacet_distribution(roughness, normal, halfway, ggx) * cosine; +} + +// Sample a microfacet distribution with the distribution of visible normals. +inline vec3f sample_microfacet( + float roughness, vec3f normal, vec3f outgoing, vec2f rn, bool ggx) { + // http://jcgt.org/published/0007/04/01/ + if (ggx) { + // move to local coordinate system + auto basis = basis_fromz(normal); + auto Ve = transform_direction(transpose(basis), outgoing); + auto alpha_x = roughness, alpha_y = roughness; + // Section 3.2: transforming the view direction to the hemisphere + // configuration + auto Vh = normalize(vec3f{alpha_x * Ve.x, alpha_y * Ve.y, Ve.z}); + // Section 4.1: orthonormal basis (with special case if cross product is + // zero) + auto lensq = Vh.x * Vh.x + Vh.y * Vh.y; + auto T1 = lensq > 0 ? vec3f{-Vh.y, Vh.x, 0} * (1 / sqrt(lensq)) + : vec3f{1, 0, 0}; + auto T2 = cross(Vh, T1); + // Section 4.2: parameterization of the projected area + auto r = sqrt(rn.y), phi = 2 * pif * rn.x; + auto t1 = r * cos(phi), t2 = r * sin(phi); + auto s = 0.5f * (1 + Vh.z); + t2 = (1 - s) * sqrt(1 - t1 * t1) + s * t2; + // Section 4.3: reprojection onto hemisphere + auto Nh = t1 * T1 + t2 * T2 + sqrt(max(0.0f, 1 - t1 * t1 - t2 * t2)) * Vh; + // Section 3.4: transforming the normal back to the ellipsoid configuration + auto Ne = normalize(vec3f{alpha_x * Nh.x, alpha_y * Nh.y, max(0.0f, Nh.z)}); + // move to world coordinate + auto local_halfway = Ne; + return transform_direction(basis, local_halfway); + } else { +#ifndef __CUDACC__ + throw std::invalid_argument{"not implemented yet"}; +#else + return {0, 0, 0}; +#endif + } +} + +// Pdf for microfacet distribution sampling with the distribution of visible +// normals. +inline float sample_microfacet_pdf( + float roughness, vec3f normal, vec3f halfway, vec3f outgoing, bool ggx) { + // http://jcgt.org/published/0007/04/01/ + if (dot(normal, halfway) < 0) return 0; + if (dot(halfway, outgoing) < 0) return 0; + return microfacet_distribution(roughness, normal, halfway, ggx) * + microfacet_shadowing1(roughness, normal, halfway, outgoing, ggx) * + max(0.0f, dot(halfway, outgoing)) / abs(dot(normal, outgoing)); +} + +// Microfacet energy compensation (E(cos(w))) +inline float microfacet_cosintegral( + float roughness, vec3f normal, vec3f outgoing) { + // https://blog.selfshadow.com/publications/s2017-shading-course/imageworks/s2017_pbs_imageworks_slides_v2.pdf + const float S[5] = {-0.170718f, 4.07985f, -11.5295f, 18.4961f, -9.23618f}; + const float T[5] = {0.0632331f, 3.1434f, -7.47567f, 13.0482f, -7.0401f}; + auto m = abs(dot(normal, outgoing)); + auto r = roughness; + auto s = S[0] * sqrt(m) + S[1] * r + S[2] * r * r + S[3] * r * r * r + + S[4] * r * r * r * r; + auto t = T[0] * m + T[1] * r + T[2] * r * r + T[3] * r * r * r + + T[4] * r * r * r * r; + return 1 - pow(s, 6.0f) * pow(m, 3.0f / 4.0f) / (pow(t, 6.0f) + pow(m, 2.0f)); +} +// Approximate microfacet compensation for metals with Schlick's Fresnel +inline vec3f microfacet_compensation( + vec3f color, float roughness, vec3f normal, vec3f outgoing) { + // https://blog.selfshadow.com/publications/turquin/ms_comp_final.pdf + auto E = microfacet_cosintegral(sqrt(roughness), normal, outgoing); + return 1 + color * (1 - E) / E; +} + +// Evaluate a diffuse BRDF lobe. +inline vec3f eval_matte( + vec3f color, vec3f normal, vec3f outgoing, vec3f incoming) { + if (dot(normal, incoming) * dot(normal, outgoing) <= 0) return {0, 0, 0}; + return color / pif * abs(dot(normal, incoming)); +} + +// Sample a diffuse BRDF lobe. +inline vec3f sample_matte(vec3f color, vec3f normal, vec3f outgoing, vec2f rn) { + auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; + return sample_hemisphere_cos(up_normal, rn); +} + +// Pdf for diffuse BRDF lobe sampling. +inline float sample_matte_pdf( + vec3f color, vec3f normal, vec3f outgoing, vec3f incoming) { + if (dot(normal, incoming) * dot(normal, outgoing) <= 0) return 0; + auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; + return sample_hemisphere_cos_pdf(up_normal, incoming); +} + +// Evaluate a specular BRDF lobe. +inline vec3f eval_glossy(vec3f color, float ior, float roughness, vec3f normal, + vec3f outgoing, vec3f incoming) { + if (dot(normal, incoming) * dot(normal, outgoing) <= 0) return {0, 0, 0}; + auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; + auto F1 = fresnel_dielectric(ior, up_normal, outgoing); + auto halfway = normalize(incoming + outgoing); + auto F = fresnel_dielectric(ior, halfway, incoming); + auto D = microfacet_distribution(roughness, up_normal, halfway); + auto G = microfacet_shadowing( + roughness, up_normal, halfway, outgoing, incoming); + return color * (1 - F1) / pif * abs(dot(up_normal, incoming)) + + vec3f{1, 1, 1} * F * D * G / + (4 * dot(up_normal, outgoing) * dot(up_normal, incoming)) * + abs(dot(up_normal, incoming)); +} + +// Sample a specular BRDF lobe. +inline vec3f sample_glossy(vec3f color, float ior, float roughness, + vec3f normal, vec3f outgoing, float rnl, vec2f rn) { + auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; + if (rnl < fresnel_dielectric(ior, up_normal, outgoing)) { + auto halfway = sample_microfacet(roughness, up_normal, rn); + auto incoming = reflect(outgoing, halfway); + if (!same_hemisphere(up_normal, outgoing, incoming)) return {0, 0, 0}; + return incoming; + } else { + return sample_hemisphere_cos(up_normal, rn); + } +} + +// Pdf for specular BRDF lobe sampling. +inline float sample_glossy_pdf(vec3f color, float ior, float roughness, + vec3f normal, vec3f outgoing, vec3f incoming) { + if (dot(normal, incoming) * dot(normal, outgoing) <= 0) return 0; + auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; + auto halfway = normalize(outgoing + incoming); + auto F = fresnel_dielectric(ior, up_normal, outgoing); + return F * sample_microfacet_pdf(roughness, up_normal, halfway) / + (4 * abs(dot(outgoing, halfway))) + + (1 - F) * sample_hemisphere_cos_pdf(up_normal, incoming); +} + +// Evaluate a metal BRDF lobe. +inline vec3f eval_reflective(vec3f color, float roughness, vec3f normal, + vec3f outgoing, vec3f incoming) { + if (dot(normal, incoming) * dot(normal, outgoing) <= 0) return {0, 0, 0}; + auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; + auto halfway = normalize(incoming + outgoing); + auto F = fresnel_conductor( + reflectivity_to_eta(color), {0, 0, 0}, halfway, incoming); + auto D = microfacet_distribution(roughness, up_normal, halfway); + auto G = microfacet_shadowing( + roughness, up_normal, halfway, outgoing, incoming); + return F * D * G / (4 * dot(up_normal, outgoing) * dot(up_normal, incoming)) * + abs(dot(up_normal, incoming)); +} + +// Sample a metal BRDF lobe. +inline vec3f sample_reflective( + vec3f color, float roughness, vec3f normal, vec3f outgoing, vec2f rn) { + auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; + auto halfway = sample_microfacet(roughness, up_normal, rn); + auto incoming = reflect(outgoing, halfway); + if (!same_hemisphere(up_normal, outgoing, incoming)) return {0, 0, 0}; + return incoming; +} + +// Pdf for metal BRDF lobe sampling. +inline float sample_reflective_pdf(vec3f color, float roughness, vec3f normal, + vec3f outgoing, vec3f incoming) { + if (dot(normal, incoming) * dot(normal, outgoing) <= 0) return 0; + auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; + auto halfway = normalize(outgoing + incoming); + return sample_microfacet_pdf(roughness, up_normal, halfway) / + (4 * abs(dot(outgoing, halfway))); +} + +// Evaluate a metal BRDF lobe. +inline vec3f eval_reflective(vec3f eta, vec3f etak, float roughness, + vec3f normal, vec3f outgoing, vec3f incoming) { + if (dot(normal, incoming) * dot(normal, outgoing) <= 0) return {0, 0, 0}; + auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; + auto halfway = normalize(incoming + outgoing); + auto F = fresnel_conductor(eta, etak, halfway, incoming); + auto D = microfacet_distribution(roughness, up_normal, halfway); + auto G = microfacet_shadowing( + roughness, up_normal, halfway, outgoing, incoming); + return F * D * G / (4 * dot(up_normal, outgoing) * dot(up_normal, incoming)) * + abs(dot(up_normal, incoming)); +} + +// Sample a metal BRDF lobe. +inline vec3f sample_reflective(vec3f eta, vec3f etak, float roughness, + vec3f normal, vec3f outgoing, vec2f rn) { + auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; + auto halfway = sample_microfacet(roughness, up_normal, rn); + return reflect(outgoing, halfway); +} + +// Pdf for metal BRDF lobe sampling. +inline float sample_reflective_pdf(vec3f eta, vec3f etak, float roughness, + vec3f normal, vec3f outgoing, vec3f incoming) { + if (dot(normal, incoming) * dot(normal, outgoing) <= 0) return 0; + auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; + auto halfway = normalize(outgoing + incoming); + return sample_microfacet_pdf(roughness, up_normal, halfway) / + (4 * abs(dot(outgoing, halfway))); +} + +// Evaluate a delta metal BRDF lobe. +inline vec3f eval_reflective( + vec3f color, vec3f normal, vec3f outgoing, vec3f incoming) { + if (dot(normal, incoming) * dot(normal, outgoing) <= 0) return {0, 0, 0}; + auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; + return fresnel_conductor( + reflectivity_to_eta(color), {0, 0, 0}, up_normal, outgoing); +} + +// Sample a delta metal BRDF lobe. +inline vec3f sample_reflective(vec3f color, vec3f normal, vec3f outgoing) { + auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; + return reflect(outgoing, up_normal); +} + +// Pdf for delta metal BRDF lobe sampling. +inline float sample_reflective_pdf( + vec3f color, vec3f normal, vec3f outgoing, vec3f incoming) { + if (dot(normal, incoming) * dot(normal, outgoing) <= 0) return 0; + return 1; +} + +// Evaluate a delta metal BRDF lobe. +inline vec3f eval_reflective( + vec3f eta, vec3f etak, vec3f normal, vec3f outgoing, vec3f incoming) { + if (dot(normal, incoming) * dot(normal, outgoing) <= 0) return {0, 0, 0}; + auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; + return fresnel_conductor(eta, etak, up_normal, outgoing); +} + +// Sample a delta metal BRDF lobe. +inline vec3f sample_reflective( + vec3f eta, vec3f etak, vec3f normal, vec3f outgoing) { + auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; + return reflect(outgoing, up_normal); +} + +// Pdf for delta metal BRDF lobe sampling. +inline float sample_reflective_pdf( + vec3f eta, vec3f etak, vec3f normal, vec3f outgoing, vec3f incoming) { + if (dot(normal, incoming) * dot(normal, outgoing) <= 0) return 0; + return 1; +} + +// Evaluate a specular BRDF lobe. +inline vec3f eval_gltfpbr(vec3f color, float ior, float roughness, + float metallic, vec3f normal, vec3f outgoing, vec3f incoming) { + if (dot(normal, incoming) * dot(normal, outgoing) <= 0) return {0, 0, 0}; + auto reflectivity = lerp( + eta_to_reflectivity(vec3f{ior, ior, ior}), color, metallic); + auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; + auto F1 = fresnel_schlick(reflectivity, up_normal, outgoing); + auto halfway = normalize(incoming + outgoing); + auto F = fresnel_schlick(reflectivity, halfway, incoming); + auto D = microfacet_distribution(roughness, up_normal, halfway); + auto G = microfacet_shadowing( + roughness, up_normal, halfway, outgoing, incoming); + return color * (1 - metallic) * (1 - F1) / pif * + abs(dot(up_normal, incoming)) + + F * D * G / (4 * dot(up_normal, outgoing) * dot(up_normal, incoming)) * + abs(dot(up_normal, incoming)); +} + +// Sample a specular BRDF lobe. +inline vec3f sample_gltfpbr(vec3f color, float ior, float roughness, + float metallic, vec3f normal, vec3f outgoing, float rnl, vec2f rn) { + auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; + auto reflectivity = lerp( + eta_to_reflectivity(vec3f{ior, ior, ior}), color, metallic); + if (rnl < mean(fresnel_schlick(reflectivity, up_normal, outgoing))) { + auto halfway = sample_microfacet(roughness, up_normal, rn); + auto incoming = reflect(outgoing, halfway); + if (!same_hemisphere(up_normal, outgoing, incoming)) return {0, 0, 0}; + return incoming; + } else { + return sample_hemisphere_cos(up_normal, rn); + } +} + +// Pdf for specular BRDF lobe sampling. +inline float sample_gltfpbr_pdf(vec3f color, float ior, float roughness, + float metallic, vec3f normal, vec3f outgoing, vec3f incoming) { + if (dot(normal, incoming) * dot(normal, outgoing) <= 0) return 0; + auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; + auto halfway = normalize(outgoing + incoming); + auto reflectivity = lerp( + eta_to_reflectivity(vec3f{ior, ior, ior}), color, metallic); + auto F = mean(fresnel_schlick(reflectivity, up_normal, outgoing)); + return F * sample_microfacet_pdf(roughness, up_normal, halfway) / + (4 * abs(dot(outgoing, halfway))) + + (1 - F) * sample_hemisphere_cos_pdf(up_normal, incoming); +} + +// Evaluate a transmission BRDF lobe. +inline vec3f eval_transparent(vec3f color, float ior, float roughness, + vec3f normal, vec3f outgoing, vec3f incoming) { + auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; + if (dot(normal, incoming) * dot(normal, outgoing) >= 0) { + auto halfway = normalize(incoming + outgoing); + auto F = fresnel_dielectric(ior, halfway, outgoing); + auto D = microfacet_distribution(roughness, up_normal, halfway); + auto G = microfacet_shadowing( + roughness, up_normal, halfway, outgoing, incoming); + return vec3f{1, 1, 1} * F * D * G / + (4 * dot(up_normal, outgoing) * dot(up_normal, incoming)) * + abs(dot(up_normal, incoming)); + } else { + auto reflected = reflect(-incoming, up_normal); + auto halfway = normalize(reflected + outgoing); + auto F = fresnel_dielectric(ior, halfway, outgoing); + auto D = microfacet_distribution(roughness, up_normal, halfway); + auto G = microfacet_shadowing( + roughness, up_normal, halfway, outgoing, reflected); + return color * (1 - F) * D * G / + (4 * dot(up_normal, outgoing) * dot(up_normal, reflected)) * + (abs(dot(up_normal, reflected))); + } +} + +// Sample a transmission BRDF lobe. +inline vec3f sample_transparent(vec3f color, float ior, float roughness, + vec3f normal, vec3f outgoing, float rnl, vec2f rn) { + auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; + auto halfway = sample_microfacet(roughness, up_normal, rn); + if (rnl < fresnel_dielectric(ior, halfway, outgoing)) { + auto incoming = reflect(outgoing, halfway); + if (!same_hemisphere(up_normal, outgoing, incoming)) return {0, 0, 0}; + return incoming; + } else { + auto reflected = reflect(outgoing, halfway); + auto incoming = -reflect(reflected, up_normal); + if (same_hemisphere(up_normal, outgoing, incoming)) return {0, 0, 0}; + return incoming; + } +} + +// Pdf for transmission BRDF lobe sampling. +inline float sample_tranparent_pdf(vec3f color, float ior, float roughness, + vec3f normal, vec3f outgoing, vec3f incoming) { + auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; + if (dot(normal, incoming) * dot(normal, outgoing) >= 0) { + auto halfway = normalize(incoming + outgoing); + return fresnel_dielectric(ior, halfway, outgoing) * + sample_microfacet_pdf(roughness, up_normal, halfway) / + (4 * abs(dot(outgoing, halfway))); + } else { + auto reflected = reflect(-incoming, up_normal); + auto halfway = normalize(reflected + outgoing); + auto d = (1 - fresnel_dielectric(ior, halfway, outgoing)) * + sample_microfacet_pdf(roughness, up_normal, halfway); + return d / (4 * abs(dot(outgoing, halfway))); + } +} + +// Evaluate a delta transmission BRDF lobe. +inline vec3f eval_transparent( + vec3f color, float ior, vec3f normal, vec3f outgoing, vec3f incoming) { + auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; + if (dot(normal, incoming) * dot(normal, outgoing) >= 0) { + return vec3f{1, 1, 1} * fresnel_dielectric(ior, up_normal, outgoing); + } else { + return color * (1 - fresnel_dielectric(ior, up_normal, outgoing)); + } +} + +// Sample a delta transmission BRDF lobe. +inline vec3f sample_transparent( + vec3f color, float ior, vec3f normal, vec3f outgoing, float rnl) { + auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; + if (rnl < fresnel_dielectric(ior, up_normal, outgoing)) { + return reflect(outgoing, up_normal); + } else { + return -outgoing; + } +} + +// Pdf for delta transmission BRDF lobe sampling. +inline float sample_tranparent_pdf( + vec3f color, float ior, vec3f normal, vec3f outgoing, vec3f incoming) { + auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; + if (dot(normal, incoming) * dot(normal, outgoing) >= 0) { + return fresnel_dielectric(ior, up_normal, outgoing); + } else { + return 1 - fresnel_dielectric(ior, up_normal, outgoing); + } +} + +// Evaluate a refraction BRDF lobe. +inline vec3f eval_refractive(vec3f color, float ior, float roughness, + vec3f normal, vec3f outgoing, vec3f incoming) { + auto entering = dot(normal, outgoing) >= 0; + auto up_normal = entering ? normal : -normal; + auto rel_ior = entering ? ior : (1 / ior); + if (dot(normal, incoming) * dot(normal, outgoing) >= 0) { + auto halfway = normalize(incoming + outgoing); + auto F = fresnel_dielectric(rel_ior, halfway, outgoing); + auto D = microfacet_distribution(roughness, up_normal, halfway); + auto G = microfacet_shadowing( + roughness, up_normal, halfway, outgoing, incoming); + return vec3f{1, 1, 1} * F * D * G / + abs(4 * dot(normal, outgoing) * dot(normal, incoming)) * + abs(dot(normal, incoming)); + } else { + auto halfway = -normalize(rel_ior * incoming + outgoing) * + (entering ? 1.0f : -1.0f); + auto F = fresnel_dielectric(rel_ior, halfway, outgoing); + auto D = microfacet_distribution(roughness, up_normal, halfway); + auto G = microfacet_shadowing( + roughness, up_normal, halfway, outgoing, incoming); + // [Walter 2007] equation 21 + return vec3f{1, 1, 1} * + abs((dot(outgoing, halfway) * dot(incoming, halfway)) / + (dot(outgoing, normal) * dot(incoming, normal))) * + (1 - F) * D * G / + pow(rel_ior * dot(halfway, incoming) + dot(halfway, outgoing), + 2.0f) * + abs(dot(normal, incoming)); + } +} + +// Sample a refraction BRDF lobe. +inline vec3f sample_refractive(vec3f color, float ior, float roughness, + vec3f normal, vec3f outgoing, float rnl, vec2f rn) { + auto entering = dot(normal, outgoing) >= 0; + auto up_normal = entering ? normal : -normal; + auto halfway = sample_microfacet(roughness, up_normal, rn); + // auto halfway = sample_microfacet(roughness, up_normal, outgoing, rn); + if (rnl < fresnel_dielectric(entering ? ior : (1 / ior), halfway, outgoing)) { + auto incoming = reflect(outgoing, halfway); + if (!same_hemisphere(up_normal, outgoing, incoming)) return {0, 0, 0}; + return incoming; + } else { + auto incoming = refract(outgoing, halfway, entering ? (1 / ior) : ior); + if (same_hemisphere(up_normal, outgoing, incoming)) return {0, 0, 0}; + return incoming; + } +} + +// Pdf for refraction BRDF lobe sampling. +inline float sample_refractive_pdf(vec3f color, float ior, float roughness, + vec3f normal, vec3f outgoing, vec3f incoming) { + auto entering = dot(normal, outgoing) >= 0; + auto up_normal = entering ? normal : -normal; + auto rel_ior = entering ? ior : (1 / ior); + if (dot(normal, incoming) * dot(normal, outgoing) >= 0) { + auto halfway = normalize(incoming + outgoing); + return fresnel_dielectric(rel_ior, halfway, outgoing) * + sample_microfacet_pdf(roughness, up_normal, halfway) / + // sample_microfacet_pdf(roughness, up_normal, halfway, outgoing) / + (4 * abs(dot(outgoing, halfway))); + } else { + auto halfway = -normalize(rel_ior * incoming + outgoing) * + (entering ? 1.0f : -1.0f); + // [Walter 2007] equation 17 + return (1 - fresnel_dielectric(rel_ior, halfway, outgoing)) * + sample_microfacet_pdf(roughness, up_normal, halfway) * + // sample_microfacet_pdf(roughness, up_normal, halfway, outgoing) / + abs(dot(halfway, incoming)) / // here we use incoming as from pbrt + pow(rel_ior * dot(halfway, incoming) + dot(halfway, outgoing), 2.0f); + } +} + +// Evaluate a delta refraction BRDF lobe. +inline vec3f eval_refractive( + vec3f color, float ior, vec3f normal, vec3f outgoing, vec3f incoming) { + if (abs(ior - 1) < 1e-3) + return dot(normal, incoming) * dot(normal, outgoing) <= 0 ? vec3f{1, 1, 1} + : vec3f{0, 0, 0}; + auto entering = dot(normal, outgoing) >= 0; + auto up_normal = entering ? normal : -normal; + auto rel_ior = entering ? ior : (1 / ior); + if (dot(normal, incoming) * dot(normal, outgoing) >= 0) { + return vec3f{1, 1, 1} * fresnel_dielectric(rel_ior, up_normal, outgoing); + } else { + return vec3f{1, 1, 1} * (1 / (rel_ior * rel_ior)) * + (1 - fresnel_dielectric(rel_ior, up_normal, outgoing)); + } +} + +// Sample a delta refraction BRDF lobe. +inline vec3f sample_refractive( + vec3f color, float ior, vec3f normal, vec3f outgoing, float rnl) { + if (abs(ior - 1) < 1e-3) return -outgoing; + auto entering = dot(normal, outgoing) >= 0; + auto up_normal = entering ? normal : -normal; + auto rel_ior = entering ? ior : (1 / ior); + if (rnl < fresnel_dielectric(rel_ior, up_normal, outgoing)) { + return reflect(outgoing, up_normal); + } else { + return refract(outgoing, up_normal, 1 / rel_ior); + } +} + +// Pdf for delta refraction BRDF lobe sampling. +inline float sample_refractive_pdf( + vec3f color, float ior, vec3f normal, vec3f outgoing, vec3f incoming) { + if (abs(ior - 1) < 1e-3) + return dot(normal, incoming) * dot(normal, outgoing) < 0 ? 1.0f : 0.0f; + auto entering = dot(normal, outgoing) >= 0; + auto up_normal = entering ? normal : -normal; + auto rel_ior = entering ? ior : (1 / ior); + if (dot(normal, incoming) * dot(normal, outgoing) >= 0) { + return fresnel_dielectric(rel_ior, up_normal, outgoing); + } else { + return (1 - fresnel_dielectric(rel_ior, up_normal, outgoing)); + } +} + +// Evaluate a translucent BRDF lobe. +inline vec3f eval_translucent( + vec3f color, vec3f normal, vec3f outgoing, vec3f incoming) { + if (dot(normal, incoming) * dot(normal, outgoing) >= 0) return {0, 0, 0}; + return color / pif * abs(dot(normal, incoming)); +} + +// Sample a translucency BRDF lobe. +inline vec3f sample_translucent( + vec3f color, vec3f normal, vec3f outgoing, vec2f rn) { + auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; + return sample_hemisphere_cos(-up_normal, rn); +} + +// Pdf for translucency BRDF lobe sampling. +inline float sample_translucent_pdf( + vec3f color, vec3f normal, vec3f outgoing, vec3f incoming) { + if (dot(normal, incoming) * dot(normal, outgoing) >= 0) return 0; + auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; + return sample_hemisphere_cos_pdf(-up_normal, incoming); +} + +// Evaluate a passthrough BRDF lobe. +inline vec3f eval_passthrough( + vec3f color, vec3f normal, vec3f outgoing, vec3f incoming) { + if (dot(normal, incoming) * dot(normal, outgoing) >= 0) { + return vec3f{0, 0, 0}; + } else { + return vec3f{1, 1, 1}; + } +} + +// Sample a passthrough BRDF lobe. +inline vec3f sample_passthrough(vec3f color, vec3f normal, vec3f outgoing) { + return -outgoing; +} + +// Pdf for passthrough BRDF lobe sampling. +inline float sample_passthrough_pdf( + vec3f color, vec3f normal, vec3f outgoing, vec3f incoming) { + if (dot(normal, incoming) * dot(normal, outgoing) >= 0) { + return 0; + } else { + return 1; + } +} + +// Convert mean-free-path to transmission +inline vec3f mfp_to_transmission(vec3f mfp, float depth) { + return exp(-depth / mfp); +} + +// Evaluate transmittance +inline vec3f eval_transmittance(vec3f density, float distance) { + return exp(-density * distance); +} + +// Sample a distance proportionally to transmittance +inline float sample_transmittance( + vec3f density, float max_distance, float rl, float rd) { + auto channel = clamp((int)(rl * 3), 0, 2); + auto distance = (density[channel] == 0) ? flt_max + : -log(1 - rd) / density[channel]; + return min(distance, max_distance); +} + +// Pdf for distance sampling +inline float sample_transmittance_pdf( + vec3f density, float distance, float max_distance) { + if (distance < max_distance) { + return sum(density * exp(-density * distance)) / 3; + } else { + return sum(exp(-density * max_distance)) / 3; + } +} + +// Evaluate phase function +inline float eval_phasefunction( + float anisotropy, vec3f outgoing, vec3f incoming) { + auto cosine = -dot(outgoing, incoming); + auto denom = 1 + anisotropy * anisotropy - 2 * anisotropy * cosine; + return (1 - anisotropy * anisotropy) / (4 * pif * denom * sqrt(denom)); +} + +// Sample phase function +inline vec3f sample_phasefunction(float anisotropy, vec3f outgoing, vec2f rn) { + auto cos_theta = 0.0f; + if (abs(anisotropy) < 1e-3f) { + cos_theta = 1 - 2 * rn.y; + } else { + auto square = (1 - anisotropy * anisotropy) / + (1 + anisotropy - 2 * anisotropy * rn.y); + cos_theta = (1 + anisotropy * anisotropy - square * square) / + (2 * anisotropy); + } + + auto sin_theta = sqrt(max(0.0f, 1 - cos_theta * cos_theta)); + auto phi = 2 * pif * rn.x; + auto local_incoming = vec3f{ + sin_theta * cos(phi), sin_theta * sin(phi), cos_theta}; + return basis_fromz(-outgoing) * local_incoming; +} + +// Pdf for phase function sampling +inline float sample_phasefunction_pdf( + float anisotropy, vec3f outgoing, vec3f incoming) { + return eval_phasefunction(anisotropy, outgoing, incoming); +} + +#ifndef __CUDACC__ + +// Conductor etas +inline pair conductor_eta(const string& name) { + static const vector>> metal_ior_table = { + {"a-C", {{2.9440999183f, 2.2271502925f, 1.9681668794f}, + {0.8874329109f, 0.7993216383f, 0.8152862927f}}}, + {"Ag", {{0.1552646489f, 0.1167232965f, 0.1383806959f}, + {4.8283433224f, 3.1222459278f, 2.1469504455f}}}, + {"Al", {{1.6574599595f, 0.8803689579f, 0.5212287346f}, + {9.2238691996f, 6.2695232477f, 4.8370012281f}}}, + {"AlAs", {{3.6051023902f, 3.2329365777f, 2.2175611545f}, + {0.0006670247f, -0.0004999400f, 0.0074261204f}}}, + {"AlSb", {{-0.0485225705f, 4.1427547893f, 4.6697691348f}, + {-0.0363741915f, 0.0937665154f, 1.3007390124f}}}, + {"Au", {{0.1431189557f, 0.3749570432f, 1.4424785571f}, + {3.9831604247f, 2.3857207478f, 1.6032152899f}}}, + {"Be", {{4.1850592788f, 3.1850604423f, 2.7840913457f}, + {3.8354398268f, 3.0101260162f, 2.8690088743f}}}, + {"Cr", {{4.3696828663f, 2.9167024892f, 1.6547005413f}, + {5.2064337956f, 4.2313645277f, 3.7549467933f}}}, + {"CsI", {{2.1449030413f, 1.7023164587f, 1.6624194173f}, + {0.0000000000f, 0.0000000000f, 0.0000000000f}}}, + {"Cu", {{0.2004376970f, 0.9240334304f, 1.1022119527f}, + {3.9129485033f, 2.4528477015f, 2.1421879552f}}}, + {"Cu2O", {{3.5492833755f, 2.9520622449f, 2.7369202137f}, + {0.1132179294f, 0.1946659670f, 0.6001681264f}}}, + {"CuO", {{3.2453822204f, 2.4496293965f, 2.1974114493f}, + {0.5202739621f, 0.5707372756f, 0.7172250613f}}}, + {"d-C", {{2.7112524747f, 2.3185812849f, 2.2288565009f}, + {0.0000000000f, 0.0000000000f, 0.0000000000f}}}, + {"Hg", {{2.3989314904f, 1.4400254917f, 0.9095512090f}, + {6.3276269444f, 4.3719414152f, 3.4217899270f}}}, + {"HgTe", {{4.7795267752f, 3.2309984581f, 2.6600252401f}, + {1.6319827058f, 1.5808189339f, 1.7295753852f}}}, + {"Ir", {{3.0864098394f, 2.0821938440f, 1.6178866805f}, + {5.5921510077f, 4.0671757150f, 3.2672611269f}}}, + {"K", {{0.0640493070f, 0.0464100621f, 0.0381842017f}, + {2.1042155920f, 1.3489364357f, 0.9132113889f}}}, + {"Li", {{0.2657871942f, 0.1956102432f, 0.2209198538f}, + {3.5401743407f, 2.3111306542f, 1.6685930000f}}}, + {"MgO", {{2.0895885542f, 1.6507224525f, 1.5948759692f}, + {0.0000000000f, -0.0000000000f, 0.0000000000f}}}, + {"Mo", {{4.4837010280f, 3.5254578255f, 2.7760769438f}, + {4.1111307988f, 3.4208716252f, 3.1506031404f}}}, + {"Na", {{0.0602665320f, 0.0561412435f, 0.0619909494f}, + {3.1792906496f, 2.1124800781f, 1.5790940266f}}}, + {"Nb", {{3.4201353595f, 2.7901921379f, 2.3955856658f}, + {3.4413817900f, 2.7376437930f, 2.5799132708f}}}, + {"Ni", {{2.3672753521f, 1.6633583302f, 1.4670554172f}, + {4.4988329911f, 3.0501643957f, 2.3454274399f}}}, + {"Rh", {{2.5857954933f, 1.8601866068f, 1.5544279524f}, + {6.7822927110f, 4.7029501026f, 3.9760892461f}}}, + {"Se-e", {{5.7242724833f, 4.1653992967f, 4.0816099264f}, + {0.8713747439f, 1.1052845009f, 1.5647788766f}}}, + {"Se", {{4.0592611085f, 2.8426947380f, 2.8207582835f}, + {0.7543791750f, 0.6385150558f, 0.5215872029f}}}, + {"SiC", {{3.1723450205f, 2.5259677964f, 2.4793623897f}, + {0.0000007284f, -0.0000006859f, 0.0000100150f}}}, + {"SnTe", {{4.5251865890f, 1.9811525984f, 1.2816819226f}, + {0.0000000000f, 0.0000000000f, 0.0000000000f}}}, + {"Ta", {{2.0625846607f, 2.3930915569f, 2.6280684948f}, + {2.4080467973f, 1.7413705864f, 1.9470377016f}}}, + {"Te-e", {{7.5090397678f, 4.2964603080f, 2.3698732430f}, + {5.5842076830f, 4.9476231084f, 3.9975145063f}}}, + {"Te", {{7.3908396088f, 4.4821028985f, 2.6370708478f}, + {3.2561412892f, 3.5273908133f, 3.2921683116f}}}, + {"ThF4", {{1.8307187117f, 1.4422274283f, 1.3876488528f}, + {0.0000000000f, 0.0000000000f, 0.0000000000f}}}, + {"TiC", {{3.7004673762f, 2.8374356509f, 2.5823030278f}, + {3.2656905818f, 2.3515586388f, 2.1727857800f}}}, + {"TiN", {{1.6484691607f, 1.1504482522f, 1.3797795097f}, + {3.3684596226f, 1.9434888540f, 1.1020123347f}}}, + {"TiO2-e", {{3.1065574823f, 2.5131551146f, 2.5823844157f}, + {0.0000289537f, -0.0000251484f, 0.0001775555f}}}, + {"TiO2", {{3.4566203131f, 2.8017076558f, 2.9051485020f}, + {0.0001026662f, -0.0000897534f, 0.0006356902f}}}, + {"VC", {{3.6575665991f, 2.7527298065f, 2.5326814570f}, + {3.0683516659f, 2.1986687713f, 1.9631816252f}}}, + {"VN", {{2.8656011588f, 2.1191817791f, 1.9400767149f}, + {3.0323264950f, 2.0561075580f, 1.6162930914f}}}, + {"V", {{4.2775126218f, 3.5131538236f, 2.7611257461f}, + {3.4911844504f, 2.8893580874f, 3.1116965117f}}}, + {"W", {{4.3707029924f, 3.3002972445f, 2.9982666528f}, + {3.5006778591f, 2.6048652781f, 2.2731930614f}}}, + }; + for (auto& [ename, eta] : metal_ior_table) { + if (ename == name) return eta; + } + return {{0, 0, 0}, {0, 0, 0}}; +} + +#endif + +} // namespace yocto + +// ----------------------------------------------------------------------------- +// CUDA SUPPORT +// ----------------------------------------------------------------------------- +#ifdef __CUDACC__ +#undef inline +#endif + // ----------------------------------------------------------------------------- // ENUM LABELS // -----------------------------------------------------------------------------