Games101-着色(插值、高级纹理映射)

三角形内部插值:重心坐标/Interpolation Across Triangles: Barycentric Coordinates

在这里插入图片描述

为什么要在三角形插值:我们有很多操作是在三角形的顶点完成的,希望在三角形内部有个平滑的过渡
插值什么内容:映射纹理uv,颜色,法线等。基本上可以对三角形顶点上任意的属性插值
如何做插值:重心坐标
在这里插入图片描述
在一个三角形ABC平面内所形成的任何一个点(x,y),都可以表示成三个顶点a、b、c坐标的线性组合,
只要满足线性组合的条件 α + β + γ = 1 \alpha+\beta+\gamma = 1 α+β+γ=1
虽然是三个系数,但有和为1的限定,实际上只是两个数。就像三角形的平面是二维的,二维只需要两个数。
如果点在三角形内部,还需满足三个系数都非负
在这里插入图片描述
A点的系数即为 (1,0,0)
在这里插入图片描述
点的重心坐标可以通过面积比求出:
一个点分别与三个顶点连线后。划分出顶点和它对面的三角形(即不相邻的三角形)。通过面积比可求出重心坐标
在这里插入图片描述
三角形的重心。重心可以把三角形均等面积划分出三份。通过上面的定义得出( 1 3 {1}\over{3} 31, 1 3 {1}\over{3} 31, 1 3 {1}\over{3} 31)
在这里插入图片描述
重心坐标最终的公式
在这里插入图片描述
任何一个点颜色的插值:可以通过重心坐标的系数求出

注意:重心坐标在投影变换下不能保证重心坐标不变:假如一个空间中的三角形,可以算出内部其中一个点的重心坐标,当三角形投影到某一平面时,会发生变化。根据投影后得到三角形坐标,重新算该点的重心坐标,会得到不一样的结果

因此需要用空间中的三角形坐标做插值,而不能用投影后的三角形坐标做插值

所以求深度时,需要把三角形的顶点值经过逆变换后,找到在三维空间中的坐标,再进行插值计算

应用纹理/Applying Textures

在这里插入图片描述
屏幕上任何一个采样点,有一个位置,即可知道在这个位置上插值出来的(u,v)在哪,在纹理上查询对应(u,v)的值。即对于纹理位置上的颜色。可以认为这个纹理的颜色就是漫反射系数

纹理的放大/Texture Magnification

如果纹理太小时,需要放大
在这里插入图片描述
Nearest
对任何墙上的一个点(一个像素),都可以找到其对齐纹理上的一个位置,可能不是整数,但把它四舍五入当做整数。这样相当于在一定范围内,我们要查找的是一个相同的纹理上的像素。
纹理上的像素叫texel
可能一个像素周围的3x3或5x5的像素都会映射到一个相同纹素。即找最近的纹素
在这里插入图片描述
双线性插值
如上图,一个高分辨率的像素映射到一个低分辨率的纹理
在这里插入图片描述
找临近的四个纹素,如上图对应左下角的四个纹素
在这里插入图片描述
红点可以找到离左下角纹素的水平距离s和竖直距离t,s和t一定在0,1之间。因为两个纹素相隔为1
在这里插入图片描述
进行双线性插值。lerp为线性插值函数
在这里插入图片描述
先对两个水平距离s进行线性插值
在这里插入图片描述
再根据结果对竖直距离t进行插值
在这里插入图片描述
双线性插值的效果

右边的是双立方/双三次插值Bicubic。取的是任一点周围的16个,对16个点进行水平和竖直的插值。每次是用4个做一次三次的插值,而非线性的插值

Texture Magnification

当纹理太大时,反而会引起严重的问题
在这里插入图片描述
远处产生了摩尔纹,近处产生了锯齿
在这里插入图片描述
近处覆盖在纹理上的一个像素相对较小。远处一个像素覆盖了大片的一个纹理,不能简单的采样一个纹素来代表像素
在这里插入图片描述
有更多的采样点,如每一个像素里用512个采样点来采样纹素,再进行平均。但是会有更大的开销
在这里插入图片描述
走样:纹理过大时,一个像素内频率很高,但只有一个采样点
一个像素内有高频的信息,则需要更高频的采样方法。即一个像素内取多个采样点来采样纹理值再平均
另一个思路:避免采样/不采样。任何一个区域立刻就可以知道平均值
在这里插入图片描述
其实是个算法问题:点查询问题和范围查询问题。
点查询:以纹理为例,给一个点,它的值是多少。如之前的双线性插值。都是点查询
范围查询:不做采样,给一个区域,立刻就能知道它内部的平均值
注意范围查询不仅仅是查一个范围的平均值,也有的是查最大值或最小值。就是完全不一样的问题,有很多不同种类的范围查询
在这里插入图片描述

Mipmap

Allowing (fast, approx., square) range queries
允许做范围查询:快,不准(因为涉及到近似),仅仅是正方形
在这里插入图片描述
从一张图生成一系列图。
比如一张纹理,原始的纹理称作第0层纹理。可以生成更多级的纹理,使每一层纹理比上一层分辨率缩小一半
在拿到一个纹理后,先把这个纹理处理一遍,在渲染之前,把这些mipmap都生成
在这里插入图片描述
引入了额外的存储量。额外的存储量是级数求和。每次边长砍一半,即每次存储量是原来的 1 4 {1}\over{4} 41

1+ 1 4 {1}\over{4} 41+ 1 16 {1}\over{16} 161+ 1 16 {1}\over{16} 161 × {\times} × 1 4 {1}\over{4} 41

得到的结果是 4 3 {4}\over{3} 34,即多了 1 3 {1}\over{3} 31的存储量

一个更简单的算法:如上上图level 6。假设把原本的图复制3份,如果把所有的每一层的存储都乘以3,不会影响最后的结果,因为要的只是比例。假设把原图放在左上右上左下,三个都是第0层,然后可以把第一层复制3份填在右下角的图,又缺了一个右下角,把第二层左上右上左下填上去。依次下去得到的极限就是整个块的大小。这样看出额外的存储就是原本的 1 3 {1}\over{3} 31
在这里插入图片描述
任何一个像素都可以映射纹理上的一个区域
如何得到这个区域:一个近似的办法。如一个三角形覆盖了一定的采样点。蓝色点有些它的邻居,红色点也有它的邻居。要算出一个红色点所占据的像素的覆盖面积。可以取它自己的中心,和邻居的中心,分别都投影到纹理上。如右图
在这里插入图片描述
做一个近似:这个点到上方的点和到右方的点都是一个像素,可求出这三个点映射到纹理上会占据多长的距离。公式如上图
这样可求出一个像素对应纹理大概的长度
在这里插入图片描述
可以用一个正方形的框来近似一个不规则的区域,相当于在做一个finite difference
即在屏幕上往一个方向移动某个距离时,要知道在纹理空间上移动了多少距离

当近似一个正方形后。如果比例和原始大小一样,是第0级。如果区域是4x4,是指原始区域上是4x4,经过第一层mipmap后区域会变成2x2,经过第2层后变成1x1.
这个区域LxL的大小,在d = log ⁡ 2 L \log_2L log2L这一层一定会对应到一个像素去
在这里插入图片描述
这个层级的变化不怎么连续,因为我们算出的层级是离散的,1,2,3…没有第1.5层
在这里插入图片描述
为了连续的查询,要进行插值
如要查1.8层。先找到第1层,再找到第2层。在这两层内部分别用双线性插值,求出在这两层上对应的插值。然后将这两个插值进行一次层与层之间的插值。总共是三线性插值
在这里插入图片描述
三线性插值的效果
在这里插入图片描述
点采样效果
在这里插入图片描述
超采样效果
在这里插入图片描述
mipmap效果。在远处会把所有的细节糊掉(overblur)
因为mipmap只能查询正方形范围内的平均值,且是个近似值
在这里插入图片描述
各项异性过滤可以部分解决三线性插值的问题
在这里插入图片描述
因为mipmap是长宽各缩一半,计算的其实是对角线上的一些图片。有一些图片是不同的长宽比,
各项异性过滤会导致比mipmap多了一些不均匀的水平和竖直方向上的压缩。这张图对应的原始的图是一个拉伸了的区域,是个矩形的区域。这样可以查询任何一个被压扁的图的区域,而不用限制在一个正方形的区域。总共的开销变成了原来的3倍
在这里插入图片描述
屏幕上的任何一个像素映射到纹理上,不一定都是一个规律的形状。可能是一个斜着的、超级细的形状。此时如果用正方形来框住,会求了一个更大的区域。造成overblur
各项异性过滤允许我们对一个长条形区域进行快速的查询
各项异性就是在不同的方向上,有不同的表现

但是对斜着的区域仍然没能解决问题,因此各项异性过滤只是部分解决mipmap的问题
在这里插入图片描述
EWA过滤:你有一个任意不规则形状。可以把其拆成很多个圆形去覆盖不规则的形状。
比如一个椭圆可以拆成三个不同的圆形去覆盖这个椭圆。
每次都是去查询一个圆形,然后多次查询,可以去覆盖一个不规则的形状。造成的代价就是多次查询有更大耗时

在这里插入图片描述
网格模型->flat shading->phong shading->贴图->反射

在这里插入图片描述
纹理其实就是一张图
现代的gpu里,纹理可以理解为就是一块内存,以及可以对这块内存上的一块区域进行点查询或着范围查询,滤波
即纹理可以理解为是一块数据,可以进行查询

环境贴图/Environment Map

在这里插入图片描述
环境光照/环境光映射/环境贴图
在一个房间里,四面八方都能看到物体,来自四面八方都有光,任何一个方向,不管是直接光照还是光源还是反射的光。如果把任何方向来的光都可以记录下来。这就是环境光照
左图就是环境光照,以此给右图做渲染。就是这个茶壶可以反射任何一个方向来的光。
环境光被假设都是方向,位置都是无限远,没有实际的深度意义
在这里插入图片描述

Spherical Map

在一个屋子里放上一个镜子球,镜子反射出来的东西,就是环境光。可以把环境光存储在球上,再展开成一个图,就像地球仪展开成世界地图一样
在这里插入图片描述
在这里插入图片描述
环境光记录在球上在展开,在上下方会有扭曲问题。即不是一个均匀的描述,在极点扭曲

Cube Map

在这里插入图片描述
天空盒
依旧是一个球,记录环境光,此外,在加一个立方体把球包住,从球的球心往外射一条线,直到打到立方体的表面。把对应的光照信息存在立方体的表面
在这里插入图片描述
需要额外的计算,计算方向对应着立方体的哪个面

注意:环境光不仅是直接里图片中的光源点。你能看到任意一个物体,一定是因为有光线从物体达到了你的眼睛。物体反射的光同样也是来自这个方向的光照信息

凹凸贴图/法线贴图

在这里插入图片描述
纹理不仅能描述颜色,可以定义任何位置任务属性。比如定义任何一个点的相对高度是多少。
例如原本有一个基础表面,纹理可以定义在这个基础表面沿着发现高度上移/下移的距离
如上图右边的橘子模型,定义表面需要大量复杂的三角形。通过纹理可以只需要定义一个球,在不把几何形体变复杂的情况下,应用纹理定义任何一个点的相对高度。相对高度改变会造成法线的变化,导致最终着色的结果发生变化。通过不同的明暗对比,让观察者认为对应区域有凹凸。
本质是为了在任意一点做一个假的法线,通过假的法线改变着色结果,欺骗人的眼睛。实际上并没有改变模型的几何。
在这里插入图片描述
通过法线贴图,可以定义一个复杂的纹理,但不改变任何几何信息。把任何一个像素的法线做一个扰动,通过定义不同位置的高度,然后根据临近位置的高度差来重新计算法线。
如上图点p,改变高度后,法线方向发生了改变
在这里插入图片描述
flatland,一维的贴图:假设任何一个点原本是平面,如图蓝色波浪线是有凹凸贴图定义的。原本在几何的平面,法线是朝上的。在p点是(0,1),
新的法线就是在对应点求导,即在一个地方像右移动一个单位距离,会向上移动多少距离。用相邻两个点的高度差 c[h(p+1)-h§]* ,除以间隔1。其中引入某一个常数c,来定义凹凸贴图影响大不大,就是做一个简单的缩放。
得到切线(1, dp);切线逆时针旋转90°,即法线为(-dp, 1).normalized()。
在这里插入图片描述
对于二维贴图,假设任一点法线为(0,0,1),求贴图如何影响某点的法线。同样是求梯度。
即求这个点在纹理水平方向 u ⃗ \vec u u 运动一个格子的高度差,以及竖直方向 v ⃗ \vec v v 运动一个格子的高度差。求得切线(即两个向量构成的平面),最终算出法线(类比一维的求法)为 (-dp/du,-dp/dv,1).normalized()。

实际上每个点的法线方向可能朝各个方向,因此在这里我们假设在局部坐标系,认为任一点的法线就是(0,0,1),并且有两个垂直的基座标 s ⃗ \vec s s t ⃗ \vec t t ,在这个坐标系里通过纹理映射求出新的法线向量,在重新计算回世界坐标

位移贴图/displacement mapping

在这里插入图片描述
更现代化的做法,起点和凹凸贴图一样,就是都是用一个纹理贴图来定义一个点应该有的相对高度的差别。输入完全一样,用的完全相同的纹理。
只不过位移贴图实际上会把不同的顶点即三角形面的顶点真的做一个位置的移动,而不是通过位置的移动,换算成法线的变化,来做一个假的顶点的移动。

凹凸贴图在边缘的地方会露馅,并且当几何比较复杂,在一些自己会给自己产生阴影的地方露馅

位移贴图要求模型的三角形足够细,即模型能跟得上纹理变化的速度,三角形顶点间的间隔的间隔要比纹理定义的频率高。不然只有一个大的三角形时无法改变内部的顶点位置

directX提供了一个方法,动态曲面细分:不需要一开始有个足够细的模型,根据需要进行细分

其他应用

在这里插入图片描述

3维的纹理(一个噪声函数):实际上定义了空间上任何一个点的值。并没有实际定义一张图,而是定义了一个在3维空间中噪声的函数。所以空间中的任何一个点,可以通过该函数算出3维空间中噪声值是多少。噪声经过一系列操作后(2值化,加减…)可以变成需要的样子
在这里插入图片描述
还可以记录一些之前已经算好的值。如计算复杂的环境光遮蔽的纹理。可以计算好后让着色的纹理相乘
在这里插入图片描述
3维纹理广泛应用于体积的渲染

作业

normal shader:
在这里插入图片描述
blinn-phong shader:
我这里实现的布林冯shader和课程的答案图片有些不一样
答案里的高光会更亮一些。
这是答案的:
在这里插入图片描述
这是我输出的
在这里插入图片描述

试了一下,如果计算镜面发射的观察方向时用:
Eigen::Vector3f viewDir(eye_pos - point);
最终的结果是和课程答案里的图片一样。但是这里的point是
rasterize_triangle(newtri, viewspace_pos);里的viewspace_pos的插值。
viewspace_pos是经过了model-view变换的

 std::array<Eigen::Vector4f, 3> mm {
         (view * model * t->v[0]),
         (view * model * t->v[1]),
         (view * model * t->v[2])
 };

也就是这里的坐标是转换成了摄像机在原点的观察坐标。屏幕绘制时默认就是以摄像机为原点绘制的。那似乎应该不需要用eye_pos减去point。
所以我的实现是
Eigen::Vector3f viewDir( - point);
texture shader:
在这里插入图片描述

bump shader:
主要是tbn矩阵的原理,课上似乎没讲过,但是公式已经给出来了。可以直接套用。
另外一点就是纹理的取色是个3维向量,理论上是说颜色的黑白程度表示不同高度。那这里是直接取rgb中的一个颜色值做高度?还是直接用向量的模做高度。我看网上其他人都是用的向量的模做高度。我两种都试了
这是取模为高度
在这里插入图片描述

这是以向量的x为高度:
在这里插入图片描述
下面的似乎效果更糊一些,感觉取模应该就是答案里的演示效果

displacement shader:
在这里插入图片描述

github作业地址

  • 29
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值