计算机图形学(5):OpenGL光照

news2025/1/16 3:56:20

参考

介绍
现实世界中的光照是极其复杂,难以计算的,因此OpenGL的光照使用的是简化的模型,其中一个模型被称为冯氏光照模型(Phong Lighting Model)。

冯氏光照模型的主要结构由三个分量组成:

环境(Ambient)光照
漫反射(Diffuse)光照
镜面(Specular)光照

环境光照

光的一大特点是,它可以向很多方向发散并反弹,所以现实环境周围通常总会有些光亮,对应的物体通常都会反射些微弱的光。
计算:

用一个很小的分量乘以光的颜色,最后乘以物体的颜色

漫反射光照

漫反射光照使物体与光线方向越接近的片段能充光源处获得更多的亮度。

image.png

如果光线垂直于物体表面,这束光对物体的影响会最大化(译注:更亮)。

可以大致理解为

片段的漫反射光照 = (光源坐标到片段坐标的向量与片段法向量的夹角) * 光源颜色

注意两个向量都要先归一化
为了计算漫反射光照我们需要知道这几个值:

片段所在面的法向量
光源的世界坐标
片段的世界坐标
最后目前片段的颜色为: (环境光照值+漫反射光照值) * 片段自身颜色

镜面光照

镜面光照决定于表面的反射特性。如果我们把物体表面设想为一面镜子,那么镜面光照最强的地方就是我们看到表面上反射光的地方。如下图:

image.png

为此我们还需要一个观察者坐标(即摄像机)。光的反射向量与观察方向之间夹角越小,镜面光照效果越强。

最终效果:
在这里插入图片描述
在这里插入图片描述
主要代码:

std::vector<float> sphereVertices;
	std::vector<int> sphereIndices;


	// 生成球的顶点
	for (int y = 0; y <= Y_SEGMENTS; y++)
	{
		for (int x = 0; x <= X_SEGMENTS; x++)
		{
			float xSegment = (float)x / (float)X_SEGMENTS;
			float ySegment = (float)y / (float)Y_SEGMENTS;
			float xPos = std::cos(xSegment * 2.0f * PI) * std::sin(ySegment * PI);
			float yPos = std::cos(ySegment * PI);
			float zPos = std::sin(xSegment * 2.0f * PI) * std::sin(ySegment * PI);


			sphereVertices.push_back(xPos);
			sphereVertices.push_back(yPos);
			sphereVertices.push_back(zPos);
		}
	}

	// 生成球的Indices
	for (int i = 0; i < Y_SEGMENTS; i++)
	{
		for (int j = 0; j < X_SEGMENTS; j++)
		{

			sphereIndices.push_back(i * (X_SEGMENTS + 1) + j);
			sphereIndices.push_back((i + 1) * (X_SEGMENTS + 1) + j);
			sphereIndices.push_back((i + 1) * (X_SEGMENTS + 1) + j + 1);

			sphereIndices.push_back(i * (X_SEGMENTS + 1) + j);
			sphereIndices.push_back((i + 1) * (X_SEGMENTS + 1) + j + 1);
			sphereIndices.push_back(i * (X_SEGMENTS + 1) + j + 1);
		}
	}

	float cube[] = {
		-0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
		 0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
		 0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
		 0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
		-0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
		-0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,

		-0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
		 0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
		 0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
		 0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
		-0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
		-0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,

		-0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
		-0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
		-0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
		-0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
		-0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
		-0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,

		 0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
		 0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
		 0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
		 0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
		 0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
		 0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,

		-0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
		 0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
		 0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
		 0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
		-0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
		-0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,

		-0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
		 0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
		 0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
		 0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
		-0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
		-0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f
	};
	//sphere---light
	glBindVertexArray(VAO[2]);
	glBindBuffer(GL_ARRAY_BUFFER, VBO[2]);
	glBufferData(GL_ARRAY_BUFFER, sphereVertices.size() * sizeof(float), &sphereVertices[0], GL_STATIC_DRAW);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); 
	glEnableVertexAttribArray(0);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO[2]);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sphereIndices.size() * sizeof(int), &sphereIndices[0], GL_STATIC_DRAW);

	//cube
	glBindVertexArray(VAO[3]);
	glBindBuffer(GL_ARRAY_BUFFER, VBO[3]);
	glBufferData(GL_ARRAY_BUFFER, sizeof(cube), cube, GL_STATIC_DRAW);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
	glEnableVertexAttribArray(1);

每次渲染时都绑定信息:

//让光源绕原点旋转
	float distance = 2.0f;
	float lightY = sin(glfwGetTime()) * distance;
	float lightZ = -(cos(glfwGetTime()) * distance);
	float lightX = sin(glfwGetTime()) * distance;
	//lightPos.x = lightX; lightPos.y = 1; lightPos.z = 1.2;						//直线移动
	lightPos.x = lightX; lightPos.y = lightY; lightPos.z = lightZ;			//环绕移动

	shader.use();
	model = glm::mat4(1.0f);
	shader.setMat4("model", model);
	shader.setMat4("projection", projection);
	shader.setMat4("view", view);
	shader.setVec3("objectColor", 1.0f, 0.5f, 0.31f);
	shader.setVec3("lightColor", 1.0f, 1.0f, 1.0f);
	shader.setVec3("lightPos", lightPos);
	shader.setVec3("viewPos", camera.position);
	glBindVertexArray(VAO[3]);
	glDrawArrays(GL_TRIANGLES, 0, 36);
	glBindVertexArray(0);


	light_shader.use();
	model = glm::mat4(1.0f);
	model = glm::translate(model, lightPos);
	model = glm::scale(model, glm::vec3(0.1f));
	light_shader.setMat4("model", model);
	light_shader.setMat4("projection", projection);
	light_shader.setMat4("view", view);
	glBindVertexArray(VAO[2]);
	glDrawElements(GL_TRIANGLES, X_SEGMENTS * Y_SEGMENTS * 6, GL_UNSIGNED_INT, 0);
	//glDrawArrays(GL_TRIANGLES, 0, 36);
	glBindVertexArray(0);

顶点着色器:

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;

out vec3 FragPos;
out vec3 Normal;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
    FragPos = vec3(model * vec4(aPos, 1.0));
    mat3 normalMatrix = mat3(transpose(inverse(model)));
	Normal = normalMatrix * aNormal; // 计算法向量经过模型变换后值

    
    gl_Position = projection * view * vec4(FragPos, 1.0);
}

片段着色器:

#version 330 core
out vec4 FragColor;

in vec3 Normal;  
in vec3 FragPos;  
  
uniform vec3 lightPos; 
uniform vec3 lightColor;
uniform vec3 objectColor;
uniform vec3 viewPos; 

void main()
{
    // ambient
    float ambientStrength = 0.1;
    vec3 ambient = ambientStrength * lightColor;
  	
    // diffuse 
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(lightPos - FragPos);
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = diff * lightColor;

    // specular
    float specularStrength = 0.5;
    vec3 viewDir = normalize(viewPos - FragPos);
    vec3 reflectDir = reflect(-lightDir, norm);  
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
    vec3 specular = specularStrength * spec * lightColor; 
    
    //距离影响光照强度,暂时是我自己想的
	float enableDistance = 4.0f;	//有效光照距离
	vec3 light2fragVec = lightPos - vec3(FragPos);
	float distan = min(sqrt(light2fragVec.x * light2fragVec.x + light2fragVec.y * light2fragVec.y + light2fragVec.z * light2fragVec.z), enableDistance);
	float strength = 1 - distan / enableDistance;
            
    //光照远近先不影响环境光照了
    vec3 result = ambient*objectColor+(diffuse + specular) * objectColor*strength;
    FragColor = vec4(result, 1.0);
} 

光源的顶点着色器:

#version 330 core
layout (location = 0) in vec3 aPos;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
	gl_Position = projection * view * model * vec4(aPos, 1.0);
}

光源的片段着色器:

#version 330 core
out vec4 FragColor;

void main()
{
    FragColor = vec4(1.0); // set all 4 vector values to 1.0
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/435606.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Docker学习三部曲】—— 入门篇

Docker 入门 Docker 概述 1️⃣ 什么是 docker &#xff1f; Docker 是一种运行应用程序的平台&#xff0c;它可以使应用程序在容器中不受环境差异的影响进行部署和运行。Docker 的流行度越来越高&#xff0c;是因为它可以帮助在不同的开发者和开发团队之间实现代码的共享和…

Linux centos搭建web服务器

文章目录 前言1. 本地搭建web站点2. 测试局域网访问3. 公开本地web网站3.1 安装cpolar内网穿透3.2 创建http隧道&#xff0c;指向本地80端口3.3 配置后台服务 4. 配置固定二级子域名5. 测试使用固定二级子域名访问本地web站点 前言 在web项目中,部署的web站点需要被外部访问,则…

动力节点Vue3课程笔记——四、Vue与AJAX

四、Vue与AJAX 4.1 回顾发送AJAX异步请求的方式 发送AJAX异步请求的常见方式包括&#xff1a; 原生方式&#xff0c;使用浏览器内置的JS对象XMLHttpRequest const xhr new XMLHttpRequest()xhr.onreadystatechange function(){}xhr.open()xhr.send() 原生方式&#xff0…

30天学会《Streamlit》(7)

30学会《Streamlit》是一项编码挑战&#xff0c;旨在帮助您开始构建Streamlit应用程序。特别是&#xff0c;您将能够&#xff1a; 为构建Streamlit应用程序设置编码环境 构建您的第一个Streamlit应用程序 了解用于Streamlit应用程序的所有很棒的输入/输出小部件 第7天 - st…

聚焦商会 | 活动彰显一个商会的战斗力与影响力

4月13日&#xff0c;北京厦航嘉年华酒店。 一个普通的日子&#xff0c;对北京茶业企业商会来说却是值得纪念的一天&#xff0c;可以浓笔重墨书写的一天。 一天成功举办了三场活动&#xff0c;得到社会各界的支持与肯定。 北京茶业企业商会是如何做到的&#xff1f;经过与商会领…

python批量下载怀俄明大学探空数据Wyoming soundings并处理

下载怀俄明大学的探空数据&#xff0c;之前用的是气象家园写的maltab脚本&#xff0c;但总是链接不上&#xff0c;而且有的站点需要用新网址&#xff0c;有的有需要用老网址&#xff0c;很麻烦&#xff0c;痛定思痛用决定终于用python了&#xff0c;主要有两种方式&#xff0c;…

nightingale-0-介绍单机二进制部署

(一) 夜莺介绍 Nightingale | 夜莺监控&#xff0c;一款先进的开源云原生监控分析系统&#xff0c;采用 All-In-One 的设计&#xff0c;集数据采集、可视化、监控告警、数据分析于一体&#xff0c;与云原生生态紧密集成&#xff0c;提供开箱即用的企业级监控分析和告警能力。于…

渗滤液除氨氮、矿井水除氨氮,氨氮吸附技术

垃圾渗滤液是生活垃圾的必然产物&#xff0c;顾名思义&#xff0c;垃圾渗滤液是指来源于垃圾填埋场中垃圾本身含有的水分、进入填埋场的雨雪水及其他水分&#xff0c;扣除垃圾、覆土层的饱和持水量&#xff0c;并经历垃圾层和覆土层而形成的一种高浓度的有机废水。垃圾在堆放和…

[golang gin框架] 24.Gin 商城项目-redis讲解以及操作

一.reids相关文章 Redis五种数据类型及其应用场景 REDIS中的缓存穿透&#xff0c;缓存击穿&#xff0c;缓存雪崩原因以及解决方案 redis实现用户签到&#xff0c;统计活跃用户&#xff0c;用户在线状态&#xff0c;用户留存率 [golang gin框架] 12.Gin 商城项目-base64Captcha生…

KDXJ-8 SF6气体泄漏报警在线检测系统

一、功能特点 1. 定量测量SF6&#xff08;六氟化硫&#xff09;气体浓度&#xff1b; 2. 定量测量O2&#xff08;氧气&#xff09;气体浓度&#xff1b; 3. 定量测量大气温湿度&#xff1b; 4. 可根据需要设置SF6和O2气体浓度的报警点&#xff1b; 5. 后台监控&#xff1b; 6.…

【通过加载plist文件显示分组数据 Objective-C语言】

一、展示汽车品牌数据 1.首先,我们还是先把控制器调成3.5英寸,再把模拟器调成iPhone4S, 2.然后呢,我们要先把素材拷进来, 我们要展示,要把一个plist文件中的数据展示到我们的这个UITableView里面,所以说,首先,是要把我们的plist文件拷进来, 找到我们这个plist文件…

爱智EdgerOS之深入解析在爱智应用中如何使用Socket.IO轻松实现双向通信

一、什么是 Socket.IO&#xff1f; Socket.IO 是一个基于事件通信的实时应用程序框架&#xff0c;它在即时通讯、通知和消息推送&#xff0c;实时分析等场景中有广泛的应用。Socket.IO 包括两个部分&#xff1a; 在 Server 端的模块&#xff08;JSRE 已提供了 socket.io 模块&…

Onnx 转Ncnn

Onnx 转Ncnn 算法工程师给了onnx,需要转成ncnn才能用到安卓上去&#xff0c;步骤如下 简化onnx 算法给了.onnx后缀的文件&#xff0c;100多兆&#xff0c;太大&#xff0c;第一步&#xff0c;先简化&#xff1a; conda env list 查看conda环境&#xff08;前提是之前已经配…

企业级实践:分布式系统中的可靠消息最终一致性方案

背景 公司由于业务爆发式增长&#xff0c;新上了许多业务系统&#xff0c;例如&#xff1a;本地生活、社区团购、旅投B2B、旅投B2C等系统&#xff1b;同时&#xff0c;由于业务系统越来越多&#xff0c;为了运营方便&#xff0c;把分销、营销、订单、会员等多个业务系统公共业…

Babel零基础教程

参考https://www.jiangruitao.com/babel/ 写的非常好&#xff0c;本人只是为了方便查找&#xff0c;记录在自己博客上&#xff0c;原文可以去该链接拜读 1、 Babel 简介 Babel是什么&#xff1f; Babel是一个工具集&#xff0c;主要用于将ES6版本的JavaScript代码转为ES5等向…

遗传算法的概念和python实现

遗传算法是一个非常经典的智能算法&#xff0c;主要用于解决优化问题。本文主要简单介绍一些原理&#xff0c;同时给出一个基于python实现的&#xff0c;用于解决实数内优化问题的模板。 本文参考&#xff1a; 原理&#xff1a;遗传算法入门详解 - 知乎 简单介绍 遗传算法就…

服务型企业如何使用飞项实现项目化管理?

服务型企业的业务模式一般都是按项目来运作的&#xff0c;其业务分为售前&#xff0c;售中和售后三个阶段&#xff0c;分别由不同部门和人员对客户进行个性化服务。在这个过程中需要对人、流程和知识的高效统筹管理&#xff0c;即项目的整体管理&#xff0c;因此存在着不小的挑…

Nvidia Jetson Orin: SPE/AON Cortex-R5 固件开发

Nvidia Jetson Orin: SPE/AON Cortex-R5 固件开发 写在最前边开发/下载 SPE 固件关于修改DTS 写在最前边 SPE 只能控制 AON GPIO 最多32个PIN 开发/下载 SPE 固件 S1&#xff1a;打开 https://developer.nvidia.com/embedded/jetson-linux S2&#xff1a;这里下载 S3&#x…

Linux环境使用日志切割工具-cronolog

Linux环境使用日志切割工具 需明白的点&#xff1a;1.安装 2.如何使用 一、安装cronolog 1.首先检查是否存在cronolog 命令&#xff1a;whereis cronolog 或 which cronolog 2.不存在则安装 安装方式&#xff1a; a.yum 安装&#xff1a;yum install cronolog b. 源码安装&am…

C/C++|物联网开发入门+项目实战|指针|嵌入式C语言高级|C语言内存空间的使用-数组-学习笔记(10)

参考&#xff1a;麦子学院-嵌入式C语言高级-内存空间 2-3 : C语言内存空间的使用-数组 内存分配的一种形式 数组的定义及初始化 定义一个空间: 1、大小 2、读取方式 数组名[]:升级为连续空间的名称&#xff0c; [m]的作用域只在申请的时候起作用 每个多大&#xff1f;数组…