赞
踩
先祭上NeRF中的体渲染公式
C
(
t
N
+
1
)
=
∑
n
=
1
N
T
n
⋅
(
1
−
exp
(
−
σ
n
δ
n
)
)
⋅
c
n
, where
T
n
=
exp
(
∑
k
=
1
n
−
1
−
σ
k
δ
k
)
\mathbf{C}(t_{N+1})=\sum_{n=1}^N\mathcal{T}_n\cdot (1-\text{exp}(-\sigma_n\delta_n))\cdot \mathbf{c}_n\text{, where }\mathcal{T}_n=\text{exp}(\sum_{k=1}^{n-1}-\sigma_k\delta_k)
C(tN+1)=n=1∑NTn⋅(1−exp(−σnδn))⋅cn, where Tn=exp(k=1∑n−1−σkδk)
其中
t
N
t_N
tN为射线参数方程中的参数
t
t
t,(
R
(
t
)
=
o
+
t
d
R(t)=o+td
R(t)=o+td)且
t
1
=
0
t_1=0
t1=0,
T
\mathcal{T}
T为透射率,
δ
n
=
t
n
+
1
−
t
n
\delta_n=t_{n+1}-t_n
δn=tn+1−tn为第n+1个采样点与第n个采样点的间距,
c
n
\mathbf{c}_n
cn为该段体中的颜色常量。以上公式是离散同质的,认为在每段中颜色和体密度不变。
通过透射率和不透明度
α
\alpha
α的关系转换
α
n
=
1
−
T
(
n
→
n
+
1
)
\alpha_n=1-T(n\to n+1)
αn=1−T(n→n+1)可以将上述公式转换为:
C
(
t
N
+
1
)
=
∑
n
=
1
N
T
n
⋅
α
n
⋅
c
n
, where
T
n
=
∏
n
=
1
N
−
1
(
1
−
α
n
)
\mathbf{C}(t_{N+1})=\sum_{n=1}^N\mathcal{T}_n\cdot \alpha_n\cdot \mathbf{c}_n\text{, where }\mathcal{T}_n=\prod_{n=1}^{N-1}(1-\alpha_n)
C(tN+1)=n=1∑NTn⋅αn⋅cn, where Tn=n=1∏N−1(1−αn)
不懂体渲染的人第一眼绝对是看不懂的,下面结合一篇专门针对NeRF中体渲染的论文细说。
在光栅化中的透明材质渲染中用到了 α \alpha α混合,在OpenGL中叫做blending,alpha test。是在渲染透明贴图中使用的技术,具体如下:
现有两个纹理都是RGBA图像,其中绿色的纹理alpha值为0.6,红色完全不透明。现在的情况是绿色纹理有一部分叠加到了红色纹理上,该怎么渲染?当然是这样渲染:
没有叠加的部分还是原来纹理中的颜色,不透明度也是原来的值。叠加部分的颜色加权相加,但是叠加部分的不透明度需要另算。
使用GL中形式化的描述如下:
红色纹理作为底片,在GL中已经是保存在某个Color Buffer中,这里称它为Destination Color。而绿色纹理作为叠加在红色底片上的纹理这里称为Source Color,现在要求的是最终的Result Color。GL以这样的方式混合:
C
R
e
s
u
l
a
t
=
C
S
o
u
r
c
e
∗
F
S
o
u
r
c
e
+
C
D
e
s
t
i
n
a
t
i
o
n
∗
F
D
e
s
t
i
n
a
t
i
o
n
C_{Resulat}={\color{green}C_{Source}*F_{Source}}+{\color{red}C_{Destination}*F_{Destination}}
CResulat=CSource∗FSource+CDestination∗FDestination
C当然是颜色,F为alpha因子,需要程序员手动设置。这里用这样的策略设置,上方的绿色alpha值为0.6,那红色就给1-0.6=0.4,这样做也有一定的道理,而这也是体渲染的核心思想:
不透明度是一个 [ 0 , 1 ] [0,1] [0,1]之间的值,说明的是多少光没有穿透这个纹理,值在 [ 0 , 1 ] [0,1] [0,1]之间很容易让人联想到概率,体渲染在此引入随机性,后面细锁。
叠加部分的颜色很好算,如上式用不透明度加权求和:
C
R
e
s
u
l
t
=
0.6
∗
Vec3
(
0.0
,
1.0
,
0.0
)
+
(
1
−
0.6
)
∗
Vec3
(
1.0
,
0.0
,
0.0
)
=
(
0.6
,
0.4
,
0
)
C_{Result}=0.6*\text{Vec3}(0.0,1.0,0.0)+(1-0.6)*\text{Vec3}(1.0,0.0,0.0)=(0.6,0.4,0)
CResult=0.6∗Vec3(0.0,1.0,0.0)+(1−0.6)∗Vec3(1.0,0.0,0.0)=(0.6,0.4,0)
而透明度怎么算?上面说不透明度是有多少光不能穿过该纹理,那么光经过两个不透明纹理有多少光不能透射就是混合后的不透明度:
α
C
o
m
p
o
s
i
t
i
n
g
=
1
−
(
1
−
0.6
)
∗
(
1
−
0.4
)
=
1
−
0.4
∗
0.6
=
0.76
\alpha_{Compositing}=1-(1-0.6)*(1-0.4)=1-0.4*0.6=0.76
αCompositing=1−(1−0.6)∗(1−0.4)=1−0.4∗0.6=0.76
就是这么简单。而这些都是发生在离散域中的,纹理就是一张没有厚度的图像(或者说表面),体渲染是在体数据中进行的,推导在连续域中推导,但是最终的计算还是要回到离散。
计算时关闭Depth Test,顺序不能反。
这是我的理解,因为时间有限,忙着开题,仅从相关的资料中了解了个大概,有问题请指出。
背景:光栅化和光追对于一些体状目标渲染质量不好,如云,火焰等在物理模拟中多用粒子系统,最后的渲染还是跟光栅化和光追的渲染方式相同,用纹理,这样的话最终的效果就被纹理限制死了,达不到逼真的效果。体渲染用更好的物理模型建模这种渲染,效果更好。
体渲染中认为在空间中有许多粒子,不考虑光照,光照射在该空间中,这些粒子会吸收光能,并辐射出光能,不考虑粒子对光的散射,在连续3D空间中通过体密度来计算视相关光能的累计从而得到最后的RGB值。
之前的体渲染使用体数据进行渲染,典型的体数据有CT,体素数据,在某个维度以固定间距堆叠的RGBA图像,或者RGB-A转移方程或者3D纹理。体渲染通过发射射线穿过这些体数据,然后计算射线上采样点的累计光能得到RGB,这个过程叫做着色与混合,具体的计算公式推导如下。
在3D空间中定义体密度 σ ( x ) \sigma(\mathbf{x}) σ(x),其中 x \mathbf{x} x为空间中的一个位置,体密度的含义为一条射线在该位置碰到粒子的可能性微元,可以这么理解,一团云中有水汽,但是云是动态的,在一个穿过云层的连续一维射线上,每个位置上都有可能有水汽也有可能没有,体密度字面理解就是这个体数据中粒子在所有地方的密度。
射线可以用参数方程 R ( t ) = o + t d R(t)=o+td R(t)=o+td表示,体密度也可以用 t t t来参数化 σ ( t ) \sigma(t) σ(t),这样的话自变量是标量 t t t方便计算。透射率 T \mathcal{T} T和体密度密切相关,含义为在区间 [ 0 , t ) [0,t) [0,t)上射线都没有碰到粒子的可能性。可以看出这体密度和透射率都是随机变量。
考虑
T
(
t
+
d
t
)
\mathcal{T}(t+dt)
T(t+dt),含义为在时间步
t
+
d
t
t+dt
t+dt射线没有碰到粒子的概率,显然等于时间步
t
t
t射线没有碰到粒子的概率乘以1减去
σ
(
t
)
\sigma(t)
σ(t)乘以
d
t
dt
dt
T
(
t
)
⋅
(
1
−
σ
(
t
)
d
t
)
T(t)\cdot(1-\sigma(t)dt)
T(t)⋅(1−σ(t)dt)
其中1减去
σ
(
t
)
\sigma(t)
σ(t)乘以
d
t
dt
dt表示在这一个时=间步微分中射线碰到粒子的概率(体密度定义)。于是有定理1:
T
(
t
+
d
t
)
=
T
(
t
)
⋅
(
1
−
σ
(
t
)
d
t
)
T
(
t
+
d
t
)
−
T
(
t
)
d
t
=
−
T
(
t
)
⋅
σ
(
t
)
T
′
(
t
)
T
(
t
)
=
−
σ
(
t
)
∫
a
b
T
′
(
t
)
T
(
t
)
d
t
=
−
∫
a
b
σ
(
t
)
d
t
l
n
(
T
(
t
)
)
∣
a
b
=
−
∫
a
b
σ
(
t
)
d
t
T
(
b
)
−
T
(
a
)
=
exp
(
−
∫
a
b
σ
(
t
)
d
t
)
⟹
T
(
b
→
a
)
=
exp
(
−
∫
a
b
σ
(
t
)
d
t
)
\mathcal{T}(t+dt)=\mathcal{T}(t)\cdot(1-\sigma(t)dt)\\ \dfrac{\mathcal{T}(t+dt)-\mathcal{T}(t)}{dt}=-\mathcal{T}(t)\cdot\sigma(t)\\ \dfrac{\mathcal{T}'(t)}{\mathcal{T}(t)}=-\sigma(t)\\ \int_a^b\dfrac{\mathcal{T}'(t)}{\mathcal{T}(t)}dt=-\int_a^b{\sigma(t)}dt\\ ln(\mathcal{T}(t))\big\vert_a^b=-\int_a^b{\sigma(t)}dt\\ \mathcal{T}(b)-\mathcal{T}(a)=\text{exp}(-\int_a^b{\sigma(t)}dt) \Longrightarrow \mathcal{T}(b\to a)=\text{exp}(-\int_a^b{\sigma(t)}dt)
T(t+dt)=T(t)⋅(1−σ(t)dt)dtT(t+dt)−T(t)=−T(t)⋅σ(t)T(t)T′(t)=−σ(t)∫abT(t)T′(t)dt=−∫abσ(t)dtln(T(t))
ab=−∫abσ(t)dtT(b)−T(a)=exp(−∫abσ(t)dt)⟹T(b→a)=exp(−∫abσ(t)dt)
定理1是透射率
T
\mathcal{T}
T和体密度
σ
\sigma
σ的关系,透射率含义为从a到b区间上射线不碰到粒子的概率。之后透射率
T
(
t
)
\mathcal{T}(t)
T(t)为
T
(
0
→
t
)
\mathcal{T}(0\to t)
T(0→t)的简写。
这个幂函数也符合直觉,一条穿过体数据的射线,时间 t t t越大,碰到粒子的可能性就越大,透射率就越小:
透射率和体密度都是有随机性的,如上所说透射率
T
(
t
)
\mathcal{T}(t)
T(t)为光线在其参数在区间
[
0
,
t
)
[0,t)
[0,t)上传播时没有碰到粒子的概率,那么
1
−
T
(
t
)
=
1
−
exp
(
−
∫
0
t
σ
(
t
)
d
t
)
1-\mathcal{T}(t)=1-\text{exp}(-\int_0^t\sigma(t)dt)
1−T(t)=1−exp(−∫0tσ(t)dt)
就是在区间
[
0
,
t
)
[0,t)
[0,t)上射线碰到粒子(alpha)的CDF。而在
t
t
t时刻射线碰到粒子而停止传播的概率为
T
⋅
σ
(
t
)
\mathcal{T}\cdot\sigma(t)
T⋅σ(t),这是对应的PDF。
因为射线在某个时刻停止传播的PDF为
T
(
t
)
σ
(
t
)
\mathcal{T}(t)\sigma(t)
T(t)σ(t),所以可以用这项计算颜色的期望:假设射线传播到D时刻停止传播,但是有一个背景色(其实跟之前GL的Destination Color一样,就算光被阻挡也不是全部被阻挡,还是能看到背景色的)
C
=
∫
0
D
T
(
t
)
⋅
σ
(
t
)
⋅
c
(
t
)
d
t
+
T
(
D
)
⋅
c
b
g
\mathbf{C}=\int_0^D\mathcal{T}(t)\cdot \sigma(t)\cdot\mathbf{c}(t)dt+\mathcal{T}(D)\cdot \mathbf{c}_{bg}
C=∫0DT(t)⋅σ(t)⋅c(t)dt+T(D)⋅cbg
后面忽略背景色。
计算离散颜色期望。
如果有RGBA Transfer Function的话,就可以通过查询该函数得到任意一个点的RGBA值,可以使用连续求法求,但是大多数情况是做不到的,比如体素。因此需要一个中离散求法,对连续函数采样得到离散值,然后将该离散的阶梯函数代替原连续函数,这样在每个离散区间内可以认为体密度和颜色都是同质的(homogeneous),Lemma 1就是对同质区间求颜色期望。
假设射线在区间
[
a
,
b
]
[a,b]
[a,b]传播,该区间上的体密度和颜色都是常量
σ
a
,
c
a
\sigma_a, c_a
σa,ca,现在求该区间颜色期望:
C
(
a
→
b
)
=
∫
a
b
T
(
a
→
t
)
⋅
σ
(
t
)
⋅
c
(
t
)
d
t
=
σ
a
⋅
c
a
∫
a
b
T
(
a
→
t
)
d
t
=
σ
a
⋅
c
a
∫
a
b
exp
(
−
∫
a
t
σ
(
u
)
d
u
)
d
t
=
σ
a
⋅
c
a
∫
a
b
exp
(
−
σ
a
u
∣
a
t
)
d
t
=
σ
a
⋅
c
a
⋅
e
x
p
(
−
σ
a
(
t
−
a
)
)
−
σ
a
∣
a
b
=
c
a
⋅
(
1
−
exp
(
−
σ
a
(
b
−
a
)
)
C(a→b)=∫baT(a→t)⋅σ(t)⋅c(t)dt=σa⋅ca∫baT(a→t)dt=σa⋅ca∫baexp(−∫taσ(u)du)dt=σa⋅ca∫baexp(−σau|ta)dt=σa⋅ca⋅exp(−σa(t−a))−σa|ba=ca⋅(1−exp(−σa(b−a))
后面会用到。
透射率可乘性
对于a到c区间透射率,如果中间有一个点b,a到c的透射率等于a到b的透射率乘以b到c的透射率:
T
(
a
→
c
)
=
exp
(
−
∫
a
c
σ
(
t
)
d
t
)
=
exp
(
−
[
∫
a
b
σ
(
t
)
d
t
+
∫
b
c
σ
(
t
)
d
t
]
)
=
exp
(
−
[
∫
a
b
σ
(
t
)
d
t
]
)
∗
exp
(
−
[
∫
b
c
σ
(
t
)
d
t
]
)
=
T
(
a
→
b
)
∗
T
(
b
→
c
)
T(a→c)=exp(−∫caσ(t)dt)=exp(−[∫baσ(t)dt+∫cbσ(t)dt])=exp(−[∫baσ(t)dt])∗exp(−[∫cbσ(t)dt])=T(a→b)∗T(b→c)
这是符合直觉的,可以将b想象为一个夹在a,c之间的半透明纹理。
离散透射率
对于离散数据,首先形式化语言描述:给定一个区间构成的集合
{
[
t
n
,
t
n
+
1
]
}
n
=
1
N
\{[t_n,t_{n+1}]\}_{n=1}^N
{[tn,tn+1]}n=1N,共N个区间,
t
1
=
0
,
δ
n
=
t
n
+
1
−
t
n
t_1=0,\delta_n=t_{n+1}-t_n
t1=0,δn=tn+1−tn,
σ
(
t
)
\sigma(t)
σ(t)表示第t个区间内的体密度。每个区间内的提数据同质,即体密度和颜色都相同。则透射率的计算如下:
T
n
=
T
(
t
n
)
=
T
(
0
→
t
n
)
=
exp
(
−
∫
0
t
n
σ
(
t
)
d
t
)
=
exp
(
∑
k
=
1
n
−
1
−
σ
k
δ
k
)
\mathcal{T}_n=\mathcal{T}(t_n)=\mathcal{T}(0\to t_n)=\text{exp}\bigg(-\int_0^{t_n}\sigma(t)dt\bigg)=\exp\bigg(\sum_{k=1}^{n-1}-\sigma_k\delta_k\bigg)
Tn=T(tn)=T(0→tn)=exp(−∫0tnσ(t)dt)=exp(k=1∑n−1−σkδk)
实际上就是阶梯函数求面积或者说是黎曼和。
离散体渲染
对于离散体数据,可以认为每个区间都是同质的,可以对每段求颜色期望然后累加:
C
(
t
N
+
1
)
=
∑
n
=
1
N
∫
t
n
t
n
+
1
T
(
t
)
⋅
σ
n
⋅
c
n
d
t
=
∑
n
=
1
N
∫
t
n
t
n
+
1
T
(
0
→
t
n
)
⋅
T
(
t
n
→
t
)
⋅
σ
n
⋅
c
n
d
t
from Lemma1
=
∑
n
=
1
N
T
(
0
→
t
n
)
∫
t
n
t
n
+
1
T
(
t
n
→
t
)
⋅
σ
n
⋅
c
n
d
t
T
(
0
→
t
n
)
is constant
=
∑
n
=
1
N
T
(
0
→
t
n
)
⋅
(
1
−
exp
(
−
σ
n
(
t
n
+
1
−
t
n
)
)
)
⋅
c
n
from Lemma2
=
∑
n
=
1
N
T
(
0
→
t
n
)
⋅
(
1
−
exp
(
−
σ
n
δ
n
)
)
⋅
c
n
C(tN+1)=N∑n=1∫tn+1tnT(t)⋅σn⋅cndt=N∑n=1∫tn+1tnT(0→tn)⋅T(tn→t)⋅σn⋅cndtfrom Lemma1=N∑n=1T(0→tn)∫tn+1tnT(tn→t)⋅σn⋅cndtT(0→tn) is constant=N∑n=1T(0→tn)⋅(1−exp(−σn(tn+1−tn)))⋅cnfrom Lemma2=N∑n=1T(0→tn)⋅(1−exp(−σnδn))⋅cn
上面两个式子经过一个代换就是NeRF中的式子。
考虑不透明度
α
\alpha
α,其实表示的是光经过一个半透明纹理时有多少光被阻挡了(只不过光栅化中纹理没有厚度),也就是不透光率
1
−
T
(
a
→
b
)
1-\mathcal{T(a\to b)}
1−T(a→b),在离散情况下为
1
−
exp
(
−
σ
k
δ
k
)
1-\text{exp}(-\sigma_k\delta_k)
1−exp(−σkδk),用opacity代替得到最终的表达式:
C
(
t
N
+
1
)
=
∑
n
=
1
N
T
n
⋅
α
n
⋅
c
n
, where
T
n
=
∏
n
=
1
N
−
1
(
1
−
α
n
)
\mathbf{C}(t_{N+1})=\sum_{n=1}^N{\color{red}\mathcal{T}_n}\cdot{\color{green}\alpha_n}\cdot {\color{blue}\mathbf{c}_n}\text{, where }\mathcal{T}_n=\prod_{n=1}^{N-1}(1-\alpha_n)
C(tN+1)=n=1∑NTn⋅αn⋅cn, where Tn=n=1∏N−1(1−αn)
其中透射率:
T
n
=
exp
(
∑
k
=
1
n
−
1
−
σ
k
δ
k
)
=
∏
k
=
1
n
−
1
exp
(
−
σ
k
δ
k
)
=
∏
k
=
1
n
−
1
(
1
−
α
k
)
\mathcal{T}_n=\exp\bigg(\sum_{k=1}^{n-1}-\sigma_k\delta_k\bigg)=\prod_{k=1}^{n-1}\text{exp}(-\sigma_k\delta_k)=\prod_{k=1}^{n-1}(1-\alpha_k)
Tn=exp(k=1∑n−1−σkδk)=k=1∏n−1exp(−σkδk)=k=1∏n−1(1−αk)
公式中的透射率来自出发点到采样点前一个坐标点
t
n
t_n
tn的透射率,
α
n
\alpha_n
αn为该坐标点到采样点之间的不透明度,
c
n
\mathbf{c}_n
cn为区间
[
t
n
,
t
n
+
1
]
[t_n,t_{n+1}]
[tn,tn+1]发光粒子的颜色