关于OpenGL中判断一个点是否可见,可以分成两种情况讨论:点在2D空间中和3D空间中的时候。并且“在2D空间中”可以看作“在3D空间中”的特殊情况。

温馨提示:

1. 以下讨论以现代OpenGL为基础,如果您只会旧OpenGL,请先学习一些与本文相关的现代OpenGL知识。

2. 文中除vertex shader以外的代码可以认为是GLSL。

2D空间

如果是2D空间,就很好判断了。这里再分成几种情况。

1. 已知原始顶点坐标,没有矩阵变换

设有如下vertex shader:

#version 330 core

layout(location = 0) in vec4 position;  // position.z为0.0,position.w为1.0

void main()

{

  gl_Position = position;

}

将position记为v,由于NDC坐标系统只渲染x、y和z位于[-1,1]区间的点(这里z为0.0),因此判断方法如下:

bool visible(vec2 v)
{
return v.x >= -1.0 && v.x <= 1.0 && v.y >= -1.0 && v.y <= 1.0;
}

2. 已知顶点数据,有矩阵变换

设有如下vertex shader:

#version 330 core

layout(location = 0) in vec4 position;  // position.z为0.0,position.w为1.0

uniform mat4 modelViewProjection;

void main()

{

  gl_Position = modelViewProjection * position;

}

将position记为v,将modelViewProjection记为M,记v' = M * v,如果v'位于NDC坐标范围内就可见。但v'.w可能不为1,因此如果v'.x/v'.w和v'.y/v'.w位于[-1,1]就是可见的。判断方法如下:

bool visible(vec2 v, mat4 m)
{
vec4 result = m * vec4(v, 0.0, 1.0);
result.x /= result.w;
result.y /= result.w;
return result.x >= -1.0 && result.x <= 1.0 && result.y >= -1.0 && result.y <= 1.0;
}

或者合并一下:

bool visible(vec2 v, mat4 m)
{
vec4 result = m * vec4(v, 0.0, 1.0);
return result.x / result.w >= -1.0 && result.x / result.w <= 1.0 &&
result.y / result.w >= -1.0 && result.y / result.w <= 1.0;
}

3. 已知某点的像素坐标

设(x,y)为点v的像素坐标(不用管原点在左下角还是左上角),窗口大小为wsize(x和y分量分别表示宽和高),则判断方法如下:

bool visible(vec2 v, vec2 wsize)
{
return v.x >= && v.x < wsize.x && v.y >= && v.y < wsize.y;
}

到这里都很简单,因为是2D的。

那么3D呢?

3D空间

3D空间中这个事就要复杂许多了(也不多),我也很久都没想出来。最后发现是因为我对OpenGL的顶点变换不够熟悉。

1. 已知NDC坐标

首先,假设有一点p(x,y,z),已经在NDC坐标了,根据NDC坐标的知识,对它的判断应该是这样的:

bool visible(vec3 v)
{
return v.x >= -1.0 && v.x <= 1.0 && v.y >= -1.0 && v.y <= 1.0 && v.z >= -1.0 && v.z <= 1.0;
}

2. 直接判断

设有如下vertex shader:

#version 330 core

layout(location = 0) in vec4 position;  // position.w为1.0

uniform mat4 modelViewProjection;

void main()

{

  gl_Position = modelViewProjection * position;

}

将position记为v,将modelViewProjection记为M,将M * v记为v',v'在clip space(NDC)中。由于v'.w可能不为1,因此需要将v'.x、v'.y和v'.z除以v'.w,再判断v'.x、v'.y和v'.z是否在[-1,1]范围内。判断方法:

bool visible(vec3 v, mat4 m)
{
vec4 result = m * vec4(v, 1.0);
result.x /= result.w;
result.y /= result.w;
result.z /= result.w;
return result.x >= -1.0 && result.x <= 1.0 && result.y >= -1.0 && result.y <= 1.0 && result.z >= -1.0 && result.z <= 1.0;
}

或者合并一下:

bool visible(vec3 v, mat4 m)
{
vec4 result = m * vec4(v, 1.0);
return result.x / result.w >= -1.0 && result.x / result.w <= 1.0 &&
result.y / result.w >= -1.0 && result.y / result.w <= 1.0 &&
result.z / result.w >= -1.0 && result.z / result.w <= 1.0;
}

(现在想了想,这也不难,但是当时死活想不出来,大概是对OpenGL顶点变换了解地太少了吧……)

3. 根据frustum进行判断

发现这种方式就要感谢Google和Stack Overflow了,Google上搜opengl determine if object visible,第一个就是Stack Overflow。里面有人提供了一个链接(来自lighthouse3d,Index标题下方有一堆链接,有兴趣的可以自己看)。顺便再提供一个附加链接:http://www.lighthouse3d.com/tutorials/maths/。文章篇幅都比较长,这里就简单地翻译一部分,有部分更改。

(1) 平面的确定方法

首先要了解一些关于平面的知识。确定一个平面有多种方式,但最常用的方式是使用如下方程(A、B、C、D是常数):

Ax + By + Cz + D = 0 (1)

如果一个平面满足(1)式,为方便,将其称为平面Ax + By + Cz + D = 0。假设已知有3个点p0、p1和p2在同一平面上,A、B、C和D可以用下面方式确定:

1. 记u = p1 - p0,v = p2 - p0

2. 记n = u × v

3. 将n归一化(normalize)

4. 记n = (nx, ny, nz),则A = nx,B = ny,C = nz

5. 由(1)式可得D = -Ax - By - Cz (2)(此时A、B、C已算出)。设p(x,y,z)为平面上的任意一点,将坐标代入(2)式,即可算出D。如果将p用p0代替,则D = -n . p0

(2) 点到平面的距离

设有平面Ax + By + Cz + D = 0和点p(px,py,pz),记

d = Apx + Bpy + Cpz + D

或

d= n . p + D

则|d|为p到平面的距离。注意虽然|d|才是p到平面的距离,但d也是有用的——d的符号可以告诉我们点p在平面的哪一边。如果d > 0,则p在法线n所在的一边;如果d < 0,则p在另一边。当然如果d = 0,则说明p在平面上。这里暂且把d称为有向距离(signed distance)。

(3)

(TO BE CONTINUED)

本文可转载,转载请注明出处:http://www.cnblogs.com/collectionne/p/6750285.html

OpenGL判断一个点是否可见的更多相关文章

  1. Android 判断一个 View 是否可见 getLocalVisibleRect(rect) 与 getGlobalVisibleRect(rect)

    Android 判断一个 View 是否可见 getLocalVisibleRect(rect) 与 getGlobalVisibleRect(rect) [TOC] 这两个方法的区别 View.ge ...

  2. [Modern OpenGL系列(三)]用OpenGL绘制一个三角形

    本文已同步发表在CSDN:http://blog.csdn.net/wenxin2011/article/details/51347008 在上一篇文章中已经介绍了OpenGL窗口的创建.本文接着说如 ...

  3. excel批处理_判断一个名称是不是药品

    把药品名称导入到sheet1的A字段 # -*- coding: utf-8 -*-"""Created on Fri Dec  9 09:38:58 2016判断一个名 ...

  4. 判断一个字符串str不为空的方法

    1.str == null; 2."".equals(str); 3.str.length 4.str.isEmpty(); 注意:length是属性,一般集合类对象拥有的属性,取 ...

  5. Android 如何判断一个应用在运行(转)

    Android 如何判断一个应用在运行  在一个应用中,或一个Service .Receiver中判断一个应用是否正在运行,以便进行一些相关的处理. 这个时候我们需要得到一个ActivityManag ...

  6. ZOJ Problem Set - 1331 Perfect Cubes 判断一个double是否为整数

    zju对时间要求比较高,这就要求我们不能简单地暴力求解(三个循环搞定),就要换个思路:因为在循环时,已知a,确定b,c,d,在外重两层循环中已经给定了b和c,我们就不用遍历d,我们可以利用d^3=a^ ...

  7. PHP如何判断一个数组是一维数组或者是二维数组?用什么函数?

    如题:如何判断一个数组是一维数组或者是二维数组?用什么函数? 判断数量即可 <?php if (count($array) == count($array, 1)) { echo '是一维数组' ...

  8. delphi 判断一个数组的长度用 Length 还是 SizeOf ?

    判断一个数组的长度用 Length 还是 SizeOf ?最近发现一些代码, 甚至有一些专家代码, 在遍历数组时所用的数组长度竟然是 SizeOf(arr); 这不合适! 如果是一维数组.且元素大小是 ...

  9. 用jQuery判断一个元素的各种状态

    用jQuery判断一个元素是否显示   用jQuery判断一个元素是否显示:$(element).is(":visible"); 类似的,判断一个元素是不是第一个子元素:$(ele ...

随机推荐

  1. kNN算法个人理解

    新手,有问题的地方请大家指教 训练集的数据有属性和标签 同类即同标签的数据在属性值方面一定具有某种相似的地方,用距离来描述这种相似的程度 k=1或则较小值的话,分类对于特殊数据或者是噪点就会异常敏感, ...

  2. Java设计模式之(工厂模式)--简单工厂模式--工厂方法模式--抽象工厂模式

    工厂模式: 工厂模式可以分为三类: 1)简单工厂模式(Simple Factory) 2)工厂方法模式(Factory Method) 3)抽象工厂模式(Abstract Factory) 简单工厂模 ...

  3. 做一个常规的banner图——负边距的使用、banner图的拼法

    在这之前,首先要了解如何设置块级元素在块级元素水平居中 方法: 设置子容器为定位元素 水平居中 left:50%:margin-left:-width/2: 垂直居中 top:50%:margin-t ...

  4. input file图片上传预览效果

    两种方法,方法一: js代码: //头像上传预览 $("#up").change(function() { var $file = $(this); var fileObj = $ ...

  5. web开发中,post与get的区别

    区别: 1.Get是从服务器上获取数据,Post是向服务器传送数据. 2.Get是把参数数据队列加到提交表单的Action属性所指的URL中,值和表单内各个字段一一对应,在URL中可以看到.Post是 ...

  6. 基于ZKWeb + Angular 4.0的开源管理后台Demo

    这是一套基于ZKWeb网页框架和Angular 4.0编写的开源管理后台Demo,实现了前后端分离和模块化开发, 地址是: https://github.com/zkweb-framework/ZKW ...

  7. Python冒泡算法和修改配置文件

    先学习之前未完成的冒泡算法 li = [13,22,6,99,11] 从小到大 从第一个数字比较把大的往后移位 for m in range(4): num1 = li[m] num2 = li[m+ ...

  8. 直接用nose进行django项目测试并输出html报告

    先说需求:1.测试django项目:2.打印测试报告(html格式)有以下几种测试方法:1.django自带的测试模块.在app目录下的tests.py文件中写测试类,类似这样: class MyTe ...

  9. [刷题]算法竞赛入门经典(第2版) 5-11/UVa12504 - Updating a Dictionary

    题意:对比新老字典的区别:内容多了.少了还是修改了. 代码:(Accepted,0.000s) //UVa12504 - Updating a Dictionary //#define _XieNao ...

  10. JEESZ分布式框架简介

    声明:该框架面向企业,是大型互联网分布式企业架构,后期会介绍Linux上部署高可用集群项目. 项目基础功能截图(自提供了最小部分) 介绍 1.      项目核心代码结构截图 <modules& ...