Skip to content

Commit 05deead

Browse files
authored
Open-sourcing Imageworks BSDF library (#1986)
Sony Pictures Imageworks BSDF library (working title BSDL) is here published and introduced to testrender. It is a header only library where everything is inlined and can be used on GPU. Although we haven't kept the BSDFs up to date with the latest research in the last couple of years, I hope you, at least, like the infrastructure. There are three main items in this PR 1. Put BSDL as an INTERFACE cmake library in src/libbsdl. 2. Introduce the switch/case automatic pseudo virtual methods in testrender to handle all the existing BSDFs. 3. As an example integrate SPI's ThinLayer BSDF in testrender and add a test for it. BSDL is a self-contained lib depending only on Imath. There is not even a need to link to OSL and it can be used as a build only dependency. Inside libbsdl directory there is a README with further information, but documentation is the usual weak point with production code. We use this code as-is directly for making movies with our in-house render, and we welcome everybody to contribute theirs. The name "BSDL" is one of my half-broken name ideas and I'm very much open to suggestions. I recommend to first look at my changes to testrender, integration was very easy. Then dive in the internals if you want. Credits for this code go mainly to Chris Kulla and me (Alejandro Conty), but there are also other smaller contributions from other Imageworks members, including alumni Laura Lediaev who wrote the LTC sheen implementation. Signed-off-by: Alejandro Conty <[email protected]>
1 parent 96a307d commit 05deead

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+9217
-392
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ add_subdirectory (src/liboslcomp)
250250
add_subdirectory (src/liboslquery)
251251
add_subdirectory (src/liboslexec)
252252
add_subdirectory (src/liboslnoise)
253+
add_subdirectory (src/libbsdl)
253254
add_subdirectory (src/oslc)
254255
add_subdirectory (src/oslinfo)
255256

src/cmake/cuda_macros.cmake

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ if (CUDA_NO_FTZ)
1515
endif ()
1616

1717
# Compile a CUDA file to PTX using NVCC
18-
function ( NVCC_COMPILE cuda_src extra_headers ptx_generated extra_nvcc_args )
18+
function ( NVCC_COMPILE cuda_src extra_headers ptx_generated extra_nvcc_args extra_libs)
1919
get_filename_component ( cuda_src_we ${cuda_src} NAME_WE )
2020
get_filename_component ( cuda_src_dir ${cuda_src} DIRECTORY )
2121
set (cuda_ptx "${CMAKE_CURRENT_BINARY_DIR}/${cuda_src_we}.ptx" )
@@ -35,6 +35,14 @@ function ( NVCC_COMPILE cuda_src extra_headers ptx_generated extra_nvcc_args )
3535
set (NVCC_FTZ_FLAG "--ftz=true")
3636
endif ()
3737

38+
set (EXTRA_INCLUDES "")
39+
foreach (LIB ${extra_libs})
40+
get_target_property(INCLUDES ${LIB} INTERFACE_INCLUDE_DIRECTORIES)
41+
foreach (INCLUDE_DIR ${INCLUDES})
42+
list (APPEND EXTRA_INCLUDES "-I${INCLUDE_DIR}")
43+
endforeach()
44+
endforeach()
45+
3846
add_custom_command ( OUTPUT ${cuda_ptx}
3947
COMMAND ${CUDA_NVCC_EXECUTABLE}
4048
"-I${OPTIX_INCLUDES}"
@@ -43,6 +51,7 @@ function ( NVCC_COMPILE cuda_src extra_headers ptx_generated extra_nvcc_args )
4351
"-I${CMAKE_BINARY_DIR}/include"
4452
"-I${PROJECT_SOURCE_DIR}/src/include"
4553
"-I${PROJECT_SOURCE_DIR}/src/cuda_common"
54+
${EXTRA_INCLUDES}
4655
${ALL_OpenImageIO_INCLUDES}
4756
${ALL_IMATH_INCLUDES}
4857
"-DFMT_DEPRECATED=\"\""
@@ -56,7 +65,7 @@ function ( NVCC_COMPILE cuda_src extra_headers ptx_generated extra_nvcc_args )
5665
${OSL_EXTRA_NVCC_ARGS}
5766
${cuda_src} -o ${cuda_ptx}
5867
MAIN_DEPENDENCY ${cuda_src}
59-
DEPENDS ${cuda_src} ${cuda_headers} oslexec
68+
DEPENDS ${cuda_src} ${cuda_headers} ${extra_libs} oslexec
6069
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" )
6170
endfunction ()
6271

src/cmake/testing.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,7 @@ macro (osl_add_all_tests)
372372
render-mx-layer
373373
render-mx-sheen
374374
render-microfacet render-oren-nayar
375+
render-spi-thinlayer
375376
render-uv render-veachmis render-ward
376377
render-raytypes
377378
select select-reg shaderglobals shortcircuit

src/libbsdl/CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Copyright Contributors to the Open Shading Language project.
2+
# SPDX-License-Identifier: BSD-3-Clause
3+
# https://github.com/AcademySoftwareFoundation/OpenShadingLanguage
4+
5+
include(bsdl.cmake)
6+
7+
# BSDL has small tables for doing spectral render in sRGB
8+
# but for other color spaces tell this function which ones
9+
# and it will bake Jakob-Hanika coefficient tables.
10+
add_bsdl_library(BSDL) # SPECTRAL_COLOR_SPACES "ACEScg")

src/libbsdl/README.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# BSDL library
2+
3+
BSDL is the working title (Bidirectional Scattering Distribution Library) for
4+
this header only collection of BSDFs. It is self-contained depending only on
5+
Imath and intended to be used in any renderer. This is a build only dependency.
6+
7+
The basic idea is that you choose a BSDF from it and give it a thin wrapper to
8+
integrate in your renderer. There is an example of this in OSL's testrender. The
9+
key features are:
10+
* BSDFs provide constructor, eval and sample methods.
11+
* Everything is inlined (split among _decl.h and _imple.h headers) to be GPU friendly.
12+
* There is a template based autiomatic switch/case virtual dispatch mechanism to be GPU firendly.
13+
* It works both in RGB or spectral mode (via hero wavelength).
14+
* It includes the Sony Pictures Imageworks set of BSDFs used for movie production.
15+
16+
## Pseudo virtual methods
17+
18+
The header static_virtual.h provides the ```StaticVirtual<type1, type2, ...>```
19+
template. If you inherit from it will implement a dispatch() method to execute
20+
"virtual" methods covering ```type1, type2, ...``` using switch case statements.
21+
The idea was borrowed from PBRT but the implementation is different. See the
22+
use in testrender (shading.h/cpp).
23+
24+
## The ```Power``` type
25+
26+
To support both RGB and spectral render with the same code we use the
27+
```Power``` type, which is just a float array. It has from RGB construction and
28+
conversion. But when 'lambda_0', the hero wavelength is zero, these are no-ops
29+
and pure RGB rendering is assueme. Take into account spectral support in BSDL
30+
is in very early stages.
31+
32+
## Lookup tables
33+
34+
A bunch of BSDFs use albedo lookup tables. Those are generated at build time
35+
very quickly and put in headers. You shouldn't have to do anything.
36+
37+
## Usage
38+
39+
Either ```include(bsdl.cmake)``` and call ```add_bsdl_library(my_name)``` to
40+
create a 'my_name' INTERFACE library that you then link, or
41+
```add_subdirectory (path_to_libbsdl)``` to get it as 'BSDL'.
42+
43+
For the integration, a config header needs to be defined as you can see in
44+
testrender's 'bsdl_config.h'.
45+
```cpp
46+
#define BSDL_INLINE static inline OSL_HOSTDEVICE
47+
#define BSDL_INLINE_METHOD inline OSL_HOSTDEVICE
48+
#define BSDL_DECL OSL_DEVICE
49+
#define BSDL_UNROLL() // Do nothing
50+
51+
#include <BSDL/config.h>
52+
53+
#include <OpenImageIO/fmath.h>
54+
55+
struct BSDLConfig : public bsdl::BSDLDefaultConfig {
56+
57+
// testrender won't do spectral render, just 3 channels covers RGB
58+
static constexpr int HERO_WAVELENGTH_CHANNELS = 3;
59+
60+
struct Fast {
61+
static BSDL_INLINE_METHOD float cosf(float x) { return OIIO::fast_cos(x); }
62+
static BSDL_INLINE_METHOD float sinf(float x) { return OIIO::fast_sin(x); }
63+
static BSDL_INLINE_METHOD float asinf(float x) { return OIIO::fast_asin(x); }
64+
static BSDL_INLINE_METHOD float acosf(float x) { return OIIO::fast_acos(x); }
65+
static BSDL_INLINE_METHOD float atan2f(float y, float x) { return OIIO::fast_atan2(y, x); }
66+
static BSDL_INLINE_METHOD void sincosf(float x, float* s, float* c) { return OIIO::fast_sincos(x, s, c); }
67+
static BSDL_INLINE_METHOD float sinpif(float x) { return OIIO::fast_sinpi(x); }
68+
static BSDL_INLINE_METHOD float cospif(float x) { return OIIO::fast_cospi(x); }
69+
static BSDL_INLINE_METHOD float expf(float x) { return OIIO::fast_exp(x); }
70+
static BSDL_INLINE_METHOD float exp2f(float x) { return OIIO::fast_exp2(x); }
71+
static BSDL_INLINE_METHOD float logf(float x) { return OIIO::fast_log(x); }
72+
static BSDL_INLINE_METHOD float log2f(float x) { return OIIO::fast_log2(x); }
73+
static BSDL_INLINE_METHOD float log1pf(float x) { return OIIO::fast_log1p(x); }
74+
static BSDL_INLINE_METHOD float powf(float x, float y) { return OIIO::fast_safe_pow(x, y); }
75+
};
76+
77+
// Don't care for colorspaces/spectral
78+
static BSDL_INLINE_METHOD ColorSpaceTag current_color_space() { return ColorSpaceTag::sRGB; }
79+
static BSDL_INLINE_METHOD const JakobHanikaLut* get_jakobhanika_lut(ColorSpaceTag cs) { return nullptr; }
80+
};
81+
```
82+
83+
And this header should be included before you include anything else from BSDL.
84+
85+
If spectral rendering is desired there is ready to use sRGB upsampling via
86+
spectral primaries by Mallett and Yuksel. For wide gamut color spaces like
87+
ACEScg we use Jakob and Hanika approach, but you have to ask for the tables
88+
in .cpp for from cmake:
89+
```
90+
add_bsdl_library(BSDL SPECTRAL_COLOR_SPACES "ACEScg" "ACES2065")
91+
```
92+
93+
which will bake tables to two cpp files and return them in 'BSDL_LUTS_CPP'.
94+
Then you need to include that in your sources.

src/libbsdl/bsdl.cmake

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Copyright Contributors to the Open Shading Language project.
2+
# SPDX-License-Identifier: BSD-3-Clause
3+
# https://github.com/AcademySoftwareFoundation/OpenShadingLanguage
4+
5+
find_package(Threads REQUIRED)
6+
7+
function(ADD_BSDL_LIBRARY NAME)
8+
cmake_parse_arguments(PARSE_ARGV 1 bsdl "" "SUBDIR" "SPECTRAL_COLOR_SPACES")
9+
# Bootstrap version of BSDL (without luts)
10+
add_library(BSDL_BOOTSTRAP INTERFACE)
11+
target_include_directories(BSDL_BOOTSTRAP INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/${bsdl_SUBDIR}/include)
12+
target_link_libraries(BSDL_BOOTSTRAP INTERFACE ${ARNOLD_IMATH_TARGETS})
13+
14+
# LUT generation tool
15+
set(BSDL_GEN_HEADERS ${CMAKE_CURRENT_BINARY_DIR}/${bsdl_SUBDIR}/geninclude)
16+
add_executable (genluts ${CMAKE_CURRENT_SOURCE_DIR}/${bsdl_SUBDIR}/src/genluts.cpp)
17+
target_link_libraries(genluts PRIVATE BSDL_BOOTSTRAP Threads::Threads)
18+
file(MAKE_DIRECTORY ${BSDL_GEN_HEADERS}/BSDL/SPI)
19+
add_custom_command(TARGET genluts POST_BUILD USES_TERMINAL COMMAND $<TARGET_FILE:genluts> ${BSDL_GEN_HEADERS}/BSDL/SPI
20+
COMMENT "Generating BSDL lookup tables ...")
21+
22+
if (DEFINED bsdl_SPECTRAL_COLOR_SPACES)
23+
add_executable(jakobhanika_luts ${CMAKE_CURRENT_SOURCE_DIR}/${bsdl_SUBDIR}/src/jakobhanika_luts.cpp)
24+
target_link_libraries(genluts PRIVATE Threads::Threads)
25+
foreach(CS ${bsdl_SPECTRAL_COLOR_SPACES})
26+
set(JACOBHANIKA_${CS} ${CMAKE_CURRENT_BINARY_DIR}/jakobhanika_${CS}.cpp)
27+
list(APPEND BSDL_LUTS_CPP ${JACOBHANIKA_${CS}})
28+
add_custom_command(
29+
OUTPUT ${JACOBHANIKA_${CS}}
30+
USES_TERMINAL
31+
COMMAND $<TARGET_FILE:jakobhanika_luts> 64 ${JACOBHANIKA_${CS}} ${CS}
32+
DEPENDS jakobhanika_luts
33+
COMMENT "Generating Jakob-Hanika RGB-Spectrum ${CS} tables")
34+
endforeach()
35+
set(${NAME}_LUTS_CPP ${BSDL_LUTS_CPP} PARENT_SCOPE)
36+
endif()
37+
38+
# Final BSDL library (with luts)
39+
add_library(${NAME} INTERFACE)
40+
target_include_directories(${NAME} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/${bsdl_SUBDIR}/include)
41+
target_link_libraries(${NAME} INTERFACE Imath::Imath)
42+
target_include_directories(${NAME} INTERFACE ${BSDL_GEN_HEADERS})
43+
add_dependencies(${NAME} genluts)
44+
endfunction()
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// Copyright Contributors to the Open Shading Language project.
2+
// SPDX-License-Identifier: BSD-3-Clause
3+
// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage
4+
5+
6+
#pragma once
7+
8+
#include <BSDL/bsdf_decl.h>
9+
10+
BSDL_ENTER_NAMESPACE
11+
12+
namespace spi {
13+
14+
struct CharlieDist {
15+
static constexpr float MIN_ROUGHNESS = 0.06f;
16+
17+
static BSDL_INLINE_METHOD float common_roughness(float alpha);
18+
BSDL_INLINE_METHOD CharlieDist(float rough)
19+
: a(CLAMP(rough, MIN_ROUGHNESS, 1.0f))
20+
{
21+
}
22+
23+
BSDL_INLINE_METHOD float D(const Imath::V3f& Hr) const;
24+
BSDL_INLINE_METHOD float get_lambda(float cosNv) const;
25+
BSDL_INLINE_METHOD float G2(const Imath::V3f& wo,
26+
const Imath::V3f& wi) const;
27+
BSDL_INLINE_METHOD float roughness() const { return a; }
28+
29+
private:
30+
float a;
31+
};
32+
33+
template<typename Dist> struct SheenMicrofacet {
34+
// describe how tabulation should be done
35+
static constexpr int Nc = 16;
36+
static constexpr int Nr = 16;
37+
static constexpr int Nf = 1;
38+
39+
static constexpr float get_cosine(int i)
40+
{
41+
return std::max(float(i) * (1.0f / (Nc - 1)), 1e-6f);
42+
}
43+
explicit BSDL_INLINE_METHOD SheenMicrofacet(float rough) : d(rough) {}
44+
BSDL_INLINE_METHOD Sample eval(const Imath::V3f& wo,
45+
const Imath::V3f& wi) const;
46+
BSDL_INLINE_METHOD Sample sample(const Imath::V3f& wo, float randu,
47+
float randv, float) const;
48+
BSDL_INLINE_METHOD float roughness() const { return d.roughness(); }
49+
50+
private:
51+
Dist d;
52+
};
53+
54+
struct CharlieSheen : public SheenMicrofacet<CharlieDist> {
55+
explicit BSDL_INLINE_METHOD CharlieSheen(float, float rough, float)
56+
: SheenMicrofacet<CharlieDist>(rough)
57+
{
58+
}
59+
struct Energy {
60+
float data[Nf * Nr * Nc];
61+
};
62+
static BSDL_INLINE_METHOD Energy& get_energy();
63+
64+
static const char* lut_header() { return "bsdf_backscatter_luts.h"; }
65+
static const char* struct_name() { return "CharlieSheen"; }
66+
};
67+
68+
template<typename BSDF_ROOT> struct CharlieLobe : public Lobe<BSDF_ROOT> {
69+
using Base = Lobe<BSDF_ROOT>;
70+
struct Data : public LayeredData {
71+
Imath::V3f N;
72+
Imath::C3f tint;
73+
float roughness;
74+
int doublesided;
75+
using lobe_type = CharlieLobe;
76+
};
77+
template<typename D> static typename LobeRegistry<D>::Entry entry()
78+
{
79+
static_assert(
80+
std::is_base_of<Data, D>::value); // Make no other assumptions
81+
using R = LobeRegistry<D>;
82+
return { name(),
83+
{ R::param(&D::closure), R::param(&D::N), R::param(&D::tint),
84+
R::param(&D::roughness),
85+
R::param(&D::doublesided, "doublesided"), R::close() } };
86+
}
87+
88+
template<typename T>
89+
BSDL_INLINE_METHOD CharlieLobe(T*, const BsdfGlobals& globals,
90+
const Data& data);
91+
static const char* name() { return "sheen"; }
92+
93+
BSDL_INLINE_METHOD Power albedo_impl() const { return Power(1 - Eo, 1); }
94+
95+
BSDL_INLINE_METHOD Sample eval_impl(const Imath::V3f& wo,
96+
const Imath::V3f& wi) const;
97+
BSDL_INLINE_METHOD Sample sample_impl(const Imath::V3f& wo,
98+
const Imath::V3f& sample) const;
99+
100+
private:
101+
CharlieSheen sheen;
102+
Power tint;
103+
float Eo;
104+
bool back;
105+
};
106+
107+
} // namespace spi
108+
109+
BSDL_LEAVE_NAMESPACE

0 commit comments

Comments
 (0)