08Shading 2着色(着色频率、图形管线、纹理映射)
- 前置知识
- 1.Specular Term 高光
- 2.Ambient Term 环境光照项
- 3.最终的成像公式
- 2.着色频率
- 1.Flat shading
- 2.Gouraud shading
- 3.Phong shading
- 4.那么如何计算逐顶点法线?
- 5.如何定义逐像素的法线?
- 3.Graphics (Real-time Rendering)Pipeline 图形(实时渲染)管线
- 1.从场景到最后一张图中间到底经历了什么?
- 1.Vertex Processing顶点处理
- 2.Triangle Processing三角形处理
- 3.Rasterization光栅化
- 4.Fragment Processing片段处理
- 2.Shader
- 案例:OpenGL上的fragment shader
- 3.纹理映射Texture Mapping
- 1.Different Colors at Different Places?
- 2.纹理案例
前置知识
1.Specular Term 高光
由于观察方向与镜面反射方向,也就是v(相机视线的方向向量(v))和R(反射光线的方向向量(R))足够接近的时候,就得到了一个高光项。
V close to mirror direction = half vector near normal (半程向量和法线接近) V和R接近=h和n接近
半程向量h是l和v的角平分线向量
为什么要替换用n和h替换原来的R和v?
因为R点乘v要求出反射光线R向量,虽然不是不可以,但是比较麻烦,计算量大。所以Blinn提出来了一个天才般的想法:
不去计算R与v的夹角余弦,而是去计算v和入射方向I的角平分线 与 表面法线的夹角余弦。于是就定义了这个角平分线为h。
如果不用这个技巧的模型,被称为Phong模型。利用这个技巧的,才叫Blinn-Phong模型。
Blinn-Phong模型大大简化了计算。因为计算反射方向是一件很繁琐的事情(想想看找到某个向量以另一个向量为轴的对称向量,并不是一件很容易的事情)。
如何衡量两个向量接近?——使用点乘
两个向量越近,点乘结果越接近1,两个向量越远,点乘结果越接近0
ks是镜面反射系数,通常认为是白的
问题:为什么不再考虑多少能量被这个点真正吸收了?
实际如果我们要做的对的话,我们确实需要在这个镜面反射上仍然要考虑多少能量被吸收,也就是有之前一项l和n的点乘。但是这里没有考虑是因为Blinn-Phong是经验性模型,把这一点简化掉了,我们主要关注的是我们是否能看见高光,是否我的这些方向保证了我能够看到高光,就是n和h足够接近。
问题:为什么多出来了一个指数P?
指数P用来控制高光有多大,因为cosin函数太平缓了,不够陡峭,所以高光的范围太大了,为了让高光范围小一点,我们可以取P次方。(P一般可以取100~200之间)
随着指数P的增长,我可以看到越来越小的高光。Ks是镜面反射系数,表示亮度。
2.Ambient Term 环境光照项
茶杯有些地方根本不可能直接被光源照亮,但它却不是暗的。因为有很多光线可以弹射很多次,然后从四面八方打到任何一个其他的点上。所以对于茶杯背面的点也一样,它能够接收到来自环境的光。
所以我们假设任何一个点接收到来自环境的光永远都是相同的,环境光强度是Ia,Ka是环境光系数。
3.最终的成像公式
2.着色频率
含义:着色的精密程度。
- 逐三角形着色。即每个三角形指定一个颜色值。Flat Shading
- 逐顶点着色。即每个顶点指定一个颜色值。Gouraud Shading(需要插值)
- 逐像素着色。即每个像素指定一个颜色值。Phong Shading(需要插值)
第一个球:把着色应用在面上,认为整个四边形面都长这个颜色。也就是一个平面只做一次shading。
第二个球:每个平面的对4个顶点都算出对应的法线,每个顶点做一次着色,然后通过差值的方法计算四边形内部点的颜色。差值:知道三角形3个顶点的颜色,求其内部的某一个点颜色是什么,并且希望有一个平滑的过渡。
第三个球:shading应用在每一个像素上。对于每个四边形或三角形,顶点求出一个法线,然后把这些法线的方向在三角形内部进行差值,然后就得到了任何一个像素都有一个自己的法线方向,并且可以做一遍着色。
1.Flat shading
每个三角形是个平面,然后把三角形的法线求出来(两边做叉积),然后算出shading的结果。
2.Gouraud shading
在任意一个顶点上求出它的法线,每个顶点做一次着色,然后三角形内部的颜色通过差值的方法计算出来。
3.Phong shading
先求顶点法线,再每个像素差值出独特的法线向量
案例:每一行使用的模型是一样的,当模型本身比较精细的时候,其实用Flat shading比Phong shading更减少计算开销,效果也差不多(如第三行)
4.那么如何计算逐顶点法线?
任何一个顶点会和很多个不同的三角形有所关联,那么这个顶点的法线我们就认为它是这个相邻这些面的法线求给4个平均。
一个简单的平均也许说明不了什么问题,比如有一个三角形超级小,有一个三角形超级大,那么这个超级大的三角形应该会贡献的更多,在求平均的过程中做一个加权(三角形的面积)的平均,会得到更好的结果。
面片衔接的地方,如何定义法线?
其实很简单,就是把邻接的面片的法线加起来求和(再归一化)就行了。
5.如何定义逐像素的法线?
当有了顶点法线后,使用重心坐标计算出中间的法线
3.Graphics (Real-time Rendering)Pipeline 图形(实时渲染)管线
图形管线:从点坐标到在屏幕上绘制图形的全流程
管线:一系列不同的操作
1.从场景到最后一张图中间到底经历了什么?
这一系列操作是在硬件中写好了的,在GPU里面进行
1.Vertex Processing顶点处理
其实就是把一些坐标点,通过模型(模型本身的旋转拉伸剪切)、视图(相机的摆放)、投影(透视投影到正交投影再到正则立方体)。以及视口变换(正则立方体到屏幕)
2.Triangle Processing三角形处理
这一步其实不需要我们来处理。我们得到模型的时候,一般就得到了顶点的连接关系。也就是指定哪三个顶点连成一个三角形(想一想OBJ文件格式就知道了)
3.Rasterization光栅化
找哪些像素是在三角形内部的。如果在内部,就按照该三角形的颜色值为像素指定颜色。
4.Fragment Processing片段处理
片段(fragment)其实就是像素(pixel),只是叫法不同罢了。
首先,做一个深度检测算法,一般是Z-Buffer。按照物体的遮挡关系绘制图形。
其次,根据光源、相机、物体三者的相对位置,绘制物体光暗的变化,也就是着色(shading)。可以使用Blinn-Phong Shading
最后,我们还可以在物体上绘制不同的纹理。例如木头就是木头的纹理,金属就是金属的纹理。不同的物体材质有不同的纹理。
2.Shader
对我们程序员来说,GPU厂商和操作系统厂商已经为我们写好了大部分的固定流程。我们无需关心许多千篇一律的流程(因为代码都是一样的,已经被写好了)。而GPU厂商和操作系统为我们留出了一些接口,即API。这就相当于我们可以写许多用户自定义的程序片段。这些程序片段就是shader 程序。这些API就是Shading Language。
有两种shader程序:
vertex shader
fragment shader
顾名思义,vertex shader就是允许你在顶点处理的步骤上填写自定义的程序。fragment shader就是允许你在像素处理的时候写自定义的程序。
案例:OpenGL上的fragment shader
作用:告诉GPU每个像素最后的颜色是什么,也就是说对于一个像素来说,我们要写清楚怎么样算它最后的颜色,并且把它输出出去。
GPU:高度并行化的处理器。64核:可以并行的线程数量为64。GPU的并行度要远远超过CPU的几十倍。
学到这里,大家就可以去使用像OpenGL这种API去写各种图形渲染了。
3.纹理映射Texture Mapping
1.Different Colors at Different Places?
对于不同的物体来说,显然其上的图案是不同的。
比如气球和木地板,他们的图案就完全不同。我们就称,他们的纹理不同。地板的漫反射:在任何一个地方地板有自己的漫反射系数,反应在木头的这个纹路上,也就是说我们希望在物体不同位置定义一个不同的属性,这就是引入纹理映射的思路。
物体是三维的,但表面都是二维的。纹理就是一张图,这张图我可以任意的把它撕开用其中一块或把它拉伸/压缩,就是认为它是一个有弹性的一张图。然后把它蒙在任何一个三维物体的表面,这个过程就叫做纹理映射。
映射是一个一一对应的关系,物体表面任何一个点与纹理上面任何一个点之前的关系。
如何把空间中的一个三角形映射到一个纹理上呢?
我们默认已经知道了纹理要如何贴到三角形上(美工/艺术家做的事),因为我们在三角形上的任何一个顶点,我们都已经规定了它在纹理上的一个坐标。
2.纹理案例
纹理映射并非一件简单的事情。
我们要实现的实际上是2维平面到3维曲面的映射关系。他们之间一一对应。
在此处,我们以三角形为纹理的最小单元。我们要做的,其实就是找到二维平面上的三角形的坐标值与三维曲面上的坐标值的对应关系。
如图所示。我们定义二维平面的坐标用(u, v)表示。这是因为三维曲面坐标已经用(x, y, z)表示了。所以我们用uv这两个字母。
我们只要记得: (u,v)不过是三维曲面展开到2维平面上的时候,我们自己给定义的坐标值而已。
上图中,红色越多代表u坐标越大,绿色越多代表v坐标值越大。
为了统一标准。我们总是让uv值在[0,1]内。
只要知道每个三角形对应的uv值,我们就做好了纹理映射。
此外,纹理是可以不断重复的,就像贴瓷砖一样。美术师只要巧妙地绘制纹理,让重复的地方看不出来接缝。纹理贴图就可以无限地循环复制。
这种就被称之为tiled texture 因为纹理设计的很好使得其在重复的时候得以无缝衔接。这种纹理的设计需要各种算法。
标注:本章部分笔记内容参考了csdn用户beidou111的笔记