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()
{// ambientfloat 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;// specularfloat 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();
}
...

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

恒创科技:服务器内存不足影响大吗?

​  服务器在为网站、应用程序和在线服务提供支持方面发挥着关键作用。这些服务器需要提供最佳性能&#xff0c;以确保正常无缝的用户体验&#xff0c;而RAM是显著影响服务器性能的关键配置之一。 RAM 是一种随机存取存储器&#xff0c;计算机和服务器使用它来临时存储正在使…

【GAMES101】Lecture 17 材质

目录 材质 漫反射 镜面反射 折射-Snell’s Law Fresnel Reflection / Term&#xff08;菲涅耳项&#xff09; 微表面模型 各向同性与各向异性 BRDF的性质 测量BRDF 材质 渲染方程中的BRDF描述了物体是如何与光线作用的&#xff0c;而物体的材质决定了它看起来是怎么样…

有趣的CSS - 按钮文字上下滑动

目录 整体效果核心代码html 代码css 部分代码 完整代码如下html 页面css 样式页面渲染效果 整体效果 这个按钮效果主要使用 :hover 伪选择器以及 transition 过渡属性来实现两个子元素上下过渡的效果。 此效果可以在主入口按钮、详情或者更多等按钮处使用&#xff0c;增加一些鼠…

zxxxxczzvdsgbhfdb

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起探讨和分享Linux C/C/Python/Shell编程、机器人技术、机器学习、机器视觉、嵌入式AI相关领域的知识和技术。 磁盘满的本质分析 专栏&#xff1a;《Linux从小白到大神》 | 系统学习Linux开发、VIM/GCC/GDB/Make工具…

Flink 动态表 (Dynamic Table) 解读

博主历时三年精心创作的《大数据平台架构与原型实现&#xff1a;数据中台建设实战》一书现已由知名IT图书品牌电子工业出版社博文视点出版发行&#xff0c;点击《重磅推荐&#xff1a;建大数据平台太难了&#xff01;给我发个工程原型吧&#xff01;》了解图书详情&#xff0c;…

深度解析ScheduledThreadPoolExecutor源码之ScheduledFutureTask

文章目录 引言一、RunnableScheduledFuture定义周期性接口二、ScheduledFutureTask源码分析2.1 ScheduledFutureTask参数解析2.2 ScheduledFutureTask源码方法解析 总结 引言 在上一章节我们已经对ScheduledThreadPoolExecutor中的延迟队列DelayedWorkQueue做了源码分析深度解…

ManimCE教程(1)快速入门

概述 Manim 是一个用于精确编程动画的动画引擎。 开始一个新项目 首先创建一个新文件夹&#xff0c;命名为&#xff1a;project&#xff0c;此文件夹是项目的根文件夹&#xff0c;它包含 Manim 运行所需的所有文件&#xff0c;以及项目产生的所有输出。 设置圆的动画 1、打…

文心一言4.0API接入指南

概述 文心一言是百度打造出来的人工智能大语言模型&#xff0c;具备跨模态、跨语言的深度语义理解与生成能力&#xff0c;文心一言有五大能力&#xff0c;文学创作、商业文案创作、数理逻辑推算、中文理解、多模态生成&#xff0c;其在搜索问答、内容创作生成、智能办公等众多…

Python入门:常用模块—time datetime模块

在python中&#xff0c;与时间处理有关的常用模块有&#xff1a;time&#xff0c;datetime&#xff0c;calendar&#xff08;很少用&#xff09; 一、在Python中&#xff0c;通常有这几种方式来表示时间&#xff1a; 时间戳格式化的时间字符串元组&#xff08;struct_time&am…

vscode 突然连接不上服务器了(2024年版本 自动更新从1.85-1.86)

vscode日志 ll192.168.103.5s password:]0;C:\WINDOWS\System32\cmd.exe [17:09:16.886] Got some output, clearing connection timeout [17:09:16.887] Showing password prompt [17:09:19.688] Got password response [17:09:19.688] "install" wrote data to te…

centos7编译安装redis

一、环境 系统&#xff1a;CentOS Linux release 7.9.2009 (Core) redis版本&#xff1a;redis 6.0.6 二、安装及部署 当前最新稳定版本是redis 6.0.6 国内网址&#xff1a;http://www.redis.cn redis下载列表&#xff1a;http://download.redis.io/releases/ 下载 wge…

肿瘤免疫分型

Elements of cancer immunity and the cancer-immune set point - PubMed (nih.gov) Daniel S Chen , Ira Mellman 人类的抗癌免疫可分为三种主要表型&#xff1a;免疫沙漠表型&#xff08;棕色&#xff09;、免疫排除表型&#xff08;蓝色&#xff09;和免疫炎症型&#xff0…