高光的强度取决于观察方向,观察方向与镜面反射方向接近时能看见高光 Intensity depends on view direction, b right near mirror reflection direction
观察方向v与镜面反射方向接近说明半程向量h与法线n接近(这样计算更加高效) V close to mirror direction ⇔ half vector near normal
在Blinn-Phong模型中镜面反射忽略了计算着色点吸收能量的比例
使用观察方向和镜面反射方向判断是否接近是Phong模型
增加p的值来限制高光范围(小高光p大约在100到200)
2.5 环境光 Ambient Term
环境光会照亮每一点,与直接光照方向,观察方向都无关,是一个常数 Shading that does not depend on anything, add constant color to account for disregarded illumination and fill in black shadows
这是一个非常大胆的简化 This is approximate / fake!
2.6 Blinn-Phong反射模型 Blinn-Phong Reflection Model
L
=
L
a
+
L
d
+
L
s
=
k
a
I
a
+
k
d
(
I
/
r
2
)
m
a
x
(
0
,
n
⋅
l
)
+
k
s
(
I
/
r
2
)
m
a
x
(
0
,
n
⋅
h
)
p
L =L_a+L_d+L_s =k_aI_a+k_d(I/r^2)max(0, n·l)+k_s(I/r^2)max(0, n·h)^p
L=La+Ld+Ls=kaIa+kd(I/r2)max(0,n⋅l)+ks(I/r2)max(0,n⋅h)p
3 着色频率 Shading Frequencies
3.1 逐三角形着色 Shade each triangle (flat shading)
每个三角形内部着色没有变化,一个平面一个法向量 Triangle face is flat — one normal vector
对于平滑的表面表现不是很理想 Not good for smooth surfaces
3.2 逐顶点着色 Shade each vertex (Gouraud shading)
从三角形的顶点插入颜色 Interpolate colors from vertices across triangle
每个顶点都有一个法线向量 Each vertex has a normal vector
定点外其余部分的颜色通过插值计算出来
3.3 逐像素着色 Shade each pixel (Phong shading)
在每个三角形上插入法线向量 Interpolate normal vectors across each triangle
在三角形内部每个像素都插值出一个法线方向,计算每个像素的完整着色模型 Compute full shading model at each pixel
不是Blinn-Phong反射模型(只是名字上撞了,不是一回事) Not the Blinn-Phong Reflectance Model
3.4 几种着色频率的对比 Shading Frequenciesy: Face, Vertex or Pixel
在模型几何较为复杂时,用简单的逐像素模型也能得到不错的效果,并且开销小
在模型几何较为简单时,逐像素效果更好,但性能要求高一些
3.5 定义逐顶点的法线 Defining Per-Vertex Normal Vectors
如果知道具体表示的几何体是什么样的,例如用多个顶点表示球体,则每个顶点的法线方向即为球心向顶点连线的方向 Best to get vertex normals from the underlying geometry, e.g. consider a sphere
否则必须从相邻三角形面推断出顶点法线,一个简单的方法是求相邻面法线的平均法线
根据相邻面的面积使用加权平均会得到更好的效果
3.6 定义逐像素的法线 Defining Per-Pixel Normal Vectors
使用顶点法线的重心插值 Barycentric interpolation of vertex normals
不要忘记对插值方向进行归一化 Don’t forget to normalize the interpolated directions
如果
α
,
β
,
γ
\alpha,\beta,\gamma
α,β,γ都是非负的,则该点在三角形内 Inside the triangle if all three coordinates are non-negative
例如,A点的坐标为(1, 0, 0)
(
x
,
y
)
=
α
A
+
β
B
+
γ
C
,
α
+
β
+
γ
=
1
(x,y)=\alpha A+\beta B+\gamma C,\quad\alpha+\beta+\gamma=1
(x,y)=αA+βB+γC,α+β+γ=1
7.2.2 利用面积确定系数 Geometric viewpoint — proportional areas
α
=
A
A
A
A
+
A
B
+
A
C
,
β
=
A
A
A
A
+
A
B
+
A
C
,
γ
=
A
A
A
A
+
A
B
+
A
C
\alpha = \frac { A _ { A } } { A _ { A } + A _ { B } + A _ { C } },\quad \beta = \frac { A _ { A } } { A _ { A } + A _ { B } + A _ { C } },\quad \gamma = \frac { A _ { A } } { A _ { A } + A _ { B } + A _ { C } }
α=AA+AB+ACAA,β=AA+AB+ACAA,γ=AA+AB+ACAA
利用叉积的模等于其形成三角形大小算出面积,得到简化公式
α
=
−
(
x
−
x
B
)
(
y
C
−
y
B
)
+
(
y
−
y
B
)
(
x
C
−
x
B
)
−
(
x
A
−
x
B
)
(
y
C
−
y
B
)
+
(
y
A
−
y
B
)
(
x
C
−
x
B
)
\alpha = \frac { - ( x - x _ { B } ) ( y _ { C } - y _ { B } ) + ( y - y _ { B } ) ( x _ { C } - x _ { B } ) } { - ( x _ { A } - x _ { B } ) ( y _ { C } - y _ { B } ) + ( y _ { A } - y _ { B } ) ( x _ { C } - x _ { B } ) }
α=−(xA−xB)(yC−yB)+(yA−yB)(xC−xB)−(x−xB)(yC−yB)+(y−yB)(xC−xB)
β
=
−
(
x
−
x
C
)
(
y
A
−
y
C
)
+
(
y
−
y
C
)
(
x
A
−
x
C
)
−
(
x
B
−
x
C
)
(
y
A
−
y
C
)
+
(
y
B
−
y
C
)
(
x
A
−
x
C
)
\beta = \frac { - ( x - x _ { C } ) ( y _ { A } - y _ { C } ) + ( y - y _ { C } ) ( x _ { A } - x _ { C } ) } { - ( x _ { B } - x _ { C } ) ( y _ { A } - y _ { C } ) + ( y _ { B } - y _ { C } ) ( x _ { A } - x _ { C } ) }
β=−(xB−xC)(yA−yC)+(yB−yC)(xA−xC)−(x−xC)(yA−yC)+(y−yC)(xA−xC)
γ
=
1
−
α
−
β
\gamma=1-\alpha-\beta
γ=1−α−β
7.2.4 重心坐标的使用 Using Barycentric Coordinates
V
=
α
V
A
+
β
V
B
+
γ
V
C
V=\alpha V_A+\beta V_B+\gamma V_C
V=αVA+βVB+γVC
其中
V
A
,
V
B
,
V
C
V_A, V_B, V_C
VA,VB,VC可以是位置、纹理坐标、颜色、法线、深度、材质等属性
V
A
,
V
B
,
V
C
V_A, V_B, V_C
VA,VB,VC can be positions, texture coordinates, color, normal, depth, material attributes…
重心坐标在投影变换下会发生变换,如果想利用重心坐标插值三维中的属性,应该在投影前就插值,具体的操作是对投影后的三角形应用逆变换再插值 Barycentric coordinates are not invariant under projection
8 纹理应用 Applying Textures
8.1 简单的纹理应用:定义漫反射系数 Simple Texture Mapping: Diffuse Color
for each rasterized screen sample (x,y):
(u,v) = evaluate texture coordinate at (x,y) //利用重心坐标算出每个采样点uv坐标
texcolor = texture.sample(u,v); //通过uv坐标查询纹理贴图
set sample’s color to texcolor; //通常定义的是漫反射系数Kd
9 纹理放大 Texture Magnification
9.1 问题
如果分辨率大,纹理小,在查询纹理贴图时会导致多个像素(pixel)映射到了同一个纹素(texel) insufficient texture resolution A pixel on a texture — a texel
解决方法:插值
9.2 双线性插值 Bilinear Interpolation
找到采样点临近的四个点,在水平竖直分别投影出s, t长度 Take 4 nearest sample locations, with texture values as labeled.
在
u
01
u_{01}
u01和
u
11
u_{11}
u11之间线性插值得到
u
1
u_{1}
u1,在
u
00
u_{00}
u00和
u
10
u_{10}
u10之间线性插值得到
u
0
u_{0}
u0,再在
u
1
u_1
u1和
u
0
u_0
u0之间线性插值得到采样点,这样操作后,采样点综合考虑了周围四个点的颜色
L
i
n
e
a
r
i
n
t
e
r
p
o
l
a
t
i
o
n
(
1
D
)
:
l
e
r
p
(
x
,
v
0
,
v
1
)
=
v
0
+
x
(
v
1
−
v
0
)
\rm{Linear\;interpolation (1D)}:lerp(x,v_0,v_1)=v_0+x(v_1-v_0)
Linearinterpolation(1D):lerp(x,v0,v1)=v0+x(v1−v0)
T
w
o
h
e
l
p
e
r
l
e
r
p
s
:
u
0
=
l
e
r
p
(
s
,
u
00
,
u
10
)
,
u
1
=
l
e
r
p
(
s
,
u
01
,
u
11
)
\rm{Two\;helper\;lerps}:u _ { 0 } = lerp( s , u _ { 00 } , u _ { 10 } ),\quad u _ { 1 } = lerp( s , u _ { 01 } , u _ { 11 } )
Twohelperlerps:u0=lerp(s,u00,u10),u1=lerp(s,u01,u11)
F
i
n
a
l
v
e
r
t
i
c
a
l
l
e
r
p
:
f
(
x
,
y
)
=
lerp
(
t
,
u
0
,
u
1
)
\rm{Final\;vertical\;lerp}:f ( x , y ) = \operatorname { lerp } ( t , u _ { 0 } , u _ { 1 } )
Finalverticallerp:f(x,y)=lerp(t,u0,u1)
9.3 双三次插值 Bicubic Interpolation
取周围16个点做竖直和水平的插值
每次用4个,做三次的插值,不是用线性的差值
双向三次插值运算量大,但是效果好
10 纹理过大的问题 Texture Magnification (hard case)
10.1 纹理过大的问题
当像素覆盖纹理过于大时,很难用一个像素代表覆盖纹理上多个纹素,就会出现走样现象
利用超采样反走样可行吗 Will supersampling work?——可行但是性能会明显降低
采样会引起走样,如果我们不采样,只得到一定范围的平均值就能解决问题
点查询 Point Query
范围查询 Range Query
10.2 解决方法:多级渐远纹理 Mipmap
10.2.1 Mipmap(L. Williams 83)
只能进行快速的、近似的、正方形的范围查询 Allowing (fast, approx., square) range queries
“Mip”来自拉丁语“multum in parvo”,意思是在一个小空间内有很多的东西 “Mip” comes from the Latin “multum in parvo", meaning a multitude in a small space
D
=
log
2
L
,
L
=
max
(
(
d
u
d
x
)
2
+
(
d
v
d
x
)
2
⋅
(
d
u
d
y
)
2
+
(
d
v
d
y
)
2
)
D=\log_2L,\quad L = \max ( \sqrt { ( \frac { d u } { d x } ) ^ { 2 } + ( \frac { d v } { d x } ) ^ { 2 } } \cdot \sqrt { ( \frac { d u } { d y } ) ^ { 2 } + ( \frac { d v } { d y } ) ^ { 2 } } )
D=log2L,L=max((dxdu)2+(dxdv)2⋅(dydu)2+(dydv)2)
在着色计算过程中逐像素地扰动表面法线,由此定义每个纹素高度偏移 Perturb surface normal per pixel(for shading computations only), “Height shift” per texel defined by a texture
二维下扰动法线的具体过程 How to perturb the normal (in flatland):定义切线然后算出法线的偏移
原本的表面法线
n
(
p
)
=
(
0
,
1
)
n(p) = (0, 1)
n(p)=(0,1)
p的导数
d
p
=
c
∗
[
h
(
p
+
1
)
−
h
(
p
)
]
dp = c * [h(p+1) - h(p)]
dp=c∗[h(p+1)−h(p)]
扰动法线是
n
(
p
)
=
(
−
d
p
,
1
)
.
n
o
r
m
a
l
i
z
e
d
(
)
n(p) = (-dp, 1).normalized()
n(p)=(−dp,1).normalized()
三维下扰动法线的具体过程 How to perturb the normal (in 3D)
原本的表面法线n§ = (0, 0, 1)
p的导数
d
p
/
d
u
=
c
1
∗
[
h
(
u
+
1
)
−
h
(
u
)
]
dp/du = c1 * [h(u+1) - h(u)]
dp/du=c1∗[h(u+1)−h(u)],
d
p
/
d
v
=
c
2
∗
[
h
(
v
+
1
)
−
h
(
v
)
]
dp/dv = c2 * [h(v+1) - h(v)]
dp/dv=c2∗[h(v+1)−h(v)]
扰动法线是
n
=
(
−
d
p
/
d
u
,
−
d
p
/
d
v
,
1
)
.
n
o
r
m
a
l
i
z
e
d
(
)
n = (-dp/du, -dp/dv, 1).normalized()
n=(−dp/du,−dp/dv,1).normalized()
注意以上都是在局部坐标中进行的 Note that this is in local coordinate!