图形学笔记-Games101

向量

  • 点乘。
  • 在图形学中一般使用:知道两个向量,算他们的夹角。
  • 点乘使用交换律、分配律、结合律

Vector3 a = new Vector(x1, y1, z1);
Vector3 b = new Vector(x2, y2, z2);
//计算对应的余弦值
double cosValue = (a.x1 * b.x2 + a.y1 * b.y2 + a.z1 * b.z2) / a.magnitude * b.magnitude;

// 计算对应的弧度值
double angleInRadians = Math.Acos(cosValue);

// 将弧度转换为角度
double angleInDegrees = angleInRadians * (180 / Math.PI);

  • 叉乘。
  • 其结果垂直于AB,垂直的方向由右手螺旋法则定义(×前的向量指向×后的向量)。
  • 由于括号内的意思的原因,使用不满足交换律,交换后需加上负号

  • 向量叉乘自己是零向量;满足结合律和交换律

  • 用于判断b在a的左侧还是右侧。
  • 用于判断P在△ABC的内侧还是外侧(AP在AB的左,BP在BC的左,CP在CA的左)。
    查看代码
    Vector3 CrossProductResult(Vector3 origin, Vector3 point1, Vector3 point2){
        Vector3 sideA = point1 - origin;
        Vector3 sideB = point2 - origin;
        
        // 使用叉乘计算平面的法线向量
        return Vector3.Cross(sideA, sideB).normalized;
    }
    bool IsPointInTriangle(Vector3 pointA, Vector3 pointB, Vector3 pointC, Vector3 target){
        Vector3 cross1 = CrossProductResult(pointA, pointB, target);
        Vector3 cross2 = CrossProductResult(pointB, pointC, target);
        Vector3 cross3 = CrossProductResult(pointC, pointA, target);
        
        return cross1 == cross2 && cross2 == cross3;
    }

矩阵

  • 矩阵:AB矩阵相乘只有符合A矩阵的列等于B矩阵的行才能乘。
  • 相乘结果怎么出来的:在结果里看他的行/列,去A的行和B的列找所有数字,把这些数字点乘起来就行了。

  • 如:如何得出(二行,三列)的61,找到A的二行5/2,找到B的三列9/8,点乘5*9+2*8。

  • 矩阵不存在交互律,但是其他的都能用。

  • 转置。

 

OpenGL一般使用列项

Unity和UE使用行项

你问我什么是列项什么是行项?

问GPT。

Transform

  • 切变:

  [x + ay]

  [y]  

  • 二维旋转

  • “三元数”官方:齐次坐标
  • 其中,出现了新的概念,点和向量的区别。

  • 在引入w之后,w=0表示向量,w=1表示点
  • v(x1,y1,0)+v(x2,y2,0)=v(x1+2,y1+2,0);
  • p(x1,x2,1)-(x2,x2,1)=v(x,y,0);
  • p(x1,x2,1)+v(x2,y2,0)=p
  • 点加点则引入了一个新概念,新产生的点为相加两点的中点(w==2)

 

  • 先旋转还是先平移->产生的结果不一样。
  • 此图表示先旋转,再平移。

  • 四元数。

  • 负角度(矩阵的逆)写来出之后发现等于矩阵的转置(官方:正交矩阵)

  • 如何定义一个视角:位置、朝向、上方向

  • 目标:将右上角的摄像机放到左下角的原点、且方向一致。
  • 如图。
  • 那么该如何写步骤呢?
  1. 自然是把e移至原点;
  2. 从t/g改为-z/y的旋转矩阵一般情况下写不出来,因此使用逆↓

  • 正交-矩阵:把任意地方的矩形变成原点±2的正方体

把左边‘f’的面压缩,变成右边的‘f’,任意点的矩阵怎么写呢

  • 首先:比例是n/z,于是我们就得出了下图

  • 其次:我们根据z不变的原则,把远面f的z乘上n/z

  • 然后我们就知道了(00 ??)那么怎么求AB呢?
  • 使用了远面f中点不会改变的例子,n符合,f也符合

  • 于是:对远面的“挤”所需要的矩阵就完成了,把远面乘以这个矩阵就得到了挤压后的面。然后再用正交的方法成像就可以了。

光栅化

  • Raster(光栅):俄语,屏幕的意思

  • 定义:左下角方块为(0,0),其中心是(0.5,0.5);蓝色方块为(2,1),其中心为(2.5,1.5)

  • 视口变换
  • 先不管Z;把XY的[-1,1]变成[0,width]x[0,height]使用的是如图矩阵;拉长->平移。

  • 场景中模型都是由一个个三角形拼出来的,那么在‘挤’->‘挤之后的矩形变成正交矩形’->视口变换之后,必定在屏幕上有一个三角形。
  • 但是屏幕是由一个个像素组成的,三角形穿过这个像素的大小有大有小,那咋办呢?

  • 如图,这方面的定义是采样。采样:给你一个函数f(x),你用x=1,2,3...去问f(x),是多少,f(x)给你的值就是采样后的答案;就是把函数离散了,就只拿点。
  • 显存在拿到图片之后,去每个像素的中心找值,后面就不用说了。

就这个。

  • 那么该如何判断这个点是否在三角形内呢?做叉乘
  • 边界处理:自行定义。
  • 问题,会有锯齿问题的出现。

反走样(抗锯齿)和深度缓冲

  • Artifact:锯齿、手机拍电脑的摩尔纹、车轮倒转现象等。翻译叫瑕疵,但不绝对。
  • 原因:信号变化太快,导致采样频率跟不上。
  • 操作:模糊。

  • 原理层面:傅里叶变换。

  • 傅里叶级数展开

  • 把低频的地方删去,只留下高频的部分↑ ↓同理


  • 卷积:取平均数。在原始的数据,取周围数的平均。(模糊用)*图形学上的简化操作。

  • 时域上:卷积。频域上:傅里叶变换。
  • 说一下个人理解吧:就是没理解。大致意思应该是通过傅里叶变换可以得到模糊的边界,然后就可以减少锯齿。
  • 但是计算机层面我看懂了:把一个像素分成n*n个点,求这n2个点的RGB平均值,然后再赋值给这个像素,最后就得到了模糊的边界。

  • MSAA(专有名词)

  • 把一个像素分成2x2个点。

  • 求平均:第一个点

  • 懂了吗。

Shading

  • 有一种渲染方法是从远处开始渲染:画家渲染法,先画山,再画树再画人。应用到计算机里就是先渲染远处的三角形,再渲染近处的三角形,但是这一做法具有较大的局限性。
  • 比如遇上这种情况

  • 使用通常使用像素渲染法:Z-Buffer。

  • 先通过算法得到深度图,然后才进行渲染。

  • 解析:一开始,每个像素所对应的点都是无限远的地方,然后一层层向视角走,如果有三角形,就拿三角形所对应的点RGB值和深度(depth)z。
  • 然后继续靠近视角,倘若在这个过程中,之前赋值过的像素点的z,有更小的z可以替代了,那么就更新这个z值。
  • 思路如图。


  • 视角看到的颜色和以上数值有关:观察方向V、法线方向n、光照方向l,还有shadingpoint。
  • 都是单位向量:这样就可以控制视角可看到的物体的材质,是金属或是木头。

  • 一个点光源,半径越小,单位面积上的能量就越大,设半径为r,光照强度为I,那么右上角那个点的强度(intensity)就是I/r方。

  • 通过这个公式就可以拿到某个点的漫反射光照强度。
  • L:漫反射;k:反射强度系数;(使用max是不考虑折射情况)
  • 因为是漫反射,所以每个方向上观察到的颜色都是一样的,因此跟v没有关系。

  • 讲完了漫反射,来讲镜面反射,虽然有镜面一词但不是镜面,镜面反射是出射角处的高光,

计算R和v的差,可以得出高光项。

  • 但是呢,布林和模型:引入了半程向量h(v和l的中心向量),这样就不需要去计算出射角R,只需要计算h,再用h和n做一下点乘,就可以求出高光项,计算优化。

  • 其次,为什么右上角有个p呢:如图,点乘后得出的值比较大,n和h相差89度也很大,因此给它一个指数,如p=64,那这样就只能在20°左右才能看见高光。(p一般100-200)

  • 结果:

  • 环境光照:光在经过一次又一次的漫反射和反射后,会打到物体背光处,从而把物体背光处打亮,但是现实显然不可以去计算如此复杂且多的光照。
  • 因此我们假设每一个点都受到相同的环境光照强度:

  • 于是我们就得到如图,环境光+漫反射+高光=布林和反射

  • 着色频率。

 

  • 平面着色:取每个面的中心点进行着色,会得到如图的模型。

  • 高罗德着色(点着色)

  • 得到三角形顶点的颜色(假设法线能求)然后在三角形内部,三个点之间的平面颜色使用三个点的插值来计算,得到如图。

 

  • phong着色,获得两个相邻三角形的法线,利用插值计算其间每个像素的法线,再依次相加计算每个反射光。

  • phong着色,获得两个相邻三角形的法线,利用插值计算其间每个像素的法线,再依次相加计算每个反射光。

  • 着色频率越高就越不用高级着色法。

渲染管线

1. **应用程序阶段(Application Stage)**:此阶段由应用程序负责设置场景、相机和物体属性等信息,并将数据传递给图形API(如OpenGL或DirectX)。

2. **几何阶段(Geometry Stage)**:首先,几何阶段通过顶点着色器(Vertex Shader)对每个顶点进行变换和处理,将其从模型空间转换到裁剪空间。接下来,进行剔除(Culling)、裁剪(Clipping)和透视除法(Perspective Division)等操作,最终得到屏幕空间的顶点坐标。

3. **光栅化阶段(Rasterization Stage)**:在这个阶段,根据裁剪后的三角形片元(Fragment)的屏幕位置,生成覆盖像素的片元。这些片元会被进一步处理以确定它们在屏幕上的最终颜色。

4. **片元处理阶段(Fragment Processing Stage)**:在这个阶段,通过片元着色器(Fragment Shader)对每个片元进行处理。片元着色器可以使用纹理、光照等信息计算最终的颜色。

5. **光栅化操作阶段(Raster Operations Stage)**:在这个阶段,进行深度测试(Depth Testing)、模板测试(Stencil Testing)和混合(Blending)等操作,以确定哪些片元应该被渲染到最终的帧缓冲区。

6. **输出阶段(Output Stage)**:最后,将最终生成的图像从帧缓冲区输出到屏幕上进行显示。

  • 也就是说,除了顶点和片元处理,其他的操作都是一样的。
  • 所以在现代计算机中,显卡都帮你写好了顶点和片元处理之外的操作。
  • 我只需要写个顶点和片元处理就行了,即Shader。

  • 上图为片段(像素)着色器。此着色器对每一个像素进行一次漫反射操作。

  • 贴图Texture,uv,取值都是0-1.

  • 重心坐标:任意三角形内的任意一点都可以用α、β、γ去乘ABC这三个点俩表示(系数相加等于一)

  • 系数可以通过面积去求
  • 之所以引入重心坐标,是为了对三维空间中,这个三角形的某个点进行插值,以此来计算这个点的颜色。
  • 之前说过。把三角形投影到2D屏幕之后再做插值,其实这是不对的。当我们把这个三维的三角形,投影到屏幕上时,重心坐标的位置会发生改变,因此在三维空间中先插值了,在投影,才是正确做法。

  • 当一个192*108的图片,想要显示在1920*1080的屏幕上时;
  • 首先,我们进行定义:texel是指贴图上的一个像素。
  • 那在进行展示的时候,屏幕上的一整块的许多像素仅对应一个texel。
  • 这种做法就导致了上图左边图的效果,而这显然不是我们想要的效果。、

  • 因此我们利用了插值的做法。
  • 首先,上图中,黑点代表texel,红点应该是密密麻麻的,此图表示的只是其中一个。
  • Nearest做法就是把离红点最近的texel给它。
  • 插值做法则是取周围四个点

  • 双线性插值Bilinear:通过t和s的运算,得到颜色
  • 上图做法是取2*2的texel,仔细观察上眼皮,还是有一定的锯齿状。
  • Bicubic就是取4*4来插值计算。

  • 相反的,贴图太大,屏幕太小,反而会出现更严重的情况,远处摩尔纹,近处锯齿。
  • 于是,我们就使用范围查询,一个屏幕像素对应这很多texel。
  • 通过计算这一屏幕像素内所有texel的平均值,来表示这个像素的颜色。
  • 特点:很快、会有错误、只能在一个正方形里采样。

  • 示例图,MinMap。
  • D=log2 X,X表示Level,每一个贴图都会有一个MinMap,原图是Level0,其他的和原图一起存在,虽然有额外7个等级,但是开销非常小,所占用的内存不会超过原图的三分之一。

  • 如上图,远处蓝色部分和近处红色部分用的是同一张贴图,但是他们使用的MinMap的层数不一样,根据屏幕两个像素之间的距离L来找贴图之间的距离L,L越大层数就越大。

  • 在正方形区域,取大一点的L的长度来做一个矩形,这个矩形内的texel的平均值就是MinMap的某个Level。

纹理部分

  • 环境光纹理:物体在场景中,一定会受到环境光的影响,然后将自身受到的环境光反射到视角里。
  • 其根本是一张贴图,有两种技术:矩形变球体、正方体变球体。

  • 凹凸贴图:此贴图通过,改变着色时法线的值来达到凹凸不平的阴影效果。

  • 如下图:凹凸贴图在扰动物体表面的法线。
  • 计算步骤:我们先假设原始的法线竖直向上,再通过凹凸的十分接近的两点来计算改点的切线的斜率dp,然后就得到了切线向量(1,dp),此切线就是该点的平面,然后可以通过交换x和y来拿到垂直于该平面的法线(-dp,1)。
  • 归一化

  • 3D版也是类似的,只不过变成了uv方向。

  • 如上图,此种做法会有缺陷,只是某块地方黑了点,只能简单的骗骗人。
  • 因此我们引入了新的技术:使用相同贴图,不同的技术,可以到达非常好的效果
  • 此技术名为位移贴图,他通过上下移动该点,来真的在物体表面创建凹凸的效果,这样,物体凸起来的部分,也能给旁边的一些地方创造阴影。
  • 但既然要上下移动某个点,就势必会产生更多的技术,同时也需要更多的三角形。
  • 现在只有在微软平台能自动划分三角形。

 

  • 同时,贴图不止有2D的,3D的也有。

几何

  • 现实生活中,纤维,细胞,等,都非常难用三角形表示出来。
  • 因此,有了隐式表达和显示表达。

  • 上图就是隐式表达,符合fx的所有点就是一个圆环,但是光看表达式看不出来是什么形状。
  • 隐式表示-优点和缺点
  • 优点:
  • 紧凑描述(例如,一个函数)
  • 某些查询很容易(内部对象,到表面的距离)(大于或小于零在形状外面)
  • 有利于射线到表面的交集
  • 对于简单的形状,准确的描述/没有抽样误差.易于处理拓扑的更改(例如,流体)
  • 缺点:
  • 难以建模复杂的形状
  • 因此,隐式表达的都是一些基础模型,在很多建模软件里的那种,通过一个个基础模型的拼拼凑凑和blend(混合),就可得到需要的形状。

  • 这是一个Cube的obj文本文件,v代表8个点,vt代表12个纹理坐标,vn代表6根法线(有8个是因为有很多冗余的地方),f内的参数表示(v/vt/vn)

  • 贝塞尔曲线

  • 三个点,点点之间,去其距离的t倍,得到两个点,这两个点之间距离的t倍,得到一个点。
  • 取不同的t,可以得到一条曲线。
  • 如下图,四个点也是一样的。

  • 既然有t,又有点,那么就可以得到一个公式,如下图。
  • 我没去记,很好理解。

  • 既然有贝塞尔曲线,那么把二维的线,应用到三维的空间中,也是很容易的。

  • 得到四条曲线,取同一垂直方向上的四个点,作为新的点,用这新的四个点再来画一条贝塞尔曲线。
  • 通过移动这个垂直,就可以得到一个贝塞尔曲面。

  • 和二维算法同根。
  • 虽然曲面成像效果非常优秀,但是市面上大部分还是用的网格。

  • 网格细分(图2变成图3)
  • 网格简化(图3变成图2)
  • 网格规划(让模型更正规化,图4的三角形长短大小不一,进行规划之后变成图5,这可以让模型拥有更多不错的性质)

细分

1.loop细分(发明它的人姓loop)

  • 取周围几个值的平均,作为新的点

  • 先产生新的顶点,根据它的度n(点连着几根线)来决定他是怎么个平均法。
  • 得到了新的点之后,原来的u点再根据新的一圈n点来计算(右下角那行)。

 

  • Catmull-Clark细分

  • 如上图:三角形表示非矩形的面(只要不是四条边就算)。
  • 紫点表示奇异点:度(和点相接的线)不为4
  • 找到这些面和点

  • 将每一条边的中点和非四边形面的重心点,两两相连,得到新的形状
  • 这个时候重心点就可能会变成奇异点,先不管点的偏移。
  • 有多少奇异点?之前的重心点都变成了奇异点,增加了2个。
  • 奇异点的度?变换前的奇异点的度不变,新增的奇异点的度和其边数有关。
  • 多少个非四边面?0(很神奇是不是,不愧是图灵奖获得者想出来的方法)

  • 再怎么细分也不会增加新的非四边形面或是奇异点

  • 计算步骤如图;
  • 先算面中点,再算线中点(这两个中点都是四个数的平均)
  • 最后调整原先的点,其系数都是研究后得到的结果。

  • 如图。

简化:

  • 取平均不是好方法,因此使用二次误差。
  • 原因是因为使用平均,会让这个原本很凸起的面,变得很扁,所以得让它凸。

  • 如上图,在合并点之后,里我最近的两条边。本来是差不多垂直于我,但是在合并之后和我成差不多45°,这个就是一个误差,称为f。
  • 在简化模型时,算法会获得整个模型所有的f,进行一个排序,同时,在简化一次之后,出现了新的f,利用堆的性质,把这个新的f塞进堆里面,重新排序之后再合并最小的f。

光追

  • 阴影制作:适用于点光源
  • 原理:光能看到的地方就是亮的。
  • 步骤1,在光源处放一个摄像机,拿到深度图。(记录了该点)
  • 步骤2,相机看过去,下图,下面相机投射过去,左边的黑点为例,将此点投射到光源,对比步骤1拿到的深度图,看看该点是不是和深度图上是同一个点,如果是,就打亮,如果不是,就只使用漫反射。
  • 显然,左边的黑点就是深度图上的点,因此打亮;右边的点投射到深度图,发现不是,因为有更近的点。

  • 如上图:将肉眼可见的点投影回光源。
  • 但上述做法有缺点:只能做硬投影,肉眼看到的点,要么亮,要么不亮。

  • 硬VS软

 

  • 首先,在开始前,我们先定义光线

  • 下图是光追的第一步,和光栅化操作类似。

  • 下图是第二步,进行反射和折射追踪,计算之后的这些点对观察点的光照影响。

  • 计算方面,定义光线:

  • 定义模型的c和R

  • 我们需要去求t的大小和p是否存在(存在就求)下图易证。

  • 点在模型外,与模型必有偶数个交点,在模型内,必有奇数个交点。
  • 因此,通过该方法,可以获得隐式模型上所有的点。

  • 其次,显式模型方面,我们也进行定义:(下图)

  • 通过一根法线和任意一点定义一个面,N和p’,面上面的任意一点p都符合上图公式。

  • 于是,我们也得到了t的大小。显式模型也能通过这种方法做出来。还有反射折射。
  • 但是这方法虽然真实,但是很慢很慢很慢,一个像素点就要去计算几倍的点。
  • 因此我们引入了包围盒模型。

  • 上图中的“卷”和“音量”代表包围盒,此处翻译错误
  • 也就是射线只需要看看有没有打到包围盒,打到了就去计算,没打到就忽略这个模型的所有面。

  • 使用6个无限延伸的面(两两相对,官方说法是3对面),去包围,其相交的空间就是盒。
  • 然后就是去打射线。
  • 如下图,仔细观察左边和中间图的tmin和tmax,这是都是为什么使用3对“对面”的原因,因为这可以再次减少计算,在拿到不同的tmin和tmax后,光线最后进去的tmin和光线最先出来的tmax,就是我们需要的tmin和tmax,如果这两个值不一样,那么就把这个模型算进去。

  • 该如何去找到这些包围盒,毕竟物体本身是没有这样一个盒子的。
  • 方法一:把整个场景用盒子平均分开

  • 通过的网格怎么计算:找到第一个网格,下一个网格只可能是它周围的四个,很好获得。
  • 弊端:假如说一个操场中央有一个茶壶,那么平均分的情况下,很多的盒子都是空的,那么为了渲染一个茶壶,要去遍历这么多的空盒子,显然是有优化方案的。(如下图)

  • Oct-Tree是八叉树
  • 竖切-横切-竖切-横切......
  • 切完之后去盒子中检测物体多不多,多的话再切
  • 弊端:每一个端点上都可能有8个分支,闹;同一个物体存在于多个盒子中。

 

  • KD-Tree是二叉树
  • 竖切-横切-竖切-横切......
  • 但不是平均切,而是通过其他算法来识别物体,然后再切,最好是把物体分开。
  • 这样就只需要计算二叉树。

ok就是这样,一个个遍历,特别快。

  • 但是弊端还是同一个,同一个物体在不同的盒子中,这样就会造成多余的检测。
  • 所以现在市面上用的最多的是下面的,以物体为界来分的包围盒。

  • 伪代码

  • 详情图

辐射度量学

  • 例如在之前学习的光照强度中,我们粗略地把光照强度定义为10,但是这个10是什么,我们没有定义,而这显然是错误的。
  • 所以辐射度量学就是带给光线各种物理量,使图形看不出来是加的。
  • 声明:本知识点的专业名词尚未有国内翻译,因此都是英文。

 

  • RadiantEnergyand Flux(power)
  • 定义:辐射能RadiantEnergy是电磁辐射的能量。单位为焦耳,用符号表示:Q[J/焦]
  • 定义:辐射通量RadiantFlux(功率)是单位时间内发射、反射、发射或接收的能量。
  • 用白话来说就是亮度

  • 光强,吸收光之后反射的光,光线
  • 这些是如何运作的。
  • Randiant表示单位立体角上光强。

  • Irradiance定义:每(垂直/投影)单位面积入射一个表面点的功率。

  • 上图是明确的概念,下图是简化的模型。

  • Radiance是描述光在环境中的分布的基本场量;
  • 是与光线相关的量;
  • 是计算亮度的;
  • 定义:辐射度Radiance(亮度)是一个表面每单位立体角和每投影单位面积发射、反射、透射或接收的功率。

  • 如上图所示,Irradionce是单位面积接收或辐射的能量;Intensity是单位立体角辐射的能量。
  • Radiance就是单位面积接收了能量之后,再辐射出去的一大块面积中单位立体角的能量
  • 同时也是单位立体角辐射出去之后,一大块面积中单位面积接收到的能量。

  • 双向反射分布函数(BRDF)表示从每个入射方向反射到每个出射方向的光量

  • BRDF会告诉我,该点从某个入射方向进来的光,反射到出射方向,有多强。

 

  • 是一种递归的算法

  • 物体假如自己会发光,因此,我们添加了一个项来表示物体发的光,于是就得到了最后的渲染方程:

  • 通过一个方程,定义所有的光线

 

  • 场景中所有光线路径的近似集合

  • 牛逼,橙色字就是光栅化,光直接射到屏幕或者弹射一次到屏幕的光。

  • 它最后会收敛到某一亮度。

  • 蒙特卡洛积分
  • 我们想知道一个不规则的积分,使用解析法太难了,以此使用蒙特卡洛积分

  • 在每一个点取它的值,累加起来。通过平均函数值的随机样本来估计函数的积分。

  • 如上图,随机到的点x对应的fx为高,b-a为宽,乘起来就是该点的积分,很多的加起来,得到的值除以随机点的个数N,就是本函数的积分,

  • Whitted-style射线跟踪:
  • 总是进行镜面反射/折射
  • 停止在扩散表面反弹

 

  • 这些简化合理吗?

高层次:让我们逐步改进Whitted-style的光线跟踪,并引出我们的路径跟踪算法。

  • Pathtraced·Whitted-style追踪是错误的·但是渲染方程是正确的·但这涉及到求解整个半球的积分递归执行·如何用数值方法求解积分?
  • 假设我们想在下面的场景中渲染一个像素(点),只用于直接照明

  • 我们去每个Wi采样,计算每一次采样得到值,并累加。

  • 于是我们就得到了上图代码:在N个样本里随机采样-初始化Lo-对每一个wi进行光先计算,如果这束光打到了光源,就用BRDF去计算。

  • 那么刚刚算了直接光照,现在来算间接光照。

  • 蓝色部分就是答案,倘若这束wi打到了物体,那么在物体上再做一次shade函数计算。
  • 但这么做是有问题的:指数爆炸,每次Foreach都要做相同倍计算。

  • 因此,我们把上图中的N变成1,也就是说一次只打一条射线,这样会大幅减少计算,我们需要做的是增加采样点。
  • 从现在开始,我们总是假设在每个阴影点只追踪1条光线。

  • 但这么做会出现很多采样不精准的情况,但问题不大,因为一个像素由场景中很多点组成,让他多做几次光追,最后取个平均就行了。

  • 下图是像素层面的计算,也是根据蒙特卡洛积分,射出平均射线去进行采样。

  • 但是到这,射线还不会停,只要不打到光源他就会一直反射下去,这显然是不对的。
  • 因此我们引入了俄罗斯轮盘赌。(下图)

以前,我们总是在阴影点拍摄光线,得到阴影结果Lo

假设我们手动设置一个概率P (O <P< 1)

返回阴影结果除以P: Lo / P

有1-P的概率,如果不发射射线,你会得到0

通过这种方式,您仍然可以期望得到Lo!:

E= P* (Lo / P) + (1 - P) *O = Lo

  • 也就是说,通过对期望的计算,我们也可以得到最后的亮度。


  • 再说回来,使用了一个点只打一次线的算法下,要怎么运气好,才能打到光源。

  • 光源大->打5条射线线,小,就可能是500/5000条。
  • 因此需要进行优化,也就是先去算光源打到该点的线。
  • 同一个BRDF,既然可以从摄像机打到光源,那么也可以从光源打到摄像机。
  • 需要将渲染方程作为dA的积分需要dw和dA之间的关系。
  • 简单!回想一下立体角的另一种定义:在单位球面上的投影面积。

  • 就是简单的替换一下就行了。

  • 然后在shade函数上的修改就是在反射前先去计算光源方向的光线。
  • 之前,我们假设光线是由均匀半球采样“意外”拍摄的(小几率采样到)

现在我们考虑射线来自两个部分:

  1. 光源(直接,无需用俄罗斯轮盘赌)
  2. 其他反射器(间接,需要俄罗斯轮盘赌)

  • 最后最后最后一件事:我们怎么知道灯上的样品是否被挡住了?(如下图)
  • 我们的做法就是先去检测一下有没有挡住,不被挡到才去算。

材质

  • 物体描述了某一种材质,那么是在渲染方程中的哪一部分决定了物体的材质呢?
  • 入射光 BRDFCOS自发光......
  • 答案是BRDF,反射不同光的方式。
  • 光在每个输出方向上的反射是相等的。假设入射光均匀:

  • Fr就是物体颜色。

 

  • 微表面:漫反射是怎么来的,因为物体表面是粗糙的,一束光打到表面上被物体表面一个个小镜子反射到不同方向。
  • 粗糙表面

宏观macrosurface:平坦和粗糙

微观microsurface:凹凸不平和镜面

  • 表面的个别元素就像镜子一样

被称为Microfacets

每个Microfacet都有自己的标准

  • 上述都是各向同性材质,其实在很多金属部件方面,用到的还有各向异性材质(从不同的方向打过去,得到的高光不一样)

渲染中的高级方法

  • 无偏光传输法
  • 无偏蒙特卡罗技术不存在任何系统误差
  • 无偏估计的期望值总是正确的值,不管使用了多少样本
  • 否则,有偏
  • 一个特殊的情况是,期望值收敛到正确的值,因为使用了无限的#samples – consistent

 

  • 其一:双向路径跟踪(BDPT)
  • 路径追踪是从相机到光源
  • 追踪摄像机和光线的子路径
  • 连接两个子路径的端点。

  • 当光线传播在光源处比较好计算时,双向路径跟踪(BDPT)效果会很好。
  • 但是会慢很多。

  • 其二:Metropolis光线传播(MLT)

  • 如上图所示,MLT在找到一条路之后,会根据这条路,去它周围找类似的路,这样就可以稍微精确一点。
  • 对于困难的光线路径非常有效。

  • 缺点:难以估计收敛速度(放那渲染一天,可能也不好看);不能保证每个像素的相同收敛速度;所以,通常会产生“脏”的结果;因此,通常不用于渲染动画。

  • 偏置光传输法
  • 其一:光子映射

PS:图中焦散不严谨,caustics

  • caustics:光在传播过程中被反射到同一点上,产生聚焦的现象。
  • 阶段1 -光子追踪从光源发射光子,弹回来,然后在扩散表面记录光子。

  • 阶段2 -光子收集(最后的收集)从相机拍摄子路径,弹跳它们,直到它们击中漫反射表面
  • 计算-局部密度估计;有更多光子的区域应该更亮;对于每个阴影点,找出最近的N个光

  • 其二:顶点连接和合并(VCM)
  • BDPT和Photon Mapping的组合
  • 关键理念:让我们不要浪费BDPT中的子路径,如果它们的端点不能连接,但可以合并
  • 使用光子映射来处理附近“光子”的合并
  • 在很多很多领域都是用这个做的。

  • 实时辐射度算法IR(VPL/多光法)
  • 有时也被称为多光法
  • 关键理念:被照亮的表面可以被视为光源
  • 方法:图左在照亮场景后,我们就把它当做图二(每个被照亮的点都有一个光源),然后如图三,当我们从视角出发,去取某一个点的值时,用其他的每个光源,去照该点,最后所有光加起来的值就是最后的值。

  • 优点:快速,通常可以在漫反射场景中得到很好的结果。
  • 缺点:当VPLs接近阴影点时,尖峰将出现无法处理有光泽的材料
  • 非表面模型:当光穿过参与的介质时,在任何一点上,它都可能被(部分)吸收和散射。

  • 用相位函数描述参与介质中任意点x的光散射角分布。

  • 后来说了头发的反光等等等等,但是就单纯了解一下就行。

 

  • 相机

成像=合成+捕获

。。。光圈、快门、ISO

  • 透镜

讲了各种折射,和折射焦距成像之间的各种算法。。


  • 光场

  • 一个物体,会向外面不同角度射出不同的光线,这个就是物体的光场。

  • 但我们在应用的时候,不使用物体,而是去把物体某个点往某个方向发射的光线用平面记录下来。,如上图是把光线记录在stuff的左平面,不过一个平面是不够的。
  • 正确操作是下图。两个平面记录uv每个点的光线往st每个方向的信息。

  • 下图理解了就理解了。

  • 色彩
  • rgb、hsl、xyz、cmyk等等的各种成像原理
  • 色彩其实是人眼感知出来的东西
  • 法国Cie公司发明了很多种很好用的颜色空间发布在计算机上。
posted @ 2023-08-15 10:13  被迫吃冰淇淋的小学生  阅读(53)  评论(0)    收藏  举报