## 23.2 漫反射光照模型 在只有环境光下,模型看起来很假。 设计师设计的效果是这样的: 这是因为人眼是靠阴影,在大脑中创建3D物体的。 一个物体,如果所有的地方,颜色相同,那人类不会认为这是一个三维物体。 真实世界,是因为光子被上层反弹走了,没有击中更下层的地方,导致下层没有反射光子到人眼。 计算机没有那么大的算力去呈现真实光子物理现象。 前人就想出了一些简单的数学公式来简单模拟,这些数学公式,我们称之为光照模型。 法线 在学习光照模型之前,先来了解法线。 法线(normal line),是指始终垂直于某平面的直线。 那么我们平时说的顶点法线,这话看起来是错误的,因为法线是属于 。 在图形学里, 是一个实际存在的东西, 却是一个逻辑上的东西。
CLion项目文件位于 samples\classic_lighting\diffuse
在只有环境光下,模型看起来很假。

设计师设计的效果是这样的:
这是因为人眼是靠阴影,在大脑中创建3D物体的。
一个物体,如果所有的地方,颜色相同,那人类不会认为这是一个三维物体。
真实世界,是因为光子被上层反弹走了,没有击中更下层的地方,导致下层没有反射光子到人眼。
计算机没有那么大的算力去呈现真实光子物理现象。
前人就想出了一些简单的数学公式来简单模拟,这些数学公式,我们称之为光照模型。
在学习光照模型之前,先来了解法线。
法线(normal line),是指始终垂直于某平面的直线。

那么我们平时说的顶点法线,这话看起来是错误的,因为法线是属于面。
在图形学里,点是一个实际存在的东西,面 却是一个逻辑上的东西。
一个模型要渲染出来,是先将顶点数据交由顶点处理器处理,组成了逻辑上的三角面。
即渲染的输入是顶点,而不是三角面。
那我要设置三角面的法线,就只能往顶点里塞数据,将法线数据作为顶点属性传入了。
这就是为什么我们平时叫顶点法线的原因。
在 Vertex 结构体中新增法线数据 normal_ 。
//file:source\renderer\mesh_filter.h line:22 class MeshFilter:public Component{ public: MeshFilter(); ~MeshFilter(); public: //顶点 struct Vertex{ glm::vec3 position_; glm::vec4 color_; glm::vec2 uv_; glm::vec3 normal_; }; ......
法线数据上传后,从顶点着色器,传递到片段着色器。
//file:data\shader\diffuse_light.vert #version 330 core uniform mat4 u_model; uniform mat4 u_view; uniform mat4 u_projection; layout(location = 0) in vec3 a_pos; layout(location = 1) in vec4 a_color; layout(location = 2) in vec2 a_uv; layout(location = 3) in vec3 a_normal;//法线数据 out vec4 v_color; out vec2 v_uv; out vec3 v_normal; out vec3 v_frag_pos; void main() { gl_Position = u_projection * u_view * u_model * vec4(a_pos, 1.0); v_color = a_color; v_uv = a_uv; v_normal = a_normal;//传递给片段着色器。 v_frag_pos = vec3(u_model * vec4(a_pos, 1.0));//计算片段位置,就是顶点世界坐标。 }
然后在片段着色器,参与计算。
将从光源点 到 片段 的向量看作光的方向。
计算 光的方向 与 法线的角度Cos值,作为片段受光照影响的强弱。
当光源直射时,光方向 与 法线角度为0,Cos值为1,此时受光最强。
当片段位于背面或侧面,光方向 与 法线角度为 -1 或0,此时不受光。

这就是漫反射光照模型。
上述计算过程,在片段着色器中,代码如下:
//file:data/shader/diffuse_light.frag #version 330 core uniform sampler2D u_diffuse_texture;//颜色纹理 uniform vec3 u_ambient_light_color;//环境光 uniform float u_ambient_light_intensity;//环境光强度 uniform vec3 u_light_pos;//光源位置 uniform vec3 u_light_color;//光颜色 uniform float u_light_intensity;//光强度 in vec4 v_color;//顶点色 in vec2 v_uv; in vec3 v_normal; in vec3 v_frag_pos; layout(location = 0) out vec4 o_fragColor; void main() { //ambient vec3 ambient_color = u_ambient_light_color * u_ambient_light_intensity * texture(u_diffuse_texture,v_uv).rgb; //diffuse vec3 normal=normalize(v_normal);//法线 vec3 light_dir=normalize(u_light_pos - v_frag_pos);//灯光方向 float diffuse_intensity = max(dot(normal,light_dir),0.0);//计算cos角度值,作为受光强度。 vec3 diffuse_color = u_light_color * diffuse_intensity * u_light_intensity * texture(u_diffuse_texture,v_uv).rgb;//计算漫反射颜色 o_fragColor = vec4(ambient_color + diffuse_color,1.0);//最终输出颜色=环境光照颜色+漫反射光照颜色 }
首先是灯光的参数:光源位置、光颜色、光强度:
uniform vec3 u_light_pos;//光源位置 uniform vec3 u_light_color;//光颜色 uniform float u_light_intensity;//光强度
这几个数据在逻辑代码中上传:
--file:samples\classic_lighting\diffuse\example\login_scene.lua line:97 function LoginScene:Update() ...... --设置灯光位置、颜色、强度 self.material_:SetUniform3f("u_light_pos",glm.vec3(0,0,20)) self.material_:SetUniform3f("u_light_color",glm.vec3(1.0,1.0,1.0)) self.material_:SetUniform1f("u_light_intensity",1.0) ...... end
然后计算 光的方向 与 法线的角度Cos值,作为片段受光照影响的强弱。
//file:data/shader/diffuse_light.frag line:21 void main() { ...... //diffuse vec3 normal=normalize(v_normal);//法线 vec3 light_dir=normalize(u_light_pos - v_frag_pos);//灯光方向 float diffuse_intensity = max(dot(normal,light_dir),0.0);//计算cos角度值,作为受光强度。 vec3 diffuse_color = u_light_color * diffuse_intensity * u_light_intensity * texture(u_diffuse_texture,v_uv).rgb;//计算漫反射颜色 o_fragColor = vec4(ambient_color + diffuse_color,1.0);//最终输出颜色=环境光照颜色+漫反射光照颜色 }
将环境光照颜色与漫反射光照颜色叠加,作为最终输出颜色。

