引言

没啥可说的,这节直接来复现光照。

Part1. Lambert模型

我们假设场景中某处有一个无限小点光源,它的位置是L;那么我们物体上每一点O的着色结果就是:

$$L_{out} = L_{light} * \rho_d *dot(n,l)$$

其中L_light是光照强度,rho_d是漫反射分量(此时就理解成纹理的颜色),n是表面法线,l是片元指向光源的单位向量。

所以我们可以分析出,首先我们需要向shader中传入光源的强度以及光源的位置,然后在片元着色器中要获得当前片元的法线和位置,这样就可以得到最终的光线出射结果了。

添加光源信息:

while (!glfwWindowShouldClose(window))     //开始渲染循环
    {
        processInput(window);                  //自定义的检测键盘输入函数
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glUseProgram(shaderProgram);

        shader.setVec3("lightPos", glm::vec3(0, 3, 2));
        shader.setVec3("lightColor", glm::vec3(1, 1, 1));

将光源位置置于前上方,然后光强设置为白色。接下来改一下顶点着色器和片元着色器:

///////////////////////顶点着色器///////////////////////
#version 330 core

layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aUV;
layout (location = 2) in vec3 aNormal;

out vec3 normal;
out vec2 texcoord;
out vec3 FragPos;  

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
   gl_Position = projection * view * model * vec4(aPos, 1.0);
   FragPos = vec3(model * vec4(aPos, 1.0));
   normal = aNormal;
   texcoord = aUV;
}
///////////////////////片元着色器///////////////////////
#version 330 core

in vec3 normal;
in vec2 texcoord;
in vec3 FragPos;  
out vec4 FragColor;
uniform sampler2D ourTexture;
uniform vec3 lightPos;
uniform vec3 lightColor;

void main()
{
   vec3 ambient = lightColor * 0.15;
   vec3 norm = normalize(normal);
   vec3 lightDir = normalize(lightPos - FragPos);
   vec3 diffuse = vec3(texture(ourTexture, texcoord));
   float cosine = max(dot(norm, lightDir), 0.0);
   FragColor = vec4(ambient + lightColor * diffuse * cosine, 1.0);
}

顶点着色器需要把顶点所处的世界坐标传给片元,这样管线插值后片元可以得到自己所在的世界坐标;片元着色器就分别计算纹理颜色、法线和光源方向点乘得到的cosine项、光源强度,将三者乘在一起便是lambert模型了。但是对于接收不到光照的地方是全黑,显然也不是很合适,因此可以再额外加一个恒定值ambient(环境光),让全黑的地方也能有一点亮度。

这样看起来没什么问题。但是如果我们给模型旋转90°,就会发现猫腻: