0%

Lecture 13:Ray Tracing 1 (Shadow Mapping,Whitted-Style Ray Tracing)

概述

这一节课主要讲了 阴影光线追踪

光纤追踪这一部分有点难啊…

挺抽象的,不太好理解…

Shadow Mapping

概述

Shadow Mapping本质是一种图像空间的算法。

我们之前学的都没有考虑到阴影,现在考虑一下这个东西;

具体算法

  • 从光源渲染,从光源看场景,可以求出深度图(点到光源的深度)(z-buffer)
  • 从相机渲染,也就是从相机发出光线,可以得到我们看到的点,然后求这些点到光源的深度;如果这个深度和从光源渲染的深度是一样的,则说明这个点没有被遮挡;否则说明,这个点被遮挡了;

结果

问题分析

  • 生成的是硬阴影(只对点光源(没有体积))。
  • 阴影的效果取决于shadow map的分辨率。
  • 涉及浮点精度比较的问题,导致渲染出来的效果一般。

软阴影和硬阴影

下面图中,左上是硬阴影,左下是软阴影;

软阴影的原理

由于点光源有大小,会形成本影(Umbra)区域半影(Penumbra)区域。所以会形成阴影的过渡。

进行光线追踪(Ray Tracing)的原因

  • 难以做软阴影
  • 难以表现光线多次弹射,比如间接光照
  • 光栅化很快但是质量较低
  • 光线追踪很准确,但是很慢

光栅化:实时(游戏),光线追踪:离线(动画)。

光线追踪一帧就要花10K个计算机小时

光栅化和光线追踪都是成像方式。

光线追踪(算法)

光线的一些定义概念

  • 光线沿直线传播。

  • 光线和光线不会发生碰撞。

  • 光线会从光源发出,打到场景中,经过反射折射等最终进入人眼(光线具有可逆性)。

光线投影(Ray Casting)

光线投射是光线追踪中用于生成初始光线的第一步。

  • 通过每个像素投射一条光线来生成图像

  • 通过向光源发送光线来检查阴影

具体的讲:

  • 从视点开始经过像素获得与场景中物体最近的交点。
  • 将交点和和光源连线,判断这个点是否对光源可见,从而确定该点是否是阴影;
  • 然后计算着色,并将结果写回像素;

Recursive(Whitted- Style) Ray Tracing

这个光线追踪算法可以看成是优化,它考虑了反射和折射的光线

每一个弹射点都要计算着色的值,然后把所有着色的值都加到像素的值里面去。

当然这里需要考虑能量的衰减:

光线有不同的分类:

上面只是讲了大概应该怎么做,下面具体的讲细节。

光线和表面的交点(Ray-Surface Intersection)

回顾一下上面,首先光线追踪的第一步,是不是要求一条光线和表面的交点?

基本概念

光线

光线在数学上定义成 有起点和方向的射线,用 t 来表示时间,则我们可以得到射线的一个表达式:

  • r(t):是 t 时刻射线上的点;
  • o:是起始点;
  • d:表示方向,感觉可以理解为速度;

平面

通过一个法向量和一个平面上的点来进行定义。

平面的表达式 平面是满足下面条件所有p点的集合。

光线 和 球 求交

这个很简单,带进去就行;

光线 和 隐式表面 求交

也是一样的,带入:

光线 和 三角形面 求交

为什么

光线和三角形求交有如下作用:

  • 渲染:可见性、阴影、光照…
  • 几何:可以判断点是再物体内还是物体外。
    对任何一个封闭的曲面,在内部找一个点,如果点在形状内,那么从这个点发出的任意方向的射线与此物体的交点数量一定是奇数。

具体算法

简单的办法是让场景中的每个三角形与光线进行求交计算,但是有如下严重问题。

  • 简单但是运算量大。
  • 可以有0或1个交点。

因为三角形在平面内,所以可以把光线和三角形求交拆成两个问题:

  • 让光线与平面求交(目前与平面求交比与三角形求交容易)。

  • 判断与平面的交点是否在三角形内。

也就是说:光线与平面求交再判断是否在三角形内!

通过上面的图,可以求出 t,进而求出 r(t) (也就是交点);

然后再判断交点是否在三角形里面;

Moller Trumbore (MT)算法

通过上面我们可以知道,我们需要两步来求解;

MT 算法通过使用重心坐标来同时判断点是否在三角形里面;

在下面的式子中,左边是光线的表达式,右边是三角形上一点的重心坐标形式。

根据上面的等式解出来 t ,b1 ,b2三个方程,三个未知数,也可以使用克莱默法则)。

如果解出来的 t ,b1 ,b2 ,1-b1-b2大于0,则说明解是有意义的。

包围盒(Bounding Volumes)

概述

原始的做法是让每个像素的光线要和所有三角形面求交点,计算量太大,那么如何让这个过程加速呢?如果我们圈定包围盒,连与包围盒都不相交的三角形就不用接着算了,那么就可以很大的减少计算量。

基本思想:使用一个简单的几何体来包围一个复杂的物体,如果光线不能碰到包围盒,那么就不用再计算其与里面物体的(三角形的)交点。

这样就能少算很多很多 三角形和光线 的求交运算;

求光线与长方体的交点

3D物体通常的包围盒是长方体。
一个重要理解:长方体是三对不同的面形成的交集

我们使用的包围盒通常是轴对齐(AABB)包围盒。Axis-Aligned Bounding Box(AABB)
经过以上分析,现在问题就变成了求光线和轴对齐包围盒的交点;

求光线和轴对齐包围盒的交点

考虑2维的情况

先分析简单的,2维的长方形;

由于前面提到的光线方程是参数方程,所以只要我们计算光线什么时候会和一对平行的边有交点(计算 t-min , t-max),就可以求出光线进入和离开这个边的时间

看上面的图:

首先注意到长方形有两对边;

左边的图:是求光线和左右两边的相交时的时间

中间的图:是求光线和上下两边的相交时的时间

然后对这两段求交集,,此时 t-min , t-max 就分别代表着光线进入和离开长方体的时间

考虑3维的情况

根据2维的情况,我们推导出3维的情况的性质

  • 只有当光线进入了所有的对面,才能说光线进入了盒子。
  • 只要光线离开任意一个对面,光线就离开了盒子。

然后通过上面我们知道,Box 有3对面,也就是每一对面,光线都能和这对面求一组 t-min , t-max;

所以再根据上面的性质可以求出光线进入 BOX 的 t (t-enter)和 离开 BOX 的 t(t-exit)

t-enter = max( t1-min , t2-min , t3-min )

t-exit = min( t1-max , t2-max , t3-max )

ti 表示第 i 对面;


t为负时的意义:

  • 如果 t-exit < 0,说明盒子在光线背后,说明没有交点
  • 如果 t-exit >= 0 && t-enter < 0,说明光线的起点在盒子里,一定有交点。

结论:

如果满足 t-enter < t-exit && t-exit >= 0,则说明光线与AABB包围盒有交点。


使用轴对齐(AABB)概念的原因

主要目的是简化计算
普通情况计算 t:

轴对齐的情况下计算t(只需要考虑各方向的分量即可):


ok,这个就是这一节课的内容…

这一节课是真的多,写了好久好久…

关于包围盒只是讲了怎么做,但是还没讲怎么用(下一节?)

Lecture 12:Geometry 3

概述

这节课主要讲的是 曲面细分曲面简化阴影

这篇文章记录 曲面细分 和 曲面简化,阴影放在下一篇文章;

曲面细分

曲面细分一般来说,需要先增加点的数量,然后再调整点的位置。

Loop细分

概述

整体步骤:

  • 增加三角形数量;
  • 改变三角形位置,让模型看上去更加光滑;

具体算法

  • 把每个三角形分成4个(这里就会产生新的顶点)

  • 根据权重分配新的顶点(新顶点和旧顶点的调整方式不同)。
    • 更新 新顶点:周围几个点的加权平均;
    • 更新 旧顶点:根据自己原本的位置和自己周围顶点的位置来进行加权平均(n是顶点的度数)

Loop细分结果

Catmull-Clark细分(General Mesh)

Loop 细分只能解决三角形网格的细分问题,对于一般的情况(一般的网格面)可以采用Catmull-Clark细分。

基本概念

非四边形面(Non-quad face):顾名思义。
奇异点(Extraordinary vertex):度数不为 4 的点。

Catmull-Clark细分算法

  • 增加新的点
    • 取每个面的一个点(比如重心)
    • 取每个边的中点
    • 将所有新添加的点做连接
    • 在一次细分后会引入两个新的奇异点。
    • 新奇异点的度数为3,其他奇异点的度数不变。
    • 经过一次细分,所有非四边形面会消失。
    • 细分之后,每个非四边形面都会变成一个奇异点(以后再细分奇异点和非四边形面都不会发生变化了)。
  • 调整规则
    • 对于 新点面上的点边上的点位置更新规则如下:
    • 对于 旧点

Catmull-Clark细分结果

曲面简化

概述

目标:减少三角形的数量并保留整体的形状。

做法:边坍缩(Collapsing An Edge)

优化:二次误差度量

二次误差度量(Quadric Error Metrics)

  • 它用来表示曲面简化带来的误差的大小。
  • 用局部顶点做平均效果是不好的。
  • 二次误差:在平面中找一个点,使得这个点与它相关联的面的距离平方和达到最小,将这个点作为新的点;

做法:

  • 首先将每条边的二次误差值,作为他们各自的分数;
  • 首先选一条分数最小的,进行边塌缩;
  • 此时其他边的分数会变化,所以更新所有边的分数;
  • 然后继续重复上面的操作;

(可以用堆来实现,是贪心算法的结果)

Lecture 11:Geometry 2 (Curves and Surfaces)

概述

这节课主要讲了几何中的 曲线 和 曲面;

前面一段是在讲几何的显示表示,放在放一篇文章了;

贝塞尔曲线

概述

用一系列的控制点来定义某一条曲线。这些控制点使用切线来定义贝塞尔曲线。

看上图,注意曲线起始方向沿着P0 P1,结束方向沿着P2 P3;

(不要求曲线经过控制点,但一定要经过起止点 和 极值点(控制点关于时间插值出来的点))

计算贝塞尔曲线——de Casteljau 算法

首先我们把整个曲线映射到 [0,1] 的时间轴上

曲线上的每个点都是关于时间 t(0,1 之间)的插值的结果

这也是为什么贝塞尔曲线是显示的表示(做了映射);

下面看看是如何具体做映射的:

  • 给定三个控制点生成的贝塞尔曲线称为二次贝塞尔曲线(quadratic Bezier)。

  • 使用线性插值插入一个点(假设起点是时间0,终点是时间1),假设当前这个点对应时间轴的上 t ,按时间轴的比例,标记出一个线段上的插值点 b01;

  • 对于每个第一层(原始的)线段都做一下插值,变成:

  • 然后继续,同理,对于第二层的线段也同样操作,直至剩下一个点;

  • 这样我们只是得到了一个 t 的插值后的点,我们对 [0, 1] 所有的 t 都进行插值,最终就能得到这个曲线;

贝塞尔曲线代数公式

推导:

如果有n+1个控制点(从0开始编号),可以得到n阶的贝塞尔曲线。

Bernstein 多项式:

例如,当 n=3 时,公式为:

贝塞尔曲线的性质

逐段(Piecewise)贝塞尔曲线

概述

由于当控制点太多,曲线难以被控制点控制。

所以逐段定义贝塞尔曲线(一般用四个控制点)。

如果要让不同的分段之间光滑过渡,只需要在切线上连续方向大小上)。

分段贝塞尔曲线的连续性

其他类型的曲线

样条 Spline

一个连续的曲线是由一系列控制点控制点,在任意一个地方可以满足一定的连续性。

简单来说就是一个可控的曲线。

B样条 B-Splines

  • 贝塞尔曲线的扩展(具有局部性,容易控制),就是基函数(basis)样条。
  • 比贝塞尔曲线需要更多的信息。
  • 满足贝塞尔曲线的所有性质。

(贝塞尔)曲面

概述

把贝塞尔曲线的概念延申到平面上。

回顾一下,每段贝塞尔曲线是4个点的线性插值的结果;

所以,每一片贝塞尔曲面是16控制点,两次线性插值的结果:

第一次先 4 组(每组四个)进行一次线性插值,得到四条贝塞尔曲线,然后对于四条曲线,我们再插值一次,得到曲面:

规范的讲:

输入:4*4 控制点

输出:一个在 [0,1] ^2 内的 (u,v) 参数化的二维曲面

反过来讲,给一个 贝塞尔曲面上参数化后的 (u,v),我们就能知道 (u,v) 对应的是曲面上的哪个点。

Lecture 8:Shading 2 (Shading, Pipeline and Texture Mapping)

Blinn-Phong 模型的后两种

高光像(高光反射 / 镜面反射)

当我们观察方向光线反射的方向很接近的时候,那么就会产生镜面反射;

怎么算??

Blinn-Phong 模型中做了一个改进,它发现了,观察方向和光线反射的方向很接近 <=> 平面的法向量和半程向量很接近,所以考虑下面的计算方法:

指数 p 的一个作用:控制高光的衰减大小

环境反射(间接反射)

大胆假设:任何一个点接收到环境的光是一样的!

Ia:表示来自环境的光强
Ka:系数

所以可以看到,环境光是一个常数,和观测方向无关!

Blinn-Phong 模型 小结:

把所有的光加起来就是最终结果:

所以:

Blinn-Phong 模型 = 漫反射模型 + 高光反射/镜面反射 + 环境反射

着色频率

着色模型

着色频率不同产生不同的效果:


第一种:对每个三角形的平面进行着色;
(求法线)


第二种:对于每个顶点求一个法线,然后进行着色;


第三种:对于每个像素我们都去着色;

(对每个像素都去求一个法线)


要看情况使用不同的着色频率,当我们的物体的平面比较多的时候(模型足够复杂),这时候我们就可以直接用简单的着色模型了,最终的效果也不会差。

定义一些东西

定义每个顶点的法向量

顶点的法向量 = 相关的面的法向量的加权平均值


定义每个像素的法向量

像素的法向量 = 顶点法向量平面的重心坐标

图形管线(实时渲染管线)

Graphics (Real-time Rendering) Pipeline

定义:如何从 一个场景 -> 一张图 的过程

就是一些列不同的操作:

  1. 3D模型
  2. 顶点处理
  3. 三角形处理
  4. 光栅化
  5. 着色
  6. 输出

现代GPU允许我们自己定义这些操作,所以也叫做 可编程渲染管线(也就是我们讲的Shader

具体的讲:

对于一个顶点或者一个像素,我们可以自己编程计算,自己规定这个点或者像素最后显示的颜色;(只用统一写一次,应用于每一个点)

顶点着色器:给每个点写一个着色的程序

像素着色器 / 片元着色器:给每个像素写一个着色的程序

现代图形学发展

纹理映射(Texture Mapping)

定义一个点的不同属性,具体讲,对于一个三维空间,我们都可以把它展开成二维平面,从而和图像对于;

那么现在我们要解决的问题就是,我们如何将一张图像(纹理)贴到三维的物体上面?(这个过程就是纹理映射)

其实问题就是,对于物体上的三角形,我们要怎么映射到纹理上的三角形。

这个问题比较复杂…这里我们规定已经是映射好了的。

纹理的坐标

U V 都在 0~1 之内


思考:

  • 假设我们知道了三角形三个点对应的纹理坐标,我们如何知道三角形内部任意一点的纹理坐标(U V)?

  • 在三角形的三个顶点有各自不同的属性,如何在三角形内部做一个平滑的过渡,使得我们能快速知道三角形内部任意一点通过插值之后的属性是多少?(重心坐标

Lecture 03: Transformation

二维变换

缩放变换

用矩阵表示,矩阵就叫缩放矩阵

对称变换一下:

复杂一点的

这里的 x 的变换 和 y 是有关系的 x’ = x + ay

旋转变换

推导:

推关系(x’ 和 x)(y’ 和 y):(利用特殊点(1,0)

从上图可以得到:

A = cosθ

C = sinθ

同理,用特殊点(0,1)算出B D

B = -sinθ

D = cosθ

所以可以得到旋转矩阵 Rθ;

记忆:

记图,然后自己推到一下就行!

性质:

旋转矩阵的转置 <=> 旋转矩阵的逆

如果一个 矩阵转置 = 矩阵的逆,则这个矩阵叫做正交矩阵

小结

线性变换:输入坐标 * 矩阵 = 输出坐标

上面的例子统称为线性变换

齐次坐标(统一变换)

为什么?

当我们平移图像的时候,可以发现我们无法用之前的简单的矩阵x向量的形式表示,而是得用下面的表示方法:

所以,平移不属于线性变换

但是我们不想要它特殊,所以引入了齐次坐标用于表示各种变换

怎么做?

首先,用三个数来表示一个二维向量

相当于就是把二维变成三维,然后用特殊的数字去表示三维

  • 点:第三维是 1
  • 向量:第三维是 0

这样就能通过齐次坐标的方式,把所有的变换都表示成 矩阵 x 向量 的形式;

为什么向量第三维是 0 ?

这么做是为了保证向量的平移不变性;

同时保证了在一些运算下仍然是正确的;

在齐次坐标下:point1 + point2 = point 1 2 的中点

(x1,y1,w)转置 + (x2,y2,w)转置

=> ( (x1+x2) , (y1+y2) , 2w )转置

=> ( (x1+x2)/2w , (y1+y2)/2w , 1)

小结

仿射变换:

齐次变换:

注意到,在表示二维坐标的仿射变换中,齐次变换矩阵的最后一行都是(0,0,1)

注意是有条件的,二维坐标,仿射变换.

验证:(缩放,旋转,平移)

逆变换

要变换回来,则乘以变换矩阵的逆矩阵

变换组合

首先规定,这里默认的旋转是 以0,0为中心,逆时针 旋转的;

变换的顺序很重要;

变换组合 其实本质上就是对应着 矩阵的乘法!!!

当矩阵乘法的顺序不同,得到的变换结果也是不同的。

举个例子

在这个变换中,我们需要先旋转45度,再平移;

那么对应的数学结果就是:

总结:

我们按照变换的顺序,在原向量的基础上,往原向量的左边,从右往左依次乘上变换矩阵

值得注意的是:矩阵乘法有结合律!

变换的分解

三维变换

同理,在三维变换中,也有平移这样的非线性变换操作;

所以继续引入齐次坐标;

用四个数去表示三维空间;

三维变成四维,第四维度取特殊值;

  • 描述,最后一维为1;
  • 描述向量,最后一维为0;

当 w != 0 时,齐次坐标下的点 (x , y, z , w) , 则表示三维空间中的点 (x/w , y/w , z/w)

将 x y z 都 /w 就可以表示成三维空间中的点了!

同样的,在描述三维空间中的反射变换时,变换矩阵的最后一行是(0,0,0,1);

平移操作还是体现在最后一列;

值得注意的是 :

当我们写出齐次坐标的时候,它表示的其实是:
先线性变换,在进行平移

具体的变换 :

这里注意一下绕y轴为啥是反的?这个和轴的顺序和右手螺旋定则有关;

补充

任意一个3D的旋转,都可以分解为分别绕x y z轴旋转;

a β γ 称为欧拉角

Rodrigues’ 旋转公式

就是能把任意一个旋转分解为绕 x y z 的旋转:

n向量:旋转轴(可以理解为一个方向向量,且规定n过原点)

a:旋转角度

推导

扩展 :

四元数 : 主要用于两个旋转之间的插值

Lecture 04 : Transformation Cont.

概述

  • View / Camera transformation:视图变换 / 相机变换

  • Projection transformation:投影变换

图形学:正交/透视投影矩阵的推导(多个思路) - 知乎 (zhihu.com)

上面这一边里面讲的非常清楚了 !

视图变换

试图变换是什么?

三元素 :

  • 把场景搭好(model transformation,模型变换
  • 找一个好的角度,把相机放好(view transformation,视图变换
  • 然后拍照(projection transformation,投影变换
    简称 MVP 变换

特别解释 :

  1. 位置:这个没啥好说的…就是一个坐标
  2. 凝视方向:也就是向哪里看,这里其实包含了两个方向,我们把头当作摄像机,那么凝视方向就包含了我们头左右看上下看的元素(不歪头)
  3. 向上方向:这个不是说向上看的意思…还是把我们的头当作摄像机,当我们确定了头的位置和凝视方向时,当我们歪头时,我们可以发现我们看到的还是不同的,所以我们还需要一个元素来约束;这里引入一个始终垂直于我们头顶的法向量,这个法向量指向的方向就是向上方向,当我们歪头时,这个法向量也不同;

因此,通过上面这3个元素,我们可以唯一确定相机看到的图像!!!

如何进行视图变换?

首先我们将相机定,其他东西都随相机移动;
(固定指的是:相机在0 0 0处,且x向前,y向上,-z向右)

具体操作:(初始相机在e点,看向g,向上是t,g叉乘t对应着x)

  • 先平移到 0 0 0
  • 将 g 旋转到 -z
  • 将 t 旋转到 y
  • 最后将 g 叉乘 t 旋转到 x

下面是矩阵表示

直接求这个变换矩阵的话不太好求,我们可以反过来想:

将(x、y、-z)旋转到(g x t、t、g),得到旋转矩阵,将这个旋转矩阵求逆就是我们结果,注意到上节课的性质,这种变换的矩阵是正交矩阵,即 矩阵的逆<=>矩阵的转置,所以直接求矩阵的转置就行。

投影变换

正交投影

近大远小效果

假设相机在 0 0 0 处,且看向-z方向,向右是x,向上是y;那么将相机x y范围内的的物体的z方向去掉得到的图像就是正交投影

更加规范的

  1. 先将物体的中心移到原点0 0 0
  2. 将物体进行缩放,缩放到(-1,1)³的立方体中
    (这里长方体等物体会变得不一样… 后面会将再一次拉伸 ( 视口变换 )…)

透视投影

**概述 : **

近大远小效果

回顾一下,前面我们讲了,齐次坐标(用四个数表示三维空间中的 点/向量)

如何进行透视投影?

还是那个问题,正面不好想,我们反着想;

透视投影要做的是将平面变成进平面 ( 挤压 ) ;

也就是如何 透视投影 -> 正交投影,得到变换矩阵之后;求这个变换矩阵的逆,那么这个逆就是 正交投影 -> 透视投影 的结果。

透视投影 -> 正交投影

  1. 将 透视投影 的梯形体挤压成长方体(近平面不变,远平面挤压,且远平面的z不变,且远平面的中心点的坐标仍然不变)
  2. 做一遍正交投影

**具体分析 : **

第二步是比较简单的,上面说过了;

下面分析第一步要怎么做???

首先我们要明确一下我们的问题,现在的问题是:

如何将在透视投影中的点(x y z)变换为正交投影中的点(x’ y’ z’),也就是 透视投影 -> 正交投影 的变换矩阵

首先我们可以发现变换时点在z轴上的变换是比较难求的,这里我们先不考虑z轴的情况,后面再求;

我们先看x轴和y轴的情况,可以发现他们形成一个相似三角形,因此 x’ x,y’ y 的关系很容易就知道了!

前面说过,齐次坐标下,每个数都同时乘上同一个数,这个点/向量不变的,表示的还是那个点;所以我们可以进一步的变换:

接着我们试着反推一下这个变换矩阵:(什么样的矩阵 乘上 透视时的点 会变成 正交时的点?)

可以发现,我们可以得到这个变换矩阵的大部分信息:

由于我们把这个z轴给展示不管了,所以导致这个变换矩阵的第三行是未知的;现在我们需要通过一些性质去把第三行解出来;可以发现在变换前后有这样两个性质

  • 变换前后,平面上的点是不变的(x y z都不变);
  • 变换前后,平面上的中心点是不变的(x y z都不变);

我们先利用第一个性质,假设在近平面上有一个点(x y n 1),那么变换之后还是(x y n 1),变换一下为(nx ny n² n);

利用上面的残缺矩阵,我们可知:

(????)(x y n 1)转置 = (nx ny n² n)转置

从而我们可以推出第三行:

同时得到一个方程:

接着我们利用第二个性质:远平面上的中心点为(0 0 f 1),变换后还是(0 0 f 1),同理变换一下(0 0 f² f)

同样,我们可以的到一个方程:

也就是说我们有两个未知数A B,有两个方程,这样就能解除A B,从而得到我们的变换矩阵了!

分析结束!!!

注意 :

到这里 , 我们只是求出了 透视投影压缩变换 , 也就是 透视投影 -> 正交投影 ;

所以如果要求透视投影矩阵 , 我们还需要乘上一个 正交投影矩阵 :

具体的话可以再看一下这篇文章的分析 :

图形学:正交/透视投影矩阵的推导(多个思路) - 知乎 (zhihu.com)

**思考 : **

在近平面和远平面之间的点,从透视投影 -> 正交投影的过程中,它们的z轴是向近缩,还是向远拉伸???

题解

离相机更远(更靠近远平面)

GANES101 透视投影的理解

Lecture 05 : Rasterization 1 (Triangles)

光栅化1:三角形的离散化

视口变化

像素的表现形式

前面我们说到,对于正交变换时,我们将物体映射到[-1,1]³ 的立方体中,那么现在我们的问题就是,我们如何把这个图像映射到屏幕([0 , width] [0 , height])上?

先不考虑z轴,我们看x y:

  • 进行缩放
  • 把原点移动到像素点的位置

这一步也叫做视口变换

光栅化

定义

定义什么是光栅化

三角形光栅化的核心思想就是:判断 像素的中心点和三角形的位置关系

三角形有很多好的性质

具体方法(采样法)

记住我们现在要干什么:判断一个像素(中心点)和三角形之间的关系

采样法

采样就是将一个函数离散化的过程;

通俗的讲就是,有一个函数,然后我们通过不同的点去经过这个函数得到不同的结果,从而来判断关系。

这里采样的方式针对的是像素的中心

具体例子:

其中 inside 函数就是判断点是否在三角形内

这里定义像素位置为(x,y),则像素中心的位置为(x+0.5 , y+0.5),下面是代码:

那么下面具体的问题就是:这个 inside 函数怎么写?

这个很简单嘛,之前学过了,用叉乘可以判断;

规定三角形方向:p2p1 , p1p0 , p0p2

然后用这三个向量分别叉乘:p2Q , p1Q,p0Q就行;

  • 如果 都<0 或 都>0 则在三角形里面;
  • 否则不在

边缘情况(中心点点在三角形的边上)我们不做处理

简化

  • 用包围盒来确定包围这个三角形的最小矩形

  • 对于每一行都搞一个包围盒(较难实现)

Lecture 06 : Rasterization 2 (Antialiasing and Z-Buffering)

光栅化:深度测试抗锯齿(反走样)

问题描述

会有锯齿状…

Aliasing:走样

采样会有一些问题/瑕疵(Artifacts):

  • 锯齿

  • 摩尔纹

  • 车轮效应

本质原因:

信号函数变换的速度太快了,导致采样的速度跟不上信号变换的速度。

解决方法

方法:

反走样(抗锯齿):

  • 先对信号进行模糊;
  • 再采样;

注意:这两个步骤的顺序不能反着来!!!

思考问题:

那么问题来了:

  • 为什么这么做可以反走样呢?

  • 为什么不能反着来呢?

具体分析:

首先总结一下走样原因

采样的频率跟不上信号变化的频率;

从而导致,同样一个采样频率,采样两个信号函数,会导致相同的采样结果;

从而出现 “走样”。

走样原因分析:

首先解释为什么信号变换很快的时候,采样速度跟不上会导致走样的情况?

看上面这幅图,当信号变换越来越快的时候,说明信号函数的频率很快也就是比较密,图中虚线表示采样时刻,那么我们可以发现,当变换越来越快的时候,采样出来的点组成的函数越无法表现真实的函数!

接着我们看下面这张图:

可以发现,对于相同的采样方式,对蓝色的函数和黑色的函数进行采样,得到的是相同的结果!

因此这就是走样

傅里叶变换的作用:

滤波:过滤掉一些特定的频率

傅里叶变换可以把一个函数从时域变成频域

也就是说:傅里叶变换能够让我们看到任何信号在各个频率下长什么样。(频谱)

上面这张图是原始图像

(左边是时域下的,右边是频域下的,表示的是同一张图哦!

在频域的表示形式下,我们用亮度来表示信息的图像多少(在这个频率亮度越亮,表示这个频率的信息越多)

其实我们可以发现,很多图像的信息都集中在低频处。

接着,我们看下面这几个例子:

  • 当图像通过高通滤波后变成下面这样:

边界的地方是高频的(因为突然发生了变化),通过高通滤波(只让高频的通过),就保留了边界

  • 低通滤波

模糊了边界

  • 某一段特定频率

滤波

滤波 = 卷积(= 平均)

首先理解一下卷积

图像处理中的卷积我们可以简单理解为下面的情况:

首先我们有一个滤波模板(滤波器),模板里面有对应的值(概率);
然后对于每个像素值,我们都通过这个模板去计算一下,生成的值填到中心位置上。(和 数学上的卷积有所不同)

定理:

时域的卷积 = 频域的乘积

时域的乘积 = 频域的卷积

看一个例子解释

上面这张图,通过时域范围内的卷积可以得到一张模糊的图像;等价于,我们首先将原图像和卷积模板(滤波器)通过傅里叶变换从时域转换为频域,然后将这两图像相乘,最后通过傅里叶逆变换把结果从频域转换为时域;

逻辑分析:可以看到,通过滤波器的乘积过后,原图像只保留的低频信息,也就是高频(边界)被丢弃了,导致了模糊。

同时我们也可以知道,当(滤波器)越大,一个像素将受到越多其他像素的影响,导致图像越模糊,高频信息越加被丢失!

从频谱角度理解 采样:

首先,采样就是在重复原始信号的频谱

采样越少,对于这频谱就越密,就可能发生混叠现象,也就是走样

反走样

怎么做?

  1. 先做模糊(把高频信息去除,就叫做模糊)
  2. 再采样

为什么?

看下面这张图就知道了!

把高频的去除后发现不会混叠了!

如何做?

那么怎么把图像模糊呢???

很简单啊,前面不是分析嘛,用一个 低通滤波器原图像 做个卷积就行了!!!

但是但是,这一步很难…

所以我们搞了一个近似的做法:MSAA

就是说,对于每个像素,我们把它分成更小的像素点,通过更多样本来近似了模糊的过程;看下面的例子(它把每个像素点分成了2x2的小像素,然后计算概率):

然后采样就行!

总结:MSAA 不是通过提高采样率,而是通过模糊操作来 实现反走样;

思考

缺点:

  • 这么规则的分割像素,极大增加了计算量(实际生活中我们用的不是那么规则的分割,可以说是一种优化)

扩展,除了MSAA,还有其他的抗锯齿的做法:

Lecture 7:Shading 1 (Illumination, Shading and Graphics Pipeline)

概述

  • 可见性 / 遮挡问题

    • 深度缓存
  • 着色

    • 光照和阴影
    • 图形管线

可见性 / 遮挡

画家算法

画家算法:先画后面的,再画前面的;(要严格规定前后顺序才不会出现问题)

缺点:

  • 要定义深度不容易…
  • 对这些东西要按照深度排序,时间复杂度较高
  • 特定情况失效(无法对定义深度)

Z-Buffer(深度缓存)

概述

对于每个像素,记录最浅的深度;

同步生成两个图:一个是渲染后的图,一个是深度图;

深度图中,离相机越进,反应出来的颜色越黑,越远越白;

整体做法

每个像素维护一个最小深度

算法分析

但是无法处理透明物体

着色

概述

定义:对不同的物体,应用不同的材质 的过程;

(不同的材质对于光的反应不同)

看个例子:

Specular highlights:镜面反射(表面光滑)

Diffuse reflection:漫反射(表面粗糙)

Ambient lighting:间接反射

分析

一些概念的定义

着色只考虑自己,不考虑阴影的情况:

漫反射模型

着色点显示的亮度(能量)和光照之间的夹角是有关的:

其次,我们接受到的能量和光源的距离是有关的:

那么通过上面这两个结论,我们就能够分析漫反射时,我们能看到着色点多少能量(以明暗的形式表示出来)

看到着色点的能量 = 到达着色点的能量 x 着色点接收了多少能量


这里有个问题:

  • 上面式子中的 Kd 是什么???

  • 着色点为什么会有颜色???

我们知道,物体之所以有能量是因为它吸收了一部分能量,反射出它不吸收的能量,从而看到了颜色;

所以这里我们用一个系数 Kd(漫反射系数)来描述能量的吸收程度:

  • 1:表示不吸收能量
  • 0:表示吸收全部能量

漫反射是从不同的角度看同一个点,看到的结果是相同的。

从公式上也能看出这一点,和观察的方向没有关系

Lecture 9:Shading 3 (Texture Mapping cont.)

图形学笔记(八)着色2 —— 纹理映射、重心坐标、双线性插值、Mipmap、三线性插值、各向异性过滤、纹理的应用(环境贴图、法线贴图等)

上面是笔记,记得贼全。

概述

问题如何在三角形内部进行任何属性的插值?

什么是插值?

插值算法

为什么要插值?

是因为我们想要在三角形内部得到一个平滑的过渡。

插值什么

插值的东西是很广泛的(任意属性),比如说 颜色(三个点有三个不同的颜色,那么通过对颜色进行插值使得三角形内部的颜色有平滑的过渡);位置(我们知道了三角形的三个顶点对应纹理上的三个顶点,然后通过对顶点位置进行插值,从而确定三角形内部每个点的位置)等等。

如何插值?

重心坐标

且三个系数非负

满足上面条件的就叫重心坐标(是三角形内的点的意思)

这里一定要注意:重心 和 重心坐标 是两个完全不同的概念!!

  • 重心:指一个三角形内部的特殊点,该点与三角形的三个顶点的连线平分三角形的三条中线。
  • 重心坐标:是一种表示点在三角形内部位置的方式。
    • 对于一个给定的三角形ABC,点P在三角形内部的位置可以由三个重心坐标(u,v,w)表示,满足以下条件:u+v+w=1,且每个重心坐标都是点P到对应顶点的距离与三角形对边长度之比。

任意一点的重心坐标:


三角形的重心


重心坐标的规范公式


重心坐标的作用

既然我们知道了重心坐标的计算,那么我们就能通过重心来对三角形内部一点的属性进行插值:


缺点:

当三角形被投影到其他平面上面去的时候,三角形三个顶点的位置可能发生变换,从而导致重心坐标发生变化;

也就是说,三维空间中的物体,我们不能计算投影后的重心坐标,而应该在三维空间中进行插值算出三维的重心坐标!

应用纹理

纹理太小的问题(双线性插值)

问题1:纹理太小的问题

当我们的图像比较大,纹理比较小的时候,做纹理映射后纹理会被拉大,导致对应纹理位置的不是一个整数值;

导致下面Nearest的情况:

但是我们想要上面 Bilinear,Bicubic 的情况;

当一个像素对应的纹理坐标不是整数值时,我们如何去设置这个值???(双线性插值

  1. 首选对于像素点,取周围四个点的纹理坐标

  1. 用U00,U10插值得到U0,用U01,U11插值得到U1。

  1. 用U0和U1进行插值得到红色点


纹理太大的问题(Mipmap解决范围平均值查询 + 三线性插值)

问题2:纹理太大的问题,导致走样,出现摩尔纹;

本质原因就是一个像素点包含了太多个纹理,导致当我们用普通的纹理映射的时候,这个像素点的重心代表了整个区域,从而导致了走样;

那么首先能想到的就是反走样方法:用多个像素点采样取平均值(但是计算量太大了)

换一种算法思路:不采样,直接对于每个像素点,我们可以直接知道这个像素点的平均值

Point Query(点查询):给一个点求值(插值)

Range Query(范围查询(平均值)):给一个区域,我们能立刻得到区域中的平均值。


怎么实现范围查询???

引入 Mipmap,特点:

  • 近似的
  • 方形查询

原理是:二进制!!!


Mipmap 具体做法步骤:

对于给定的一张图,我们先拆分成不同分辨率的图像(每次向下除二):

记住上面这个操作:

接着,对于每个像素点(在原图上面找的),我们取它周围的像素点,同时把这些点在纹理图上的纹理坐标也找出来:

接着,我们通过计算,近似的算出这个像素点在纹理坐标上大概占多少区域(不规则)

然后我们把这个不规则的区域再近似的规则成一个正方形(为什么要规则成正方形下面就知道了)

且是2的幂次

那么对于每个正方形区域,我们就能快速(O(1))的在第log2 L层去查询就行了(因为上面预处理好了!)

问题分析

但是这样会有,因为只预处理了2的幂次,我们并不知道1.5层是怎样的,所以导致不连续了;

怎么解决?

仍然是插值

比如说,对于1.5层,我们可以选第1层和第2层,分别在层内进行 Mipmap 范围查询,然后再对这两个结果进行层与层之间的插值:(这就是三线性插值


但是还是有问题:

可以发现远处细节都被模糊了…

原因是 mipmap 只考虑正方形,而有些像素对应的纹理其实可能是不规则的,从而导致模糊;


解决方法:

  • 各向异性过滤

引入矩形查询的区域,但是不规则咋办…


  • EWA 过滤

对于不规则形状,我们可以通过圆形的多次查询来确定;

缺点:多次查询,开销较大…


纹理的应用

靠,真的复杂…看这个笔记:

图形学笔记(八)着色2 —— 纹理映射、重心坐标、双线性插值、Mipmap、三线性插值、各向异性过滤、纹理的应用(环境贴图、法线贴图等)