OpenGL学习总结(四)

来源:互联网 发布:宝鸡国企再造新矩阵 编辑:程序博客网 时间:2024/05/22 17:08

OpenGL光照模型总结

一、 冯氏光照
所谓的冯氏光照分为三大部分:环境光照、漫反射光照以及镜面反射光照。对于同一个物体来讲,当有多个光源发出光线照亮该物体时,都可以将其划分成上述三种情况分别进行计算,最后将三部分组合即为其中一个光源照射物体后得到的最终结果,再将每个光源的照射结果进行叠加即为复杂照射环境下的最终结果。
1、 环境光照:
这是冯氏光照中最简单的光照,只需要用环境光向量乘上物体本身的颜色即可,要注意的是环境光往往比较弱,所以环境光向量要设置的比较小。

vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));

解释:
light.ambient为环境光线,material.diffuse为漫反射光照贴图(因为大部分情况下环境光贴图与漫反射贴图相同),TexCoords为纹理坐标,texture函数会在纹理坐标内生成一些插值坐标,用来采样纹理图片其他地方的颜色。
2、 漫反射光照:
这里写图片描述
如图可知,计算漫反射光照最重要的就是计算光线向量与法向量的夹角角度。法向量可以通过叉乘获得。而光线向量要通过光源位置向量与片段位置向量相减获得。

vec3 lightDir = normalize(light.position - fragPos);float diff = max(dot(normal, lightDir), 0.0);vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));

解释:
LightDir为光线向量,normalize是向量标准化函数,将向量变为长度为1的单位向量。Diff为法向量与光线向量夹角,注意该夹角不能小于0,normal是法向量我们一般可以通过数据输入得到。
重要注意点:
当模型发生不均匀缩放是会破坏法向量导致光线扭曲。
解决方法:使用正规矩阵(模型矩阵左上角的逆矩阵的转置矩阵)
得到如下顶点着色器代码:

#version 330 corelayout (location = 0) in vec3 position;//顶点位置属性layout (location = 1) in vec3 normal;//顶点法向量属性layout (location = 2) in vec2 texCoords;//顶点纹理坐标属性out vec3 Normal;out vec3 FragPos;out vec2 TexCoords;uniform mat4 model;//转换成世界空间坐标系所需要的模型矩阵。uniform mat4 view;//转换成观察者空间坐标系所需要的观察矩阵。uniform mat4 projection;//转换成裁剪空间坐标系所需要的裁剪矩阵(投影矩阵)。void main(){    //记录最后各个顶点的最终位置向量    gl_Position = projection * view *  model * vec4(position, 1.0f);    //在世界空间坐标系下的各个片段的位置向量    FragPos = vec3(model * vec4(position, 1.0f));    //通过正规矩阵处理的法向量,inverse函数是求逆函数,transpose是转置函数。    //inverse函数对于GPU来说开销很大,    //所以最好在主程序中将model矩阵求逆并传入着色器当中。    Normal = mat3(transpose(inverse(model))) * normal;      TexCoords = texCoords;}

3、 镜面反射光照
这里写图片描述
计算该光照需要以下几个数据:光源位置,片段位置,法向量,观察者位置。
光源位置和片段位置能够求出光线向量,通过光线向量与法向量能够计算出反射光线向量,观察者位置和片段位置能够计算出观察者向量,观察者向量与反射光线向量能够求出角度,该角度即为参数spec,用来计算镜面反射光线向量。

vec3 lightDir = normalize(light.position - fragPos);vec3 viewDir = normalize(viewPos - FragPos);vec3 reflectDir = reflect(-lightDir, normal);float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));

viewDir为观察者向量。
reflectDir是反射光线向量,reflect是求反射向量的函数其中第一个参数是入射光线,它必须是从光源出发,所以lightDir要取反。
Material.shininess是发光值一个物体的发光值越高,反射光的能力越强,散射得越少,高光点越小。往往设置为32。
4、 光照结果:

vec3 result = ambient + diffuse + specular;color = vec4(result, 1.0f);

二、 投光物
1、 定向光
当一个光源很远的时候,来自光源的每条光线接近于平行。这看起来就像所有的光线来自于同一个方向,无论物体和观察者在哪儿。当一个光源被设置为无限远时,它被称为定向光(Directional Light),因为所有的光线都有着同一个方向;它会独立于光源的位置。
这里写图片描述

struct DirLight {    vec3 direction;//定向光的光线向量(定向光的光线向量不发生改变)    vec3 ambient;//环境光向量    vec3 diffuse;//漫反射光线向量    vec3 specular;//镜面反射光线向量};uniform DirLight dirLight;vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir){    vec3 lightDir = normalize(-light.direction);    // 计算漫反射系数    float diff = max(dot(normal, lightDir), 0.0);    // 计算镜面反射系数    vec3 reflectDir = reflect(-lightDir, normal);    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);    // 计算各光照分量向量    vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));    vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));    vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));    return (ambient + diffuse + specular);}

2、 点光源
点光是一个在时间里有位置的光源,它向所有方向发光,光线随距离增加逐渐变暗。想象灯泡和火炬作为投光物,它们可以扮演点光的角色。
这里写图片描述
重要特性——衰减:
随着光线穿越距离的变远使得亮度也相应地减少的现象,通常称之为衰减(Attenuation)。
衰减方程:
这里写图片描述
在这里d代表片段到光源的距离。为了计算衰减值,我们定义3个(可配置)项:常数项Kc,一次项Kl和二次项Kq。

struct PointLight {    vec3 position;//点光源位置    float constant;//衰减公式常数项    float linear;//衰减公式一次项系数    float quadratic;//衰减公式二次项系数    vec3 ambient;//环境光向量    vec3 diffuse;//漫反射向量    vec3 specular;//镜面反射向量};vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir){    vec3 lightDir = normalize(light.position - fragPos);    // 计算漫反射系数    float diff = max(dot(normal, lightDir), 0.0);    // 计算镜面反射系数    vec3 reflectDir = reflect(-lightDir, normal);    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);    // 计算衰减系数    float distance = length(light.position - fragPos);    float attenuation = 1.0f / (light.constant + light.linear * distance +light.quadratic * (distance * distance));        // 计算各个光照分量向量    vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));    vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));    vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));    //各个光照分量乘上衰减系数    ambient *= attenuation;    diffuse *= attenuation;    specular *= attenuation;    return (ambient + diffuse + specular);}

3、 聚光光源
聚光是一种位于环境中某处的光源,它不是向所有方向照射,而是只朝某个方向照射。结果是只有一个聚光照射方向的确定半径内的物体才会被照亮,其他的都保持黑暗。聚光的好例子是路灯或手电筒。
这里写图片描述
• LightDir:从片段指向光源的向量。
• SpotDir:聚光所指向的方向。
• Phiϕ:定义聚光半径的切光角。每个落在这个角度之外的,聚光都不会照亮。
• Thetaθ:LightDir向量和SpotDir向量之间的角度。θ值应该比Φ值小,这样才会在聚光内。
重要特性——平滑/软化边缘
为创建聚光的平滑边,我们希望去模拟的聚光有一个内圆锥和外圆锥。我们可以把内圆锥设置为前面部分定义的圆锥,我们希望外圆锥从内边到外边逐步的变暗。
为创建外圆锥,我们简单定义另一个余弦值,它代表聚光的方向向量和外圆锥的向量(等于它的半径)的角度。然后,如果片段在内圆锥和外圆锥之间,就会给它计算出一个0.0到1.0之间的亮度。如果片段在内圆锥以内这个亮度就等于1.0,如果在外面就是0.0。
这里写图片描述
这里ϵ是内部圆锥(ϕ)和外部圆锥(γ)(epsilon = phi - gamma)的差。结果I的值是聚光在当前片段的亮度。

struct SpotLight{    vec3 position;//光源位置    vec3 direction;//光的方向,即上图的SpotDir    float cutOff;//内圆锥切角    float outerCutOff;//外圆锥切角    float constant;    float linear;    float quadratic;    vec3 ambient;    vec3 diffuse;    vec3 specular;};vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir){    vec3 lightDir = normalize(light.position - fragPos);    // 计算漫反射系数    float diff = max(dot(normal, lightDir), 0.0);    // 镜面反射系数    vec3 reflectDir = reflect(-lightDir, normal);    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);    // 光线衰减    float distance = length(light.position - fragPos);    float attenuation = 1.0f / (light.constant + light.linear * distance + light.quadratic * (distance * distance));        // 计算聚光系数    float theta = dot(lightDir, normalize(-light.direction));     float epsilon = light.cutOff - light.outerCutOff;    float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0);    // 计算各个光照分量答案    vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));    vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));//给每个光照分量乘上衰减系数和聚光系数    ambient *= attenuation * intensity;    diffuse *= attenuation * intensity;    specular *= attenuation * intensity;    return (ambient + diffuse + specular);}

三、 完整的顶点着色器和片段着色器
1、 Vertexshader:

#version 330 corelayout (location = 0) in vec3 position;layout (location = 1) in vec3 normal;layout (location = 2) in vec2 texCoords;out vec3 Normal;out vec3 FragPos;out vec2 TexCoords;uniform mat4 model;uniform mat4 view;uniform mat4 projection;void main(){    gl_Position = projection * view *  model * vec4(position, 1.0f);    FragPos = vec3(model * vec4(position, 1.0f));    Normal = mat3(transpose(inverse(model))) * normal;      TexCoords = texCoords;}

2、 Fragmentshader

#version 330 corestruct Material {    sampler2D diffuse;    sampler2D specular;    float shininess;}; struct DirLight {    vec3 direction;    vec3 ambient;    vec3 diffuse;    vec3 specular;};struct PointLight {    vec3 position;    float constant;    float linear;    float quadratic;    vec3 ambient;    vec3 diffuse;    vec3 specular;};struct SpotLight{    vec3 position;    vec3 direction;    float cutOff;    float outerCutOff;    float constant;    float linear;    float quadratic;    vec3 ambient;    vec3 diffuse;    vec3 specular;};#define NR_POINT_LIGHTS 4in vec3 FragPos;in vec3 Normal;in vec2 TexCoords;out vec4 color;uniform vec3 viewPos;uniform DirLight dirLight;uniform PointLight pointLights[NR_POINT_LIGHTS];uniform SpotLight spotLight;uniform Material material;// Function prototypesvec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir);vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir);vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir);void main(){        // Properties    vec3 norm = normalize(Normal);    vec3 viewDir = normalize(viewPos - FragPos);    // == ======================================    // Our lighting is set up in 3 phases: directional, point lights and an optional flashlight    // For each phase, a calculate function is defined that calculates the corresponding color    // per lamp. In the main() function we take all the calculated colors and sum them up for    // this fragment's final color.    // == ======================================    // Phase 1: Directional lighting    vec3 result = CalcDirLight(dirLight, norm, viewDir);    // Phase 2: Point lights    for(int i = 0; i < NR_POINT_LIGHTS; i++)        result += CalcPointLight(pointLights[i], norm, FragPos, viewDir);        // Phase 3: Spot light    result += CalcSpotLight(spotLight, norm, FragPos, viewDir);        color = vec4(result, 1.0);}// Calculates the color when using a directional light.vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir){    vec3 lightDir = normalize(-light.direction);    // Diffuse shading    float diff = max(dot(normal, lightDir), 0.0);    // Specular shading    vec3 reflectDir = reflect(-lightDir, normal);    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);    // Combine results    vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));    vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));    vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));    return (ambient + diffuse + specular);}// Calculates the color when using a point light.vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir){    vec3 lightDir = normalize(light.position - fragPos);    // Diffuse shading    float diff = max(dot(normal, lightDir), 0.0);    // Specular shading    vec3 reflectDir = reflect(-lightDir, normal);    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);    // Attenuation    float distance = length(light.position - fragPos);    float attenuation = 1.0f / (light.constant + light.linear * distance + light.quadratic * (distance * distance));        // Combine results    vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));    vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));    vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));    ambient *= attenuation;    diffuse *= attenuation;    specular *= attenuation;    return (ambient + diffuse + specular);}vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir){    vec3 lightDir = normalize(light.position - fragPos);    // Diffuse shading    float diff = max(dot(normal, lightDir), 0.0);    // Specular shading    vec3 reflectDir = reflect(-lightDir, normal);    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);    // Attenuation    float distance = length(light.position - fragPos);    float attenuation = 1.0f / (light.constant + light.linear * distance + light.quadratic * (distance * distance));        // Spotlight intensity    float theta = dot(lightDir, normalize(-light.direction));     float epsilon = light.cutOff - light.outerCutOff;    float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0);    // Combine results    vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));    vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));    vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));    ambient *= attenuation * intensity;    diffuse *= attenuation * intensity;    specular *= attenuation * intensity;    return (ambient + diffuse + specular);}
0 0