OpenGL学习笔记(五)-投光物-多光源

参考网址:LearnOpenGL 中文版
哔哩哔哩教程

2.5 投光物

2.5.1 平行光

1、当一个光源处于很远的地方时,来自光源的每条光线就会近似于互相平行,例如太阳。不论物体和/或者观察者的位置,看起来好像所有的光都来自于同一个方向。当假设光源处于无限远处的模型时,它就被称为定向光。
在这里插入图片描述
2、通过定义光线方向向量即可模拟一个定向光。

struct LightDirect {
    vec3 lightDir;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};

3、直接使用光的direction向量来计算lightDir向量。

vec3 ambient= lightDirect.ambient*texture(material.diffuse,TextCoords).rgb;

vec3 lightDir=normalize(lightDirect.lightDir);//直接获取光照方向
vec3 norm=normalize(Normal);
float diff=max(dot(norm,lightDir),0.0);	
vec3 diffuse= lightDirect.diffuse*diff*texture(material.diffuse,TextCoords).rgb;

vec3 viewDir=normalize(viewPos-Frapos);
vec3 reflectDir=reflect(-lightDir,norm);
float spec=pow(max(dot(viewDir,reflectDir),0.0),material.shiness);
vec3 specular= lightDirect.specular*spec*texture(material.specular,TextCoords).rgb;

FragColor=vec4(ambient+diffuse+specular,1.0);	

4、定义平行光类LightDirectional

//构造函数
LightDirectional::LightDirectional(Shader * _shader, glm::vec3 _angles,  glm::vec3 _ambient, glm::vec3 _diffuse, glm::vec3 _specular):
	shader(_shader),angles(_angles), ambient(_ambient),
	diffuse(_diffuse), specular(_specular)
{
	UpdateDirection();
}

//通过欧拉角计算光源方向
void LightDirectional::UpdateDirection()
{
	direction = glm::vec3(0, 0, 1.0f);
	direction = glm::rotateZ(direction, angles.z);
	direction = glm::rotateY(direction, angles.y);
	direction = glm::rotateX(direction, angles.x);
	direction = -1.0f*direction;//方向应该为目标物指向光源,所以要取反

}

5、创建平行光对象

//创建对象
LightDirectional *myLightDirect=new LightDirectional(myShader,glm::vec3(glm::radians(75.0f),0,0), 
glm::vec3(0.5f, 0.5f, 0.5f), glm::vec3(0.5f, 0.5f, 0.5f), glm::vec3(1.0f, 1.0f, 1.0f));

//对uniform变量进行赋值
myLightDirect->shader->setVec3f("lightDirect.lightDir", myLightDirect->direction);
myLightDirect->shader->setVec3f("lightDirect.ambient", myLightDirect->ambient);
myLightDirect->shader->setVec3f("lightDirect.diffuse", myLightDirect->diffuse);
myLightDirect->shader->setVec3f("lightDirect.specular", myLightDirect->specular);

2.5.2 点光源

1、点光源是处于世界中某一个位置的光源,它会朝着所有方向发光,但光线会随着距离逐渐衰减。想象作为投光物的灯泡和火把,它们都是点光源。
在这里插入图片描述
2、衰减:在现实世界中,灯在近处会非常亮,随着距离的增加,光源的亮度一开始会下降非常快,到远处亮度会下降的非常缓慢了。所以,需根据片段距光源的距离计算了衰减值,再将衰减值乘以光的强度向量。
在这里插入图片描述
该衰减函数可以通过以下函数来表达:
在这里插入图片描述
3、点光源的结构体,有光源位置,增加衰减函数参数。

struct LightPoint {
	vec3 lightPos;

	vec3 ambient;
	vec3 diffuse;
	vec3 specular;

	float constant;
	float linear;
	float quadratic;
};

4、光照强度表示,将衰减值包含到光照计算中,将它分别乘以漫反射和镜面光颜色。如果我们使用多于一个的光源,所有的环境光分量将会开始叠加,在这种情况下也可以衰减环境光照。

//漫反射光照
vec3 lightDir = normalize(lightPoint.lightPos-Frapos);
vec3 norm = normalize(Normal);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = lightPoint.diffuse*diff*texture(material.diffuse, TextCoords).rgb;
//衰减
float dist = length(lightPoint.lightPos - Frapos);//光源衰减,离光源越远,光照越弱
float attenuation = 1.0 / (lightPoint.constant + lightPoint.linear * dist + lightPoint.quadratic * (dist * dist));

FragColor = vec4((ambient + (diffuse + specular)*attenuation), 1.0);

5、定义点光源类,通常衰减参数设为常量。

constant=1.0f;
linear=0.09f;
quadratic=0.032f;

2.5.3 聚光

1、聚光是位于环境中某个位置的光源,它只朝一个特定方向照射光线,只有在聚光方向的特定半径内的物体才会被照亮,如路灯或手电筒。
在这里插入图片描述
2、聚光是用一个世界空间位置、一个方向和一个切光角来表示的,切光角指定了聚光的半径。对于每个片段,我们会计算片段是否位于聚光的切光方向之间,如果是的话,我们就会相应地照亮片段。

  • lightDir:从片段指向光源的向量。
  • SpotDir:聚光所指向的方向。
  • Phiϕ:指定了聚光半径的切光角。落在这个角度之外的物体都不会被这个聚光所照亮。
  • Thetaθ:LightDir向量和SpotDir向量之间的夹角。在聚光内部的话θ值应该比ϕ值小,而cosθ值应该比cosϕ值大。

3、聚光的结构体,包括光源的位置,光源方向,光照分量,切光角的余弦值

struct LightSpot {
	vec3 lightPos;
	vec3 spotDir;

	vec3 ambient;
	vec3 diffuse;
	vec3 specular;
	float cosPhi;
};

4、切光角Phi将余弦结果传递到片段着色器中,是因为片段着色器中LightDirSpotDir向量的点积,返回的是Theta角余弦值。因此,通过比较cosPhi与cosTheta,可决定片段是否在聚光的内部。

vec3 lightDir = normalize(lightSpot.lightPos- Frapos);

float cosTheta = dot(lightDir, lightSpot.spotDir);//(片段与光源向量,光源指向)
float spotRatio;
if (cosTheta>lightSpot.cosPhiInner)
{
	//inner
	spotRatio = 1.0f;
}
else
{
	spotRatio = 0;
}

FragColor = vec4((ambient + (diffuse + specular)*spotRatio), 1.0);

在这里插入图片描述

聚光仅会照亮聚光圆锥内的片段,当一个片段遇到聚光圆锥的边缘时,它会完全变暗,没有一点平滑的过渡。一个真实的聚光将会在边缘处逐渐减少亮度。
在这里插入图片描述
5、为了平滑边缘,要改变点光源的分量值,模拟聚光有一个内圆锥(Inner Cone)和一个外圆锥(Outer Cone)。让光从内圆锥逐渐减暗,直到外圆锥的边界。

在这里插入图片描述

float cosTheta = dot(lightDir, normalize(lightSpot.spotDir));//(片段与光源向量,光源指向)
float spotRatio;
if (cosTheta>lightSpot.cosPhiInner)
{
//inner
spotRatio = 1.0f;
}
else if (cosTheta > lightSpot.cosPhiOutter)
{
//middle
spotRatio = (lightSpot.cosPhiOutter - cosTheta) / (lightSpot.cosPhiOutter - lightSpot.cosPhiInner);
}

else
{
//outter
spotRatio = 0;
}

FragColor = vec4((ambient + (diffuse + specular)*spotRatio), 1.0);

6、将相机(视角)的位置和方向,作为光源的位置和方向,即可形成类似于手电筒的光源效果。

myLightSpot->shader->setVec3f("lightSpot.spotDir", -camera.Front);
myLightSpot->shader->setVec3f("lightSpot.lightPos", camera.Position);

在这里插入图片描述

2.6 多光源

为了在场景中使用多个光源,将光照计算封装到GLSL函数中,对每个光照类型都创建一个不同的函数。在场景中使用多个光源时,对于每一个光源,它对片段的贡献颜色将会加到片段的输出颜色向量上,结合为一个最终的输出颜色。

out vec4 FragColor;

void main()
{
  // 定义一个输出颜色值
  vec3 output;
  // 将定向光的贡献加到输出中
  output += someFunctionToCalculateDirectionalLight();
  // 对所有的点光源也做相同的事情
  for(int i = 0; i < nr_of_point_lights; i++)
    output += someFunctionToCalculatePointLight();
  // 也加上其它的光源(比如聚光)
  output += someFunctionToCalculateSpotLight();

  FragColor = vec4(output, 1.0);
}

1、定向光

vec3 CalclightDirectionnal(LightDirect lightDirect,vec3 norm, vec3 viewDir)
{
	vec3 result;

	vec3 ambient= lightDirect.ambient*texture(material.diffuse,TextCoords).rgb;
	
	vec3 lightDir=normalize(lightDirect.lightDir);//直接使用光源方向
	float diff=max(dot(norm,lightDir),0.0);	
	vec3 diffuse= lightDirect.diffuse*diff*texture(material.diffuse,TextCoords).rgb;
	
	vec3 reflectDir=reflect(-lightDir,norm);
	float spec=pow(max(dot(viewDir,reflectDir),0.0),material.shiness);
	vec3 specular= lightDirect.specular*spec*texture(material.specular,TextCoords).rgb;
	
	result = ambient + diffuse + specular;

	return result;
}

2、点光源

vec3 CalclightPoint(LightPoint lightPoint, vec3 norm, vec3 viewDir)
{
	vec3 result;

	vec3 ambient = lightPoint.ambient*texture(material.diffuse, TextCoords).rgb;

	vec3 lightDir = normalize(lightPoint.lightPos-Frapos);
	float diff = max(dot(norm, lightDir), 0.0);
	vec3 diffuse = lightPoint.diffuse*diff*texture(material.diffuse, TextCoords).rgb;

	vec3 reflectDir = reflect(-lightDir, norm);
	float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shiness);
	vec3 specular = lightPoint.specular*spec*texture(material.specular, TextCoords).rgb;

	float dist = length(lightPoint.lightPos - Frapos);//光源衰减,离光源越远,光照越弱
	float attenuation = 1.0 / (lightPoint.constant + lightPoint.linear * dist + lightPoint.quadratic * (dist * dist));

	result = ambient + (diffuse + specular)*attenuation;

	return result;
}

3、聚光

vec3 CalclightSpot(LightSpot lightSpot, vec3 norm, vec3 viewDir)
{
	vec3 result;

	vec3 ambient = lightSpot.ambient*texture(material.diffuse, TextCoords).rgb;

	vec3 lightDir = normalize(lightSpot.lightPos - Frapos);//片段与光源向量
	float diff = max(dot(norm, lightDir), 0.0);
	vec3 diffuse = lightSpot.diffuse*diff*texture(material.diffuse, TextCoords).rgb;

	vec3 reflectDir = reflect(-lightDir, norm);
	float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shiness);
	vec3 specular = lightSpot.specular*spec*texture(material.specular, TextCoords).rgb;

	float cosTheta = dot(lightDir, normalize(lightSpot.spotDir));//(片段与光源向量,光源指向)
	float spotRatio;
	if (cosTheta>lightSpot.cosPhiInner)
	{
		//inner
		spotRatio = 1.0f;
	}
	else if (cosTheta > lightSpot.cosPhiOutter)
	{
		//middle
		spotRatio = (lightSpot.cosPhiOutter - cosTheta) / (lightSpot.cosPhiOutter - lightSpot.cosPhiInner);
	}
	else
	{
		//outter
		spotRatio = 0;
	}

	float dist = length(lightSpot.lightPos - Frapos);//光源衰减,离光源越远,光照越弱
	float attenuation = 1.0 / (lightSpot.constant + lightSpot.linear * dist + lightSpot.quadratic * (dist * dist));


	result = ambient + (diffuse + specular)*attenuation*spotRatio;

	return result;
}

4、主函数

void main() 										
{		
		vec3 norm = normalize(Normal);
		vec3 viewDir = normalize(viewPos - Frapos);
		
		vec3 finalResult = vec3(0, 0, 0);
		finalResult += CalclightDirectionnal(lightDirect, norm, viewDir);
		finalResult += CalclightPoint(lightPoint, norm, viewDir);
		finalResult += CalclightSpot(lightSpot, norm, viewDir);

		FragColor = vec4(finalResult, 1.0f);

}

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值