这些光照模型都是基于我们对光的物理特性的理解。其中一个模型被称为冯氏光照模型(Phong Lighting Model)。冯氏光照模型的主要结构由3个分量组成:环境(Ambient)、漫反射(Diffuse)和镜面(Specular)光照
环境光照
全局照明(Global Illumination)算法,但是这种算法既开销高昂又极其复杂,所以我们将会先使用一个简化的全局照明模型,即环境光照。
怎样实现一个环境光照?
把环境光照添加到场景里非常简单。我们用光的颜色乘以一个很小的常量环境因子,再乘以物体的颜色,然后将最终结果作为片段的颜色
漫反射光照
原理:
我们知道两个单位向量的夹角越小,它们点乘的结果越倾向于1。当两个向量的夹角为90度的时候,点乘会变为0。这同样适用于θ,θ越大,光对片段颜色的影响就应该越小。
如何计算漫反射光照?
- 法向量:一个垂直于顶点表面的向量。
- 定向的光线:作为光源的位置与片段的位置之间向量差的方向向量。为了计算这个光线,我们需要光的位置向量和片段的位置向量
//注意要标准化 vec3 norm = normalize(Normal); vec3 lightDir = normalize(lightPos - FragPos); //dot点乘,max取正 float diff = max(dot(norm, lightDir), 0.0); vec3 diffuse = diff * lightColor; //ambient :环境光照 = 光的颜色 * 环境系数 vec3 result = (ambient + diffuse) * objectColor; FragColor = vec4(result, 1.0);
注意:为了(只)得到两个向量夹角的余弦值,我们使用的是单位向量(长度为1的向量),所以我们需要确保所有的向量都是标准化的,否则点乘返回的就不仅仅是余弦值了。
点乘表示cosθ,在(0,π/2)中,是减小的
光线向量 点乘 法向量:与法向量的夹角越小,cosθ越大,表示漫反射的影响越大。
法线矩阵
之前的计算都是方向量的坐标是局部坐标,片段位置的坐标是世界坐标,有没有什么办法可以将法向量的坐标变换到世界坐标呢?
Normal = mat3(transpose(inverse(model))) * aNormal;
镜面光照
反射向量与观察方向的角度差,它们之间夹角越小,镜面光的作用就越大
观察方向 = 摄像机位置 - 片段位置
注意:我们应该从哪个坐标系中开始计算?
我们选择在世界空间进行光照计算,但是大多数人趋向于更偏向在观察空间进行光照计算。在观察空间计算的优势是,观察者的位置总是在(0, 0, 0),所以你已经零成本地拿到了观察者的位置。然而,若以学习为目的,我认为在世界空间中计算光照更符合直觉。如果你仍然希望在观察空间计算光照的话,你需要将所有相关的向量也用观察矩阵进行变换(不要忘记也修改法线矩阵)。
为什么计算反射向量的时候是用的-的?
vec3 reflectDir = reflect(-lightDir, norm);
因为:
reflect
函数要求第一个向量是从光源指向片段位置的向量,但是lightDir
当前正好相反,是从片段指向光源(由先前我们计算lightDir
向量时,减法的顺序决定)pow () 函数用来求 x 的 y 次幂(次方),其原型为:. double pow (double x, double y);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32); vec3 specular = specularStrength * spec * lightColor;
这个32是高光的反光度(Shininess)。一个物体的反光度越高,反射光的能力越强,散射得越少,高光点就会越小。在下面的图片里,你会看到不同反光度的视觉效果影响:
向量的运算
向量的数乘满足交换律、各种结合律、对数和向量的分配率.(ka=ak,k(a+b)=ka+kb,(k+l)a=ka+la,k,l是数a,b是向量)向量的点乘:交换律、分配率(不满足结合律)a·b=b·aa·(b+c)=a·b+a·c(结果是一个数)向量的外积(叉乘):只满足对点、叉的分配率,交换变相反方向(a*b=-b*a)(结果是一个向量)
在OpenGL中,顶点着色器的输出是如何传递到片段着色器的?
比如一个三角形有三个顶点,调用三次顶点着色器,三角形光栅话后会有很多的片段(插值的结果),每个片段都会调用一次片段着色器。那么问题来了,片段着色器的输入到底是处理哪个顶点时的输出呢?
属性插值的方法可以得到片元的颜色、法向量、深度值、纹理坐标
原文链接:08_从顶点到片元 - 百度文库