冯氏光照模型(Phong Lighting Model)
环境光照(Ambient Lighting):即使在黑暗的情况下,世界上通常也仍然有一些光亮(月亮、远处的光),所以物体几乎永远不会是完全黑暗的。为了模拟这个,我们会使用一个环境光照常量,它永远会给物体一些颜色。
漫反射光照(Diffuse Lighting):模拟光源对物体的方向性影响(Directional Impact)。它是冯氏光照模型中视觉上最显著的分量。物体的某一部分越是正对着光源,它就会越亮。
镜面光照(Specular Lighting):模拟有光泽物体上面出现的亮点。镜面光照的颜色相比于物体的颜色会更倾向于光的颜色。
生成GPU管线
Shader lightShader("./lightShader.vs", "./lightShader.fs");
由程序传入GPU(管线)的变量
使用冯氏光照处理管线
lightShader.use();
传入顶点属性
float vertices[] = {图形顶点位置,图形顶点法向量}
一般情况,法向量不能通过由顶点数据直接给定,而要通过三角形的表达式计算。
向管线传入冯氏模型需要的数据:物体颜色,光照颜色,光照位置,摄像机位置。
lightShader.setVec3("objectColor", 1.0f, 0.5f, 0.31f);//物体颜色
lightShader.setVec3("lightColor", 0.5f, 0.5f, 0.3f);//光照颜色
lightShader.setVec3("lightPos", 2.1f, 1.0f, 0.5f );//光照位置
lightShader.setVec3("viewPos",maincamera.getCameraPos() );//摄像机位置
传入视口变化矩阵
lightShader.setMat4("projection", projection);
lightShader.setMat4("view", view);
lightShader.setMat4("model", model);
绘制
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
GPU(管线)内对数据的操作
1.顶点着色器
#version 450 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
out vec3 FragPos;
out vec3 Normal;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
Normal = aNormal;
//Normal = mat3(transpose(inverse(model))) * aNormal;//用于图形变换
FragPos = vec3(model * vec4(aPos, 1.0));
gl_Position = projection * view * vec4(FragPos, 1.0);
}
顶点着色器处理顶点在观察坐标系中的位置,故只是用model,view,projection三个全局值。
而对model(物体模型在世界坐标)的变化,会影响图形法线在世界坐标的方向,故使用Normal = mat3(transpose(inverse(model))) * aNormal;
用于图形变化中法线的变换。链接。
但在GPU中对model进行逆运算和转置运算会导致大量的重复计算降低效率,因此对model的运算(法线变换矩阵)应该放在cpu中进行(在model变化时计算),通过全局变量引入GPU替代mat3(transpose(inverse(model)))
。
2.片段着色器
#version 330 core
out vec4 FragColor;
in vec3 Normal;
in vec3 FragPos;
uniform vec3 lightPos;
uniform vec3 lightColor;
uniform vec3 objectColor;
uniform vec3 viewPos;
void main()
{
// ambient
float ambientStrength = 0.4;
vec3 ambient = ambientStrength * lightColor;
// diffuse
vec3 norm = normalize(Normal);//单位法向量
vec3 lightDir = normalize(lightPos - FragPos);//该渲染点指向光源
float diff = max(dot(norm, lightDir), 0.0);//向量点乘,角度越大数值越小.(不为负数)
vec3 diffuse = diff * lightColor;//漫反射强度与光线到该点与该平面的夹角有关,夹角越小,光照越弱.
// specular
float specularStrength = 0.5;//反光强度
vec3 viewDir = normalize(viewPos - FragPos);//绘制点指向摄像机
vec3 reflectDir = reflect(-lightDir, norm); //reflect(入射光线,法线),返回反射向量
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);//两向量点乘后的32次方,次方数大--集中程度高
vec3 specular = specularStrength * spec * lightColor; //反光强度*反光角度权值*光照颜色
vec3 result = (ambient + diffuse + specular) * objectColor;
FragColor = vec4(result, 1.0);
}
片段着色器主要处理该点在屏幕中显示的颜色。
使用lightPos(光照位置)
, lightColor(光照颜色)
, objectColor(物体颜色)
, viewPos(观察值位置 、摄像机位置)
, Normal(该点法线方向)
, FragPos(该点在世界空间坐标系位置);
由于光照位置,摄像机位置,处理点位置及法线都是世界坐标系下坐标,故可通过世界坐标系计算在摄像机角度观察该点时光照对该点颜色的影响。
全局光照强度
// ambient
float ambientStrength = 0.4;//全局光照系数
vec3 ambient = ambientStrength * lightColor;//全局光照强度
漫反射光照强度
// diffuse
vec3 norm = normalize(Normal);//单位法向量
vec3 lightDir = normalize(lightPos - FragPos);//该渲染点指向光源
float diff = max(dot(norm, lightDir), 0.0);//向量点乘,角度越大数值越小.(不为负数)
vec3 diffuse = diff * lightColor;//漫反射强度与光线到该点与该平面的夹角有关,夹角越小,光照越弱.
反射光照强度
// specular
float specularStrength = 0.5;//反光强度系数
vec3 viewDir = normalize(viewPos - FragPos);//绘制点指向摄像机
vec3 reflectDir = reflect(-lightDir, norm); //reflect(入射光线,法线),返回反射向量
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);//两向量点乘后的32次方,次方数大--集中程度高
vec3 specular = specularStrength * spec * lightColor; //反光强度系数*反光角度权值*光照颜色
reflect(入射光线向量,法线)
返回反射光线向量,是GLSL内置函数。
最终颜色值
vec3 result = (ambient + diffuse + specular) * objectColor;
FragColor = vec4(result, 1.0);
光照总强度(全局光照强度+漫反射光照强度+反射光照强度)* 物体颜色。
该管线BUG或不足
1.环境光照不能很好的替代物体间的光照影响。
2.该管线只能处理但单一光源对单一摄像机的影响。
3.最终光照强度有可能大于光源本身,不符合实际。
4.每种材质对光线的反射,漫反射强度系数不同(这将在下一节提到)。
暂无评论内容