├── README.md
├── custom_bsdf
├── measuredbtf.py
└── utils
│ ├── btf_interpolator.py
│ └── coord_system_transfer.py
├── documents
├── cloth_diffuse_checker.jpg
├── cloth_fabric12.jpg
├── cloth_wool.jpg
├── dou2023.jpg
├── kavoosighafi2023.jpg
├── matpreview_corduroy.jpg
├── matpreview_impalla.jpg
├── matpreview_leather09.jpg
├── simple_sphere_impalla.jpg
├── simple_sphere_leather10.jpg
├── simple_sphere_p1.jpg
├── simple_sphere_p2.jpg
├── simple_sphere_p32.jpg
└── simple_sphere_p4.jpg
├── download_large_data.py
├── rendering.py
└── scenes
├── cloth
├── cloth.obj
├── cloth.xml
├── cloth_diffuse_checker.xml
├── cloth_ubo2003_gpu.xml
└── cloth_ubo2014_gpu.xml
├── matpreview
├── matpreview.serialized
├── matpreview.xml
├── matpreview_ubo2003_gpu.xml
└── matpreview_ubo2014_gpu.xml
└── simple_sphere
├── simple_sphere.xml
├── simple_sphere_ubo2003_gpu.xml
└── simple_sphere_ubo2014_gpu.xml
/README.md:
--------------------------------------------------------------------------------
1 | # BTF Rendering
2 | Custom plugin in Python to render measured BTF (Bidirectional Texture Function) with Mitsuba 2.
3 |
4 | 
5 |
6 | ## Requirement in Python
7 | Make sure that the following libraries are available in python.
8 | - [SciPy](https://www.scipy.org/)
9 | - [BTF Extractor](https://github.com/2-propanol/BTF_extractor)
10 | - [tqdm](https://github.com/tqdm/tqdm) (optional)
11 |
12 | ## Usage
13 | 1. Clone and compile [Mitsuba 2](https://github.com/mitsuba-renderer/mitsuba2). Check whether you can `import mitsuba` in python.
14 | 2. Clone this repository and move it there.
15 | ```bash
16 | git clone https://github.com/elerac/btf-rendering.git
17 | cd btf-rendering
18 | ```
19 | 3. Download [UBO2003 BTF Dataset](https://cg.cs.uni-bonn.de/en/projects/btfdbb/download/ubo2003/). Make directory named `UBO2003/` and save the data under there. OR, you can run [download_large_data.py](https://github.com/elerac/btf-rendering/blob/master/download_large_data.py) and the data will download automatically.
20 | Have you placed BTF Dataset as shown below?
21 | ```
22 | .
23 | ├── UBO2003
24 | │ ├── UBO_IMPALLA256.zip
25 | │ ├── ...
26 | ├── custom_bsdf
27 | ├── download_large_data.py
28 | ├── rendering.py
29 | ├── scenes
30 | ├── ...
31 | ```
32 | 4. Run [rendering.py](https://github.com/elerac/btf-rendering/blob/master/rendering.py) and get [rendered image using BTF](https://github.com/elerac/btf-rendering/blob/master/documents/simple_sphere_impalla.jpg).
33 |
34 | ## Measured BTF (*measuredbtf*)
35 | | Parameter | Type | Description |
36 | | :-- | :-- | :-- |
37 | | filename | string | Filename of the BTF database file to be loaded. The type of dataset is distinguished by its extension. .zip for UBO2003, .btf for UBO2014.|
38 | | reflectance| float | Adjust the reflectance of the BTF. (Default: 1.0) |
39 | | apply_inv_gamma | boolean | Whether to apply inverse gamma correction. If the input is the gamma-corrected image, this process should be applied. (Default: *true*) |
40 | | power_parameter | float | Determine the smoothness of the interpolation. The smaller the value, the smoother it is. (Default: 4.0) |
41 | | wrap_mode | string | Controls the behavior of texture evaluations that fall outside of the [0,1] range. The `repeat` and `mirror` options are currently available. (Default: `repeat`)|
42 | | to_uv | transform | Specifies an optional 3x3 UV transformation matrix. A 4x4 matrix can also be provided, in which case the extra row and column are ignored. (Default: none) |
43 |
44 | | | |
45 | | :-: | :-: |
46 | | |  |
47 | | UBO2003 IMPALLA | UBO2014 leather09 |
48 |
49 | This custom plugin implements a BTF for rendering reflections of textures taken in a real scene. The BTF is a set of images with different illumination and viewing directions.
50 |
51 | `filename` is the name of the BTF database file. This file should follow the format of the BTF dataset of University of Bonn.
52 | Download the [UBO2003](https://cg.cs.uni-bonn.de/en/projects/btfdbb/download/ubo2003/) or [ATRIUM](https://cg.cs.uni-bonn.de/en/projects/btfdbb/download/atrium/) or [UBO2014](https://cg.cs.uni-bonn.de/en/projects/btfdbb/download/ubo2014/) dataset for rendering.
53 |
54 | ```xml
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | ```
63 |
64 | ### Interpolation and Power Parameter
65 | This custom plugin interpolates BTF. The interpolation is done by [k-nearest neighbor sampling](https://en.wikipedia.org/wiki/K-nearest_neighbors_algorithm) and [inverse distance weighting](https://en.wikipedia.org/wiki/Inverse_distance_weighting).
66 | The weight of the inverse distance weighting can be adjusted by the `power_parameter` *p*. The parameter *p* determines the influence of the distance between the interpolation points. The smaller *p* is, the smoother the interpolation will be.
67 |
68 | The following figures show the difference in appearance when *p* is changed. When *p* is small (*p*=1), the texture is smooth and specular reflection is weak. On the other hand, when *p* is large (*p*=32), the texture has discontinuous boundaries. This is because the angle of the captured BTF data is sparse.
69 | | | | | |
70 | | :-: | :-: | :-: | :-: |
71 | |  |  |  |  |
72 | | *p* = 1 | *p* = 2 | *p* = 4 | *p* = 32 |
73 |
74 |
75 | ## Warning
76 | In the `scalar_rgb` variant, the execution of this plugin is **extremely slow**. Thus, using the `gpu_rgb` variant is recommended.
77 |
78 | ## Rendered Images from Other Papers
79 |
80 | I collected the rendered images from other papers that employ the 3D model of cloth originated at this repository ([`scenes/cloth/cloth.obj`](scenes/cloth/)). These beautifully rendered images enhance the effectiveness of their excellent methods for BTF data processing.
81 |
82 | 
83 | *Kavoosighafi et al., "SparseBTF: Sparse Representation Learning for Bidirectional Texture Functions", Eurographics Symposium on Rendering, 2023. [[Link]](https://diglib.eg.org/handle/10.2312/sr20231123)*
84 |
85 | 
86 | *Dou et al., "Real-Time Neural BRDF with Spherically Distributed Primitives", arXiv, 2023. [[Link]](https://arxiv.org/abs/2310.08332)*
87 |
88 | You are free to use my 3D model and scene file of cloth (and also my code) for academic purposes. If you use them, it is not necessary, but I would appreciate it if you refer to my name.
89 |
--------------------------------------------------------------------------------
/custom_bsdf/measuredbtf.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from .utils.btf_interpolator import BtfInterpolator
3 | from .utils.coord_system_transfer import orthogonal2spherical, mirror_uv
4 |
5 | import enoki as ek
6 | from mitsuba.core import Bitmap, Struct, Thread, math, Properties, Frame3f, Float, Vector3f, warp, Transform3f
7 | from mitsuba.render import BSDF, BSDFContext, BSDFFlags, BSDFSample3f, SurfaceInteraction3f, register_bsdf, Texture
8 |
9 | class MeasuredBTF(BSDF):
10 | def __init__(self, props):
11 | BSDF.__init__(self, props)
12 |
13 | # BTDFFのzipファイルのパス
14 | self.m_filename = props["filename"]
15 |
16 | # 逆ガンマ補正をかけるかどうか
17 | if props.has_property("apply_inv_gamma"):
18 | self.m_apply_inv_gamma = props["apply_inv_gamma"]
19 | else:
20 | self.m_apply_inv_gamma = True
21 |
22 | # 反射率
23 | if props.has_property("reflectance"):
24 | self.m_reflectance = Float(props["reflectance"])
25 | else:
26 | self.m_reflectance = Float(1.0)
27 |
28 | # Power parameter
29 | if props.has_property("power_parameter"):
30 | self.m_power_parameter = Float(props["power_parameter"])
31 | else:
32 | self.m_power_parameter = Float(4.0)
33 |
34 | # Wrap mode
35 | if props.has_property("wrap_mode"):
36 | self.m_wrap_mode = str(props["wrap_mode"])
37 | else:
38 | self.m_wrap_mode = "repeat"
39 |
40 | # UVマップの変換
41 | self.m_transform = Transform3f(props["to_uv"].extract())
42 |
43 | # 読み込んだBTF
44 | self.btf = BtfInterpolator(self.m_filename, p=self.m_power_parameter)
45 |
46 | self.m_flags = BSDFFlags.DiffuseReflection | BSDFFlags.FrontSide
47 | self.m_components = [self.m_flags]
48 |
49 | def get_btf(self, wi, wo, uv):
50 | """
51 | BTFの生の値を取得する
52 |
53 | wi : enoki.scalar.Vector3f
54 | Incident direction
55 |
56 | wo : enoki.scalar.Vector3f
57 | Outgoing direction
58 |
59 | uv : enoki.scalar.Vector2f
60 | UV surface coordinates
61 | """
62 | # カメラ側の方向
63 | _, tv, pv = orthogonal2spherical(*wi)
64 |
65 | # 光源側の方向
66 | _, tl, pl = orthogonal2spherical(*wo)
67 |
68 | # 画像中の座標位置を求める
69 | uv = self.m_transform.transform_point(uv)
70 |
71 | # warp_modeがmirrorなら座標の変換
72 | if self.m_wrap_mode == "mirror":
73 | uv = mirror_uv(uv).T
74 |
75 | u, v = uv
76 |
77 | # BTFの画素値を取得
78 | bgr = self.btf.angles_uv_to_pixel(tl, pl, tv, pv, u, v)
79 |
80 | # 0.0~1.0にスケーリング
81 | bgr = np.clip(bgr / 255.0 * self.m_reflectance, 0, 1)
82 |
83 | # 逆ガンマ補正をかける
84 | if self.m_apply_inv_gamma:
85 | bgr **= 2.2
86 |
87 | return Vector3f(bgr[..., ::-1])
88 |
89 | def sample(self, ctx, si, sample1, sample2, active):
90 | """
91 | BSDF sampling
92 | """
93 | cos_theta_i = Frame3f.cos_theta(si.wi)
94 |
95 | active &= cos_theta_i > 0
96 |
97 | bs = BSDFSample3f()
98 | bs.wo = warp.square_to_cosine_hemisphere(sample2)
99 | bs.pdf = warp.square_to_cosine_hemisphere_pdf(bs.wo)
100 | bs.eta = 1.0
101 | bs.sampled_type = +BSDFFlags.DiffuseReflection
102 | bs.sampled_component = 0
103 |
104 | value = self.get_btf(si.wi, bs.wo, si.uv) / Frame3f.cos_theta(bs.wo)
105 |
106 | return ( bs, ek.select(active & (bs.pdf > 0.0), value, Vector3f(0)) )
107 |
108 | def eval(self, ctx, si, wo, active):
109 | """
110 | Emitter sampling
111 | """
112 | if not ctx.is_enabled(BSDFFlags.DiffuseReflection):
113 | return Vector3f(0)
114 |
115 | cos_theta_i = Frame3f.cos_theta(si.wi)
116 | cos_theta_o = Frame3f.cos_theta(wo)
117 |
118 | value = self.get_btf(si.wi, wo, si.uv) * math.InvPi
119 |
120 | return ek.select((cos_theta_i > 0.0) & (cos_theta_o > 0.0), value, Vector3f(0))
121 |
122 | def pdf(self, ctx, si, wo, active):
123 | if not ctx.is_enabled(BSDFFlags.DiffuseReflection):
124 | return Vector3f(0)
125 |
126 | cos_theta_i = Frame3f.cos_theta(si.wi)
127 | cos_theta_o = Frame3f.cos_theta(wo)
128 |
129 | pdf = warp.square_to_cosine_hemisphere_pdf(wo)
130 |
131 | return ek.select((cos_theta_i > 0.0) & (cos_theta_o > 0.0), pdf, 0.0)
132 |
--------------------------------------------------------------------------------
/custom_bsdf/utils/btf_interpolator.py:
--------------------------------------------------------------------------------
1 | """
2 | BTFDBBを元に.任意角度のBTF画像を補間して返すためのライブラリ.
3 | 任意の角度(tl, pl, tv, pv)から,補間したndarray形式の画像を取得.
4 |
5 | BTFDBBの読み込みについては,btf-extractor(https://github.com/2-propanol/BTF_extractor)を参照.
6 | """
7 | import os
8 | import datetime
9 | import numpy as np
10 | from scipy.spatial import cKDTree
11 | from btf_extractor import Ubo2003, Ubo2014
12 | from .coord_system_transfer import spherical2orthogonal
13 |
14 | # import tqdm if available
15 | import importlib
16 | tqdm_spec = importlib.util.find_spec("tqdm")
17 | use_tqdm = tqdm_spec is not None
18 | if use_tqdm:
19 | from tqdm import tqdm
20 |
21 | class BtfInterpolator:
22 | """
23 | BTFDBBを元に,任意角度のBTF画像を補間して返す.
24 | 補間は,k近傍のデータから,逆距離加重法(Inverse Distance Weighting)を用いている.
25 | """
26 | def __init__(self, filepath, k=4, p=4.0):
27 | """
28 | BTFDBBを読み込み,補間器を生成する.
29 | (読み込みには少し時間がかかります)
30 |
31 | Parameters
32 | ----------
33 | filepath : str
34 | BTFDBBのファイルのパス
35 | 拡張子が".zip"の場合は"Ubo2003"
36 | 拡張子が".btf"の場合は"Ubo2014"
37 | k : int
38 | 補間に用いる近傍点の数
39 | p : float
40 | 逆距離加重補間の近傍点の影響の強さを決定する
41 | pは小さいほど近傍点の値の影響が大きくなる
42 |
43 | Methods
44 | -------
45 | angles_xy_to_pixel(tl, pl, tv, pv, x, y)
46 | `tl`, `pl`, `tv`, `pv`の角度条件で`x`,`y`の座標の画像値を補間して返す
47 | angles_uv_to_pixel(tl, pl, tv, pv, u, v)
48 | `tl`, `pl`, `tv`, `pv`の角度条件で`u`,`v`の座標の画像値を補間して返す
49 | angles_to_image(tl, pl, tv, pv)
50 | `tl`, `pl`, `tv`, `pv`の角度条件の画像値を補間して返す
51 |
52 | Notes
53 | -----
54 | 光源側の角度は`tl`,`pl`
55 | 観測側の角度は`tv`,`pv`
56 | """
57 | self.k = k
58 | self.p = p
59 |
60 | # BTFDBBの読み込み
61 | root, ext = os.path.splitext(filepath)
62 | if ext==".zip":
63 | btf = Ubo2003(filepath)
64 | elif ext==".btf":
65 | btf = Ubo2014(filepath)
66 | elif ext==".npz":
67 | # https://github.com/2-propanol/btfnpz_helper
68 | from btfnpz import Btfnpz
69 | btf = Btfnpz(filepath)
70 | else:
71 | raise Exception("The filepath must have a .zip or .btf extension.")
72 |
73 | # 画像のサイズを取得
74 | angles_list = list(btf.angles_set)
75 | num = len(angles_list)
76 | img_dummy = btf.angles_to_image(*angles_list[0])
77 | height, width, channel = img_dummy.shape
78 | dtype = img_dummy.dtype
79 | self.__num = num
80 | self.__height = height
81 | self.__width = width
82 |
83 | # すべてのファイルの実態と角度情報を読み込みリストを生成
84 | self.__images = np.empty((num, height, width, channel), dtype=dtype)
85 | points = np.empty((num, 6), dtype=np.float32)
86 |
87 | time_now = (datetime.datetime.now()).strftime('%Y-%m-%d %H:%M:%S')
88 | print(f"{time_now} INFO [MeasuredBTF] Loading BTF dataset \"{filepath}\" ..")
89 | if use_tqdm:
90 | angles_iterable = tqdm(angles_list)
91 | else:
92 | angles_iterable = angles_list
93 |
94 | for i, (tl, pl, tv, pv) in enumerate(angles_iterable):
95 | # 画像と角度を読み込み
96 | img_bgr = btf.angles_to_image(tl, pl, tv, pv)
97 |
98 | # 角度を球面座標から直交座標へ変換
99 | xl, yl, zl = spherical2orthogonal(1.0, tl, pl)
100 | xv, yv, zv = spherical2orthogonal(1.0, tv, pv)
101 | point = np.array([xl, yl, zl, xv, yv, zv], dtype=np.float32)
102 |
103 | # 画像と角度をリストに保存
104 | self.__images[i] = img_bgr
105 | points[i] = point
106 |
107 | # 角度の情報からKDTreeを構築
108 | self.__kd_tree = cKDTree(points)
109 |
110 | def __uv_to_xy(self, u, v):
111 | """uv座標(float)を,BTF画像に対応するxy座標(int)に変換する
112 | """
113 | xf = np.mod(u * (self.__width-1), self.__width)
114 | yf = np.mod(v * (self.__height-1), self.__height)
115 | x = np.array(xf, dtype=np.uint16)
116 | y = np.array(yf, dtype=np.uint16)
117 | return x, y
118 |
119 | def angles_xy_to_pixel(self, tl, pl, tv, pv, x, y):
120 | """
121 | `tl`, `pl`, `tv`, `pv`の角度条件で`x`,`y`の座標の画像値を補間して返す
122 |
123 | Parameters
124 | ----------
125 | tl, pl : array of floats
126 | 光源の方向(tl:theta, pl:phi)
127 | tv, pv : array of floats
128 | カメラの方向(tv:theta, pv:phi)
129 | x, y : array of floatss
130 | BTFのxy画像座標
131 |
132 | Returns
133 | -------
134 | pixel : array of floats
135 | BTFの画素値
136 | """
137 | # 角度は球面座標から直交座標へ変換する.
138 | xl, yl, zl = spherical2orthogonal(1.0, tl, pl)
139 | xv, yv, zv = spherical2orthogonal(1.0, tv, pv)
140 | point = np.array([xl, yl, zl, xv, yv, zv], dtype=np.float32).T
141 |
142 | # k近傍探索を実行
143 | # 距離はl2ノルム
144 | distance, index = self.__kd_tree.query(point, k=self.k, p=2, workers=-1)
145 |
146 | # 対応する角度・xy座標のBTF画像の値を取得
147 | index = np.clip(index, 0, self.__num-1)
148 | x = np.expand_dims(np.clip(x, 0, self.__width-1), axis=-1)
149 | y = np.expand_dims(np.clip(y, 0, self.__height-1), axis=-1)
150 | btf_values = self.__images[index, y, x]
151 |
152 | if self.k==1:
153 | # 補間なし
154 | pixel = btf_values
155 | else:
156 | # 逆距離加重法による補間
157 | weights = np.expand_dims( 1/(distance+10**-32)**self.p, axis=-1)
158 | pixel = np.sum(btf_values * weights, axis=-2) / np.sum(weights, axis=-2)
159 |
160 | return pixel
161 |
162 | def angles_uv_to_pixel(self, tl, pl, tv, pv, u, v):
163 | """
164 | `tl`, `pl`, `tv`, `pv`の角度条件で`u`,`v`の座標の画像値を補間して返す.
165 |
166 | Parameters
167 | ----------
168 | tl, pl : array of floats
169 | 光源の方向(tl:theta, pl:phi)
170 | tv, pv : array of floats
171 | カメラの方向(tv:theta, pv:phi)
172 | u, v : array of floatss
173 | BTFのuv画像座標
174 |
175 | Returns
176 | -------
177 | pixel : array of floats
178 | BTFの画素値
179 | """
180 | x, y = self.__uv_to_xy(u, v)
181 | return self.angles_xy_to_pixel(tl, pl, tv, pv, x, y)
182 |
183 | def angles_to_image(self, tl, pl, tv, pv):
184 | """
185 | `tl`, `pl`, `tv`, `pv`の角度条件の画像を補間して返す.
186 |
187 | Parameters
188 | ----------
189 | tl, pl : array of floats
190 | 光源の方向(tl:theta, pl:phi)
191 | tv, pv : array of floats
192 | カメラの方向(tv:theta, pv:phi)
193 |
194 | Returns
195 | -------
196 | image : array of floats
197 | BTFの画像
198 | """
199 | x = np.arange(self.__width)
200 | y = np.arange(self.__height)
201 | return self.angles_xy_to_pixel(tl, pl, tv, pv, x, y)
202 |
--------------------------------------------------------------------------------
/custom_bsdf/utils/coord_system_transfer.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 | def spherical2orthogonal(r, theta, phi):
4 | """
5 | 球面座標から直交座標へ変換する.
6 | thetaとphiの単位は度.
7 | """
8 | theta = np.deg2rad(theta)
9 | phi = np.deg2rad(phi)
10 | x = r * np.sin(theta) * np.cos(phi)
11 | y = r * np.sin(theta) * np.sin(phi)
12 | z = r * np.cos(theta)
13 | return x, y, z
14 |
15 | def orthogonal2spherical(x, y, z):
16 | """
17 | 直交座標から球面座標へ変換する.
18 | thetaとphiの単位は度.
19 | """
20 | r = np.sqrt(x*x+y*y+z*z)
21 | theta = np.arccos(z/r)
22 | x_for_arccos = x / (np.sqrt(x*x+y*y) + 10**-32)
23 | phi = np.sign(y) * np.arccos( np.clip(x_for_arccos, -1, 1) )
24 | return r, np.rad2deg(theta), np.rad2deg(phi)
25 |
26 | def mirror_uv(uv):
27 | """
28 | UV座標の境界が反転するように変換
29 |
30 | Parameters
31 | ----------
32 | uv : np.ndarray
33 | 変換前のUV座標
34 |
35 | Returns
36 | -------
37 | uv_mirror : np.ndarray
38 | 変換後のUV座標
39 |
40 | Notes
41 | -----
42 | 変換前 -> 変換後
43 | 0.0 -> 0.0
44 | 0.1 -> 0.1
45 | 0.9 -> 0.9
46 | 1.1 -> 1.9
47 | 1.9 -> 1.1
48 | 2.1 -> 2.1
49 | 2.9 -> 2.9
50 | 3.1 -> 3.9
51 | 3.9 -> 3.1
52 | 4.1 -> 4.1
53 | """
54 | uv_int = np.floor(uv)
55 | mask_to_mirror = np.mod(uv_int, 2)
56 | uv_mirror = np.where(mask_to_mirror, 1 - uv + 2*uv_int, uv)
57 | return uv_mirror
58 |
--------------------------------------------------------------------------------
/documents/cloth_diffuse_checker.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elerac/btf-rendering/c7209b865b1bfe54ee0b6df6d3c3f06e46a7bcad/documents/cloth_diffuse_checker.jpg
--------------------------------------------------------------------------------
/documents/cloth_fabric12.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elerac/btf-rendering/c7209b865b1bfe54ee0b6df6d3c3f06e46a7bcad/documents/cloth_fabric12.jpg
--------------------------------------------------------------------------------
/documents/cloth_wool.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elerac/btf-rendering/c7209b865b1bfe54ee0b6df6d3c3f06e46a7bcad/documents/cloth_wool.jpg
--------------------------------------------------------------------------------
/documents/dou2023.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elerac/btf-rendering/c7209b865b1bfe54ee0b6df6d3c3f06e46a7bcad/documents/dou2023.jpg
--------------------------------------------------------------------------------
/documents/kavoosighafi2023.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elerac/btf-rendering/c7209b865b1bfe54ee0b6df6d3c3f06e46a7bcad/documents/kavoosighafi2023.jpg
--------------------------------------------------------------------------------
/documents/matpreview_corduroy.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elerac/btf-rendering/c7209b865b1bfe54ee0b6df6d3c3f06e46a7bcad/documents/matpreview_corduroy.jpg
--------------------------------------------------------------------------------
/documents/matpreview_impalla.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elerac/btf-rendering/c7209b865b1bfe54ee0b6df6d3c3f06e46a7bcad/documents/matpreview_impalla.jpg
--------------------------------------------------------------------------------
/documents/matpreview_leather09.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elerac/btf-rendering/c7209b865b1bfe54ee0b6df6d3c3f06e46a7bcad/documents/matpreview_leather09.jpg
--------------------------------------------------------------------------------
/documents/simple_sphere_impalla.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elerac/btf-rendering/c7209b865b1bfe54ee0b6df6d3c3f06e46a7bcad/documents/simple_sphere_impalla.jpg
--------------------------------------------------------------------------------
/documents/simple_sphere_leather10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elerac/btf-rendering/c7209b865b1bfe54ee0b6df6d3c3f06e46a7bcad/documents/simple_sphere_leather10.jpg
--------------------------------------------------------------------------------
/documents/simple_sphere_p1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elerac/btf-rendering/c7209b865b1bfe54ee0b6df6d3c3f06e46a7bcad/documents/simple_sphere_p1.jpg
--------------------------------------------------------------------------------
/documents/simple_sphere_p2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elerac/btf-rendering/c7209b865b1bfe54ee0b6df6d3c3f06e46a7bcad/documents/simple_sphere_p2.jpg
--------------------------------------------------------------------------------
/documents/simple_sphere_p32.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elerac/btf-rendering/c7209b865b1bfe54ee0b6df6d3c3f06e46a7bcad/documents/simple_sphere_p32.jpg
--------------------------------------------------------------------------------
/documents/simple_sphere_p4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elerac/btf-rendering/c7209b865b1bfe54ee0b6df6d3c3f06e46a7bcad/documents/simple_sphere_p4.jpg
--------------------------------------------------------------------------------
/download_large_data.py:
--------------------------------------------------------------------------------
1 | import urllib.error
2 | import urllib.request
3 | import os
4 | import time
5 | import ssl
6 | ssl._create_default_https_context = ssl._create_unverified_context
7 |
8 | def download_file(url, dst_dir):
9 | dst_path = os.path.join(dst_dir, os.path.basename(url))
10 | headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36"}
11 | request = urllib.request.Request(url, headers=headers)
12 | try:
13 | with urllib.request.urlopen(request) as web_file, open(dst_path, 'wb') as local_file:
14 | local_file.write(web_file.read())
15 | except urllib.error.URLError as e:
16 | print(e)
17 |
18 | def main():
19 | print("UBO2003 BTF Dataset")
20 | ubo2003_dir = "UBO2003"
21 | os.makedirs(ubo2003_dir, exist_ok=True)
22 |
23 | file_path = f"{ubo2003_dir}/UBO_IMPALLA256.zip"
24 | if os.path.exists(file_path):
25 | print(f" {file_path} is already exists")
26 | else:
27 | print(f" Download {file_path}...")
28 | download_file("http://cg.cs.uni-bonn.de/fileadmin/btf/UBO2003/IMPALLA/ONEFILE/UBO_IMPALLA256.zip", ubo2003_dir)
29 | time.sleep(1)
30 |
31 | file_path = f"{ubo2003_dir}/UBO_WOOL256.zip"
32 | if os.path.exists(file_path):
33 | print(f" {file_path} is already exists")
34 | else:
35 | print(f" Download {file_path}...")
36 | download_file("http://cg.cs.uni-bonn.de/fileadmin/btf/UBO2003/WOOL/ONEFILE/UBO_WOOL256.zip", ubo2003_dir)
37 | time.sleep(1)
38 |
39 |
40 | print("Envmap from hdrihaven.com")
41 | cloth_dir = "scenes/cloth"
42 | os.makedirs(cloth_dir, exist_ok=True)
43 |
44 | file_path = f"{cloth_dir}/skylit_garage_1k.hdr"
45 | if os.path.exists(file_path):
46 | print(f" {file_path} is already exists")
47 | else:
48 | print(f" Download {file_path}...")
49 | download_file("https://hdrihaven.com/files/hdris/skylit_garage_1k.hdr", cloth_dir)
50 | time.sleep(1)
51 |
52 |
53 | if __name__=="__main__":
54 | main()
55 |
--------------------------------------------------------------------------------
/rendering.py:
--------------------------------------------------------------------------------
1 | import os
2 | import argparse
3 | parser = argparse.ArgumentParser()
4 | parser.add_argument("-i", "--input", type=str, default="scenes/simple_sphere/simple_sphere.xml", help="Input scene filepath (.xml)")
5 | parser.add_argument("-o", "--output", type=str, default="rendered.jpg", help="Output image filepath (.jpg, .png)")
6 | parser.add_argument("-m", "--mode", type=str, default="scalar_rgb", help="Rendering mode (scalar_rgb or gpu_rgb)")
7 | args = parser.parse_args()
8 |
9 | import mitsuba
10 | mitsuba.set_variant(args.mode)
11 | from mitsuba.core import Bitmap, Struct, Thread
12 | from mitsuba.core.xml import load_file
13 | from mitsuba.render import register_bsdf
14 |
15 | from custom_bsdf.measuredbtf import MeasuredBTF
16 |
17 | def main():
18 | # Register MeasuredBTF
19 | register_bsdf('measuredbtf', lambda props: MeasuredBTF(props))
20 |
21 | # Filename
22 | filename_src = args.input
23 | filename_dst = args.output
24 |
25 | # Load an XML file
26 | Thread.thread().file_resolver().append(os.path.dirname(filename_src))
27 | scene = load_file(filename_src)
28 |
29 | # Rendering
30 | scene.integrator().render(scene, scene.sensors()[0])
31 |
32 | # Save image
33 | film = scene.sensors()[0].film()
34 | bmp = film.bitmap(raw=True)
35 | bmp.convert(Bitmap.PixelFormat.RGB, Struct.Type.UInt8, srgb_gamma=True).write(filename_dst)
36 |
37 | if __name__=="__main__":
38 | main()
39 |
--------------------------------------------------------------------------------
/scenes/cloth/cloth.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/scenes/cloth/cloth_diffuse_checker.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/scenes/cloth/cloth_ubo2003_gpu.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/scenes/cloth/cloth_ubo2014_gpu.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/scenes/matpreview/matpreview.serialized:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elerac/btf-rendering/c7209b865b1bfe54ee0b6df6d3c3f06e46a7bcad/scenes/matpreview/matpreview.serialized
--------------------------------------------------------------------------------
/scenes/matpreview/matpreview.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/scenes/matpreview/matpreview_ubo2003_gpu.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/scenes/matpreview/matpreview_ubo2014_gpu.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/scenes/simple_sphere/simple_sphere.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/scenes/simple_sphere/simple_sphere_ubo2003_gpu.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/scenes/simple_sphere/simple_sphere_ubo2014_gpu.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------