OpenGL实现光源位置指示

案例需求:

在三维超声显示中,需要一个光源指示功能来示意光源是从什么方向照向胎儿的,从而帮助用户去理解当前胎儿三维显示的效果。如下图

基于以上需求需要实现以下几点功能:

1. 构造球体模型和光源模型;

2. 绕球体旋转光源;

3. 渲染光源和球体任意位置关系。

案例实现

胎儿用一个球体示意,光源用一个立方体示意。因为立方体的构造原理很简单,所以这里只对球体的构造原理进行说明。

构造球体模型

球是由一组经线和纬线上的点组成,如下图所示:

图1

计算球上点的坐标

下面以球的模型坐标系来计算球上任意一点P的坐标(xpos, ypos, zpos),如下图:

为了简化分析图,把坐标系和P点从球上拿出来,如下图:

为了进一步简化分析,把P点及其在坐标系中各个轴上的投影点一起构造一个立方体,如下图:

θ :表示经线与X轴正方向的角

φ :表示纬线与Y轴正方向的角

半径:单位长度,即是图中OP的长度为1.

从图中可以看出:

∠poq = soz =  φ

所以P点的坐标计算如下:

xpos = cos(φ) * sin(θ)

ypos = sin(φ)

zpos  = cos(φ) * cos(θ)

我们可以根据需要设置经线和纬线的条数(太少的话,球会有棱有角,不够光滑):

LONGITUDE_SEGMENTS = 64

LATITUDE_SEGMENTS = 64

θ和φ的计算如下:

x,y∈[0, 64]

θ = (x / LONGITUDE_SEGMENTS) * 2 * π

φ  = (y / LATITUDE_SEGMENTS) * 2 * π

从图1中我们看到纬线的角度范围实际是[-90, 90],所以φ的计算更正为:

φ  = (π / 2) - (y / LATITUDE_SEGMENTS) * π

std::vector<glm::vec3>positions, normals;
const unsigned int LONGITUDE_SEGMENTS = 64;
const unsigned int LATITUDE_SEGMENTS = 64;
const float PI = 3.14159265359f;
for (unsigned int x = 0; x <= LONGITUDE_SEGMENTS; ++x)
{
	for (unsigned int y = 0; y <= LATITUDE_SEGMENTS; ++y)
	{
		float theta = ((float)x / (float)LONGITUDE_SEGMENTS) * 2 * PI;
		float phi = (PI / 2) - ((float)y / (float)LATITUDE_SEGMENTS) * PI;
		float xPos = std::cos(phi) * std::sin(theta);
		float yPos = std::sin(phi);
		float zPos = std::cos(phi) * std::cos(theta);

		positions.push_back(xPos);
		positions.push_back(yPos);
		positions.push_back(zPos);

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

生成点的索引

我们将使用EBO的方式来绘制球,所以需要生成点的索引。下图是基于优先遍历纬线方向的索引点的示意图:

上图简化为一个的平面网格图如下:

indices[] = {0, 3, 1, 3, 4, 1, 1, 4, 2, 4, 5, 2, 3, 6, 4, 6, 7,4, 4, 7, 5, 7, 8, 5  }

if(x < LONGITUDE_SEGMENTS  && y < LATITUDE_SEGMENTS)
{
	indices.push_back(x * (LATITUDE_SEGMENTS + 1) + y);
	indices.push_back((x + 1) * (LATITUDE_SEGMENTS + 1) + y);
	indices.push_back(x * (LATITUDE_SEGMENTS + 1) + y + 1);

	indices.push_back((x + 1) * (LATITUDE_SEGMENTS + 1) + y);
	indices.push_back(x * (LATITUDE_SEGMENTS + 1) + y + 1);
	indices.push_back((x + 1) * (LATITUDE_SEGMENTS + 1) + y + 1);
}

至此,球模型构造完成。

绕球体旋转光源

光源绕球体旋转是通过鼠标移动实现,所以需要计算鼠标屏幕偏移量到球上偏移量的计算:

这种计算的方法很多,我采用的是将鼠标偏移量转换为球的经线和纬线方向偏移角的方法,代码实现如下,原理与计算球上任意一点坐标的一致。

float light_theta = 0;
float light_phi = 0;
void MaptoSphere(glm::vec3& lightPos)
{
    const float PI = 3.14159265359f;
    float length = glm::distance(lightPos, glm::vec3(0, 0, 0));
    float theta = glm::radians(light_theta);
    float phi = glm::radians(light_phi);;    
    float xPos = length * std::cos(phi) * std::sin(theta);
    float yPos = length * std::sin(phi);
    float zPos = length * std::cos(phi) * std::cos(theta);
    lightPos = glm::vec3(xPos, yPos, zPos);
}

// glfw: whenever the mouse moves, this callback is called
// -------------------------------------------------------
void mouse_callback(GLFWwindow* window, double xposIn, double yposIn)
{
    float xpos = static_cast<float>(xposIn);
    float ypos = static_cast<float>(yposIn);

    if (firstMouse)
    {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }

	float xoffset = xpos - lastX;
	float yoffset = ypos - lastY; // reversed since y-coordinates go from bottom to top  

	const float MouseSensitivity =  0.5f;
	xoffset *= MouseSensitivity;
	yoffset *= MouseSensitivity;

	light_theta += xoffset;
	light_phi += yoffset;

	lastX = xpos;
	lastY = ypos;
	if (fabs(xoffset) < 0.0001 && fabs(yoffset) < 0.0001)
	{
		return;
	}
	
	MaptoSphere(lightPos);
}

渲染光源和球体任意位置关系

立方体光源的渲染比较简单,这里只对球体的渲染进行说明。

顶点着色器代码:

#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));
    Normal = mat3(transpose(inverse(model))) * 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 viewPos; 
uniform vec3 lightColor;
uniform vec3 objectColor;
uniform bool blinn;

void main()
{
    // ambient
    float ambientStrength = 0.6;
    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.2;
    float spec = 0.0;
    vec3 viewDir = normalize(viewPos - FragPos);
    if(blinn)
    {
        vec3 halfwayDir = normalize(lightDir + viewDir);
        spec = pow(max(dot(Normal, halfwayDir), 0.0), 64);
    }
    else
    {
        vec3 reflectDir = reflect(-lightDir, norm);  
        float spec = pow(max(dot(viewDir, reflectDir), 0.0), 64);
    }    
    vec3 specular = specularStrength * spec * lightColor; 
    
    vec3 result = (ambient + diffuse + specular) * objectColor;
    FragColor = vec4(result, 0.5);
} 

FragColor = vec4(result, 0.5);

这里0.5表示透明度,不能设置为1.0。只有在透明的情况下,当光源转到球背后时,才依然能看到光源的位置。

混合和面剔除

...
while (!glfwWindowShouldClose(window))
{
	// render
	// ------
	glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glDisable(GL_BLEND);
	glDisable(GL_CULL_FACE);
	
	cube.Render();

	if (lightPos.z <= 0)
	{
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glEnable(GL_CULL_FACE);
	}
	else
	{
		glDisable(GL_BLEND);
		glDisable(GL_CULL_FACE);
	}

	sphere.Render();
	// 
	// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
	// -------------------------------------------------------------------------------
	glfwSwapBuffers(window);
	glfwPollEvents();
}
...

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【多模态MLLMs+图像编辑】MGIE:苹果开源基于指令和大语言模型的图片编辑神器(24.02.03开源)

项目主页&#xff1a;https://mllm-ie.github.io/ 论文 :基于指令和多模态大语言模型图片编辑 2309.Guiding Instruction-based Image Editing via Multimodal Large Language Models &#xff08;加州大学圣巴拉分校苹果&#xff09; 代码&#xff1a;https://github.com/appl…

全世界 LoRA 训练脚本,联合起来!

太长不看版 我们把 Replicate 在 SDXL Cog 训练器中使用的枢轴微调 (Pivotal Tuning) 技术与 Kohya 训练器中使用的 Prodigy 优化器相结合&#xff0c;再加上一堆其他优化&#xff0c;一起对 SDXL 进行 Dreambooth LoRA 微调&#xff0c;取得了非常好的效果。你可以在 diffuser…

Linux Rootkit实验|0200 基本功能之阻止模块加载

Linux Rootkit实验&#xff5c;0200 基本功能之阻止模块加载 11 May 2017 文章目录 Linux Rootkit实验&#xff5c;0200 基本功能之阻止模块加载实验说明实验环境实验过程控制内核模块加载 实验总结与思考拓展延伸参考资料参考资料 醉里挑灯看剑&#xff0c;梦回吹角连营。八百…

Multisim14.0仿真(五十三)时、分、秒、毫秒数字计时器

一、仿真效果&#xff1a; 二、时钟脉冲配置&#xff1a; 三、24进制计数&#xff1a; 四、60进制计数&#xff1a;

docker安装zpan

安装 1.创建数据库 docker run -di --namezpan_mysql -p 3309:3306 -e MYSQL_ROOT_PASSWORD123456 mysql 2.手动新建数据库zpan 3.创建目录 mkdir -p /opt/zpan cd /opt/zpan 4.编写配置文件 vim config.yml #详细配置文档可参考&#xff1a; https://zpan.space/#/zh…

Python OpenCV实现图片像素区域缩放

Python OpenCV实现图片像素区域缩放 前言项目安装OpenCV和Pillow思路代码编写 前言 遇到一个要将大量图片缩放成统一规格的难题&#xff0c;并且这些图片周围还有很多空白像素&#xff0c;所以用Python实现一下。 项目 安装OpenCV和Pillow pip install opencv-python pip …

ubuntu22.04@laptop OpenCV Get Started: 001_reading_displaying_write_image

ubuntu22.04laptop OpenCV Get Started: 001_reading_displaying_write_image 1. 源由2. Read/Display/Write应用Demo2.1 C应用Demo2.2 Python应用Demo 3. 过程分析3.1 导入OpenCV库3.2 读取图像文件3.3 显示图像3.4 保存图像文件 4. 总结5. 参考资料 1. 源由 读、写、显示图像…

[python]anaconda3里面pyqt6配置到pycharm配置designer pyuic pyrcc

安装pyqt6 pip install pyqt6 pyqt6-tools pycharm 配置 配置designer pycharm -->> setting —>> External Tools 点击 Program : D:\ProgramData\Anaconda3\Lib\site-packages\qt6_applications\Qt\bin\designer.exe Working directory : $ProjectFileDir$…

私有化部署一个吃豆人小游戏

目录 效果 安装步骤 1.安装并启动httpd 2.下载代码 3.启动httpd 使用 效果 安装步骤 1.安装并启动httpd yum -y install httpd 2.下载代码 进入目录 cd /var/www/html/ 下载 git clone https://gitee.com/WangZhe168_admin/pacman-canvas.git 3.启动httpd syste…

layui-实现上下表,父子表复选框加载事件

layui-实现上下表&#xff0c;父子表复选框加载事件 实现效果说明代码HTML代码表格数据加载监听复选框选择事件点击表格任意单元格&#xff0c;选中复选框和取消复选框选中 效果图 实现效果说明 点击任意单元格&#xff0c;选中复选框&#xff0c;并加载子表数据&#xff0c;选…

Visual Studio 20XX控制台程序鼠标点击阻塞问题

文章目录 方法一方法二 在Visual Studio 20xx编写的控制台程序中&#xff0c;当鼠标点击控制台时&#xff0c;会阻塞控制台程序运行&#xff0c;不按回车无法继续运行。 方法一 右击控制台标题栏&#xff0c;选择属性&#xff0c;去掉快速编辑模式(Q)的勾选&#xff0c;如&…

政安晨:政安晨:机器学习快速入门(三){pandas与scikit-learn} {模型验证及欠拟合与过拟合}

这一篇中&#xff0c;咱们使用Pandas与Scikit-liarn工具进行一下模型验证&#xff0c;之后再顺势了解一些过拟合与欠拟合&#xff0c;这是您逐渐深入机器学习的开始&#xff01; 模型验证 评估您的模型性能&#xff0c;以便测试和比较其他选择。 在上一篇中&#xff0c;您已经…

橘子学linux调优之工具包的安装

今天在公司无聊的弄服务器&#xff0c;想着有些常用的工具包安装一下&#xff0c;这里就简单记录一下。 一、sysstat的安装和使用 1、安装 我是通过源码的方式安装的&#xff0c;这样的好处在于可以自由选择你的版本&#xff0c;很直观。 直接去github上找到sysstat的地址&a…

P1131 [ZJOI2007] 时态同步

题目描述 小 Q 在电子工艺实习课上学习焊接电路板。一块电路板由若干个元件组成&#xff0c;我们不妨称之为节点&#xff0c;并将其用数字 1,2,3⋯1,2,3⋯ 进行标号。电路板的各个节点由若干不相交的导线相连接&#xff0c;且对于电路板的任何两个节点&#xff0c;都存在且仅存…

回归预测 | Matlab实现OOA-CNN-LSTM-Attention鱼鹰算法优化卷积长短期记忆网络注意力多变量回归预测(SE注意力机制)

回归预测 | Matlab实现OOA-CNN-LSTM-Attention鱼鹰算法优化卷积长短期记忆网络注意力多变量回归预测&#xff08;SE注意力机制&#xff09; 目录 回归预测 | Matlab实现OOA-CNN-LSTM-Attention鱼鹰算法优化卷积长短期记忆网络注意力多变量回归预测&#xff08;SE注意力机制&…

若依整合mybatis-plus

文章目录 1.注释掉原本的MybatisConfig2. 将mybatis的配置文件改为mybatis-plus文件 ##前言 出先下列异常&#xff1a; 请求地址’/prod-api/user’,发生未知异常. org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.ruoyi.web.mapper.Us…

PHP客服系统-vue客服聊天系统

PHP-Vue客服聊天系统是一款高效、灵活的客户服务解决方案&#xff0c;基于ThinkPHP6、Vue3和Workerman(Gateworker)框架开发&#xff0c;专为单商户场景打造。 系统亮点&#xff1a; 分布式部署支持&#xff0c;轻松应对高并发场景&#xff1b;本地消息存储功能&#xff0c;确…

js中typeof 与 instanceof 的区别

文章目录 一、typeof二、instanceof三、区别 一、typeof typeof 操作符返回一个字符串&#xff0c;表示未经计算的操作数的类型 使用方法如下&#xff1a; typeof operand typeof(operand)operand表示对象或原始值的表达式&#xff0c;其类型将被返回 举个例子&#xff1a;…

网络爬虫,使用存放在C的谷歌驱动报错

月 06, 2024 11:43:40 上午 org.openqa.selenium.os.OsProcess checkForError 严重: org.apache.commons.exec.ExecuteException: Execution failed (Exit value: -559038737. Caused by java.io.IOException: Cannot run program "C:\chromedriver121.exe" (in dir…

nvm安装node后,npm无效

类似报这种问题&#xff0c;是因为去github下载npm时下载失败&#xff0c; Please visit https://github.com/npm/cli/releases/tag/v6.14.17 to download npm. 第一种方法&#xff1a;需要复制这里面的地址爬梯子去下载&#xff08;github有时不用梯子能直接下载&#xff0c;有…