GAMES101-闫令琪 https://www.bilibili.com/video/BV1X7411F744/
07 Shading 1 (Illumination, Shading and Graphics Pipeline)
Z-Buffer
从远到近绘制,覆盖帧缓冲区,但实际上会有互相遮挡的情况不可深度排序的情况,所以提出了深度缓存 Z-Buffer

- 存储每个样本的当前最小z值(像素)
- 需要额外的缓冲区来存储深度值
- 帧缓冲区存储颜色值
- 深度缓冲区(z-buffer)只存储存储深度信息
为简单起见,假设相机位于原点方向-Z(z越小越近,z越大越远)
for (each triangle T)
for (each sample (x,y,z) in T)
if (z < zbuffer[x,y]) // closest sample so far
framebuffer[x,y] = rgb; // update color
zbuffer[x,y] = z; // update depth
else
; // do nothing, this sample is occluded
- 每一个三角形
- 每一个三角形对应的像素
- 如果小于之前记录的值,则把这个像素颜色画进去,并更新深度缓存的值

着色


着色:对不同物体应用不同材质的过程
- 高光
- 漫反射
- 环境光

计算反射到相机的光线在特定的阴影点 Shading Point(在一个局部范围内是一个平面),这些向量都是单位向量;
- 观测方向, v
- 表面正常,n(可以在这个平面定义法线)
- 光照方向, l(对于许多灯中的每一个)
- 表面参数(颜色、光泽度等)
着色是局部的,不会生成阴影!(shading ≠ shadow)
漫反射
当光线照射到物体上某一点,光向各个方向均匀散射,所有观察方向的表面颜色都是相同的

- 考虑单位面积接受了多少能量,夹角不同接受到光的单位面积能量要不同
- 接收到的能量与光照方向和法线方向他们夹角的余弦是成正比的
cosθ=l·n

- 假设光是一个点光源,在任意一个时刻,点光源向外辐射的能量集中在一个球壳上
- 根据能量守恒定律,距离中心近的球壳和距离中心远的球壳,能量是相同的,但面积变大了所以能量变小
- 距离点光源 1 时定义强度为 I ,距离 r I/r²
这样我们知道了两个事情:出来有多少光从传播到了Shading Point;有多少光被Shading Point吸收;就可以得出这样一个公式算出漫反射Diffuse(漫反射与观察方向无关)
08 Shading 2 (Shading, Pipeline and Texture Mapping) 着色频率、图形管线、纹理映射
高光
观察方向和镜面反射方向接近的时候会看到高光,就意味着法线方向和半程向量很接近,v和R接近意味着n和h接近

- 省略了受到光照的倾斜角度
- n和h点乘要比计算v和R要简单
- 增加一个指数p,来调整不同角度高光的大小(100~200)

环境光照
假设任何一个点接受的来自环境的光都是相同的,强度 Ia,环境光系数ka

- 环境光与 光线方向l、观察角度v、法线n 无关;环境光是一个常数

环境光 + 漫反射 + 高光 = Blinn-Phong Reflection Model
着色频率
把着色应用在哪些点上;面、定点、像素

逐片元:Flat shading(平直着色)
- 三角形面是平的——一个法向量
- 不适合光滑表面
逐顶点:Gouraud shading
- 从三角形的顶点插值颜色
- 每个顶点都有一个法向量(如何?)
逐像素:Phong shading
- 对每个三角形的法向量进行插值
- 计算每个像素的全着色模型
- 不是Blinn-Phong反射模型

着色频率取决于面、顶点、像素的频率,效果取决于具体物体

- 逐定点法线:任何一个顶点会和很多个不同的三角形所关联,顶点的法线是相邻的面的法线的平均(简单平均、加权平均)
- 逐像素法线:使用重心坐标
图形管线(实时渲染管线)
从3D场景到2D的图,经历的一系列流程

- 输入是空间中的一些点
- 将空间中的点投影到屏幕上
- 这些点会形成三角形
- 光栅化离散成像素
- 着色
- 输出显示
shader:shader是通用的,只需要写一个顶点/像素如何运作
uniform sampler2D myTexture; // program parameter
uniform vec3 lightDir; // program parameter
varying vec2 uv; // per fragment value (interp. by rasterizer)
varying 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
}
纹理映射
希望在物体不同位置定义一个不同的属性

- 定义在物体表面上,每个3D表面点在2D图像(纹理)中有位置映射
- 纹理上有一个坐标系UV(都在0~1之内)
- 纹理重复、无缝衔接
09 Shading 3 (Texture Mapping Cont.) 插值、高级纹理映射
差值
为了指定顶点处的值,在三角形内做平滑过渡,所以使用差值;插入纹理坐标、颜色、法向量等

重心坐标:在三角形内的任意坐标xy都可以表示成三个顶点ABC的线性组合(α,β,γ),同时满足两个条件
- α+β+γ=1(限制在三角形的平面内)
- α、β、γ都是非负的
重心坐标可以靠面积比求出来,从这一点分别连接三角形三个顶点,与当前顶点对应的三角形(不相邻)的面积与整个三角形面积的比值就是α、β、γ的值
重心坐标的计算公式

可以用重心坐标做任意一点在三角形内的差值,做差值的属性也应该用重心坐标去把它线性地组合出来
在投影下不能保持重心坐标不变
简单纹理映射:漫反射颜色:
屏幕→三维→纹理→漫反射系数;对于每个光栅化的屏幕样本 (x, y):
- 在 (x, y)处计算纹理坐标 (u, v)。
- 获取纹理采样颜色 texcolor = texture.sample(u, v)(使用重心坐标)
- 将样本的颜色设置为texcolor。(通常漫射反照率Kd)

临近差值:
对于一个需要插值的点,选择距离最近的已知像素值作为其值。优点是计算速度快,但可能导致图像出现锯齿状的边缘。
双线性插值:
过对四个最近的像素进行加权平均来计算未知点的值。这种方法比临近差值更平滑,但仍可能导致一些模糊。

- 映射到一个点后,找到临近的四个点,
- 得到距离左下角的水平s和垂直t距离
- 首先在水平方向进行线性插值,然后在垂直方向进行插值。
双三次差值:
考虑16个邻近像素(4x4的网格)进行插值。使用三次多项式进行加权平均,结果比双线性插值更平滑。能够更好地保留图像细节,但计算量较大。
Mipmap

近处像素覆盖区域较小,远处覆盖区域太大,一个点就不能代表一个区域,会导致走样
超采样,增加采样频率可以得到一个比较好的结果,但是计算量会变大
如果不采用,只需要得到一个范围内的平均值
- 点查询:一个点的值是多少
- 范围查询:不做采样,直接得到范围内的平均值

Mipmap是一种用于提高纹理渲染效率和质量的技术。它预先生成了一系列分辨率逐渐降低的纹理图像。
从原始高分辨率纹理开始生成,逐级缩小,直到达到最小尺寸。每一级都是前一级的缩小版本。在渲染时,根据物体与摄像机的距离选择合适的纹理级别。这减少了计算负担,因为远处物体使用较低分辨率的纹理。通过使用更合适的纹理级别,避免了在缩放时出现的锯齿和闪烁。

求投影的像素与其相邻像素在UV上的距离,然后通过近似得到一个正方形区域(微分)
查询这个区域在第几层(D=log₂L)会变成一个像素的大小,然后取那一个像素,就可得平均值
三线性插值:
因为可能查询的并不是在同一层,所以可能不连续,所以可以使用双线性插值

分别查询 Mipmap Level D 和 Mipmap Level D+1 并分别进行差值,然后将两个结果再进行差值,这叫做三线性插值

因为只能查询正方形区域的平均值,所以会导致画面出问题
各向异性过滤:

可以部分解决这个问题,各向异性过滤可以允许我们对矩形区域做快速的范围查询,但是斜着的不行
各向异性:在不同方向上表现各不相同
EWA过滤:
将不规则形状拆成许多不同圆形去覆盖,多次查询这个圆形,耗时
环境光照
可以将纹理理解成一块数据,可以快速地做点查询、范围查询

将任何一个方向的光照都记录下来,环境贴图,纹理可以用来表示环境光;
假设光线距离无限远,不记录深度信息

可以将环境光记录在球形贴图上,并且展开(会有扭曲现象)

把环境光记录在立方体上,
凹凸贴图
应用凹凸贴图,可以再不把几何形体变复杂的情况下,定义三角形上任意一个点的相对高度

相对高度变化 → 法线发生变化 → 着色结果发生变化
法线贴图:
- 通过法线贴图,可以定义一个复杂的纹理,但却不更改几何信息
- 把任何一个像素的对法线进行一个扰动(通过定义不同的高度,根据临近位置的高度差重新计算法线)

一维:假设原版是平面,定义了一张凹凸贴图
- 求得某一个点的切线,法线法向(0,1)(导数,用相邻两个点的高度差除以间隔,引入常数控制影响幅度)
- 法线垂直于切线,把切线逆时针旋转90度((-dp,1),需要归一化长度得是1)
在二维情况下:定义局部法线位置为(0,0,1),计算完成后再通过坐标变换回到全局坐标,平面上两个偏切向量叉乘得到法向量
- 求出UV两个方向上的切线,
- 通过算出来的导数/梯度/微分 算出法线
位移贴图:

位移贴图会真的移动顶点位置,而法线贴图并没有;
要求三角形足够细致(Direct X提供了一个动态曲面细分的方法Dynamic tessellation)
三维纹理:

可以通过三维空间中的噪声函数来定义
三维扫描纹理
环境光遮蔽:
提供预计算阴影,计算好后写进一张纹理图

10 Geometry 1 (Introduction) 几何介绍

- 隐式几何:隐式几何则是通过一个函数来定义几何体的内部和外部区域,而不是直接给出边界的方程。隐式几何通常使用一个标量场来表示,形状是通过等值面来定义的。 判断有哪些点很难,但判断在不在这个物体内是很容易的一件事
- 显式几何:显式几何是指通过明确的数学方程或函数来描述几何体的形状。几何体的边界是通过明确的方程来定义的,通常容易进行计算和渲染。 几何体的边界是通过明确的方程来定义的,显示判断是否在物体内很难
隐式几何
代数曲面:
使用代数式来描述几何

构造性立体几何:
通过基本几何的基本运算(布尔),得到新的几何

距离函数:
不直接描述表面,描述任意一个点到这个表面的任意距离

- 假如想得到A-B运动中间的结果,直接混合会导致中间变成灰色,
- 分别求得A、B的距离函数,因为两者都是最短距离,然后将其混合就可以得到中间值,通过混合两个的SDF就等于混合他们两个的边界
水平集:
跟距离函数表示形式不一样,但结果都是函数等于0得到的曲线

水平集不仅可以是二维也可以是三维
分形:
自己的一个部分和自己整体比较像

与计算机中的递归一个道理
显示几何
点云:
只要点足够密,就可以变成任何物体;最简单的表示方式:点列表(x,y,z);可以轻松表示任何类型的几何图形适用于大型数据集(>>1点/像素);经常转换为多边形网格;在样本不足的地区难以绘制
多边形网格:
存储顶点和多边形(通常是三角形或四边形);更易于处理/模拟;自适应采样;更复杂的数据结构;也许是图形中最常见的表示
OBJ:通过文本文件存储,(以正方体为例)定义8个点的位置v、6个法线vn、12个UV纹理坐标vt、连接关系(f 顶点索引 / UV索引 / 法线索引)(有冗余&共用)

11 Geometry 2 (Curves and Surfaces) 曲线与曲面
贝塞尔曲线
贝塞尔曲线一定经过起止点
画出贝塞尔曲线:
- 假设这条曲线的起点是在时间0,终点在时间1,任意一个时间t在0~1之间的时间t,求出所对应的点在空间中的位置
- 一直 t 在 0~1 之间的位置,找到 b0 到 b1之间找到 t 的位置,b1 到 b2 之间t的位置早到 t 的点,将新得到的两个点连起来,再在这条线上找 t 的位置,最后这个点就是时间 t 在贝塞尔曲线上的位置

可以通过线性插值将公式列出来,起点*(1-t)+终点*t,再将求出的两个点继续运算,整合得到一个展开式(s+t的n次方的展开式)

给一个n+1个控制点,可以得到一个n阶的贝塞尔曲线,曲线在任意时间 t 都是给定的这些控制点的线性组合,组合的系数是一个与时间有关的多项式(伯恩斯坦多项式)
任意一个时间 t ,这个点的位置就是由伯恩斯坦多项式作为系数,对给的的控制点的加权
这样的话也可以输入三维的点,求空间中的贝塞尔曲线

这个多项式相当于对 1 自己的 n阶展开,把几个同一阶上多项式加起来等于1
- 贝塞尔曲线规定了必须过起点和终点,所以t=0时一定在起点,t=1一定在终点
- 四个控制点的贝塞尔曲线,起始位置的切线一定是三倍的(b1-b0)
- 如果相对贝塞尔曲线做仿射变换,可以直接对控制点做仿射变换然后再画出来(限制仿射变换,对投影不是)
- 画出来的贝塞尔曲线一定在几个控制点形成的凸包内

逐段贝塞尔曲线:控制点太多不容易控制形状,可以将贝塞尔曲线分段(通常是4个点)

连续线:
- C0连续:第一段终止点等于第二段起点
- C1连续:切线连续,两个控制点距离相同且共线
- ……

曲面
在两个方向上分别利用贝塞尔曲线就得到了贝塞尔曲面

输入:4x4个控制点,输出是由[0,1]2中的(u,v)参数化的二维曲面;
找到贝塞尔曲面找到任意一点,需要两个不同的时间 t ,先求出水平四条曲线上的四个点U,再在这四个点形成的曲线上找V
因为贝赛尔曲线是UV映射过去的,所以是显示的表示
12 Geometry 3

- 网格细分(上采样)
- 网格简化(降采样)
- 网格正则化(相同的三角形)
网格细分
- 分出更多的三角形
- 让三角形位置发生变化,使其更圆滑
Loop细分:
增加新的三角形,然后新产生的顶点和老的顶点分别处理
- 新的顶点:取周围几个点的加权平均,3/8*(A+B)+1/8*(C+D)
- 旧的顶点:一部分保留自己位置,另一部分取周围顶点的平均值,二者加权

Catmull-Clark细分:
- 非四边形面
- 奇异点(极点):度不为4的点(度:与这个点相连的线)

取边和面的中点,并且把它们相连。
在 Catmull-Clark细分前有多少个非四边形面,在一次细分后就增加多少个奇异点,非四边形面都消失了,所以再细分就不会再增加奇异点了

- 新生成的顶点,面的中心点和边的中心点分别考虑;
- 老的点使用面中心、边中心和自己做加权平均
Loop细分只能用于三角形面做细分,Catmull-Clark细分可以用作各种不同的面做细分
曲面简化

边坍缩:将点合并
- 对顶点进行局部平均不是一个好主意
- 二次误差:新顶点 与先前相关三角形平面的平方和(L2距离)最小

假设坍缩每一条边,将坍缩后的顶点放在什么位置上,得出一个多大二次误差度量,,给每一条边打上一个分数,从小的开始逐个做坍缩,
坍缩一条边会影响其他边,使用堆 / 优先队列,既能取最小值又能动态更新这些元素
相当于在通过对局部做最优解的方式,找到一个全局的最优解(贪心算法)
阴影贴图 Shadow Mapping
一种图像空间算法:阴影时不知道场景的几何形状。会产生走样现象。
关键思想:不在阴影中的点必须同时被光线和相机看到
点光源生成硬阴影:
- 从光源看向场景,记录你所看到任何点的深度
- 从摄像机看向场景,投影看到的点,投影回光源视角的哪一个位置上,
- 两者深度一致,不在阴影中
- 两者深度不一致,在阴影中

软阴影:
- 一个地方完全看不到光源,叫做本影Umbra
- 一个地方可以部分看到光源,叫做半影Penumbra
- 完全看到光源就没有阴影
软阴影就是本影、半影、到没有阴影的影子过度。点光源不可能拥有软阴影,有软阴影光源一定有大小








Comments | NOTHING