Visibility/ occlusion
Z-buffering
Shading
Illumination Shading
Graphics Pipline
Painter’s Algorithm
灵感来自画家的绘画方式,从后到前绘制,覆盖帧缓冲区
逐步覆盖
需要对深度排序(O(n log n)对于n个三角形)
但会有不可解析的深度顺序
遮挡
Z-buffer
这就是最终获胜的算法。
思想:
- 存储每个样本(像素)的当前最小z值
- 需要一个额外的深度值缓冲区
- 帧缓冲区存储颜色值
- 深度缓冲区(z-buffer)存储深度
important:为了简单起见,我们假设z总是正的
(小z ->近,大z ->远)
初始化depth buffer to $\infty$
在光栅化期间:
for(each triagle T)
for(each sample (x,y,z) in T)
if(z < zbuffer[x,y]) //当前最近的样本
framebuffer[x,y]=rgb; //更新颜色
zbuffer[x,y]=z; //更新深度
else
; // 该点被遮挡
复杂度
O
(
n
)
O(n)
O(n) n个三角形,假设每个三角形有一定大小
用不同的顺序画三角形?
最重要的可见性算法
为所有GPU在硬件上实现
处理不了透明
着色
在韦氏词典中
Shad·ing, [[s] e / d / n],名词
用平行线或色块使插图或图表变暗或上色
将材质material应用到物体上的过程。
Blinn-Phong反射模型
感性的观察:
镜面高光、漫反射、环境照明
着色是局部的
计算在特定着色点反射到相机的光
输入:
- 观察方向, v v v
- 表面法向量, n n n
- 光方向, l l l()
- 表面参数(颜色colro, 反光度shininess
不会产生阴影shadows!(着色≠阴影)
漫反射
光在各个方向均匀地散射
表面颜色对所有观看方向是相同的
但是接收了多少光(能量)呢?
Lambert’s cosine law朗伯余弦定律
灯光衰减Light Falloff 能量守恒
Lambertian(漫反射)着色
着色独立于视图方向
产生扩散外观diffuse appearance
Specular 镜面高光
强度取决于观察方向
- 明亮的近镜反射方向
接近镜像方向的v⇔接近法线的半程矢量
用单位向量的点积来度量“近”
Cosine Power Plots
增加 p p p使反射lobe变窄
Ambient Term
认为是恒定的
着色不依赖于任何东西
- 添加恒定的颜色来解释忽略的照明和填充黑色阴影
- 这是近似的/假的!
Blinn-Phong Reflection Model
L
=
L
a
+
L
d
+
L
s
=
k
a
I
a
+
k
d
(
I
/
r
2
)
max
(
0
,
n
⋅
l
)
+
k
s
(
I
/
r
2
)
max
(
0
,
n
⋅
h
)
p
L=L_a+L_d+L_s=k_aI_a+k_d(I/r^2)\max(0,\mathbf{n}\cdot \mathbf{l})+k_s(I/r^2)\max(0,\mathbf{n}\cdot \mathbf{h})^p
L=La+Ld+Ls=kaIa+kd(I/r2)max(0,n⋅l)+ks(I/r2)max(0,n⋅h)p
Shading Frequencies 着色频率
着色每个三角形(平面着色)
三角形面是平的-一个法向量
不适合光滑的表面
着色每个顶点(Gouraud着色)
从三角形的顶点插值颜色
每个顶点都有一个法向量(如何?)
像素着色(冯氏着色Phong)
在每个三角形上插值法向量
在每个像素处计算完整的着色模型
不是Blinn-Phong反射模型
着色频率:面,顶点或像素
定义每个顶点的法向量
最好从底层几何中获得顶点法线
考虑一个球:
否则必须从三角形面推断顶点法线
- 简单方案:**取周围面法线的平均值
N v = ∑ i N i ∣ ∣ ∑ i N i ∣ ∣ N_v=\frac{\sum_i N_i}{||\sum_iN_i||} Nv=∣∣∑iNi∣∣∑iNi
顶点法线的重心插值Barycentric interpolation
不要忘记对插值方向进行规范化
图形(实时渲染) 管线
着色器编程Shader Programs
编程顶点和片段处理阶段
描述对单个顶点(或片段)的操作
示例GLSL片段着色编程、
uniform sampler2D myTexture; // program parameter
uniform vec3 lightDir; // program parameter
varying vec2 uv; // per fragment value (interp. by rasterizer)
arying vec3 norm; // per fragment value (interp. by rasterizer)
void diffuseShader() {
vec3 kd;
kd = texture2d(myTexture, uv); // material color from texture
kd *= clamp(dot(–lightDir, norm), 0.0, 1.0); // Lambertian shading model
gl_FragColor = vec4(kd, 1.0); // output fragment color
}
Shader函数每个片段fragment执行一次。
输出当前片段屏幕样本位置表面的颜色。
这个着色器执行纹理查找以获得表面的材质颜色,然后执行漫射照明计算。
蜗牛着色器
Snail
目标:高度复杂的3D场景在实时
一个场景中有成千上万到数百万个三角形
复杂的顶点和片段着色计算
高分辨率(200 - 400万像素+超采样)
每秒30-60帧(VR更高)
图形管道实现:gpu
用于执行图形管道计算的专用处理器
GPU:异构多核处理器
Texture Mapping纹理映射
不同的地方有不同的颜色?
表面是二维的
表面存在于三维世界空间
每个3D表面点在2D图像(纹理)中也有一个位置。
纹理应用于表面
每个三角形将纹理图像的一部分“复制”到表面。
纹理坐标的可视化
每个三角形顶点分配一个纹理坐标(u,v)
纹理应用于表面
纹理坐标的可视化
纹理可以多次使用
三角形内部插值:重心坐标
- 重心坐标
- 纹理问题
- 纹理应用
为什么要插值
- 指定顶点处的值
- 在三角形上获得平滑变化的值
插什么 - 纹理坐标,颜色,法向量,…
如何 - 重心坐标
一个三角形的坐标系统
(
α
,
β
,
γ
)
(\alpha,\beta,\gamma)
(α,β,γ)
(
x
,
y
)
=
α
A
+
β
B
+
γ
C
,
α
+
β
+
γ
=
1
(x,y)=\alpha A+\beta B + \gamma C, \alpha+\beta+\gamma=1
(x,y)=αA+βB+γC,α+β+γ=1如果三个坐标非负,则点在三角形内部
A
=
(
1
,
0
,
0
)
A=(1,0,0)
A=(1,0,0)
几何视角-成比的例面积
α
=
A
A
A
A
+
A
B
+
A
C
\alpha=\frac{A_A}{A_A+A_B+A_C}
α=AA+AB+ACAA
β
=
A
B
A
A
+
A
B
+
A
C
\beta=\frac{A_B}{A_A+A_B+A_C}
β=AA+AB+ACAB
γ
=
A
C
A
A
+
A
B
+
A
C
\gamma=\frac{A_C}{A_A+A_B+A_C}
γ=AA+AB+ACAC
质心的重心坐标
(
1
3
,
1
3
,
1
3
)
(\frac{1}{3},\frac{1}{3},\frac{1}{3})
(31,31,31)
公式:
在顶点处线性插值值
VA、VB、VC可以进行位置、纹理坐标、颜色、法线、深度、材质属性…
然而,重心坐标在投影下不是不变的。在三维空间做插值
应用纹理
简单纹理映射:漫反射颜色
for each resterized screen sample(x,y)://通常是一个像素的中心
(u,v) = evaluate texture coordinate at (x,y);//使用重心坐标
texcolor = texture.sample(u,v);
set sample’s color to texcolor;//通常是漫射反照率Kd(回想一下Blinn-Phong反射模型)
纹理放大 Texture Magnification
如果纹理太小怎么办?
简单情形:
一般不希望这样-纹理分辨率不足
纹理上的一个像素——一个纹理(纹理元素、纹素)
插值方法:
双线性插值
双线性插值通常以合理的成本给出相当好的结果
困难情形 如果纹理太大怎么办?
点采样纹理 - 问题 锯齿 摩尔纹
纹理中的屏幕像素“足迹”
超采样做抗锯齿
有效,但代价高
- 当高度缩小时,许多像素占用空间
- 一个像素的信号频率太大
- 需要更高的采样频率
让我们用另一种方式来理解这个问题 - 如果我们不取样呢
- 只需要得到一个范围内的平均值
点查询Point Query与(平均)范围查询Range Query
不同像素->不同大小的足迹
Mipmap 多级贴图
允许(快,近似,方形)范围查询 (图像金字塔)
“Mip”来自拉丁语“multum in parvo”,意思是一个小空间里的许多人
mipmap的存储开销是多少?(1+1/4+1/16+…=4/3)
也即多了三分之一
计算Mipmap层次D
使用相邻屏幕样本的纹理坐标估计纹理占用
Mipmap层级的可视化
1.5层怎么办?
三维插值
限制:
Anisotropic Filtering各向异性过滤
纹理中的不规则像素足迹
Ripmaps和summed area tables求和面积表
- 可以查找轴对齐的矩形区域
- 对角线仍然是个问题
EWA过滤
- 使用多个查找
- 加权平均
- Mipmap层次结构仍然有帮助
- 能处理不规则足迹
纹理的应用
在现代GPU中,纹理=内存+范围查询(过滤)
- 将数据带入段计算的一般方法
许多应用程序
- 环境光照
- 存储微观几何图形 microgeometry
- 程序纹理
- 实体建模
- 体绘制
环境贴图
环境光照
球面环境贴图
球面贴图 - 问题
容易出现变现(上下部分)
立方体贴图
一个矢量沿着这个方向映射到一个立方体点。
立方体有6个正方形纹理贴图。
更少的失真
纹理可以影响阴影!
纹理不一定只代表颜色
- 如果它存储高度/法线呢?
- 凹凸/法线贴图
- 模拟(fake)细节几何
凹凸贴图Bump mapping
添加表面细节而不添加更多三角形
- 每像素的扰动表面法线 (只适用于阴影计算)
- 每个纹理定义的“高度移动”
- 如何修改法向量?
如何扰乱常态(在二维平面上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()。
在3d表面上
原始表面法线
n
(
p
)
=
(
0
,
0
,
1
)
n(p) = (0, 0, 1)
n(p)=(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()
注意,这是在局部坐标!local coordinate
更多细节将在HW3的FAQ中详细说明
位移贴图Displacement mapping——一种更高级的方法
使用与凹凸贴图相同的纹理
实际上是移动顶点
代价:模型的三角形要更细致。
3D程序噪声Procedural Noise+实体建模
提供预先计算的阴影
3D纹理和体渲染