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

2023-10-29

参考网址: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);

}

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

OpenGL学习笔记(五)-投光物-多光源 的相关文章

  • wglCreateContextAttribsARB 函数崩溃

    我尝试写下代码 我有InitializeOGL 的代码 bool Ogl InitializeOGL bool vSync cout lt lt Init OpenGL lt
  • 帧缓冲区和在 opengl 中使用着色器

    我对帧缓冲区有点困惑 我想要做的是使用附加了多个纹理的帧缓冲区 填充每个纹理 然后使用着色器组合 混合 所有纹理以创建新的输出 听起来很容易 是的 我也是这么想的 但我不明白 如何将当前绑定的纹理传递给着色器 您需要的是将纹理放入特定的槽中
  • 纹理映射 C++ OpenGL

    我已经阅读了相关内容 包括 Nehe 和此处的解决方案 但我找不到具体的答案 我正在尝试加载一张名为stars jpg 的照片 我想通过使用 uv 坐标映射它来使其成为场景的背景 方法是 glBegin GL QUADS glTexCoor
  • 在 Ubuntu 中与未编译的着色器链接

    我需要加载 glsl 来绘制一些东西 我的环境是Ubuntu 13 04 因此它不存在GLuint InitShader GLuint GLuint 这是我的对象创建 预链接步骤和链接的配置 不幸的是 它仍然出现错误 该错误与未编译的着色器
  • 帧缓冲区/颜色缓冲区?

    有人可以指出我两者是否相同吗 我的意思是我一直在阅读有关它的信息 这里的红皮书说 颜色缓冲区本身可以由多个子缓冲区组成 系统上的帧缓冲区包含所有这些缓冲区 here http glprogramming com red chapter10
  • OpenGL:顶点越多,性能越慢

    我正在开发一个程序的一部分 其中给定 xyz 坐标集合 制作 3D 模型 我已经完成了这张图片所需的所有功能 即平移 旋转 缩放 但是给出的 xyz 坐标越多 程序运行速度就越慢 我的程序在处理 29 000 个坐标时运行得非常流畅 但当我
  • 为什么 OpenGL 有远裁剪平面,以及使用什么惯用法来处理这个问题?

    我一直在学习 OpenGL 持续困扰我的一个话题是远裁剪平面 虽然我可以理解近剪裁平面和侧剪裁平面 它们永远不会产生任何实际效果 因为它们之外的对象无论如何都不会被渲染 背后的推理 但远剪裁平面似乎只是一个烦恼 由于 OpenGL 背后的人
  • 为什么 glClear 不清除我的屏幕?

    这是我编写的一个简单的 opengl 程序 我试图在绘制三角形之前清除屏幕 我在 init 函数中调用了 glClear 但是 它似乎无法清除屏幕 include
  • MATLAB 图形渲染:OpenGL 与 Painters?

    当谈到使用哪个渲染器来处理 MATLAB 图形或何时它很重要时 我一无所知 但我遇到过某些示例 其中does matter plot 0 0 ko markersize 50 linewidth 8 set gcf renderer ope
  • GLSL 中统一浮点行为和常量浮点行为的不同

    我正在尝试在 GLSL 中实现模拟双精度 并且观察到一种奇怪的行为差异 导致 GLSL 中出现细微的浮点错误 考虑以下片段着色器 写入 4 浮点纹理以打印输出 layout location 0 out vec4 Output unifor
  • 使用 glGetFloatv 检索 pyglet 中的模型视图矩阵

    我正在使用 pyglet 在 python 中进行 3D 可视化 并且需要检索模型视图和投影矩阵来进行一些选择 我使用以下方式定义我的窗口 from pyglet gl import from pyglet window import wi
  • 移动/调整大小期间 opengl 窗口冻结

    我正在使用 LWJGL 开发游戏 移动窗口时 计划将来添加调整大小代码 渲染循环冻结 我希望它在移动时继续以某种方式运行 LWJGL 不包括 glutMainLoop Display属于OpenGL 而不是Java 相关代码 regular
  • Windows下使用GLEW使用OpenGL扩展

    我一直在 Windows 上使用 OpenGL 扩展痛苦的方式 https stackoverflow com questions 14413 using opengl extensions on windows GLEW 是更简单的方法吗
  • OpenGL:始终相同的颜色

    我正在 Windows 上编写一个程序 使用c opengl 2 1 and SDL我在顶点颜色方面遇到了一些问题 我在用着glColor3f设置每个顶点集的颜色 但它似乎不起作用 无论我选择什么颜色 我都会将每个顶点绘制为红色 我检查了传
  • Qt 5.5 QOpenGLWidget 链接错误未链接任何 openGL 调用

    我尝试使用 Qt 5 5 1 构建一个简单的 OpenGL 应用程序 一切都很好 直到我尝试使用 glClearColor 等 openGL 本机函数调用 该小部件实际上编译并产生黑屏 但在我尝试使用任何 openGL 本机函数后 它甚至不
  • OpenGL 与 OpenCL,选择哪个以及为什么?

    哪些功能使 OpenCL 能够独特地选择 OpenGL 和 GLSL 进行计算 尽管有与图形相关的术语和不实用的数据类型 OpenGL 是否有任何真正的警告 例如 可以通过使用其他纹理将 a 渲染到纹理来完成并行函数评估 减少操作可以通过迭
  • OpenGL 缓冲区、glFlush 和 glutSwapBuffers()

    使用之间有什么区别吗 glutInitDisplayMode GLUT SINGLE GLUT RGB with glFlush and glutInitDisplayMode GLUT DOUBLE GLUT RGB with glutS
  • 在 OpenGL 中设置 MVP 矩阵

    我正在尝试学习 OpenGL 的基础知识 但我在设置变换矩阵时遇到问题 我制作了模型 视图和投影矩阵 但将它们发送到我的顶点着色器时遇到问题 这是代码 Set up MVP glm mat4 model glm mat4 GLint uni
  • 如何连接重叠的圆圈?

    我想在视觉上连接两个重叠的圆圈 以便 becomes 我已经有部分圆的方法 但现在我需要知道每个圆的重叠角度有多大 但我不知道该怎么做 有人有主意吗 Phi ArcTan Sqrt 4 R 2 d 2 d HTH Edit 对于两个不同的半
  • 在OpenGL中,我可以在坐标(5, 5)处精确地绘制一个像素吗?

    我所说的 5 5 正是指第五行第五列 我发现使用屏幕坐标来绘制东西非常困难 OpenGL 中的所有坐标都是相对的 通常范围从 1 0 到 1 0 为什么阻止程序员使用屏幕坐标 窗口坐标如此严重 最简单的方法可能是通过以下方式设置投影以匹配渲

随机推荐