zoukankan      html  css  js  c++  java
  • OpenGL判断一个点是否可见

    关于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 >= 0 && v.x < wsize.x && v.y >= 0 && 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 - p0v = p2 - p0

    2. 记nu × 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

  • 相关阅读:
    Chrome开发工具Elements面板(编辑DOM和CSS样式)详解
    Chrome调试大全
    横向子菜单栏ul根据其子元素li个数动态获取宽度,并与父li绝对垂直居中的jquery代码段
    IE6滤镜在实战测试中能让父层里面的子元素产生阴影
    导航栏项目滑过时子菜单显示/隐藏jquery代码
    IE6下完美兼容css3圆角和阴影属性的htc插件PIE.htc
    js函数对象
    兼容ie6及以上和firefox等标准浏览器的表格行滑过时背景色切换的效果
    对js原型对象的拓展和原型对象的重指向的区别的研究
    小结IE6的坑
  • 原文地址:https://www.cnblogs.com/collectionne/p/6750285.html
Copyright © 2011-2022 走看看