0%

深度缓存、光照与纹理

深度缓存

Q:为什么先采样再模糊是错误的

A:因为不先做滤波会导致信号混叠

深度缓存——Z-buffer

不同图形存在相互遮挡,一般在绘制图形的时候,后绘制的图形会遮挡先绘制的图形,所以会对图形设置深度。

在实际的计算机图像生成当中,会生成两个图,一个是成品图,一个是每个像素对应的深度值。

深度可以理解成这个点到摄像机的距离,当然深度数值越大,离摄像机越远(z轴上大小越小)

深度缓存算法的原理,是每次遍历记录每个像素的最浅深度。

2222.png

复杂度O(n),好处是与三角形绘制顺序无关,最终结果都一样

 

着色

对不同材质的物体提供不同的着色策略。

 

局部着色

只看光照方向,不看任何遮挡,只看自己本身

 

漫反射

  • 光线打到物体表面,会被均匀反射出去,利用这个”结果“,我们通过光照射角度来计算接收到的光照能量,再”反射“出相应亮度(冯-布林光照模型)——光照“数量”
  • 把光看成能量,离光源越远能量越小(I/r^2)——光照“质量”
  • 999.png

kd为吸收的能量(颜色反射)

max防止负数

 

高光(Blinn--Phong模型)

如果材质接近金属等接近镜面反射的材质,则会再某个角度得到接近镜面高光的效果。

目前的模型需要解决的问题是计算出观察方向与高光方向的接近程度,做到越接近越高光的效果。

Blinn--Phong模型的解释是如果观察向量与光线向量的半程向量和法向量足够接近,则说明观察向量越接近高光向量。

即比较半程向量与法向量

  • 000.png

如果直接使用的是观察向量与高光向量比较,那么那个模型叫Phong模型

优势是前者是优化过后的,半程向量比较好算

p指数的解释(64常用)——用来控制高光程度有多大:

  • 0000.png

p指数与镜面反射系数的直观对比:

  • 11111.png

 

环境光照

环境光从四面八方射入,保证每一个像素都获取得到

La = ka Ia

ka为光照系数,la为光照强度

 

高光、漫反射、环境光三者结合就是完整的布林-冯光照模型

  • 333.png

 

着色频率

含有插值的新用法

  • Flat Shading——每个基础三角形的两条边,求其叉积,得到法线,用这个法线来计算着色
  • Gouraud Shading——求每个基础三角形的三顶点法线,计算着色,而三角形内部的着色由各自顶点的着色进行插值算出

顶点法线求法

  • Phong Shading——(和着色模型是一个发明者)对三角形三顶点法线进行插值,算出三角形中每一个像素的法线,再对三角形中的每个像素进行着色

 

虽然从上到下效果越来越好,但其实实际运用中如果物体本身几何图形面数很多,那么其实不需要使用高质量的逐像素着色,这个时候使用最简单的逐图形着色,有时候效果差不了很多,而且开销更小。

比如极端情况下面数比像素更多的情况

 

Q:顶点法线怎么算?

A:通过顶点原本背后视图表示的物体(球)来计算,不过最常用最合适的方法是直接运算顶点周围面的法线进行根据面面积的加权平均来得出来。

 

Q:逐像素法线怎么算?

A:对顶点法线进行插值计算,根据重心坐标来计算(即将讲到)。

 

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

管线:从场景到最后一张图的一系列操作

  1. Input:输入空间中的各种点
  2. Vertices:把各种点进行mvp变换,视口变换投影到屏幕上
  3. Triangles:划分出一个个三角形
  4. Fragment:光栅化,将图形打散成一个个像素——也包含Z-buffer
  5. Shaded:着色——如果是Gouraud,则这一步其实可以发生在Vertices那一步,如果是Phong,那就至少三角形划分出来之后再进行,不过现代GPU支持编程化Shaded,也就是说可自定义化控制每个像素使用哪种策略进行着色,我们就把进行编程的这个动作叫写Shader
  6. Output:输出结果

Shader中不需要写For循环

  • 顶点着色器:如果写的shader是对顶点进行操作
  • 像素着色器:如果写的是对像素进行操作

shader语言——GLSL

 

纹理映射

以上讲到的都是单个物体同一个基础颜色,纹理则是用来决定一个物体多个不同颜色的——改变漫反射系数

纹理的本质就是一张图,可拉伸可伸缩到形态不同的物体上——叫做纹理映射

映射的规则是默认已知的,一般由其他艺术家直接在模型上进行处理

UV图:模型上的纹理展开后的图(0~1)

常用最多的“优秀纹理”其实就是例如木纹这样的材质图,这种类型的纹理一般能够实现纹理无缝衔接

 

三角形插值运算

重心坐标:(a,b)=aA+bB+cC

a+b+c=1 ——点在三角形平面内,如果三者都是非负,则在三角形内

面积表示法:(略)

 

纹理映射应用

(u,v)图记录了每一个采样点的纹理颜色——即Kd漫反射系数

屏幕上的每一个位置,可以转换成纹理坐标——三角形插值运算可计算出所有点了

利用纹理坐标在(u,v)上的映射,将Kd代替进去

 

纹理放大

例如:高分辨率的模型(texture),墙等贴了一张低分辨率的纹理(texel)

四舍五入——会导致很多相同像素映射到同一块texture上,没有“丝滑”的效果

双线性插值(Bilinear interpolation)

  • 888.png

在计算texture中,却并不完全对应在texel上的点时(例如此红点),对其进行双线性插值计算出实际纹理颜色。

实际操作是(例)计算出u00到u10,以及u01到u11的线性插值,得出红点的两个水平插值,然后再对两个水平插值进行插值计算算出红点的插值,这就是双线性插值的名字由来

Bicubic——取周围16个点来插值,计算量更大,但图像更准确

  • 55.png

 

纹理缩小

小模型,纹理UV比像素多

  • 555.png

原因:屏幕上的区域,其实纹理覆盖的大小都不相同

比如一块纹理覆盖到地面上,但从水平角度看,地平线方向纹理密度会非常非常大

解决方法1:之前其实提到过抗锯齿的方法,在这里同样适用,比如在远处使用更多的采样点——超采样

解决方法2:避免采样,进行范围查询——Mipmap

Mipmap将纹理图处理成大小不同的纹理图,存储量多了三分之一

  • 333.png

mipmap生成不同的纹理图,一般是按照倍数减少大小的,这张图做了放大处理,实际上像素面积大小都是一样的,也就是说从左往右图片面积也是越来越小的。

具体要如何使用呢,在我们要渲染出的图像中,由于UV点密度在图像中不同位置不太一样,我们通过计算某采样点上面右边与自身采样点对应UV,计算出两长度大小。再根据这个长度大小来“模拟”出一个正方形,因为不同密度UV点一般情况下生成不了正方形但Mipmap的范围查询只能通过正方形查询。这个正方形大小就能代表像素点映射到纹理上的区域大小。

这个时候重点来了,我们都知道像素点在屏幕上都是设定好的大小,排好队一个个显示好。但映射过后的UV图不一样,可能一个像素对应四个像素大小的UV图,也就是说大概四个像素面积的UV图要“挤”在一个像素内,这个时候所需求的采样点就会变多,不然就容易发生前面图中发生的“走样”现象,锯齿现象会严重。这个时候如果将这四个像素的内容“平均”一下,再将这个“平均”结合后的单个像素放入原来的位置,那么锯齿现象就会平滑很多。而这个“平均”从哪里找呢,其实就是Mipmap处理过后的纹理图,比如上图Level1就代表四个像素模糊到一个像素过后 的图像,如果正方形的结果是边长为2,那么就在Level1中找,如果边长为L,那么就在log2L层级上找。

 

三线性插值

如果范围查询的结果是需要在1.5层级中查找,那么该如何处理呢

在游戏中的设置中,经常会发现有一个叫”插值“的选项

如果要在1与2层级中寻找1.5层级,那么直接在两层之间加一次插值,就被称为”三线性插值。

其实有点没太理解似乎老师没细聊

 

Overblur

三线性插值过后,远处的场景发生了模糊化

  • 666.png

问题出在三线性过滤的基础都是发生在查询单位都是正方形的情况下

各向异性过滤——每X2,三倍开销,和性能关系不大,和显存关系大

在不同的方向上表现各不相同

000.png

首先,图中的每一个像素对应在UV图上都是一个个拉伸了的结果,如果对于正常拉伸的矩形,可以快速对矩形进行查询。

还不够,也可以使用EWA过滤