Games101 shading part 02



镜面反射(Specular reflection)

我们引入了高光这一概念,那么在什么情况下我们可以看到高光?

我们在上节课提到过,当观察方向V接近于镜面反射的方向时才会产生高光,那么什么是镜面反射?

镜面反射:我们将光照照向镜面一点,此时通过入射向量v,法线向量n,我们可以求出此时在镜面上唯一的反射向量R,我们称其为镜面反射.我们假设镜面是最光滑的平面,其余物体表面均不如精面光滑.

而当我们在其他比较光滑的物体表面进行上面操作时,由于物体表面没有镜面光滑,那么其反射方向与镜面反射R有一定误差但仍在R附近.

当我们的观察方向V与反射方向接近时,我们可以看见高光.
当观察方向和光的反射方向越近,高光效果越强。
在这里插入图片描述
上面是冯氏光照模型的做法,他主要是通过n和r的关系来判断高光,Blinn - Phong光照模型是对冯氏模型的一个改进,因为半程向量比较好求从而减少了很多的计算量,所以说是一个改进.

下面是Blinn - Phong的做法:

Blinn - Phong认为观察方向和反射方向接近的时候,其实就说明了法线方向n和半程向量h很接近.
在这里插入图片描述
我们有入射方向l,出射方向v,我们就可以求他们的角平分线,根据平行四边形法则只需要向量l+向量v,我们再将求出来的向量进行单位化,就可以求得半程向量h.此时h和n接近,也就是侧面表达了v和r接近.
在这里插入图片描述
最终计算某一点的高光强度的计算方法如下:
在这里插入图片描述
ks:镜面反射系数,不同的材质对光的反射程度不同,如金属和木头对光的反射效果就不同。通常大家认为高光是白色的,因此我们通常认为ks是白的一个颜色.

Blinn - Phong光照模型是一个经验模型,我们在此只考虑是否能看见高光,因此我们将点接受能量这一部分给忽略掉了.

我们可以看到公式上存在一个指数p是因为,向量之间的Cos值确实可以判断判断是否足够接近,但是他的容忍度太高了.
在这里插入图片描述
如图所示,当p=1时,此时取夹克角度为45度,他们之间的夹角余弦值仍然告诉我们他有一个较大的值,如果此时去生成高光的话,会产生一个很大的高光,这是不合理的.

高光通常是非常亮且集中于一个很小的区域内的, 因此我们想要得到的是角度只要稍微改变,其余弦值就会剧烈变化.因此我们引入指数p,p越大,其对应的函数变化也越大,以p=64为例,也就是表示当角度在大约30度时,就不会发生高光了.正常情况下,我们会取指数为100~200.
在这里插入图片描述
下图是ks和p对高光效果的影响,同一行使用的材质相同,同一列使用的p相同。

在这里插入图片描述
可以看到ks越大,高光越明显;p越大,高光范围越小。

环境光(Ambient Term)

上节课我们提到了环境光,并且说了环境光就是许多光线经过多次漫反射最终打在某一点上,因为环境光太复杂,所以我们假设任何一个点接收到的环境光强度为Ia,任何一个点肯定有自己的颜色,因此ka为环境光系数,两项结合在一起我们可以近似的得到环境光。

环境光不用考虑光的方向,与观察的方向也无关,也不用考虑法线,因此与v,l,n均无关系,从而可以说明环境光是一个常数。
在这里插入图片描述

Blinn - Phong 反射模型

在这里插入图片描述

将环境光,漫反射,高光三种光照效果加在一起,就能够得到最终的光照效果,这看起来像是一个塑料。

着色频率(Shading Frequencies)

在这里插入图片描述

图中三个球是同一个模型经过不同的着色之后的结果,左图能够明显的看到一个个四边形 ,中间的球的效果就平滑一些,右边的图更平滑一些。之所以产生这样的差异是因为着色频率不同.

Flat Shading

在这里插入图片描述
我们说过着色是应用在一个shading point上,而这种着色方法,是在一个三角形上取一个点然后根据平面的法向量对点进行着色,最后将这个点作为该面的着色(一个面上着色一次),在三角形的内部并不会发生颜色的变化,所以可以明显的看到球上的每个三角形。

Gouraud shading

在这里插入图片描述
这种着色方法,是在三角形每个顶点我们算出其对应的法线,然后对每个顶点进行一次着色,至于三角形内部某一点的颜色我们用三个顶点的插值来进行计算,这样三角形内部的颜色就会有一个渐变的效果,也能够更加平滑。

Phong shading

在这里插入图片描述
我们对三角形的三个顶点分别求出法线,然后把法线的方向在三角形内部进行插值,从而我们在每一个像素上都可以求得一个独有的法向量,然后就可以在每一个像素上都进行一次着色,这样就可以得到一个相对比较好的结果。

这种方法是在每一个像素上都进行一次着色。

着色频率的比较:面着色、点着色和像素着色

在这里插入图片描述
从上面的图可以看出,并不能简单的说哪种着色效果更好,着色的频率取决于面、点或者像素出现的频率,如果面的频率出现很高的话,那么用flat shading也是可以达到一个平滑的效果,如上图中最后一行。如果使用flat shading就能达到一个不错的效果,那么如果使用Phong shading的话就会增加额外的计算量,效率上就会降低.

因此我们在实际中使用的时候是根据模型来决定的.

定义逐顶点的法线(Defining Per-Vertex Normal Vectors)

在这里插入图片描述
比如要找一个球上的顶点的法线,根据几何知识我们知道圆心和球面上点的连线就是该点的法线。但是这是在我们知道我们想表示出一个球的情况下,因此我们知道任意一点代表的是球的一点从而通过球心得到法向量,那么如果我们不知道背后的视图要表达什么呢?

因为每一个物体都是由许多三角形相连无缝贴合组成的,所以每一个顶点就会和许多不同的三角形有所关联,即一个顶点就是多个三角形的顶点,所以我们就认为这个顶点的法线就是周围相邻的几个面的法线的平均。

在这里插入图片描述

但是简单的平均没有意义,比如说一个三角形的面积很小,而另一个三角形的面积很大,那么这个大的三角形的法向量平均的份就应该多一些,相反,小三角形的法向量占平均的份就应该少一些。所以在平均时,将每个三角形的面积作为权值,对每个法向量进行加权平均,并且在求平均时要对向量进行标准化。

定义逐像素的法线(Defining Per-Pixel Normal Vectors)

在这里插入图片描述
如图,我们已经知道了两个顶点的法线,那么我们如何去求出中间的平滑过渡的法线呢?

给出顶点的法线,如何插值出中间的法线?这里就需要用到重心坐标,重心坐标我们会在下一节课中进行阐述。

图形(实时)渲染管线(Graphics(Real-time) Rendering Pipeline)

从场景到最后屏幕上显示的图,在这中间经历了什么样的过程,这个过程就是管线。其实表示的是一系列不同的操作,过程如下:
在这里插入图片描述

  • 首先输入了1,2,3,4四个顶点的数据
  • Vertex Processing : 经过变换、投影等操作,将这些点投影到屏幕上。
  • Triangle Processing : 投影到屏幕上的一个个点都是离散的,将其连接起来,成为三角形
  • Rasterization : 形成三角形之后要将三角形画在屏幕上,屏幕是由一个个像素组成的,是离散的,所以要经过光栅化将三角形离散成一个个像素(这里fragment opengl中的概念,片段的意思,在这里我们就把它理解成像素)
  • Fragment Processing : 对每个像素进行着色
  • Framebuffer Operations : 得到屏幕上最终显示出来的图像

具体每一步做的操作如下:
在这里插入图片描述
首先,我们需要对模型的每一个顶点的位置进行变换。
在这里插入图片描述
然后我们进行光栅化,采样.

在这里插入图片描述
光栅化产生了一个个fragment之后我们接着通过Z-BUffer判定是不是可见.
在这里插入图片描述
最后是shading,此时我们可以看到在着色会发生在顶点操作和像素操作两处中.

这是因为我们要考虑着色频率,如果我们时Gouraud shading,那么肯定是在顶点操作中进行的着色,如果是phong shading,那么就是在fragment processing中进行的着色.

在GPU里这一套操作,有些部分是可编程的,也就是由你自己来规定顶点如何进行着色,像素如何进行着色,对一部分的代码我们称之为shader.
接下来我们来简单的介绍一下shader.

Shader

shader本质上是一些能在硬件上执行的语言,我们以openGl为例,他是一个图形学的API,我们可以用它来写一些shader.

shader在每一个顶点或者fragment会执行一次,所以我们写出的shader是通用的,适合于每一个顶点或者fragment的.因此我们不需要写for循环,只需要写出适用于一个的代码就可以在任意一个上使用.

如果我们写的是对于顶点的操作,那么叫做vertex shader,如果是在fragment操作,叫做pixel shader.
在这里插入图片描述
上图一个fragment shader的例子,它的作用是告诉最终像素的颜色是什么,也就是对于一个fragment来说,我们要写清楚怎么样算他的颜色并且将它输出出去.
上述代码中uniform代表全部变量,也就是我们有两个全局变量,一个是myTexture(纹理),一个是 lightDir(光照方向),我们认为每一个fragment有一个固定的光照方向.norm是顶点法线,这个顶点法线是插值出来的.
其中

kd *= clamp(dot (-lightdir,norm),0.0,1.0)
其实就是phong模型的那个公式

在这里插入图片描述

kd是漫反射系数,我们用kd乘以 l和n的点乘,在这里l就是-lightDir,之所以是负的是因为这里定义了入射方向是向内的. 然后我们求出-lightDir和Norm的点乘,也就是kd*-lightDir和Norm的点乘.我们用clamp函数保证了kd乘以的数在0-1之间,最后再将这个值存入到kd中,此时的kd其实就是Ld.

最后再返回一个固定的值—gl_FragColor,也就是表达这个fragment的颜色是什么.

引出纹理映射:
在这里插入图片描述
三角形内部每一个点都对应木头纹理的某一个点,我们如何定义三角形内部每一个点都具有不同的属性?这就是纹理映射,我们在下节课进行学习.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值