zoukankan      html  css  js  c++  java
  • 在3D世界中创建不同的相机模式——检查对象是否可见

    2.5 检查对象是否可见

    问题

    你想检查对象是否可见来决定是否要绘制这个物体。

    解决方案

    XNA拥有BoundingFrustum类支持这个功能,你可以通过传递View和Projection矩阵创建一个BoundingFrustum类,然后就可以很容易地在这个类中检查对象是否包含在视锥体中。

    工作原理

    视锥体定义了相机可以看到的范围,类似于削去顶部的金字塔,如图2-4所示,它的边是由视域角,*裁*面和远裁*面决定的,更多的信息可见教程2-1。

    你应该让XNA只绘制包含在视锥体中的物体,否则,你会浪费显卡的处理能力。所以你需要一个方法用来检测对象是否在视锥体中。

    XNA Framework中的BoundingFrustum类包含了实现这个功能完整方法,你只需通过指定相机的观察矩阵和投影矩阵创建BoundingFrustum类即可。

    1

    图2-4 相机的视锥体

    BoundingFrustum对象允许你使用它的Contain方法,你可以传递一个Vector3和一个BoundingSphere或BoundingBox检查是否包含在相机的视锥中。这个方法会返回一个Containment Type对象,这个对象包含三个值:

    • Contains:完整包含在视锥体中的测试对象。
    • Intersects:部分包含在视锥体中的测试对象,这时这个对象与视锥体相交。
    • Disjoint:不在视锥体中的测试对象。
    检查3D空间中的点是否在相机可视范围内

    要检查一个点是否可见,只需将这个点传递到Contains方法:

    Vector3 pointToTest = new Vector3(0, 0, 0); 
    BoundingFrustum cameraFrustum = new BoundingFrustum(fpsCam.ViewMatrix * fpsCam.ProjectionMatrix);
    ContainmentType containmentType = cameraFrustum.Contains(pointToTest); 
    if (containmentType != ContainmentType.Disjoint) 
    {
        Window.Title = "Point inside frustrum"; 
        graphics.GraphicsDevice.Clear(Color.CornflowerBlue); 
    }
    else
    {
        Window.Title = "Point outside frustrum"; 
        graphics.GraphicsDevice.Clear(Color.Red); 
    }

    这个例子中你检查了点(0,0,0)是否在视锥体中,将检查结果写在了窗口标题栏中并相应地改变背景颜色。

    检查一个物体是否可见

    如果你想检查一个物体是否可见,一个方法是事先检查模型的每个顶点,这样做非常耗时。更快的方法(尽管不太确切)是定义一个包含模型的包围体,然后检查这个包围体是否在视锥体中。能包含模型的最简单的包围体是一个球。

    XNA已在BoundingSphere类中支持创建一个包围体,使用教程4-5中介绍的LoadModelWithBoundingSphere方法载入模型,并在Tag属性中保存BoundingSphere:

    myModel = XNAUtils.LoadModelWithBoundingSphere(ref modelTransforms, "content/tiny", content);

    技巧:因为模型的Tag属性可以存储任何类型的数据结构,你可以用让它存储任何与模型相关的数据,例如BoundingSphere,纹理等。你甚至还可以创建一个包含所有数据的结构并将这个结构存储在Tag属性中。

    现在有了包围体你就可以测试它是否在视锥体中了:

    BoundingFrustum cameraSight = new BoundingFrustum(fpsCam.ViewMatrix* fpsCam.ProjectionMatrix); 
    ContainmentType containmentType = cameraSight.Contains( (BoundingSphere)myModel.Tag); 
    if (containmentType != ContainmentType.Disjoint)
    {
        Window.Title = "Point inside frustrum"; 
        graphics.GraphicsDevice.Clear(Color.CornflowerBlue); 
        Window.Title = "Point outside frustrum"; 
        graphics.GraphicsDevice.Clear(Color.Red); 
    } 

    检查一个设置了世界矩阵的对象在大多数情况中,你的模型并不在(0,0,0)位置而且还可能旋转,缩放或通过设置世界矩阵放置在其他位置,更多信息可见教程4-2。显然包围体也应该旋转/缩放/移动已匹配模型。这可以通过模型的世界矩阵变换包围体实现:

    Matrix worldMatrix = Matrix.CreateScale(0.01f, 0.01f, 0.01f) *Matrix.CreateTranslation(5, 0, 0); 
    BoundingFrustum cameraSight = new BoundingFrustum(fpsCam.ViewMatrix * fpsCam.ProjectionMatrix); 
    BoundingSphere origSphere = (BoundingSphere)myModel.Tag; 
    BoundingSphere transSphere = origSphere.Transform(worldMatrix); 
    ContainmentType containmentType = cameraSight.Contains(transSphere); 
    if (containmentType != ContainmentType.Disjoint) 
    {
        Window.Title = "Model inside frustrum";
        graphics.GraphicsDevice.Clear(Color.CornflowerBlue); 
        myModel.CopyAbsoluteBoneTransformsTo(modelTransforms); 
        foreach (ModelMesh mesh in myModel.Meshes)
        {
            foreach (BasicEffect effect in mesh.Effects)
            {
                effect.EnableDefaultLighting(); 
                effect.World = modelTransforms[mesh.ParentBone.Index] * worldMatrix; 
                effect.View = fpsCam.ViewMatrix; 
                effect.Projection = fpsCam.ProjectionMatrix; 
            }
            mesh.Draw(); 
        }
        Window.Title = "Model outside frustrum";
        graphics.GraphicsDevice.Clear(Color.Red); 
    }

    注意可见教程第三章学习如何绘制一个模型。这个代码中的主要变化是你调用了origSphere. Transform方法,这个方法获取经过世界变换的BoundingSphere。本例中transSphere的大小是origSphere百分之一,中心沿x轴移动了5个单位。只有当模型在视锥体时才会绘制这个模型。

    代码

    下面的代码会在一个指定位置绘制一个模型,当它在视锥体之外就不会被绘制:

    protected override void LoadContent()
    {
        device = graphics.GraphicsDevice; 
        basicEffect = new BasicEffect(device, null); 
        cCross = new CoordCross(device); 
        myModel = XNAUtils.LoadModelWithBoundingSphere(ref modelTransforms,"content/tiny", content); 
    }
    protected override void Draw(GameTime gameTime)
    {
        Matrix worldMatrix = Matrix.CreateScale(0.01f,0.01f,0.01f)*Matrix.CreateTranslation(5, 0, 0); 
        BoundingFrustum cameraSight = new BoundingFrustum(fpsCam.ViewMatrix * fpsCam.ProjectionMatrix); 
        BoundingSphere origSphere = (BoundingSphere)myModel.Tag; 
        BoundingSphere transSphere = origSphere.Transform(worldMatrix); 
        ContainmentType containmentType = cameraSight.Contains(transSphere); 
        if (containmentType != ContainmentType.Disjoint) 
        {
            Window.Title = "Model inside frustrum"; 
            graphics.GraphicsDevice.Clear(Color.CornflowerBlue); 
            //draw model 
            myModel.CopyAbsoluteBoneTransformsTo(modelTransforms); 
            foreach (ModelMesh mesh in myModel.Meshes) 
            {
                foreach (BasicEffect effect in mesh.Effects) 
                {
                    effect.EnableDefaultLighting(); 
                    effect.World = modelTransforms[mesh.ParentBone.Index] * worldMatrix; 
                    effect.View = fpsCam.ViewMatrix; 
                    effect.Projection = fpsCam.ProjectionMatrix; 
                }
                mesh.Draw(); 
            }
        }
        else
        {
            Window.Title = "Model outside frustrum"; 
            graphics.GraphicsDevice.Clear(Color.Red); 
        }
        
        //draw coordcross 
        cCross.Draw(fpsCam.ViewMatrix, fpsCam.ProjectionMatrix); 
        base.Draw(gameTime); 
    } 
    		
    扩展阅读

    这个教程提出了一个检查点或物体是否在视锥体中的方法。但是如果场景中有大量的物体,那么要对每个对象进行检查会使程序变得很慢。在教程2-9会介绍一个更好的方法处理大场景。

  • 相关阅读:
    This theme is released under creative commons licence, all links in the footer should remain intact解决方法 Fred
    增加ubuntu的内存——设置Swap增加内存
    OpenFlow硬件交换机制作及刷机教程
    ubuntu设置中文
    配置树莓派/Linux默认声卡设备
    github基础操作
    Linux手动添加系统环境共享库路径
    Ubuntu登录界面添加root用户登录选项
    python实现树莓派开机自动发送IP到指定邮箱
    语音信号实时采集与处理
  • 原文地址:https://www.cnblogs.com/AlexCheng/p/2120172.html
Copyright © 2011-2022 走看看