参考课程:
https://www.bilibili.com/video/BV1X7411F744?p=7
https://www.bilibili.com/video/BV1X7411F744?p=8 第54分钟之前的部分
一、深度缓存的工作
对每一个像素作处理,存了两张图:
- frame buffer 渲染图
- depth buffer 深度图
for(each triangel T)
for(each sample (x,y,z) in T)
if(z < zbuffer[x,y])//closet sample so far
framebuffer[x,y] = rgb;//update color
zbuffer[x,y] = z;//update depth
else
;//do nothing,this sample is occluded
1.zbuffer深度缓存算法
只是在对每个像素求最小值,所以复杂度是O(n)
Q:有没有两个完全相同深度值?
A:解释是记录的是浮点数,不会有完全相同的
Q:如果考虑MSAA呢?
A:就不是对每个像素,而是每个采样点
Q:对透明物体怎么处理?
A:处理不了,需要特殊处理
二、shading 着色
着色(shading)不同于阴影(shadow),着色不考虑其它物体的存在,没有阴影。阴影在其它地方处理。
三、Lambert模型(漫反射 diffuse reflection)
1.参考兰伯特余弦定理(Lambert)
假设有一块很小的区域dA。当法线向量n与光照向量L平行时,区域dA受到的光线照射最多。随着n和L之间的夹角θ逐渐增大,区域dA受到的光线照射量会越来越少 (因为很多光线都无法照射到dA表面上了)。
当θ>90º时,说明光线照射的是物体背面,此时我们应该将强度设置为0。兰伯特(Lambert)余弦定理给出了上述函数的定义:
f(θ) = max(cosθ,0) = max(L•n,0) 其中,L和n是单位向量。
在P7视频49分钟左右,闫大佬用了能量这个角度来解释,就像太阳能电路板一样,可以理解为平均单位面积接受到的能量变少了,注意是单位面积。
2.光照衰减
在上图中,球的表面积公式是4πR^2,根据能量守恒,在内圈单位1的地方是I,到了外圈就是I/ r^2
3.漫反射公式
其中kd可以理解为漫反射系数,吸收率,能量吸收系数。如果为1表示完全不吸收能量,是最亮的。如果为0,就是黑的,所有能量都被吸收了。这个系数又可以细分成RGB三个通道。
漫反射和观察方向没有关系(上面公式中,看不到v向量)。理想的漫反射表面把光线向所有方向均匀的散射,因此,这样的表面在所有观察者看来亮度都一样,理想的慢反射表面是如此粗糙,以至于向各个方向反射的光线强度都相等.
四、高光 specular highlights
1.Phong反射模型
Phong, Bui Tuong. "Illumination for computer generated pictures."Communications of the ACM 18.6 (1975): 311-317. Phong(风式)光照,图形学中最有影响力并被持续用到今天的光照模型之一,以纯几何方法简单有效地模拟了漫反射和高光。作者裴祥风在攻读博士期间完成此工作。当时已白血病晚期的他,以仅仅两年的时间攻读并获得了博士学位,并在毕业后获得斯坦福大学教职。裴祥风在拿到教职的第二年病逝(享年33岁)。
作者:Raymond Fei
链接:https://www.zhihu.com/question/361604494/answer/952054338
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
除了考虑漫反射中提到的光源到反射点的距离 r 之外,需要注意的是,观察方向在镜面反射时是很重要的,具体来说,只有当观察方向集中在反射方向周围很近的时候才能看见反射光
可以看出,此时模型其实已经非常接近真实效果了!那么Blinn-Phong反射模型是什么呢?它只是对phong模型计算反射方向与人眼观察方向角度的一个优化!
2.Blinn-Phong模型
扩展阅读:Blinn-Phong光照模型从定义到实现,一文就够了(1.5w字)
高光反射也称为镜面反射,若物体表面很光滑,当平行入射的光线射到这个物体表面时,仍会平行地向一个方向反射出来。
下图中一根入射光线l,照射在光滑的平面上,会沿着R方向反射,由于平面并非完全光滑,所以反射光的方向并非只有R一个点,而是R周边的一小块区域,只要眼睛(摄像机)在R附近都可以看得到,越靠近R反射光照强度越大。我们可以得出一条结论:高光反射和观察角度有关。
Blinn-Phong模型做了一个很聪明的事情,观察方向和镜面反射方向足够接近时,意味着法线方向和半程向量接近。
所谓半程向量,就是l和v的角平分线方向。这个向量很好求出,直接向量加法,再归一化即可。如下图:
现在计算就变得很简单了,要判断n和h足够接近,只要用点乘即可。
3.问题一
公式中的ks是镜面反射系数,这里没有像漫反射那样考虑能量吸收系数,这是因为这个模型将其简化掉了。
4.问题二
为什么要用半程向量判断,而不是直接判断R和v的角度呢。其实这样做就是Phong模型。Blinn-Phong模型相当于是一种改进,计算半程向量比计算反射向量R要容易许多。
5.问题三
公式右上角有个小写字母p,是做什么的?
向量夹角的余弦确实能体现两个向量是否足够接近,但是容忍度太高了。如果直接使用的话,就会生成一个范围超级大的高光,这样就不太合理。实现生活中看到的高光,应该是非常亮,集中在一个很小的区域。所以应该是两个向量离得稍微远一点,就算它们离开高光点了。所以就用了上图最右边的曲线,加了64次方。在实际应用中,p的值一般在100到200之间,大概就是3到5度之外,就看不到高光了。
五、环境光 ambient lighting
假设任何一个点接收的环境光都是相同的
相当于是一个常数
六、最终效果
七、着色频率 Shading Frequencies
1.Shade each triangle(flat shading)
2.Shade each vertex(Gouraud shading)
求出三角形每个顶点的法线,进行着色。三角形内部,使用差值算法着色。效果一般,如上图,右侧的橙球,已经看不到高光。
3.Shade each pixel(Phong shading)
求出三角形每个顶点的法线,逐个像素插值一个法线,对每个像素进行着色。
多说一点,这个Phong shading和之前的Phong 模型是两个概念,但都是同一个人提出的。
4.总结
这三种着色频率,并没有好坏之分。而是应该根据不同情况下去选择,如果一个模型本身足够复杂,有非常多的三角面,实际上用逐面着色就足够了。如下图对比:
在第一列往下,模型的面数越来越多,可以看出Flat shading已经和其它两种看不出差别。
5.如何求顶点的法线
利用顶点关联的所有面的法线,进行一个加权平均。
6.如何定义逐像素的法线
如果已知左右两侧的顶点法线,如果求出中间的逐像素法线,这个需要用到重心
插值,后面会介绍。另外,法线向量都是单位向量,不要忘记归一化。
八、图形管线
1.
先定义所有顶点,再定义它们之间的连接关系(triangle mesh)。这样分成两步后,只要把顶点投到屏幕上即可,连接关系在投影之后是不会变化的。
2.Shading同时发生在顶点和片元(也可以叫逐像素,后面不再区分)
这就是考虑不同的着色频率的结果,如果是顶点着色,则是发生在Vertex Processing,又做投影又做着色。如果是逐像素着色,自然要等到像素都产生,也就是Fragment Processing。
现有的可编程渲染管线,允许去定义着色。这两个位置写代码去定义,就是shader,对应着顶点着色器和片元着色器。shader每一个顶点,或每一个像素会执行一次,也就是通用的,不用每一个顶点每一个像素去写(GPU是并行的)。
3.Example GLSL fragment shader program
uniform sampler2D myTexture;
uniform vec3 lightDir;
varying vec2 uv;
varying vec3 norm;
void diffuseShader()
{
vec3 kd;
kd = texture2d(myTexture,uv);
kd *= clamp(dot(-lightDir,norm),0.0,1.0);
gl_fragColor = vec4(kd,1.0);
}
上面的代码就是glsl语言,可以参考上面讲到的漫反射:
clamp(dot(-lightDir,norm),0.0,1.0);
这一句就对应了max(0,n.l)。下面从第一行开始看:
uniform 定义了两个全局变量,一个纹理,一个光照方向。norm是差值出来的每一个像素法线。uv是用来帮助找到漫反射系数kd的,暂时不用深究。最后,返回给gl_fragColor。
有个问题就是,公式中的I/r^2去哪了呢?弹幕中有人回答,这个例子中没有光源颜色,只有从纹理采样的物体固有色。
然后就是推荐了一个网站,在网页上可以直接执行(如果看不到画面,换到chrome浏览器):
http://shadertoy.com/view/ld3Gz2