OpenGL光照

        冯氏光照模型的主要结构由3个元素组成:环境(Ambient)、漫反射(Diffuse)和镜面(Specular)光照。这些光照元素看起来像下面这样:

一、环境光照

        使用环境光照来模拟这种情况,也就是无论如何永远都给物体一些颜色。设置环境光比较简单,做法是在片段着色器中将光源的颜色向量乘以一个较小的常量得到环境光,最后在乘以物体的颜色得到反射后的颜色,代码如下。

#version 330 core
out vec4 color;
in vec3 lightColor;
in vec3 objColor;
void main()
{
    float ambientStrength = 0.2;
    vec3 ambient =ambientStrength*lightColor;
    vec3 result = ambient*objColor;
    color = vec4(result,1.0);
}

二、漫反射光照

(1)环境关照的光照效果不明显,但漫反射会产生明显的光源关照效果,漫反射光使物体上与光线排布越近的片段越能从光源处获得更多的亮度,如下图:

(2)N是顶点的法向量,当光照的射入方向与法向量的夹角越小,光照强度将会越大,当与发现重合时,达到最大。为此,为了计算漫反射光照反射强度,我们需要计算,光的方向向量还有法向量。

(3)法向量(Normal Vector)是垂直于顶点表面的(单位)向量。由于顶点自身并没有表面(它只是空间中一个独立的点),我们利用顶点周围的顶点计算出这个顶点的表面。我们能够使用叉乘这个技巧为立方体所有的顶点计算出法线,但是由于3D立方体不是一个复杂的形状,所以我们可以简单的把法线数据手工添加到顶点数据中。

float vertices[] = {
    //顶点位置属性        //顶点法向量属性
    -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
     0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f, 
     0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f, 
     0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f, 
    -0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f, 
    -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f, 

    -0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
     0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
     0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
     0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
    -0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
    -0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,

    -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
    -0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
    -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
    -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
    -0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
    -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,

     0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
     0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
     0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
     0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
     0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
     0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,

    -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
     0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
     0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
     0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
    -0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
    -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,

    -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
     0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
     0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
     0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
    -0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
    -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f
};

然后在顶点着色器中声明法向量属性,再把传给 片段着色器中。

//顶点着色器
#version 330 core
layout (location=0) vec3 aPos;
layout (location=1) vec3 normal;
in mat4 model;    //模型矩阵
in mat4 view;     //视角矩阵
in mat4 project;  //投影矩阵
out vec3 Normal;
void main()
{
    gl_Position =project*view*model*vec4(apos,1.0f);
    //因为所有的光照计算都是在世界坐标空间下计算的,所以这里需要将法向量转换到世界坐标下
    vNormal = mat3(transpose(inverse(model)))*normal;
}

(4)光照的方向向量,要计算光照方向向量,需要知道光源的位置和片段的位置。光源的位置好计算,直接在片段着色器中定义个uniform变量,在程序中传入;片段的位置可以通过把顶点位置属性乘以模型矩阵(Model Matrix,只用模型矩阵不需要用观察和投影矩阵)来把它变换到世界空间坐标得到,最后把它传个片段着色器。

out vec3 FragPos;
out vec3 Normal;

void main()
{
    gl_Position = projection * view * model * vec4(position, 1.0f);
    FragPos = vec3(model * vec4(position, 1.0f));
    Normal = normal;
}

(5)所有的需求变量都设置好后,就可以计算漫反射光照计算了。我们需要做的第一件事是计算光源和片段位置之间的方向向量。光的方向向量是光的位置向量与片段的位置向量之间的向量差。同时确保所有相关向量最后都转换为单位向量,所以我们把法线和方向向量这个结果都进行标准化:

vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);

下一步,我们对normlightDir向量进行点乘,来计算光对当前片段的实际的散射影响。结果值再乘以光的颜色,得到散射因子。两个向量之间的角度越大,散射因子就会越小:

float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * lightColor;

上述代码中,为了确保夹角是在0-90之间,我们需要对其进行使用max函数,确保散射因子不会变成负数。负数的颜色是没有实际定义的。最后将散射因子乘以光照颜色就可以得到漫反射光照。

三、镜面反射

        镜面光照同样依据光的方向向量和物体的法向量,但是这次它也会依据观察方向。我们通过反射法向量周围光的方向计算反射向量。然后我们计算反射向量和视线方向的角度,如果之间的角度越小,那么镜面光的作用就会越大。它的作用效果就是,当我们去看光被物体所反射的那个方向的时候,我们会看到一个高光。

(1)为了得到观察者的世界空间坐标,可以简单地使用摄像机对象的位置坐标代替。所以我们把另一个uniform添加到片段着色器,把相应的摄像机位置坐标传给片段着色器:

uniform vec3 viewPos;

GLint viewPosLoc = glGetUniformLocation(lightingShader.Program, "viewPos");
glUniform3f(viewPosLoc, camera.Position.x, camera.Position.y, camera.Position.z);

(2)下一步,我们计算视线方向坐标,和沿法线轴的对应的反射坐标:

vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);

(3)剩下要做的是计算镜面亮度分量。下面的代码完成了这件事:

float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
vec3 specular = specularStrength * spec * lightColor;

 最后将环境光照、漫反射光照、镜面反射的光照颜色相加得到总的输出颜色:

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值