【OpenGL】复杂光照理论与实践
目录
一、高光蒙版(Specular Mask)
1.1问题导入:
1.2蒙版(Mask)
1.3加入高光蒙版
1.4效果对比
二、点光源(Point Light)
2.1概念
2.2特点
2.3光能衰减(Lighting Attenuation)
2.4代码实现
2.4.1实体的实现
2.4.2功能的实现
三、探照灯/聚光灯(SpotLight)
3.1概念
3.2代码实现
3.3边缘模糊
四、着色器(Shader)的封装
前言:刚学OpenGL,主要想要记录下学到的东西,也当作一个笔记,部分理解可能有偏差,也是不全面的,如果有发现问题的话也很高兴大家可以指正,我会尽快修改的,在后续学习过程中,也会进行相应的补充与修改。(除了运行结果的截图,课件截图一般均来源于bilibili赵新政老师[后面有水印的])
(OpenGL老师:赵新政的个人空间-赵新政个人主页-哔哩哔哩视频)
一、高光蒙版(Specular Mask)
1.1问题导入:
考虑去绘制一个图形,其一部分显示蓝色,而另外一部分则显示红色,且红蓝交错,该如何去实现呢?
这其实就用到了纹理混合,提供如下的一张图片,使用采样器获取其颜色数据,取其颜色R分量为权值,最终像素点的颜色就是glm::vec4(R,0,1.0-R,1.0)。
1.2蒙版(Mask)
蒙版本质上是一张图片使用本身固有像素来决定渲染的时候如何进行颜色混合。
而接下来我们有一个很常见的需求,那就是我们希望物体上一部分区域能够显示高光(specular),而另一部分不能显示高光。就比如下图所示的箱子,按照常理来说,其金属边缘应该可以产生高光,而其木制区域不能产生高光。这就是接下来所需要探讨的高光贴图了。
实际上我们需要两张贴图,一张diffuse贴图,用于表示物体本身的颜色;一种specular贴图,作为蒙版表示哪里有高亮。
1.3加入高光蒙版
(1)材质类(material)中,加入高光蒙版的Texture对象,并且将纹理单元设置为1
这里物体本身的图片资源是绑定在了0号纹理单元上,所以就将高光蒙版绑定在了1号纹理单元上。
class PhongMaterial :public Material
{
public:PhongMaterial();~PhongMaterial();public:Texture* m_diffuse{ nullptr };Texture*m_specular_mask{ nullptr };float m_shininess{ 1.0f };};
(2)在FragmentShader当中,增加specular_mask_sampler采样器,用作采样高光蒙版
uniform sampler2D specular_mask_sampler;//高光蒙版贴图采样器
(3)在render函数中,每帧更新specualr_mask_sampler,传入采样单元为1
//高光蒙版的帧更新shader->set_uniform_1i("specular_mask_sampler", 1);phong_material->m_specular_mask->bind();
(4)FragmentShader中,根据uv对specular_mask贴图进行采样,获取其某一颜色通道,作为mask
float specular_mask=texture(specular_mask_sampler,uv).r;
(5)FragmentShader中,specular(高光系数)最终计算,需要乘以mask
vec3 specular_color=light_color*specular*flag*specular_intensity*specular_mask;
1.4效果对比
分别为没有使用高光蒙版的效果与使用之后的效果。
可以看出后者展示的效果更为逼真
二、点光源(Point Light)
2.1概念
点光源模拟了一个点作为光源,向四面八方发射光能的过程;比如灯泡
2.2特点
(1)点光源是从一个点发出的光线,所以光线方向屈居于:点光源位置+物体像素位置
(2)点光源拥有位置属性,所以光能会按照到光源的距离进行衰减
2.3光能衰减(Lighting Attenuation)
点光源的照射会形成一个球面,向外扩散。
而问题就是,如何去计算该光源对物体的影响。
我们知道,把光源看成一个点,随着其向外距离的扩大(也就是半径的增加),其所构成的球体面积也在扩大,而光源的能量固定,其往所有方向发散的能量相同,那么每个点上都会分布着相同的能量大小。因而,我们可以取考虑其当前构成球面的光能密度。
可以得到光能在球面上的分布密度为:α=power/S=power/(4Πd²)
(α:光能密度 power:光能 S:球面面积 d:物体与点光源的距离)
(可以看到随着距离d越来越大,光能密度以d的二次方衰减)
但是,光能是多少呢?
设置的点光源颜色(光能)为距离光源为1的地方的光能的大小
那么,当物体距离光源距离为d的时候,其光能为:color=lightColor/d²
不过,这样又会造成一个问题,那就是光能衰减如果直接采用距离二次方,则衰减速度过快,可调的参数太少。
因而在这里将会对衰减公式进行改进:color=lightColor/(Kc+K1*d+K2*d²)
可以看到,我们加入了常数项,一次项和二次项系数
最后,在我们设置参数的时候,往往是需要考虑当前的点光源能够照射到多远,该如何确定呢?
在这,Learn-OpenGL也提供了相应的表格数据:
2.4代码实现
2.4.1实体的实现
在实现点光源的具体功能之前,我们希望先去实现下方所示的一个纯白色的球,用来表示点光源所在的位置。
于是,就需要为其准备对应的材质white_material,并写好对应的着色器white_shader
对于这样的白球来说,其vertexShader中正常接收信息,不过在FragmentShader当中不需要进行任何计算,只需要输出纯白色即可。在主函数中,就只需要根据对应的几何外观geometry和材质类型material创建对应的mesh并固定好其世界坐标位置即可 。
(代码只是关键部分的实现,并非完整代码)
material.h
#pragma once
#include"material.h"
class WhiteMaterial :public Material {
public:WhiteMaterial();~WhiteMaterial();
};
white_shader.vert
#version 460 corelayout(location = 0) in vec3 aPos;
layout(location = 1) in vec2 aUV;
layout(location = 2) in vec3 aNormal;out vec2 uv;
out vec3 normal;uniform mat4 model_matrix;
uniform mat4 view_matrix;
uniform mat4 projection_matrix;
uniform mat3 normal_matrix;void main()
{vec4 transform_position=vec4(aPos,1.0);transform_position=model_matrix*transform_position;gl_Position=projection_matrix*view_matrix*transform_position;uv=aUV;normal=normal_matrix*aNormal;
}
white_shader.frag
#version 460 core
out vec4 FragColor;in vec2 uv;
in vec3 normal;void main()
{FragColor = vec4(1.0,1.0,1.0,1.0);
}
main.cpp
//创建白色球体(代表点光源)
auto geometry_sphere = Geometry::create_sphere(0.1f);
auto material_sphere = new WhiteMaterial();
mesh_sphere = new Mesh(geometry_sphere, material_sphere);
mesh_sphere->set_position(glm::vec3(2.0f, 0.0f, 0.0f));
mesh_list.push_back(mesh_sphere);
2.4.2功能的实现
首先需要创建一个点光源PointLight类继承自Object类和,我们知道其光能衰减当中需要使用到三个系数:k1,k2,kc,这些都需要作为其成员变量。
point_light.h
#pragma once#include"light.h"
#include"../object.h"class PointLight :public Light ,public Object
{
public:PointLight();~PointLight();public:float m_k1 = 1.0f;float m_k2 = 1.0f;float m_kc = 1.0f;
};
由于点光源和平行光的渲染逻辑不同,所以需要修改对应的着色器代码。
首先便是方向问题,点光源的方向是物体的世界坐标world_position-点光源的世界坐标light_position,同时需要传入光能衰减的参数,之后计算出attenuation并乘到对应的结果上即可。
phong.vert
#version 460 core
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec2 aUV;
layout(location = 2) in vec3 aNormal;out vec2 uv;
out vec3 normal;
out vec3 world_position;uniform mat4 model_matrix;
uniform mat4 view_matrix;
uniform mat4 projection_matrix;
uniform mat3 normal_matrix;void main()
{vec4 transform_position=vec4(aPos,1.0);transform_position=model_matrix*transform_position;world_position=transform_position.xyz;gl_Position=projection_matrix*view_matrix*transform_position;uv=aUV;normal=normal_matrix*aNormal;
}
phong.frag
#version 460 core
out vec4 FragColor;uniform sampler2D sampler;//diffuse贴图采样器
uniform sampler2D specular_mask_sampler;//高光蒙版贴图采样器
in vec2 uv;
in vec3 normal;
in vec3 world_position;
//光源参数
uniform vec3 light_position;
uniform vec3 light_color;
uniform float k1;
uniform float k2;
uniform float kc;uniform vec3 ambient_color;
uniform vec3 camera_position;
uniform float specular_intensity;uniform float shininess;void main()
{//1. 计算光照的通用数据(物体颜色,法线方向,光照方向)vec3 object_color=texture(sampler,uv).xyz;vec3 normalN=normalize(normal);vec3 lightDirN=normalize(world_position-light_position);vec3 view_direction=normalize(world_position-camera_position);//计算衰减值float dist=length(world_position-light_position);float attenuation=1.0/(kc+k1*dist+k2*dist*dist);//2 准备diffuse(漫反射)//为了解决背光的一面法线方向与光线反方向向量点乘之后为负数的问题,需要加上clampfloat diffuse=clamp(dot(-lightDirN,normalN),0.0f,1.0f);vec3 diffuse_color=light_color*diffuse*object_color;//3.计算specular(高光反射)//防止背面光的照入float dot_result=dot(-lightDirN,normalN);float flag=step(0.0,dot_result);vec3 light_reflect=normalize(reflect(lightDirN,normalN));float specular=max(dot(light_reflect,-view_direction),0.0);//控制光斑大小specular=pow(specular,shininess);float specular_mask=texture(specular_mask_sampler,uv).r;vec3 specular_color=light_color*specular*flag*specular_intensity*specular_mask;//为什么diffuse乘以object_color而specular不乘以object_color?//diffuse是光照射到物体表面,吸收一部分后剩下的反射给 viewer//而specular是直接反射,并不是吸收之后再反射给 viewer//(物理上并非正确的,只是从视觉上看是比较正确的)//环境光计算vec3 ambient_color=object_color*ambient_color;vec3 final_color=(diffuse_color+specular_color)*attenuation+ambient_color;FragColor = vec4(final_color,1.0);
}
然后在渲染器renderer中,就需要根据其材质类型选择对应的着色器,然后设置对应的uniform变量
//渲染功能函数,每次调用都会渲染一帧
void Renderer::render(std::vector<Mesh*>& mesh_list,Camera* camera,PointLight* light,/*这里没有考虑多个光的照射*/AmbientLight* ambient
)
{//1.设置当前帧绘制的时候,opengl的必要状态机参数glEnable(GL_DEPTH_TEST);glDepthFunc(GL_LESS);//2.清理画布glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//3.遍历mesh列表,对每个mesh进行绘制for (auto mesh : mesh_list){auto geometry = mesh->m_geometry;auto material = mesh->m_material;//1.决定使用哪一个shaderShader* shader = pick_shader(material->m_type);//2.更新shader的uniform变量//2.1.绑定programshader->on_begin();switch (material->m_type){case MaterialType::PhongMaterial:{PhongMaterial* phong_material = (PhongMaterial*)material;//diffuse贴图shader->set_uniform_1i("sampler", 0);//将纹理与纹理单元进行挂钩phong_material->m_diffuse->bind();//高光蒙版的帧更新shader->set_uniform_1i("specular_mask_sampler", 1);phong_material->m_specular_mask->bind();//mvp变换shader->set_uniform_4matrix("model_matrix", mesh->get_model_matrix());shader->set_uniform_4matrix("view_matrix", camera->get_view_matrix());shader->set_uniform_4matrix("projection_matrix", camera->get_projection_matrix());//光源参数更新shader->set_uniform_3vec("light_position", light->get_position());shader->set_uniform_3vec("light_color", light->m_color);shader->set_uniform_3vec("ambient_color", ambient->m_color);shader->set_uniform_1f("specular_intensity", light->m_specular_intensity);shader->set_uniform_1f("shininess", phong_material->m_shininess);shader->set_uniform_1f("k1", light->m_k1);shader->set_uniform_1f("k2", light->m_k2);shader->set_uniform_1f("kc", light->m_kc);//相机信息更新shader->set_uniform_3vec("camera_position", camera->m_position);//计算normal_matrixauto normal_matrix = glm::transpose(glm::inverse(glm::mat3(mesh->get_model_matrix())));shader->set_uniform_3matrix("normal_matrix", normal_matrix);break;}break;case MaterialType::WhiteMaterial:{//mvp变换shader->set_uniform_4matrix("model_matrix", mesh->get_model_matrix());shader->set_uniform_4matrix("view_matrix", camera->get_view_matrix());shader->set_uniform_4matrix("projection_matrix", camera->get_projection_matrix());}break;default:continue;//直接执行循环的下一个}//3.绑定vaoglBindVertexArray(mesh->m_geometry->get_vao());//4.执行绘制命令glDrawElements(GL_TRIANGLES, geometry->get_indices_count(), GL_UNSIGNED_INT, (void*)0);//5.解绑glBindVertexArray(0);shader->on_end();}}
在main函数中,进行好初始化,将其放到白球的圆心处
point_light =new PointLight();
point_light->m_color = glm::vec3(1.0f);
point_light->m_specular_intensity = 1.0f;
point_light->set_position(mesh_sphere->get_position());
point_light->m_k2 = 0.017f;
point_light->m_k1 = 0.07f;
point_light->m_kc = 1.0f;
效果展示:(白球放到需要的角度进行观察)
三、探照灯/聚光灯(SpotLight)
3.1概念
聚光灯(SpotLight)模拟了向一个方向发射光能的光源;类似手电筒,它在一个特定的角度内,可以保持对场景的照亮;在角度外,光能就会变为0。
因而,去实现聚光灯的效果最关键的就是计算光线照射位置和聚光灯的可视角,这里可视角和照射目标的方向是已经给出的,而光线照射方向依然是world_position-light_position。最后只要计算角度进行比较即可。
从图中可知:只有γ<=θ,也就是cosγ>=cosθ,才可以被照亮
计算可知:
cosγ=dot(lightDir,targetDirection);
又∵cosθ已知
可以得到flag=step(cosθ,cosγ)(如果cosγ<cosθ就会返回0,反之返回1)
最终的颜色finalColor=(diffuseColor+specular)*flag+ambientColor
3.2代码实现
其代码实现依然是添加对应的SpotLight类,编写好shader代码,写好对应的render函数修改uniform变量。这里的话只给出最关键的FragmentShader代码:
#version 460 core
out vec4 FragColor;uniform sampler2D sampler;//diffuse贴图采样器
uniform sampler2D specular_mask_sampler;//高光蒙版贴图采样器
in vec2 uv;
in vec3 normal;
in vec3 world_position;
//光源参数
uniform vec3 light_position;
uniform vec3 light_color;
uniform vec3 target_direction;
uniform float visible_angle;uniform vec3 ambient_color;
uniform vec3 camera_position;
uniform float specular_intensity;uniform float shininess;void main()
{//1. 计算光照的通用数据(物体颜色,法线方向,光照方向)vec3 object_color=texture(sampler,uv).xyz;vec3 normalN=normalize(normal);vec3 lightDirN=normalize(world_position-light_position);vec3 view_direction=normalize(world_position-camera_position);vec3 target_directionN=normalize(target_direction);float c_theta=dot(target_directionN,lightDirN);float c_alpha=cos(visible_angle);float spot_flag=step(c_alpha,c_theta);//2 准备diffuse(漫反射)//为了解决背光的一面法线方向与光线反方向向量点乘之后为负数的问题,需要加上clampfloat diffuse=clamp(dot(-lightDirN,normalN),0.0f,1.0f);vec3 diffuse_color=light_color*diffuse*object_color;//3.计算specular(高光反射)//防止背面光的照入float dot_result=dot(-lightDirN,normalN);float flag=step(0.0,dot_result);vec3 light_reflect=normalize(reflect(lightDirN,normalN));float specular=max(dot(light_reflect,-view_direction),0.0);//控制光斑大小specular=pow(specular,shininess);float specular_mask=texture(specular_mask_sampler,uv).r;vec3 specular_color=light_color*specular*flag*specular_intensity*specular_mask;//为什么diffuse乘以object_color而specular不乘以object_color?//diffuse是光照射到物体表面,吸收一部分后剩下的反射给 viewer//而specular是直接反射,并不是吸收之后再反射给 viewer//(物理上并非正确的,只是从视觉上看是比较正确的)//环境光计算vec3 ambient_color=object_color*ambient_color;vec3 final_color=(diffuse_color+specular_color)*spot_flag+ambient_color;FragColor = vec4(final_color,1.0);
}
最终效果:
3.3边缘模糊
从上面的运行结果可以感觉得到,这个探照灯的边缘太过分明了,给人一种不真实的感觉,这是因为使用了step函数,导致超出边缘区域的地方全都无法照亮,给人一种“非黑即白”的视觉效果。
而我们希望找到一种方法,能够让超出光照范围的区域呈现光能逐步递减的样子。让亮到不亮的区域能够有一定的过渡。
因而,在这里是在原本的可视角范围外增加了一个新的过渡区域。最终的光能会在增加的过渡区中递减。计算公式:intensity=clamp((cosγ-cosβ)/(cosθ-cosβ),0,1);
- 当γ<θ,(cosγ-cosβ)/(cosθ-cosβ)>1,intensity=1
- 当θ<γ <β,(cosγ-cosβ)/(cosθ-cosβ)<1,intensity∈(0,1)
- 当γ>β,(cosγ-cosβ)/(cosθ-cosβ)>1,intensity=0
在代码实现当中呢,也是直接带入公式计算即可(角的cos值相信大家肯定是会计算的了),这里需要注意的是内角θ(原来的可视角)和外角β(可照亮区域)的cos值对于每个点来说都是固定的 ,属于共有数据,可以在CPU端经过一次计算之后直接传入,不需要再GPU端进行多次的重复运算。
这里只提供最关键的FragmentShader代码:
#version 460 core
out vec4 FragColor;uniform sampler2D sampler;//diffuse贴图采样器
uniform sampler2D specular_mask_sampler;//高光蒙版贴图采样器
in vec2 uv;
in vec3 normal;
in vec3 world_position;
//光源参数
uniform vec3 light_position;
uniform vec3 light_color;
uniform vec3 target_direction;
//代表cos值,在CPU中计算,这里直接使用
uniform float inner_line;//cosθ
uniform float outer_line;//cosβuniform vec3 ambient_color;
uniform vec3 camera_position;
uniform float specular_intensity;uniform float shininess;void main()
{//1. 计算光照的通用数据(物体颜色,法线方向,光照方向)vec3 object_color=texture(sampler,uv).xyz;vec3 normalN=normalize(normal);vec3 lightDirN=normalize(world_position-light_position);vec3 view_direction=normalize(world_position-camera_position);vec3 target_directionN=normalize(target_direction);float c_gamma=dot(lightDirN,target_directionN);float spot_intensity=clamp((c_gamma-outer_line)/(inner_line-outer_line),0,1);//2 准备diffuse(漫反射)//为了解决背光的一面法线方向与光线反方向向量点乘之后为负数的问题,需要加上clampfloat diffuse=clamp(dot(-lightDirN,normalN),0.0f,1.0f);vec3 diffuse_color=light_color*diffuse*object_color;//3.计算specular(高光反射)//防止背面光的照入float dot_result=dot(-lightDirN,normalN);float flag=step(0.0,dot_result);vec3 light_reflect=normalize(reflect(lightDirN,normalN));float specular=max(dot(light_reflect,-view_direction),0.0);//控制光斑大小specular=pow(specular,shininess);float specular_mask=texture(specular_mask_sampler,uv).r;vec3 specular_color=light_color*specular*flag*specular_intensity*specular_mask;//为什么diffuse乘以object_color而specular不乘以object_color?//diffuse是光照射到物体表面,吸收一部分后剩下的反射给 viewer//而specular是直接反射,并不是吸收之后再反射给 viewer//(物理上并非正确的,只是从视觉上看是比较正确的)//环境光计算vec3 ambient_color=object_color*ambient_color;vec3 final_color=(diffuse_color+specular_color)*spot_intensity+ambient_color;FragColor = vec4(final_color,1.0);
}
运行效果:(在增加了15°的过渡角之后,就显得自然多了)
此外,将清屏颜色调整为黑色,是不是就有一种恐怖的氛围了呢
四、着色器(Shader)的封装
在前面的单独实现当中,均只使用了一个光源
而当使用多个光源的时候,似乎就有许多重复的代码,而且其代码结构也显得十分混乱。因而有必要对shader中的代码进行一下封装。
从功能上进行拆分,我们可以封装以下几个功能:
(1)计算漫反射的函数
(2)计算镜面反射的函数
(3)计算聚光灯的函数
(4)计算平行光的函数
(5)计算点光源的函数
并实现好三个结构体:
聚光灯SpotLight、平行光DirectLight、点光源PointLight
在OpenGL中,glsl语言也可以使用结构体并对函数进行封装,其语法与C语言相同
于是代码可进行如下更改:(之后在其他文件去更改对应的render部分即可)
这里给出最关键的FragmentShader的代码:
#version 460 core
out vec4 FragColor;uniform sampler2D sampler;//diffuse贴图采样器
uniform sampler2D specular_mask_sampler;//高光蒙版贴图采样器
in vec2 uv;
in vec3 normal;
in vec3 world_position;uniform vec3 ambient_color;
uniform vec3 camera_position;uniform float shininess;struct DirectionLight
{vec3 direction;vec3 color;float specular_intensity;
};struct PointLight
{vec3 position;vec3 color;float specular_intensity;float k1;float k2;float kc;
};struct SpotLight
{vec3 position;vec3 color;vec3 target_direction;float inner_line;float outer_line;float specular_intensity;
};uniform SpotLight spot_light;
uniform DirectionLight directional_light;
uniform PointLight point_light;
//计算漫反射光照
vec3 calc_diffuse(vec3 light_color,vec3 object_color,vec3 light_direction,vec3 normal)
{//2 准备diffuse(漫反射)//为了解决背光的一面法线方向与光线反方向向量点乘之后为负数的问题,需要加上clampfloat diffuse=clamp(dot(-light_direction,normal),0.0f,1.0f);vec3 diffuse_color=light_color*diffuse*object_color;return diffuse_color;
}
//计算镜面反射
vec3 calc_specular(vec3 light_color,vec3 light_direction,vec3 normal,vec3 view_direction,float intensity)
{//1.计算specular(高光反射)//防止背面光的照入float dot_result=dot(-light_direction,normal);float flag=step(0.0,dot_result);//2.计算specularvec3 light_reflect=normalize(reflect(light_direction,normal));float specular=max(dot(light_reflect,-view_direction),0.0);//3.控制光斑大小specular=pow(specular,shininess);// float specular_mask=texture(specular_mask_sampler,uv).r;//4.计算最终颜色vec3 specular_color=light_color*specular*flag*intensity/**specular_mask*/;//为什么diffuse乘以object_color而specular不乘以object_color?//diffuse是光照射到物体表面,吸收一部分后剩下的反射给 viewer//而specular是直接反射,并不是吸收之后再反射给 viewer//(物理上并非正确的,只是从视觉上看是比较正确的)return specular_color;
}
//聚光灯
vec3 calc_spot_light(SpotLight light,vec3 normal,vec3 view_direction)
{//1. 计算光照的通用数据(物体颜色,法线方向,光照方向)vec3 object_color=texture(sampler,uv).xyz;vec3 lightDir=normalize(world_position-light.position);vec3 target_directionN=normalize(light.target_direction);//2.计算spot_light的照射范围float c_gamma=dot(lightDir,light.target_direction);float intensity=clamp((c_gamma-light.outer_line)/(light.inner_line-light.outer_line),0,1);//3.计算diffuse和specularvec3 diffuse_color=calc_diffuse(light.color,object_color,lightDir,normal);vec3 specular_color=calc_specular(light.color,lightDir,normal,view_direction,light.specular_intensity);return (diffuse_color+specular_color)*intensity;
}
//平行光
vec3 calc_direction_light(DirectionLight light,vec3 normal,vec3 view_direction)
{//1. 计算光照的通用数据(物体颜色,法线方向,光照方向)vec3 object_color=texture(sampler,uv).xyz;vec3 lightDir=normalize(light.direction);//2.计算diffuse和specularvec3 diffuse_color=calc_diffuse(light.color,object_color,lightDir,normal);vec3 specular_color=calc_specular(light.color,lightDir,normal,view_direction,light.specular_intensity);return (diffuse_color+specular_color);
}
//点光源
vec3 calc_point_light(PointLight light,vec3 normal,vec3 view_direction)
{ //1. 计算光照的通用数据(物体颜色,法线方向,光照方向)vec3 object_color=texture(sampler,uv).xyz;vec3 lightDir=normalize(world_position-light.position);//2.计算衰减值float dist=length(world_position-light.position);float attenuation=1.0/(light.kc+light.k1*dist+light.k2*dist*dist);//3.计算diffuse和specularvec3 diffuse_color=calc_diffuse(light.color,object_color,lightDir,normal);vec3 specular_color=calc_specular(light.color,lightDir,normal,view_direction,light.specular_intensity);return (diffuse_color+specular_color)*attenuation;
}void main()
{vec3 result=vec3(0.0,0.0,0.0);//1. 计算光照的通用数据(物体颜色,法线方向,光照方向)vec3 object_color=texture(sampler,uv).xyz;vec3 normalN=normalize(normal);vec3 lightDirN=normalize(world_position-spot_light.position);vec3 view_direction=normalize(world_position-camera_position);vec3 target_directionN=normalize(spot_light.target_direction);result+=calc_spot_light(spot_light,normalN,view_direction);result+=calc_direction_light(directional_light,normalN,view_direction);result+=calc_point_light(point_light,normalN,view_direction);//环境光计算vec3 ambient_color=object_color*ambient_color;vec3 final_color=result+ambient_color;FragColor = vec4(final_color,1.0);
}
当然,在后面的使用学习当中,也可能会用到好几个光源,a个平行光,b个聚光灯,c个点光源这样,我们也可以去使用光源数组进行遍历。在glsl中,其遍历的语法与C语言是完全相同的。