Learn OpenGL光照(二)
基础光照
- 冯氏光照模型(Phong Lighting Model):主要结构由3个分量组成:环境(Ambient)、漫反射(Diffuse)和镜面(Specular)光照。
- 环境光照(Ambient Lighting):即使在黑暗的情况下,世界上通常也仍然有一些光亮(月亮、远处的光),所以物体几乎永远不会是完全黑暗的。为了模拟这个,我们会使用一个环境光照常量,它永远会给物体一些颜色。
- 漫反射光照(Diffuse Lighting):模拟光源对物体的方向性影响(Directional Impact)。它是冯氏光照模型中视觉上最显著的分量。物体的某一部分越是正对着光源,它就会越亮。(光照分量和表面点的法向量点乘的值 再乘于光的颜色,越垂直光照就越亮)
- 镜面光照(Specular Lighting):模拟有光泽物体上面出现的亮点。镜面光照的颜色相比于物体的颜色会更倾向于光的颜色。
- 漫反射:
- 光的方向向量是光源位置向量与片段位置向量之间的向量差。
- 求法向量:vec3 norm = normalize(Normal); vec3 lightDir = normalize(lightPos - FragPos);
- 漫反射击:float diff = max(dot(norm, lightDir), 0.0); vec3 diffuse = diff * lightColor;
- 最终颜色:vec3 result = (ambient + diffuse) * objectColor; FragColor = vec4(result, 1.0);
-
镜面光照:
- 计算反射向量和视线方向的角度差,如果夹角越小,那么镜面光的影响就会越大。它的作用效果就是,当我们去看光被物体所反射的那个方向的时候,我们会看到一个高光。
- 计算镜面光强度,用它乘以光源的颜色,再将它加上环境光和漫反射分量。
- 观察者的世界空间坐标,我们简单地使用摄像机对象的位置坐标代替
- 求镜面光照:float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32); vec3 specular = specularStrength * spec * lightColor;
- 最终颜色:vec3 result = (ambient + diffuse + specular) * objectColor; FragColor = vec4(result, 1.0);
- 材质:
当描述一个物体的时候,我们可以用这三个分量来定义一个材质颜色(Material Color):环境光照(Ambient Lighting)、漫反射光照(Diffuse Lighting)和镜面光照(Specular Lighting)。
- ambient材质向量定义了在环境光照下这个物体反射得是什么颜色,通常这是和物体颜色相同的颜色。
- diffuse材质向量定义了在漫反射光照下物体的颜色。(和环境光照一样)漫反射颜色也要设置为我们需要的物体颜色。
- specular材质向量设置的是镜面光照对物体的颜色影响(或者甚至可能反射一个物体特定的镜面高光颜色)。
- shininess影响镜面高光的散射/半径。
材质和光照定义成一个整体:
struct Material {
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shininess;
};
uniform Material material;
struct Light {
vec3 position;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
uniform Light light;
- 光照贴图:
现实世界中的物体通常并不只包含有一种材质,而是由多种材质所组成。想想一辆汽车:它的外壳非常有光泽,车窗会部分反射周围的环境,轮胎不会那么有光泽,所以它没有镜面高光,轮毂非常闪亮(如果你洗车了的话)。汽车同样会有漫反射和环境光颜色,它们在整个物体上也不会是一样的,汽车有着许多种不同的环境光/漫反射颜色。总之,这样的物体在不同的部件上都有不同的材质属性。
引入漫反射和镜面光贴图(Map),允许我们对物体的漫反射分量(以及间接地对环境光分量,它们几乎总是一样的)和镜面光分量有着更精确的控制。
- 将漫反射和环境光由一个vec3替换成一张贴图,由贴图来反映材质漫反射的强度。
struct Material {
sampler2D diffuse;
vec3 specular;
float shininess;
};
...
in vec2 TexCoords;
- 镜面光贴图:
将镜面光由一个vec3替换成一张贴图,由贴图来反映材质镜面光的强度。
struct Material {
sampler2D diffuse;
sampler2D specular;
float shininess;
};
通过使用漫反射和镜面光贴图,我们可以给相对简单的物体添加大量的细节。我们甚至可以使用法线/凹凸贴图(Normal/Bump Map)或者反射贴图(Reflection Map)给物体添加更多的细节
添加一个叫做放射光贴图(Emission Map)的东西,它是一个储存了每个片段的发光值(Emission Value)的贴图。发光值是一个包含(假设)光源的物体发光(Emit)时可能显现的颜色,这样的话物体就能够忽略光照条件进行发光(Glow)。游戏中某个物体在发光的时候,你通常看到的就是放射光贴图(比如 机器人的眼,或是箱子上的灯带)。
- 平行光:将直接使用光的direction向量而不是通过direction来计算lightDir向量,平行光对于照亮整个场景的全局光源是非常棒的。
旧OpenGL(固定函数式)决定光源是定向光还是位置光源(Positional Light Source)的方法,是根据w分量是0和1来判断,0判断是定向光照,1判断是点光源位置计算方向。
- 点光源:处于世界中某一个位置的光源,它会朝着所有方向发光,但光线会随着距离逐渐衰减。
- 衰减:随着光线传播距离的增长逐渐削减光的强度通常叫做衰减(Attenuation)
常数项KcKc在所有的情况下都是1.0。一次项KlKl为了覆盖更远的距离通常都很小,二次项KqKq甚至更小。
- 聚光:是位于环境中某个位置的光源,它只朝一个特定方向而不是所有方向照射光线。(路灯或手电筒),基本上说,手电筒就是普通的聚光,但它的位置和方向会随着玩家的位置和朝向不断更新。
- 平滑/软化边缘:为了创建一种看起来边缘平滑的聚光,我们需要模拟聚光有一个内圆锥(Inner Cone)和一个外圆锥(Outer Cone)。我们可以将内圆锥设置为上一部分中的那个圆锥,但我们也需要一个外圆锥,来让光从内圆锥逐渐减暗,直到外圆锥的边界。
- 平滑公式:
- 多光源:我们需要有一个单独的颜色向量代表片段的输出颜色。对于每一个光源,它对片段的贡献颜色将会加到片段的输出颜色向量上。所以场景中的每个光源都会计算它们各自对片段的影响,并结合为一个最终的输出颜色。