在Qt中使用OpenGL(七)

本文介绍了在Qt中使用OpenGL实现3D光照效果的过程,包括环境光、漫反射和镜面反射的概念。通过添加法线、材质和Shader,模拟了光照对3D模型的影响,展示了如何初始化带法线的顶点缓存,并提供了3D窗口类的更新方法。
摘要由CSDN通过智能技术生成

前言

在Qt中使用OpenGL(一)
在Qt中使用OpenGL(二)
在Qt中使用OpenGL(三)
在Qt中使用OpenGL(四)
在Qt中使用OpenGL(五)
在Qt中使用OpenGL(六)
在前面的文章中,我们实现了对模型的封装。目前我们对于简单的3D世界的构建已经快要接近完成了。
接下来,我们需要的实现的,就是3D世界中很重要的一个元素:光照。

现实中的光照

很显然,在OpenGL的3D世界中,你看到的绝大部分看似符合现实世界中的样子,都是通过数学手段模拟出来的。
光照当然也不例外。
让我们思考一个问题,在阴天你根本看不到太阳的时候,事实上你依旧可以看清楚周围的事物,无论你从任何角度观察一个物体,大概率你看到的这个物体的亮度在各个方向上看起来都是一样。
可是当晚上你在屋里开了灯,你应该能够注意到一个物体的各个表面的亮度并不是一致的——面向灯的那一面比较亮,背向灯的那一面比较暗。而且面向灯的那一面,你还能看到一些闪亮亮的高光的部分。
我们接下来要做的事情,就是将你在现实中看到的情况给模拟出来。

环境光,漫反射与镜面反射

首先,在阴天,虽然没有太阳的直射,你也可以清晰的看到周围的事物,是因为环境光的缘故。
也就是你所处的位置,周围的环境的亮度。
它的成因在于绝大部分的物体都可以反射与透射太阳光,于是虽然没有太阳光的直射,在各种各样的物体上来回反射的太阳光造就了你的周围环境的亮度。
对比一下,哪怕在屋子里你拉上了窗帘,实际上你依旧可以看到屋子里的物品,只不过变暗了而已。这就是光无孔不入的反射造成的。
为了模拟这个现象,我们需要有一个叫做环境光的概念,它实际上就表示了在没有光线直射时,物体受到的光照
那么我们很容易的就可以推测出来,接下来我们就需要一个在有光线直射时,物体受到的光照的概念了吧。对,它叫做漫反射
你看到的物体的颜色,绝大部分都是因为它可以反射一部分的光造成的。
例如,光线照射到了物体上,但是物体只会反射绿色,那么你看到的物体就是绿色的。
没错,你周围的所有绿色植物,是因为它们不喜欢绿色的光,将绿色反射回去了,你看到的绿色植物才是绿色的。
什么是镜面反射?顾名思义,将所有的光给反射回去的,就是镜面反射。有时,你会在物体上看到亮闪闪的部分,那就是镜面。比如光滑的地面,虽然不是镜子,但是有时是可以看到反射过来的光源的。这一部分的反射,就被称为镜面反射
有了这三个概念,我们就可以将这三个概念赋予给模型了。
没错,我们依旧需要使用模型中的逻辑来处理光照,虽然光照是一个全局性的概念,但是我们只能逐个模型的进行处理。
一般的,我们将这三个概念,统称材质

法线 与 材质

再继续之前,我们还需要了解另一个概念,它和漫反射还有镜面反射息息相关。
让我们思考一个简单的问题,我们怎么知道,物体的一个面,是朝向光源的?
现实中我们可以很容的通过物体和光源的位置来观察得到,但是具体到了OpenGL的3D世界中,我们怎么就能说一个面就是朝向光源而不是背向光源的呢?
索性我们不需要进行过多的思考,因为在3D世界中,已经存在着这么一个概念了,那就是法线
什么是法线?数学概念中,垂直于一个平面的向量就被称为这个平面的法线。有了法线,我们就可以计算这个面到底是朝向光源还是背向光源了。
简单来讲,就是计算一下法线向量和从平面上一个点到光源方向向量的点乘,也就是cos值即可。‘
众所周知,cos值在角度[-90°, +90°]范围内的时候是正值,并且越接近±90°值越接近0。让我们想象一下,一个平面的法线,就是你站在这个平面上向上方看,平面上到光源的向量,就是你站在这个平面上向光源看。如果你需要低下头才能看到光源,就证明这个光源和你的头顶的夹角大于90°了,同时这个光源已经在这个平面的下方了,对吧。
于是,只要cos值大于0,就证明这个面被光照到了。

有了以上的知识,我们就可以开始改造之前的模型类了。
首先,我们需要在顶点信息中增加法线的概念,以便可以用来计算光是否照到了这个顶点所在的平面。
其次,我们需要材质这个概念,来模拟环境光漫反射镜面反射
所以,我们就可以这么定义顶点与材质:

struct Vertex
{
   
	QVector3D pos;
	QVector2D texture;
	QVector3D normal;
};

struct Material
{
   
	float ambient = 1;
	float diffuse = 0;
	float specular = 0;
	float shininess = 16;
};

normal就是法线。
ambient 指的是环境光,范围[0,1]。事实上,之前我们的所有模型就是按照100%的环境光来渲染的。也就是哪怕没有任何直射的光线,你也可以以100%亮度看到这个物体。如果你完全不关心光照,那么就可以将这个值设置为1即可(但你依旧会受到光源颜色的影响)。
diffuse是指漫反射,范围[0,1]。一般情况下,环境光和漫反射加起来要达到1,但是达不到也不要紧。
因为,环境光和漫反射共同决定了你看到的物体的颜色是什么(对应到3D中,实际上就指是你能以多高的亮度看到物体的贴图),如果最终的值不到1,那么只不过是一个看起来比较暗的物体罢了,可以用来模拟晚上或者昏暗的地下室+昏暗的灯光。
specular是指镜面反射,范围[0,1]。值越大,越闪亮。
shininess是指最终计算出来的镜面反射的效果。值越大,光斑越明显。

然后,我们仿照纹理,也增加添加材质的接口(因为一个模型既然可以有多个纹理,那有多个材质也无可厚非):

public:
	void setMaterial(const Material &material, int index  = -1);
private:
	QMap<int, Material> m_materials;
void Model::setTexture(QOpenGLTexture *texture, int index)
{
   
	if (index == -1)
	{
   
		if (m_textures.isEmpty())
		{
   
			index = 0;
		}
		else
		{
   
			index = m_textures.keys().last() + 1;
		}
	}
	m_textures.insert(index, texture);
}

设置法线和材质

我们依旧使用之前的色子模型来举例,由于我们修改了基类,于是最新的包含了法线初始化顶点数据的代码如下:

setVertices({
   
	//   顶点           纹理	          法线
	// 1
		{
   {
   -1,  1,  1,}, {
   0.50, 0.25}, {
   0, 0, 1}},	// 左上
		{
   {
   -1, -1,  1,}, {
   0.50, 0.50}, {
   0, 0, 1}},	// 左下
		{
   {
    1, -1,  1,}, {
   0.75, 0.50}, {
   0, 0, 1}},	// 右下
		{
   {
    1,  1,  1,}, {
   0.75, 0.25}, {
   0, 0, 1}},	// 右上
	// 6							
		{
   {
    1,  1, -1,}, {
   0.00, 0.25}, {
   0, 0, -1}},	// 左上
		{
   {
    1, -1, -1,}, {
   0.00, 0.50}, {
   0, 0, -1}},	// 左下
		{
   {
   -1, -1,
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值