OpenGL ES 光照
冯氏光照
冯氏光照模型(Phong Lighting Model))的主要结构由3个分量组成:环境(Ambient)、漫反射(Diffuse)和镜面(Specular)光照。
image.png环境光照(Ambient Lighting):即使在黑暗的情况下,世界上通常也仍然有一些光亮(月亮、远处的光),所以物体几乎永远不会是完全黑暗的。为了模拟这个,我们会使用一个环境光照常量,它永远会给物体一些颜色。
漫反射光照(Diffuse Lighting):模拟光源对物体的方向性影响(Directional Impact)。它是冯氏光照模型中视觉上最显著的分量。物体的某一部分越是正对着光源,它就会越亮。
镜面光照(Specular Lighting):模拟有光泽物体上面出现的亮点。镜面光照的颜色相比于物体的颜色会更倾向于光的颜色。
我们在现实生活中看到某一物体的颜色并不是这个物体真正拥有的颜色,而是它所反射的(Reflected)颜色。换句话说,那些不能被物体所吸收(Absorb)的颜色(被拒绝的颜色)就是我们能够感知到的物体的颜色。
白光照在蓝色的玩具上,蓝色的玩具会吸收白光中除了蓝色以外的所有子颜色,不被吸收的蓝色光被反射到我们的眼中。我们可以使用不同的光源颜色来让物体显现出意想不到的颜色。
当把光源的颜色与物体的颜色值相乘,所得到的就是这个物体所反射的颜色才是我们所感知到的颜色。
1 环境光照
环境光照可以简化成用光的颜色乘以一个很小的常量环境因子,再乘以物体的颜色,然后将最终结果作为片段的颜色。
float ambientStrength = 0.3;
vec3 lightColor = vec3(1.0, 1.0, 1.0);
// 环境光照
vec3 ambient = ambientStrength * lightColor;
2 漫反射
image.png漫反射光照计算需要:
1、法向量:一个垂直于顶点表面的向量。
2、定向的光线:作为光源的位置与片段的位置之间向量差的方向向量。
varying vec3 fragPos;//当前片段坐标
varying vec3 norm;//当前片段法向量(归一化处理)
uniform vec3 aLightPos;//光源位置
//材质漫反射系数
float diffuseStrength = 0.5;
// 归一化光源线
vec3 lightDir = normalize(aLightPos - fragPos);
// norm是片段表面的法向量
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * lightColor;
向量需要都是标准化的,保证点乘得到余弦值,用点乘结果计算光线对片段颜色的影响。点乘结果和0比较,避免漫反射分量为负数。
向量点乘
两个向量的点乘等于它们的数乘结果乘以两个向量之间夹角的余弦值。
3 镜面反射
image.png // 镜面光照
float specularStrength = 2.5;
//vec3 viewDir = normalize(viewPos - FragPos); //视线方向向量
//在观察空间计算,观察者的位置总是(0, 0, 0),
vec3 viewDir = normalize(-fragPos);
// lightDir向量进行了取反。reflect函数要求第一个向量是从光源指向片段位置的向量
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 16.0);
vec3 specular = spec * specularStrength * lightColor;
vec4 textColor = texture2D(texture,TexCoord);
如何理解reflect的计算:
如上2幅图可以看到lightDir方向以及,-lightDir方向。-lightDir作为入射光,根据法向量n,计算出发射光向量reflectDir。
我们选择在世界空间进行光照计算,但是大多数人趋向于在观察空间进行光照计算。在观察空间计算的好处是,观察者的位置总是(0, 0, 0),所以这样你直接就获得了观察者位置。可是我发现在学习的时候在世界空间中计算光照更符合直觉。如果你仍然希望在观察空间计算光照的话,你需要将所有相关的向量都用观察矩阵进行变换(记得也要改变法线矩阵)。
如果要在观察空间进行光照计算,则将相关向量进行model和view矩阵变换。
顶点着色器代码:
uniform mat4 uMVMatrix;
uniform mat4 uMVPMatrix;
attribute vec4 aPosition;
// 法向量
attribute vec3 aNormal;
attribute vec3 objectColor;
varying vec3 fragPos;
varying vec3 norm;
// 纹理坐标
attribute vec2 aTexCoords;
varying vec2 TexCoord;
void main() {
// 乘以model view矩阵(uMVMatrix),转换到观察空间
fragPos = vec3(uMVMatrix * aPosition);
norm = normalize(vec3(uMVMatrix * vec4(aNormal, 0.0)));
gl_Position = uMVPMatrix * aPosition;
// 纹理坐标
TexCoord = aTexCoords;
}
片元着色器代码:
precision mediump float;
varying vec2 TexCoord;
varying vec3 fragPos;
varying vec3 norm;
uniform vec3 aLightPos;
uniform sampler2D texture;
void main() {
//1--- 环境光照
float ambientStrength = 0.3;
vec3 lightColor = vec3(1.0, 1.0, 1.0);
vec3 ambient = ambientStrength * lightColor;
//2-- 漫反射光照
//材质漫反射系数
float diffuseStrength = 0.5;
// 归一化光源线
vec3 lightDir = normalize(aLightPos - fragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diffuseStrength*diff * lightColor;
//3-- 镜面光照
float specularStrength = 2.5;
//vec3 viewDir = normalize(viewPos - FragPos); //视线方向向量
vec3 viewDir = normalize(-fragPos);//在观察空间计算的好处是,观察者的位置总是(0, 0, 0),
// lightDir向量进行了取反。reflect函数要求第一个向量是从光源指向片段位置的向量
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 16.0);
vec3 specular = spec * specularStrength * lightColor;
vec4 textColor = texture2D(texture,TexCoord);
// 结果
vec3 result = (ambient + diffuse + specular) * vec3(textColor);//-- 1颜色
gl_FragColor = vec4(result, 1.0);
}
4 效果
image.png参考
https://learnopengl-cn.github.io/02%20Lighting/02%20Basic%20Lighting/
https://blog.csdn.net/jklwan/article/details/103633239?spm=1001.2014.3001.5501
https://learnopengl-cn.github.io/01%20Getting%20started/07%20Transformations/
https://blog.csdn.net/jklwan/article/details/103744812?spm=1001.2014.3001.5501