概述及目录(版权所有,请勿转载,欢迎读者提出错误)

  之前用kanzi的3D UI引擎和cocos-2d的时候都有遇到过这个问题,就如何把3D场景中的XY平面的尺寸映射为与屏幕像素一一对应的,即XY平面上的一个单位对应平面上的一个像素。这个在3D UI开发过程中似乎并非必须,或者说很少有人这样用,因为在游戏场景中,UI可以处于场景的任何位置,并不局限于XY平面内。

  本次的分享总结所述的3D UI应用场景并非在游戏中,而是注重在GUI应用上(类似QT等),即使用3D绘图技术实现的一套类似2D UI一样效果的引擎,由于UI系统是3D的,故能实现3D的动画效果。把3D场景中的XY平面的尺寸映射为平面像素一一对应的优点,是能保持并延续我们在2D开发时候的习惯,方便精准地控制UI控件在整个屏幕上的位置布局。

  本文的重点是“3D UI场景中把XY平面的尺寸映射为屏幕像素”,因此需要您有如下的基本知识:

  1、基本3D数理知识;

  2、Opengl相关知识;

  3、对3D计算机图形学中“摄像机”概念有所了解;

  本文包括如下内容:

glm 3D数学库简介

  什么是3D数学库?

  所谓3D数学库,简单地说就是把在3D计算机编程中常用到的数据类型、数学函数、3D处理公式及方法等统一集中起来,方便我们在处理3D场景时使用。

  glm 3D数学库是Opengl官网推荐使用的,包含了几乎所有我们在处理3D场景是需要的数学函数。

  glm的使用也非常简单,glm提供的源码全部都是头文件,我们只需把glm的头文件引用到自己需要使用的工程中即可。

  如下实例代码中,我们通过glm创建了一个4x4的矩阵,并对该矩阵进行了平移变换(详细的glm使用介绍,大家可以参考glm官网的教程或文档)。

//示例代码 1.0 http://www.cnblogs.com/feng-sc/
#include <glm/glm.hpp> //注意: glm的工程路径需要自己配置
#include <glm/gtc/matrix_transform.hpp>
int main()
{
glm::mat4 matrix(1.0);
matrix = glm::translate(matrix, glm::vec3(100.0f, 0.0f, 0.0f));
return ;
}

  作为简介,glm的介绍就到此结束。

透视视锥体介绍

  所谓的透视不是你所想的眼睛能看穿墙的意思,别多想了!简单点,透视就是表示物体近大远小的效果的意思。

  如下图所示,透视视锥体梯体几何图形,它类似于人的眼睛所能看到的范围,在梯体之外的物体将不可见。

  在3D数学里,用什么表示这个透视视锥体呢?没错,是矩阵!

  使用glm函数库能简单地生成透视视锥体的矩阵,如下实例代码:

// 示例代码1.0: www.cnblogs.com/feng-sc/
// fovyInRadians : 弧度表示下图中FOV
// aspect : 视锥体宽与高的比例,可以理解为绘图区域的宽高比
// zNear : 近平面离摄像机的距离
// zFar :远平面离摄像机的距离
glm::mat4 projection = glm::perspective(fovyInRadians, aspect, zNear, zFar); 

(透视视锥体)

  上诉实例代码中,projection又被成为透视矩阵,所有3D世界里的物体,经过与projection矩阵相乘后,最终得到的物体将呈现如下两种特点:

  1、远小近大的效果;

  2、处于透视视锥外的物体将被忽略;

使用glm函数库生成摄像机矩阵

  本段我们先以一段代码起头,如下:

// 示例代码1.0: www.cnblogs.com/feng-sc/
glm::mat4 view = glm::lookAt(m_position, m_target, m_up);

  lookAt函数得到的结果是一个视图矩阵。有人把视图矩阵称为摄像机,也有人把视图矩阵和透视投影矩阵合在一起称为摄像机,我喜欢后者。

  结合投影矩阵,我们总结一下,摄像机分别由如下参数决定:

  1、透视投影矩阵projection决定了摄像机的视野范围,包括视觉张角FOV、近平面、远平面;

  2、视图矩阵决定了摄像机的位置、观察方向;

  最后投影矩阵与视图矩阵将共同决定我们整个场景的显示效果。

// 示例代码1.0 www.cnblogs.com/feng-sc/
glm::mat4 projection = glm::perspective(fovyInRadians, aspect, zNear, zFar);
glm::mat4 view = glm::lookAt(m_position, m_target, m_up);
glm::mat4 vpMat = projection * view

分析如何调整摄像机和透视视锥体,使的3D场景中的XY平面的尺寸与屏幕像素对应;

  OK,终于来到了本文标题讨论的问题点,3D UI场景中把XY平面的尺寸映射为屏幕像素。

  其实到现在为止,我们问题的解决方案也清晰了,如何实现“3D UI场景中把XY平面的尺寸映射为屏幕像素” 呢?是的,就是调整摄像机的位置、远/近平面、摄像机视角,使XY平面的单位尺寸恰好与平面像素的单位对应即可。

  那么现在剩下的问题是:如何调整摄像机,使得我们的XY平面恰好与平面像素对应呢?

  在我们继续之前,我们先来了解一个概念:齐次坐标。

  百度百科解释说:齐次坐标就是将一个原本是n维的向量用一个n+1维向量来表示。例如,二维点(x,y)的齐次坐标表示为(hx,hy,h)(h可以是任意值)。

  我们可以理解为,任何三维的点(hx,hy,h),在二维平面上的投影点均为(x,y)。

(透视视锥体侧面平面图)

  上图为透视视锥体侧面平面图,其中GI为透视视锥体的近平面,BF为远平面,LS和TZ分别为视锥体的两个不同位置的截面。

  从2D平截视锥体看,透视视锥体GBFI范围内的三维物体最后均被投影到GI平面上。由齐次坐标概念可知,点B、U、M在GI平面上的投影均为点M,同理点F、W、P在GI平面上的投影均为点I。

  我们:

   假设TZ平面为XY平面且与屏幕像素对应,屏幕高度像素为h,角∠BAF = FOV (FOV为摄像机张角)

   故,UW = h,UV = h/2;

   故,

    即,由屏幕宽度和摄像机张角,要使XY平面与屏幕像素对应,我们求得摄像机位置点距离XY屏幕距离长度为AV。

  下面的代码设置为屏幕左上角为原点是,摄像机的设置。

// 示例代码1.0 www.cnblogs.com/feng-sc/
float fov = ;
glm::perspective(glm::radians<float>(fov), (float)width / (float)height, 0.1f, 10000.0f);
float z = height / ( * tan(((float)(fov / 2.0)* glm::pi<float>()) / 180.0));
glm::vec3 positon((float)width / 2.0f, (float)height / 2.0f, -z);
glm::vec3 target((float)width / 2.0f, (float)height / 2.0f, 0.0f);
glm::vec3 up(0.0f, -1.0f, 0.0f);
m_view = glm::lookAt(positon target, up);

  

3D UI场景中,把XY平面的尺寸映射为屏幕像素的数学模型推导的更多相关文章

  1. 获取屏幕中某个点的RGB值与CAD屏幕像素值

    '获取CAD屏幕像素的比值 Function ViewScreen() As Double Dim ScreenSize As Variant ScreenSize = ThisDrawing.Get ...

  2. [Unity3D]Unity3D游戏开发3D选择场景中的对象,并显示轮廓效果强化版

    大家好,我是秦培,欢迎关注我的博客,我的博客地址blog.csdn.net/qinyuanpei. 在上一篇文章中,我们通过自己定义着色器实现了一个简单的在3D游戏中选取.显示物体轮廓的实例. 在文章 ...

  3. [Unity3D]Unity3D游戏开发之在3D场景中选择物体并显示轮廓效果

    大家好,我是秦元培,欢迎大家关注我的博客,我的博客地址是blog.csdn.net/qinyuanpei. 在<仙剑奇侠传>.<古剑奇谭>等游戏中,常常须要玩家在一个3D场景中 ...

  4. 3D游戏引擎中常见的三维场景管理方法

    对于一个有很多物体的3D场景来说,渲染这个场景最简单的方式就是用一个List将这些物体进行存储,并送入GPU进行渲染.当然,这种做法在效率上来说是相当低下的,因为真正需要渲染的物体应该是视椎体内的物体 ...

  5. Unity3D NGUI制作的Button放到场景中,按钮从2D变到3D

    通常我们使用Button都是在UI界面,即NGUI的摄像机下,如果想换到场景中,即不让按钮以UI形式显现,而是和场景中的物体一起随着摄像机移动而缩小,放大. 很简单,把Button从NGUi的摄像机中 ...

  6. 基于 HTML5 WebGL 的 3D 场景中的灯光效果

    构建 3D 的场景除了创建模型,对模型设置颜色和贴图外,还需要有灯光的效果才能更逼真的反映真实世界的场景.这个例子我觉得既美观又代表性很强,所以拿出来给大家分享一下. 本例地址:http://www. ...

  7. 3D场景中的鼠标响应事件

    原文:3D场景中的鼠标响应事件 今天要讲的是3D场景中的鼠标响应事件的处理,首先Button的响应是大家熟知的,只要加上一个click事件,然后写一个响应的处理时间就行了.对于二维平面上的一些控件也很 ...

  8. 如何在3D场景中在模型上面绘制摄取点

    有些时候,我们在屏幕上面绘制一个摄取点,在单屏玩游戏的模式下,我们并不能觉得有什么不妥.但是最近VR的热火朝天,我们带上眼镜看双屏的时候,总觉得这个摄取点看着很不舒服. 这个问题该怎么解决?在这里我首 ...

  9. 在WebGL场景中管理多个卡牌对象的实验

    这篇文章讨论如何在基于Babylon.js的WebGL场景中,实现多个简单卡牌类对象的显示.选择.分组.排序,同时建立一套实用的3D场景代码框架.由于作者美工能力有限,所以示例场景视觉效果可能欠佳,本 ...

随机推荐

  1. Word 2003-在一个方框里打勾或打叉

    最近有个同事问我,如何在Word中输出一个方框中打勾的符号?查了一下帮助,其实很简单,特记录如下,供碰到的朋友参考: 一.在方框中打勾的方法: 先输入一个大写字母R,然后将R选中,将字体改为“Wind ...

  2. 《你不知道的JavaScript下卷》知识点笔记

    1. [1, 2, 3] == [1, 2, 3] 返回false [1, 2, 3] == “1, 2, 3” 返回true 2. 如果 < 比较的两个值都是字符串, 就像在b < c中 ...

  3. linux下的计算器

    (1)bc bc在默认的情况下是个交互式的指令.在bc工作环境下,可以使用以下计算符号:+ 加法 - 减法 * 乘法 / 除法 ^ 指数 % 余数如: 3+4;5*2;5^2;18/4      &l ...

  4. Python 字符串操作,截取,长度

    1.字符串操作: 字符串长度: s = "; slen = len(s); 字符串截取: print s[:-:-] #截取,逆序隔1个取一个字符 print s[:-:-] #截取,逆序隔 ...

  5. electron sendInputEvent keyboard

    https://github.com/electron/electron/issues/5005 webview.getWebContents().sendInputEvent({ type: 'ch ...

  6. jsp页面添加时间

    <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <body& ...

  7. obstacle

    obstacle - 必应词典 美[ˈɑbstək(ə)l]英[ˈɒbstək(ə)l] n.障碍:障碍物:阻碍:绊脚石 网络妨碍:干扰:妨害

  8. 编辑距离12 · Edit Distance12

    [抄题]: 给出两个单词word1和word2,计算出将word1 转换为word2的最少操作次数. 你总共三种操作方法: 插入一个字符 删除一个字符 替换一个字符 [思维问题]: [一句话思路]: ...

  9. struts框架中OGNL表达式的使用之jsp页面获取action中的属性值

    在jsp页面中获取action中的值: 1.写一个action类OgnlAction类: 需要注意的地方: 如果在aciton中直接使用ognl表达式,将值存储的值栈中,是不能通过跳转将值传到jsp页 ...

  10. Navicat Premium 12破解补丁

    Navicat Premium 12破解补丁是专门针对Navicat 12制作的一款破解工具,它可以帮助大家成功激活软件,激活后就可以免费使用软件所有功能了,小编亲测可用,有需要的可以下载试试. Na ...