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);}
- OpenGL学习总结(四)
- OpenGL学习笔记(四)
- OpenGL入门学习(四)
- OpenGL入门学习(四)
- OpenGL入门学习(四)
- OpenGL入门学习(四)
- OpenGL入门学习(四)
- OpenGL学习笔记(四)
- OpenGL ES总结(四)OpenGL 渲染视频画面
- OpenGL ES总结(四)OpenGL 渲染视频画面
- OpenGL学习总结(一)
- OpenGL学习总结(二)
- OpenGL学习总结(三)
- OpenGL学习总结(五)
- OpenGL学习总结(六)
- OpenGL学习总结(七)
- OpenGL学习总结(八)
- OpenGL 学习总结(一)
- Qt中的sizeHint,minimumSizeHint,sizePolicy属性
- spark几万个分区表无法执行
- C++ 数据结构链表的实现代码
- Luogu P2799 国王的魔镜
- 【matlab】global的用法
- OpenGL学习总结(四)
- 关于几种语言(c#,php,python,javascript)字符串的gzip压缩与解压的整理
- Uva536 Tree Recovery 【递归建树】【习题6-3】
- 《或者你需要一场轰轰烈烈的爱情-雾满拦江》
- html5摇一摇代码
- 浏览器同源政策及其规避方法
- 不需要产品经理分流版
- Android LruCache 源码分析
- cocos2d 中 scene(), create(), init() 调用关系