0 我们已经学了什么 What We’ve Covered So Far
- 空间中定义了一个摄像机,通过某些变换使模型摆好姿势(Model)
- 通过观测变换让摄像机放在原点,得到标准的 [ − 1 , 1 ] 3 [-1,1]^3 [−1,1]3立方体空间(View)
- 通过视口变换将三维模型投影到二维的屏幕上(Projection)
- 通过采样对屏幕空间中结果进行光栅化(Rasterization)
1 着色的定义 Shading:Definition
- 字典中的定义:通过平行线或色块在插画、图像上加深或上色 The darkening or coloring of an illustration or diagram with parallel lines or a block of color
- 在本课程中的定义:对不同的物体应用不同的材质 The process of applying a material to an object
2 一种简单的模型:Blinn-Phong反射模型 A Simple Shading Model (Blinn-Phong Reflectance Model)
Blinn-Phong模型是经验模型,与现实相比仍有很多不足
2.1 感性观察 Perceptual Observations
- 高光(Specular highlights):光滑表面镜面反射处附近形成高光
- 漫反射(Diffuse reflection):在整个杯子上变化比较柔和,粗糙表面形成漫反射
- 环境光(Ambient lighting):上图中背光处仍然有颜色,因为其接受了周围环境的间接光照,为了简化模型,我们会使用一个环境光常量来表示环境光对物体的影响
2.2 着色具有局部性 Shading is Local
-
着色点(Shading point)在局部上是一个平面,在这个平面上定义:
- 观看方向v(Viewer direction)
- 法线方向n(Surface normal)
- 光照方向l(Light direction)
- 表面参数颜色、光泽度等(Surface parameters color, shininess, …)
-
着色过程不包括阴影的生成 No shadows will be generated! (shading ≠ shadow)
2.3 漫反射 Diffuse Reflection
-
定义:光线打到某一点后,光线均匀的向各个方向散射,表面的颜色在各个观察方向都相同 Light is scattered uniformly in all directions, surface color is the same for all viewing directi
-
多少光被接受与面和光线的夹角有关,根据兰伯特余弦定理(Lambert’s cosine law),单位面积上接收到的光的能量的能量等于光线与法线夹角的余弦
- 太阳能板在板面垂直于光照接受光能最大
- 地理上冬夏之分与光线照射方向与地面夹角有关(太阳高度角)
-
从光在传播中的衰减 Light Falloff
- 从能量守恒的角度来看,单位面积上光的强度与距离的平方成反比
- 从能量守恒的角度来看,单位面积上光的强度与距离的平方成反比
-
兰伯特(漫反射)着色 Lambertian (Diffuse) Shading
2.4 镜面反射 Specular Term (Blinn-Phong)
- 高光的强度取决于观察方向,观察方向与镜面反射方向接近时能看见高光 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
4 图形(实时渲染)管线 Graphics (Real-time Rendering) Pipeline
4.1 图形(实时渲染)管线 Graphics (Real-time Rendering) Pipeline
- 模型、观测、投影变换(Model, View, Projection transforms):Vertex Processing
- 采样三角区的覆盖范围(Sampling triangle coverage):Rasterization
- 深度缓冲可见性测试(Z-Buffer Visibility Tests):Fragment Processing
- 着色和纹理贴图(Shading, Texture mapping):Vertex Processing & Fragment Processing
4.2 着色器程序 Shader Programs
- 描述了顶点和片元处理过程的操作 Program vertex and fragment processing stages
- 着色器会对每个顶点或片元执行一次,只写出单次操作即可 Shader function executes
once per fragment. - 例如,以下是一段GLSL语言的片元着色器 Example GLSL fragment shader program
uniform sampler2D myTexture; //全局变量 固定的纹理
uniform vec3 lightDir; //全局变量 固定的光照方向
varying vec2 uv; //每个片元插值得到的uv坐标
varying vec3 norm; //每个片元插值得到的法线方向
void diffuseShader()
{
vec3 kd; //Eigen库定义三维向量kd
kd = texture2d(myTexture, uv); //进行纹理查找以获取此时表面材质颜色
kd *= clamp(dot(–lightDir, norm), 0.0, 1.0); //漫反射系数、光照都为常数的最简单的漫反射
gl_FragColor = vec4(kd, 1.0); //将得到的值返回到gl_GragColor
}
- 练习shader:https://www.shadertoy.com
5 当下实时渲染的发展
5.1 目标:实时高度复杂的 3D 场景 Goal: Highly Complex 3D Scenes in Realtime
- 一个场景中有数百到数百万个三角形 100’s of thousands to millions of triangles in a scene
- 复杂的顶点和片段着色器计算 Complex vertex and fragment shader computations
- 高分辨率(200-400万像素+超采样)High resolution (2-4 megapixel + supersampling)
- 每秒 30-60 帧(VR 甚至更高)30-60 frames per second (even higher for VR)
5.2 用于执行图形管线的硬件:GPU Graphics Pipeline Implementation: GPUs
- GPU分为独立显卡(Discrete GPU Card)与集成显卡(Integrated GPU)
- 学过高性能计算的话,会知道GPU的并行度高,适合处理图形学的相关计算
6 纹理映射 Texture Mapping
6.1 纹理是二维的图像 Surfaces are 2D
- 纹理映射的根本作用是定义物体表面任何一点的不同属性
- 每个三维物体表面上的一个点在2D图像中对应一个位置 Every 3D surface point also has a place
where it goes in the 2D image (texture)
6.2 纹理映射 Texture Applied to Surface
-
定义纹理坐标uv(u, v约定俗成范围0~1)、将纹理映射到表面的过程
-
纹理可以被重复使用多次,当纹理是四方连续时可以无缝衔接(tiled),其中一种算法是Wang Tiling
7 重心坐标插值 Interpolation Across Triangles: Barycentric Coordinates
7.1 为什么要进行插值 Why do we want to interpolate?
- 为了在三角形内部得到平滑的过渡 Obtain smoothly varying values across triangles
- 纹理坐标、颜色、向量的插值等属性都可以插值 Texture coordinates, colors, normal vector
7.2 重心坐标 Barycentric Coordinates
7.2.1 什么是重心坐标 What’s the barycentric coordinate of A?
- 三角形所在平面上任意一点都可以用三个顶点坐标的线性组合来表示
- 任意一点可以通过重心坐标表示为 ( α , β , γ ) (\alpha,\beta,\gamma) (α,β,γ)
- 如果 α , β , γ \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
- 质心的重心坐标为 ( 1 3 , 1 3 , 1 3 ) (\frac{1}{3},\frac{1}{3},\frac{1}{3}) (31,31,31)
7.2.3 重心坐标的计算公式 Barycentric Coordinates: Formulas
- 利用叉积的模等于其形成三角形大小算出面积,得到简化公式
α = − ( 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
- 相比只存储一张原图,多占用了1/3的空间
- 在计算机视觉中也叫图像金字塔(Image Pyramid)
10.2.2 计算mipmap等级 Computing Mipmap Level D
- 任何一个像素会在纹理上对应一片区域,可以通过将像素的中点映射到纹理上再计算距离的方法来得到一个像素近似的正方形区域边长L是多少,然后取2的对数得到等级D(即为求在第几层是该像素映射的纹理也是一个像素大小的区域)
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 )
10.2.3 三线性插值 Trilinear Interpolation
- 简单通过这种方法只能查询整数层数上的情况,在实际应用纹理时不连续,因此在查询时可以先用双线性插值分别得到相邻两个整数层级上的值,再用得到的值再做一次插值(三线性插值)得到连续的值
- 三线性插值在实时渲染有着广泛应用,因为它能得到连续的结果,并且只需要做两次查询和一次插值,开销较小
10.3 各向异性过滤 Anisotropic Filtering
10.3.1 Mipmap的问题
-
与超采样结果相比,使用mipmap得到的结果在远处出现了过渡模糊的现象(Overblur)
-
这是因为mipmap只能查询正方形区域,映射得到的结果近似为正方形太过勉强,另外三线性插值本身也是近似
10.3.2 各向异性过滤 Anisotropic Filtering
-
使用各向异性过滤可以得到比mipmap更好的画面
-
mipmap相当于只可以查询下图(Ripmap)左上到右下对角线上为正方形的纹理,而使用各向异性过滤可以查询矩形区域,可以较好解决映射在纹理上的竖直或横向的矩形区域,但是对于斜向的长条状区域仍然不能很好的解决。游戏设置中多少X就是分别在两个方向压缩几次,随着X不断变大,最终会收敛到原图3倍的空间
10.4 EWA过滤 EWA filtering
- EWA过滤可以把任意不规则的形状拆成很多个的圆形去覆盖这个形状,但是多次查询会增加耗时
11 纹理贴图的应用 Application of Texture
11.1 总述:有许多种类的贴图 Many, Many Uses for Texturing
- 在现代GPU中,纹理就是一块可以快速做点查询、范围查询的内存区域,不仅限于图像 In modern GPUs, texture = memory + range query (filtering)
- Environment lighting
- Store microgeometry
- Procedural textures
- Solid modeling
- Volume rendering
- …
11.2 环境光贴图 Environment Map
-
环境光贴图把来自各个方向的光照记录下来,假设环境光来自无限远,只记录方向,来表示环境光(右侧为经典模型犹他茶壶 Utahpot)
-
球面环境映射(Spherical Environment Map)可以类比为地球仪,但是贴图上下部分存在拉伸和扭曲问题
-
Cube Map把环境光记录在立方体的表面上,沿法线方向把正方体映射到球面上,缺点是多了计算方向判断在正方体哪个面上
11.3 凹凸贴图 Bump Mapping
-
纹理可以存储高度/法线移动,但是不改变几何信息,由此可以得到假的着色效果,产生凹凸的感受
-
在着色计算过程中逐像素地扰动表面法线,由此定义每个纹素高度偏移 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!
11.4 位移贴图 Displacement mapping
- 相比于凹凸贴图更真实,使用相同的原理和纹理使顶点发生移动
- 代价是要求模型本身面数足够多,需要顶点间间隔比定义的采样频率高(DrictX提供了动态的曲面细分,仅在需要的时候提供足够的面数)
11.5 程序化纹理 3D Procedural Noise&Procedural textures
- 定义了三维空间中的噪声函数,并没有真正生成一张图像
- 函数可以经过各种处理变成自己想要的样子,如使用纹理表示山脉起伏的信息
11.6 预计算着色 Precomputed Shading
- 环境光遮蔽(Ambient occlusion)可以提前计算好储存在贴图中,使用时将其他着色结果乘以环境光遮蔽的结果得到带有环境光遮蔽的结果
11.7 三维纹理和体渲染 Solid modeling and Volume rendering
三维纹理广泛应用到体渲染之中,比如在医学领域使用核磁共振得到三维空间中的密度信息进行渲染