新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   XML论坛     W3CHINA.ORG讨论区     计算机科学论坛     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> 本版讨论.NET,C#,ASP,VB技术
    [返回] 中文XML论坛 - 专业的XML技术讨论区计算机技术与应用『 Dot NET,C#,ASP,VB 』 → 使用 DirectX 的假日屏幕保护程序 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 7590 个阅读者浏览上一篇主题  刷新本主题   树形显示贴子 浏览下一篇主题
     * 贴子主题: 使用 DirectX 的假日屏幕保护程序 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     卷积内核 帅哥哟,离线,有人找我吗?
      
      
      威望:8
      头衔:总统
      等级:博士二年级(版主)
      文章:3942
      积分:27590
      门派:XML.ORG.CN
      注册:2004/7/21

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给卷积内核发送一个短消息 把卷积内核加入好友 查看卷积内核的个人资料 搜索卷积内核在『 Dot NET,C#,ASP,VB 』的所有贴子 访问卷积内核的主页 引用回复这个贴子 回复这个贴子 查看卷积内核的博客楼主
    发贴心情 使用 DirectX 的假日屏幕保护程序

    Coding4Fun 小组询问:“我们能提供一些假日主题的 Coding4Fun 文章吗?”

    当然,回答读者的问题也很重要。因此在后面的几篇文章中,我将构建一个项目,以便于回答一些问题赋予 Coding4Fun“目前的成员”的愉快体验。以下是本文将涵盖的几个问题:

    1.
    如何使用托管 DirectX 构建屏幕保护程序?

    2.
    您是否能够解释 MatrixStack 函数的工作方式?

    3.
    是否能够将图形存储在资源文件中?

    该项目将是一个假日主题的屏幕保护程序,假设将其命名为 DirectXmas(这并不是因为我要区分某个特定的假日庆典,而是因为我不想在 DirectX 中使用两次 X)。我将使用在以前文章中说明的纹理技术,来制作一系列圣诞树和其他假日装饰的动画。

    创建屏幕保护程序
    幸运的是,C# 和 VB.Net Express Editions 均随附有屏幕保护程序初学者工具包(图 1)。

    按此在新窗口浏览图片

    图 1. C# 中的屏幕保护程序初学者工具包项目类型。

    “重用”在教程中与在代码中同等重要,因此,有关如何在初学者工具包中创建项目的说明,请参阅 [URL=http://msdn.microsoft.com/Coding4Fun/weekend/weekend1/default.aspx]Andrew Coates 的 weekend warriors 专栏[/URL],该专栏还可在 [URL=http://msdn.microsoft.com/Coding4Fun/]Coding4Fun[/URL] 中找到。您无需阅读整篇文章(当然,除非您打算如此)。只需阅读到创建并运行项目即可。

    该项目将使用 DirectX 示例框架代码以及以前文章中提及的代码,因此需要添加对 Microsoft.DirectX、.Direct3D 和 .Direct3DX 的引用。浏览到示例框架源(通常位于 C:\Program Files\Microsoft DirectX 9.0 SDK (October 2005)\Samples\Managed\Common 下),并添加与 dx*.cs 匹配的文件(避开以 w 开头的文件;它们是未来 DirectX SDK 的测试文件)。最后,添加 Media 文件夹和[URL=http://msdn.microsoft.com/Coding4Fun/zman/zmanTextures3/default.aspx]上一篇纹理文章[/URL]中的三个源文件。解决方案资源管理器如图 2 所示。

    按此在新窗口浏览图片

    图 2. 项目起始点。

    如果您试图在此处编译,会遇到两个错误。第一个错误表明代码不安全。由于性能原因,DirectX 框架具有一些不安全的代码。在项目属性中启用了该选项,如图 3 所示。

    按此在新窗口浏览图片

    图 3. 该项目启用了不安全代码。

    第二个错误表明有两个入口点 — 这很有意义,因为此时有两个项目合并在一起。由于屏幕保护程序示例具有一些有用的代码,因此您应该将其保留为主入口点,并重命名 Texture.cs 中的入口点:

    static int Main()

    更改为

    public static int DirectXmas()

    现在,可以编译并运行整个项目,并且您仍然可以看到示例的默认屏幕保护程序。

    让屏幕保护程序使用 DirectX
    看一下 program.cs 源文件,特别是 Main() 函数。屏幕保护程序只是重命名为 .SCR 的一般 .EXE 程序。当它们运行时,系统会传递一个命令行参数,以告诉它们是否按一般方式运行、显示选项对话框或者在预览窗口中显示。当命令行是 /s 或者没有参数时,屏幕保护程序就会运行。代码位于 ShowScreenSaver() 函数中。要调用原来的入口点并运行我们的 DirectX 代码,请用以下代码取代该例程中的两行代码:

    AskZman.Textures.DirectXmas();

    现在运行程序,您将看到上一篇教程中带纹理的地球。但请等一下,这并不是屏幕保护程序的预期外观。首先,它位于一个窗口中;其次,当您移动鼠标或按下某个按键时,它没有关闭。

    示例框架创建全屏应用程序很容易。找到创建 DirectX 设备的行:

    sampleFramework.CreateDevice( 0, true, Framework.DefaultSizeWidth,
          Framework.DefaultSizeHeight, sample);

    然后,将“true”更改为“false”。第二个参数控制是否在窗口中创建设备。您可能会注意到,程序以较低的分辨率运行,原因是默认的高度和宽度为 640x480。这是屏幕保护程序设置屏幕中的一个设置,但是现在可以使用当前的屏幕分辨率。将代码更改为:

    sampleFramework.CreateDevice(0, false,
    System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width,
    System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height,
    sample);

    现在,程序以全屏方式运行;但是,您只能通过按 ESC 键退出。

    打开示例屏幕保护程序 ScreenSaverForm.cs 的源代码,了解它们如何检查鼠标和键盘。KeyDownMouseDown 事件只负责关闭窗体并退出程序。MouseMove 事件有一点复杂。首次调用它时,代码会存储鼠标的位置。在后续调用中,代码检查鼠标是否从存储点进行了明显的移动。在本例中,明显移动意味着在任意方向上移动超过 10 个像素。我并不十分确定编写该代码的原因,但我怀疑这只是为了防止桌面震动和微弱的地震会取消屏幕保护程序(这在美国西海岸十分重要)。

    如果仔细查看过任何 DirectX 示例,就会知道,尽管您看到的是一个窗体,但代码实际上并不是从 Windows 窗体继承的,因此代码必须经过更改才能使用。幸运的是,示例框架提供了类似的功能。

    如果您还要查看上面的 CreateDevice() 调用,则只需查看应用程序挂钩 KeyDown 事件的上一行内容。

    sampleFramework.Window.KeyDown += new System.Windows.Forms.KeyEventHandler(sample.OnKeyEvent);

    您应该为 MouseMoveMouseDown 事件另外添加两个事件处理程序:

    sampleFramework.Window.MouseMove += new System.Windows.Forms.MouseEventHandler(sample.OnMouseMove);
    sampleFramework.Window.MouseDown += new System.Windows.Forms.MouseEventHandler(sample.OnMouseDown);

    新的 OnMouseDown() 函数如下所示:

    private void OnMouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
    {
                    GraphicsWindow window = (GraphicsWindow)sender;
      window.Close();
    }

    此外,您应该用相同的两行代码取代 OnKeyEvent() 代码。

    以屏幕保护程序示例为例,OnMouseMove() 函数应更改为:

    private bool isActive = false;
    private Point mouseLocation;
    private void OnMouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
    {
      // Set IsActive and MouseLocation only the first time this event is called.
      if(!isActive)
      {
        mouseLocation = e.Location;
        isActive = true;
      }
      else
      {
        // If the mouse has moved significantly since first call, close.
        if ((Math.Abs(e.Location.X - mouseLocation.X) > 10) ||
            (Math.Abs(e.Location.Y - mouseLocation.Y) > 10))
        {
          GraphicsWindow window = (GraphicsWindow)sender;
          window.Close();
        }
      }
    }

    现在,运行程序将获得所需的效果。

    如果您从上一个教程开始就一直进行代码的构建,那么需要改正上次的代码中的一个小“错误”。该代码包含一个到地球纹理的硬编码路径,但当屏幕保护程序位于屏幕保护程序所在的 system32 目录中时,该路径不可用。您可以使用示例框架 Utility.FindMediaFile() 函数来帮助定位该路径。如果查看该函数的源代码,您就会发现它的工作方式是搜索一组“类似的”路径,直到找到命名文件。在 texture.cs 中,OnResetDevice() 用以下代码取代加载地球纹理的代码:

    //Load the textures
    sphericalTexture = TextureLoader.FromFile(e.Device, Utility.FindMediaFile("earth2k.jpg"));

    要使程序作为屏幕保护程序运行,还需要手动执行几个步骤:

    1.
    浏览到 debug 目录并找到 DirectXmas.exe,然后将其重命名为 DirectXmas.scr。

    2.
    将该文件复制到 c:\windows\system32 中。

    3.
    浏览到 Media 目录,并找到 earth2k.jpg 和 UI 文件夹。

    4.
    将它们复制到 c:\windows\system32 中。确保复制整个 UI 文件夹及其内容。

    现在,右键单击桌面并选择 Properties,然后选择 Screen Saver 选项卡(图 4)。

    按此在新窗口浏览图片

    图 4. 选择 DirectXmas 屏幕保护程序。

    选择 DirectXmas 并按 preview 键来查看结果。这里没有屏幕快照,因为屏幕的外观与上一教程末尾的屏幕外观完全相同;只不过它作为屏幕保护程序运行。

    代码还远远没有完成:

    • “Settings”对话框是用于旧示例的;

    • 它只在两个监视器的其中之一上运行;

    • 最糟糕的是,它还没有将我置于假日气氛中!
    上述内容将在后续文章中说明,如果您有任何建议,请告诉我们。

    这次的家庭作业(虽然我从未让任何人将答案发给我):

    • 从示例中移除 UI 代码和特殊呈现(例如,形状选择和透明度),因为它现在确实没有用 — 除了取消屏幕保护程序外,鼠标和键盘没有其他功能。

    • 更改照相机/视图矩阵代码,使屏幕保护程序显示整个世界旋转 — 额外加分点是让地球指向正确方位!

    本文中的代码使用了 C# 2005 Express Edition 和 October 2005 DirectX SDK。

    ZMan 将在这里解决托管 DirectX 编程问题。如果您有问题,请发送到 zman@thezbuffer.com。

    鸣谢:

    感谢

    Carlos Aguilar 提供了代码着色程序

    Fridger Schrempp 提供了 Celestia MotherLode 中的地球纹理

    Copyright ® 2005 TheZBuffer.com

    ZMan 创建了第一个托管 DirectX 社区 Web 站点 http://www.thezbuffer.com。最近,他脱离了 Microsoft 的工作任务,以便专注于游戏技术和 TheZBuffer。您可以在他的网络日记中看到他的工作进度。

    转到原英文页面


       收藏   分享  
    顶(0)
      




    ----------------------------------------------
    事业是国家的,荣誉是单位的,成绩是领导的,工资是老婆的,财产是孩子的,错误是自己的。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2008/2/1 15:25:00
     
     卷积内核 帅哥哟,离线,有人找我吗?
      
      
      威望:8
      头衔:总统
      等级:博士二年级(版主)
      文章:3942
      积分:27590
      门派:XML.ORG.CN
      注册:2004/7/21

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给卷积内核发送一个短消息 把卷积内核加入好友 查看卷积内核的个人资料 搜索卷积内核在『 Dot NET,C#,ASP,VB 』的所有贴子 访问卷积内核的主页 引用回复这个贴子 回复这个贴子 查看卷积内核的博客2
    发贴心情 
    本页内容
    [URL=http://www.microsoft.com/china/MSDN/library/enterprisedevelopment/softwaredev/holidayDirectXmas2.mspx#ENB]按此在新窗口浏览图片[/URL] [URL=http://www.microsoft.com/china/MSDN/library/enterprisedevelopment/softwaredev/holidayDirectXmas2.mspx#ENB]绘制树[/URL]
    [URL=http://www.microsoft.com/china/MSDN/library/enterprisedevelopment/softwaredev/holidayDirectXmas2.mspx#EHC]按此在新窗口浏览图片[/URL] [URL=http://www.microsoft.com/china/MSDN/library/enterprisedevelopment/softwaredev/holidayDirectXmas2.mspx#EHC]问题,问题,还是问题[/URL]
    [URL=http://www.microsoft.com/china/MSDN/library/enterprisedevelopment/softwaredev/holidayDirectXmas2.mspx#E5D]按此在新窗口浏览图片[/URL] [URL=http://www.microsoft.com/china/MSDN/library/enterprisedevelopment/softwaredev/holidayDirectXmas2.mspx#E5D]解决方案,解决方案,解决方案[/URL]
    [URL=http://www.microsoft.com/china/MSDN/library/enterprisedevelopment/softwaredev/holidayDirectXmas2.mspx#E4G]按此在新窗口浏览图片[/URL] [URL=http://www.microsoft.com/china/MSDN/library/enterprisedevelopment/softwaredev/holidayDirectXmas2.mspx#E4G]第二次绘制树[/URL]
    [URL=http://www.microsoft.com/china/MSDN/library/enterprisedevelopment/softwaredev/holidayDirectXmas2.mspx#E2H]按此在新窗口浏览图片[/URL] [URL=http://www.microsoft.com/china/MSDN/library/enterprisedevelopment/softwaredev/holidayDirectXmas2.mspx#E2H]使用 MatrixStack 绘制树[/URL]
    [URL=http://www.microsoft.com/china/MSDN/library/enterprisedevelopment/softwaredev/holidayDirectXmas2.mspx#EJBAC]按此在新窗口浏览图片[/URL] [URL=http://www.microsoft.com/china/MSDN/library/enterprisedevelopment/softwaredev/holidayDirectXmas2.mspx#EJBAC]家庭作业[/URL]

    [URL=http://msdn.microsoft.com/coding4fun/holiday/DirectXmas/default.aspx]上次[/URL],您创建了托管 DirectX 屏幕保护程序的基本外壳程序。如果您做了“家庭作业”,就应该有一个旋转地球屏幕保护程序。如果没有做家庭作业,您一定会感到有些羞愧。您可以从之前[URL=http://download.microsoft.com/download/d/4/5/d4556729-81f9-4578-936a-111721b27e49/DirectXmas1Homework-CS.msi]第 1 部分中的 DirectXmas 家庭作业[/URL]链接下载稍加修改的源代码。

    本周,我将开始将形状添加到场景以创建假日主题的过程,您将了解到场景图形和 DirectX MatrixStack。

    绘制树
    在[URL=http://msdn.microsoft.com/coding4fun/zman/]纹理文章[/URL]中,我讨论了 DirectX 通过不包含 API 来映射纹理的方式,因为这项任务最好由艺术家来完成。当然,创建模型也属于同一范畴。如果您想要一棵漂亮的圣诞树,可以在网上寻找,可以学习如何创建模型,或者向艺术家购买。但是,由于本专栏是关于编程的,因此我将在 API 中用基本形状制作一棵简单的树。您可以选择使用球形、方盒形、圆柱形和茶壶形。圆柱形使您能够为顶部和底部设置半径,从而允许我们绘制锥形。分别使用细圆柱形和 3 个锥形绘制树干和分枝,这样您就可以绘制出一个树形状。它看上去有点像是卡通画,但那就是游戏开发人员称之为“程序员艺术”的东西!

    首先,用以下代码取代呈现地球的代码:

    类声明:该代码可以创建转换矩阵,以便将四个对象中的每一个放在屏幕的正确位置。由于所创建圆柱形的长度指向错误的方向,您必须旋转并转换它们:

    private Mesh trunk;
    private Mesh treeBottom;
    private Mesh treeMiddle;
    private Mesh treeTop;
    private Material trunkMaterial;
    private Material treeMaterial;
    private static Matrix treeOrientation = Matrix.RotationX((float)(-Math.PI / 2.0));
    private Matrix trunkPosition = treeOrientation * Matrix.Translation(0f, -1.1f, 0f);
    private Matrix treeBottomPosition = treeOrientation * Matrix.Translation(0f, -.4f, 0f);
    private Matrix treeMiddlePosition = treeOrientation * Matrix.Translation(0f, .4f, 0f);
    private Matrix treeTopPosition = treeOrientation * Matrix.Translation(0f, 1.2f, 0f);

    OnCreateDevice:该代码可以为树干创建圆柱形,并为树体创建三个锥形:

    //Create materials and Meshes here
    trunkMaterial = new Material();
    trunkMaterial.Ambient = Color.Brown;
    trunkMaterial.Diffuse = trunkMaterial.Ambient;
    treeMaterial = new Material();
    treeMaterial.Ambient = Color.Green;
    treeMaterial.Diffuse = treeMaterial.Ambient;
    trunk = Mesh.Cylinder(e.Device, .2f, .2f, .4f, 32, 16);
    treeBottom = Mesh.Cylinder(e.Device, 1.0f, .4f, 1f, 32, 16);
    treeMiddle = Mesh.Cylinder(e.Device, .8f, .2f, 1f, 32, 16);
    treeTop = Mesh.Cylinder(e.Device, .6f, 0f, 1f, 32, 16);
    OnDestroyDevice:
    //Dispose meshes here
    if (trunk != null) trunk.Dispose();
    if (treeBottom != null) treeBottom.Dispose();
    if (treeMiddle != null) treeMiddle.Dispose();
    if (treeTop != null) treeTop.Dispose();

    OnFrameRender:在每个呈现通道中,以正确的材质以及正确的位置和方向绘制树干和三个树体部分:

    //Draw the tree
    device.Material = trunkMaterial; ;
    device.Transform.World = trunkPosition;
    trunk.DrawSubset(0);
    device.Material = treeMaterial; ;
    device.Transform.World = treeBottomPosition;
    treeBottom.DrawSubset(0);
    device.Transform.World = treeMiddlePosition;
    treeMiddle.DrawSubset(0);
    device.Transform.World = treeTopPosition;
    treeTop.DrawSubset(0);

    您可能会问,在哪里可以获得大小和位置的所有数值。在呈现树时,我将其草拟在一个老式纸张上,然后将它们拧在一起。

    还有几行与纹理有关的额外代码需要移除,但是在完成后,结果如下所示:

    按此在新窗口浏览图片


    问题,问题,还是问题
    我确定,许多人会认为“这看上去不像是一个具有高度可伸缩性的 3D 图形绘制方法”,没错,正是这样。我希望用尽可能简单的方法来展示绘制多个物体的基本操作。您可以看到,对于每个物体,您必须先设置状态(材质、转换、呈现状态、纹理、纹理状态等),然后再实际绘制它。通过在每个绘制调用之前设置环境转换,来完成物体的定位、缩放和方向。如果发生变化,您只需设置状态。例如,请注意,我没有为树的各个部分设置材质,因为它们都是相同的。

    对于具有成百上千或成千上万个物体的场景而言,不应该这样做。一个较好的面向对象的体系结构可以在单个类中集合有关每个物体的信息;显然,如果有多个物体,最好将它们存储在一个集合中。但是,在解决这些问题之前,让我们来看一下有关当前代码的另一个问题。

    假设要将树移到左边。您可能认为,按如下方式更改树干的位置即可,但实际上不是这样:

    private Matrix trunkPosition = treeOrientation * Matrix.Translation(-1f, -1.1f, 0f);

    它只能移动树干。虽然树看上去好像是单个物体,但它实际上是由 4 个毫无关系的部分组成的。因此,您必须更改每个部分的 X 位置,才能移动整棵树。请考虑一下您喜欢的 RPG 游戏:当角色移动时,他的剑与其一起移动,因此,当树干移动时,树以及树上的所有装饰物也应该一起移动。这里的问题在于,每个物体都是绝对定位的。换句话说,当明确指定物体在屏幕中的位置时,它就应该放置在那里。要能够连接物体,您需要使用相对位置。这意味着,treeBottom 不是在位置 (x,y,z) 上,而是位于 (dx, dy, dz) 上,其中,dx 是相对于其所连接的物体的位置更改 — 在本例中是树干。那些进行过 Web 或 Windows 窗体编程的人员应该十分了解绝对位置和相对位置。这是完全相同的,只不过多一个维度。这意味着,除了状态和位置以外,还需要了解其相对于哪个物体。如果绘出树的各个部分(以及某些装饰物)之间的关系,则应该如下所示:

    按此在新窗口浏览图片


    现在,树对象具有一个根,称为树干,其他所有物体在结构中都以相对于该物体的方式描述。这意味着,如果您移动或旋转根,其他所有物体将随之一起移动和旋转。我确定,您在其他代码中看到过类似的树结构,并且知道它们非常易于实现。树中的每个节点都具有一组相同类型的子对象。如果您阅读过有关 3D 编程原理的书,就会知道这称为[URL=http://en.wikipedia.org/wiki/Scene_graph]场景图形[/URL]。显然,“场景”是因为它在我们的场景中表示物体;“图形”部分的来源是,这种结构称为[URL=http://en.wikipedia.org/wiki/Directed_acyclic_graph]有向无环图[/URL]更为正确。我将这留给 [URL=http://www.wikipedia.org/]Wikipedia[/URL] 来告诉您全部内容!

    解决方案,解决方案,解决方案
    有 3 个问题需要解决:

    1.
    将有关某个物体的所有信息收集到一起

    2.
    将其存储在结构中以便于操作

    3.
    更改对象,以便它们相对于其父对象进行绘制

    将一个新类添加到名为 GameObject 的解决方案中。(我知道这不是一个游戏,但您不能使用 3dObject 作为类名。)添加 DirectX 命名空间和 System.Drawing。确保您使用了与主源代码文件相同的命名空间,或者包含正确的 using 语句:

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Drawing;

    using Microsoft.DirectX;
    using Microsoft.DirectX.Direct3D;

    namespace AskZMan
    {
        public class GameObject
        {
            
        }
    }

    您需要为网格、颜色、位置、方向和子集合添加属性访问器。这是一个使用 Visual C# 2005 Express Edition 中片断功能的绝好机会。键入“prop”并按 Tab 键两次。您将看到一个属性模板,其中带有一个私有成员和一个公共的 get/set 访问器。

    按此在新窗口浏览图片


    键入类型“Mesh”并按 Tab 键。键入私有成员变量的名称“mesh”,并再次按 Tab 键。最后,键入公共属性的名称“Mesh”,并按 Enter 键。请注意,Visual Studio 已经在模板其余部分的正确位置上为您填写了内容。

    您应该对以下属性重复这些操作:

    类型 私有名称 公共名称
    Vector3
    position
    Position

    Vector3
    orientation
    Orientation

    List
    children
    Children

    请注意,我使用了 .NET 2.0 中新的集合泛型。以前,该集合只是一个 List,它只能存储 Object 类型的内容,因此不是强类型的。如果您已经使用 List 编写了代码,就会知道代码中包含了类型转换。对于泛型集合,所有成员都是强类型的,因此编译器和 VS 能够确切知道存储的内容,并对其进行强制。

    修改私有定义,以设置某些合理的默认值:

    private Vector3 position = Vector3.Empty;
    private Vector3 orientation = Vector3.Empty;
    private List<;GameObject>; children = new List<;GameObject>;();

    我们的示例只需要公开一种普通颜色,而不是整个材质,因此可以将属性简化为接受一种颜色,并根据需要创建材质:

    private Material material = new Material();

    public Color Color
    {
        get { return material.Ambient; }
        set
        {
            material.Ambient = value;
            material.Diffuse = value;
        }
    }

    由于在大多数情况下,您将同时设置所有属性,因此使用一个构造函数来为您执行此操作是有意义的:

    public GameObject(Mesh Mesh, Color Color, Vector3 Position, Vector3 Orientation)
    {
        this.Mesh = Mesh;
        this.Color = Color;
        this.Position = Position;
        this.Orientation = Orientation;
    }

    该类中唯一所需的额外代码是呈现 GameObject 的代码。由于数据结构可存储物体之间的所有关系,因此绘制整个物体所需的所有操作就是调用根节点上的一个方法。根节点知道它必须绘制本身及其子代。用于呈现的代码可以从 OnRender 例程复制;呈现子代是一个用于呈现每个子物件的 foreach 循环和递归调用:

    public void Render(Device device)
    {
        //draw this object
        device.Material = material; ;
        device.Transform.World =
            Matrix.RotationYawPitchRoll(orientation.Z, orientation.Y, orientation.X)
            * Matrix.Translation(Position.X, Position.Y, Position.Z);  
        mesh.DrawSubset(0);

        //if there are any children then draw them too
        foreach (GameObject child in Children)
        {
            child.Render(device);
        }
    }

    环境转换是根据该物体的方向成员和位置成员创建的。这可通过在位置和方向更改时只创建环境矩阵进行优化,但是我想确保每个人了解它的来源。

    ----------------------------------------------
    事业是国家的,荣誉是单位的,成绩是领导的,工资是老婆的,财产是孩子的,错误是自己的。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2008/2/1 15:27:00
     
     卷积内核 帅哥哟,离线,有人找我吗?
      
      
      威望:8
      头衔:总统
      等级:博士二年级(版主)
      文章:3942
      积分:27590
      门派:XML.ORG.CN
      注册:2004/7/21

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给卷积内核发送一个短消息 把卷积内核加入好友 查看卷积内核的个人资料 搜索卷积内核在『 Dot NET,C#,ASP,VB 』的所有贴子 访问卷积内核的主页 引用回复这个贴子 回复这个贴子 查看卷积内核的博客3
    发贴心情 
    第二次绘制树
    让我们再来一次,现在有一个更好的体系结构。在最后的源代码中,我保留了原始代码,但进行了注释,以便您可以同时看到两个版本。

    为泛型集合添加一个 using 子句:

    using System.Collections.Generic;

    类声明:

    private Mesh trunk;
    private Mesh treeBottom;
    private Mesh treeMiddle;
    private Mesh treeTop;
    private GameObject tree;

    OnCreateDevice:请注意,旋转仅应用于根节点。由于所有转换都是相对的,这意味着,应用于父节点的任何转换都会应用于其下的所有子代;因此,以正确方向旋转树干会旋转整棵树。还要注意的是,现在的位置是相对的,因此更加易于理解:

    //Create materials and Meshes here
    trunk = Mesh.Cylinder(e.Device, .2f, .2f, .4f, 32, 16);
    treeBottom = Mesh.Cylinder(e.Device, 1.0f, .4f, 1f, 32, 16);
    treeMiddle = Mesh.Cylinder(e.Device, .8f, .2f, 1f, 32, 16);
    treeTop = Mesh.Cylinder(e.Device, .6f, 0f, 1f, 32, 16);

    //For the screen saver we create the meshes and the scene graph at the same time
    //this may not be the most efficient thing for your application to do
    tree = new GameObject(trunk, Color.Brown, new Vector3(0f, -1.1f, 0f),new Vector3(0f, (float)(-Math.PI / 2),  0f));
    GameObject bottom = new GameObject(treeBottom, Color.Green, new Vector3(0f, 1f, 0f), Vector3.Empty);
    GameObject middle = new GameObject(treeMiddle, Color.Green, new Vector3(0f, 1f, 0f), Vector3.Empty);
    GameObject top = new GameObject(treeTop, Color.Green, new Vector3(0f, 1f, 0f), Vector3.Empty);
    tree.Children.Add(bottom);
    bottom.Children.Add(middle);
    middle.Children.Add(top);
    OnFrameRender:
    //Draw the tree
    tree.Render(device);

    但是,如果运行该程序,就会发现它看上去不再像是一棵树了。

    按此在新窗口浏览图片


    虽然代码以彼此相对的方式存储位置和旋转,但 DirectX 并未以该方式工作。DirectX 知道的所有事情就是在绘制调用之前应用的单一环境转换。因此,树干可以正确地定位和旋转,因为它位于根位置。但是,其他物体都位于绝对位置 (0,1,0) 并且没有旋转,而不是相对于树干。您需要修改 Render() 例程,以使其记住父代的位置,以便在绘制子代时能够考虑该位置。

    由于这是呈现场景的一个普通方式,因此 DirectX 会提供 helper 例程。它们封装在 [URL=http://msdn.microsoft.com/library/?url=/library/en-us/directx9_m/directx/ref/ns/microsoft.directx/c/matrixstack/matrixstack.asp]MatrixStack[/URL] 类中。

    使用 MatrixStack 绘制树
    演练时,矩阵堆栈类会提供一种方式,使您的代码记住树中每个节点的正确环境转换。当开始绘制每个物体时,您可以使用 [URL=http://msdn.microsoft.com/library/?url=/library/en-us/directx9_m/directx/ref/ns/microsoft.directx/c/matrixstack/m/push.asp]Push[/URL] 方法保存当前的环境转换。然后,为该特定节点添加转换,它们会与堆栈上已有的转换结合在一起。您可以通过读取[URL=http://msdn.microsoft.com/library/?url=/library/en-us/directx9_m/directx/ref/ns/microsoft.directx/c/matrixstack/p/top.asp] Top[/URL] 属性来检索最后结合的转换,并使用它来设置环境转换。然后,可以绘制节点及其关联的子代。请记住,每个子代还使用 MatrixStack 来记住它的转换。在绘制了节点及其子代之后,您可以通过调用 [URL=http://msdn.microsoft.com/library/default.asp?url=/library/en-us/directx9_m/directx/ref/ns/microsoft.directx/c/matrixstack/m/pop.asp]Pop[/URL] 来还原环境转换的状态,然后就可以从绘制例程返回了。

    修改 GameObject 类以包含单个静态 MatrixStack

    static MatrixStack stack = new MatrixStack();

    Render() 方法更新为 push 和 pop,并使用 MatrixStack 操作进行转换:

    public void Render(Device device)
    {
        //draw this object
        device.Material = material; ;
        stack.Push();
        stack.TranslateLocal(position.X, position.Y, position.Z);
        stack.RotateYawPitchRollLocal(orientation.Z, orientation.Y, orientation.X);
        device.Transform.World = stack.Top;
        mesh.DrawSubset(0);

        //if there are any children then draw them too
        foreach (GameObject child in Children)
        {
            child.Render(device);
        }

        //Restore the transformations
        stack.Pop();
    }

    最后的呈现看上去应该再次像是一棵树。您应该能够更改单个物品的位置和旋转,并看到此更改会自动影响所有子代。

    在最终的源代码中,我为树添加了一些装饰品。

    按此在新窗口浏览图片


    家庭作业
    您已经看到了由 DirectX 基元组成的树。请尝试只使用这些基元以及创建的转换、旋转和基本场景图形来制作其他假日模型。如果我发现了较好的内容,将在下一篇专栏中使用,并会加上您的姓名(如果您愿意)。

    本文中的代码使用了 [URL=http://msdn.microsoft.com/vstudio/express/visualCsharp/default.aspx]Visual C# 2005 Express Edition[/URL] 和 [URL=http://www.microsoft.com/downloads/details.aspx?FamilyId=1C8DC451-2DBE-4ECC-8C57-C52EEA50C20A]October 2005 DirectX SDK[/URL]。

    ZMan 将在这里解决托管 DirectX 编程问题。如果您有问题,请发送到 [URL=mailto:ZMan@thezbuffer.com]ZMan@thezbuffer.com[/URL]。

    鸣谢:

    感谢

    [URL=mailto:web@carlosag.NET]Carlos Aguilar[/URL] 提供了[URL=http://www.carlosag.net/Tools/CodeColorizer/Default.aspx]代码着色程序[/URL]

    Copyright &reg; 2005 TheZBuffer.com

    ZMan 创建了第一个托管 DirectX 社区 Web 站点:[URL=http://www.thezbuffer.com/]http://www.thezbuffer.com[/URL]。最近,他脱离了 Microsoft 的工作任务,以便专注于游戏技术和 TheZBuffer。您可以[URL=http://www.indiegameguy.com/blogs/ZMan/default.aspx]在他的网络日记[/URL]中看到他的工作进度。

    [URL=http://msdn.microsoft.com/coding4fun/holiday/DirectXmas2/default.aspx]转到原英文页面[/URL]

    ----------------------------------------------
    事业是国家的,荣誉是单位的,成绩是领导的,工资是老婆的,财产是孩子的,错误是自己的。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2008/2/1 15:27:00
     
     卷积内核 帅哥哟,离线,有人找我吗?
      
      
      威望:8
      头衔:总统
      等级:博士二年级(版主)
      文章:3942
      积分:27590
      门派:XML.ORG.CN
      注册:2004/7/21

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给卷积内核发送一个短消息 把卷积内核加入好友 查看卷积内核的个人资料 搜索卷积内核在『 Dot NET,C#,ASP,VB 』的所有贴子 访问卷积内核的主页 引用回复这个贴子 回复这个贴子 查看卷积内核的博客4
    发贴心情 
    本页内容
    [URL=http://www.microsoft.com/china/MSDN/library/enterprisedevelopment/softwaredev/holidayDirectXmas3.mspx#ERB]按此在新窗口浏览图片[/URL] [URL=http://www.microsoft.com/china/MSDN/library/enterprisedevelopment/softwaredev/holidayDirectXmas3.mspx#ERB]闪烁的灯[/URL]
    [URL=http://www.microsoft.com/china/MSDN/library/enterprisedevelopment/softwaredev/holidayDirectXmas3.mspx#EQD]按此在新窗口浏览图片[/URL] [URL=http://www.microsoft.com/china/MSDN/library/enterprisedevelopment/softwaredev/holidayDirectXmas3.mspx#EQD]再见,Frosty[/URL]
    [URL=http://www.microsoft.com/china/MSDN/library/enterprisedevelopment/softwaredev/holidayDirectXmas3.mspx#ERE]按此在新窗口浏览图片[/URL] [URL=http://www.microsoft.com/china/MSDN/library/enterprisedevelopment/softwaredev/holidayDirectXmas3.mspx#ERE]清理屏幕保护程序[/URL]
    [URL=http://www.microsoft.com/china/MSDN/library/enterprisedevelopment/softwaredev/holidayDirectXmas3.mspx#EYE]按此在新窗口浏览图片[/URL] [URL=http://www.microsoft.com/china/MSDN/library/enterprisedevelopment/softwaredev/holidayDirectXmas3.mspx#EYE]家庭作业[/URL]

    [URL=http://www.microsoft.com/china/MSDN/library/enterprisedevelopment/softwaredev/holidayDirectXmas2.mspx]上次[/URL],您使用了一个基本的场景图形数据结构来绘制多个形状,以创建带有装饰品的第一棵树。我希望,您能够从 DirectX 提供的基本网格形状中获得一些创建新物品的乐趣。自从上次的最终下载以后,我又添加了另一个假日对象 — 雪人。它的身体是三个球形,帽子是两个圆柱形,鼻子是一个圆锥形(就像树体部分一样),眼睛是一对较小的球形。此外,我还添加了一个非常大的细方框来表示地面,并将树创建放到一个循环中。现在,由于绘制过程是相对的,您只需更改树干的位置,这样整棵树就会在另一个位置绘制。所有代码的样式都与上次的树创建完全相同,它们几乎都在 OnCreateDevice 函数中。我还稍微移动了一下照相机以获得一个更好的视角,并减慢相机的旋转速度。您可以在从本文开始处的链接下载本次的起点位置,并将其与上次终止的位置进行比较。起点如下所示:

    按此在新窗口浏览图片


    本周,您将在场景中添加一些动画,并清理代码。

    闪烁的灯
    计算机游戏中的动画是屏幕中随时间变化的任何事物 — 不只限于位置或形状的更改,而是以绘制方式进行的任何更改,如颜色或特殊效果。

    我将介绍的第一个动画是更改树上装饰物的颜色。根据当前描述场景的方式,对这些特定形状的唯一引用位于树结构中。每当需要更改并搜寻某物件时,可以深入树结构,但最好能够直接找到想要更改的物件。这样,要做的第一件事情是,将装饰物添加到一个列表中以便于访问。添加以下装饰物以便存储:

    private List<;GameObject>; decorations=new List<;GameObject>;();

    然后,在将它们添加到场景图形时存储一个引用。将 addOrnaments 中的循环内容修改为:

    GameObject decoration=new GameObject(bauble, Color.Red, new Vector3((float)(baseWidth* Math.Sin(angle)),
          (float)(baseWidth * Math.Cos(angle)), -0.55f), Vector3.Empty);
    decorations.Add(decoration);
    treepart.Children.Add(decoration);

    示例框架提供了一个名为 OnFrameMove 的函数,该函数始终在任何呈现发生之前调用。这里是对场景(例如,动画)进行更新的正确位置。动画几乎总是根据当前的游戏时间正确完成,而不是每次调用 OnFrameMove 时的固定增量。不同的处理器速度和不同的图形硬件相结合意味着您无法保证 OnFrameMove 的调用频率。因此,虽然某个动画在您的计算机上以正常速度播放,但您应该知道,它在其他计算机上的播放速度可能会加快或减慢。如果您将所有动画基于当前的游戏时间,则可以确保无论帧速率是多少,所有一切将按所需的速度进行。

    例如,假设您打算在一秒钟内将某个物体在 x 方向上移动 5.0 个单位。您可以向每个帧的 x 值添加 0.1。如果帧速率是 60fps,则每秒钟物体将会移动 6.0 个单位;但是,在一台速度较快的计算机上,如果帧速率是 120fps,则会在相同的时间内将物体移动 12.0 个单位。正确的方法是根据应用程序的当前耗用时间进行计算,例如 x=startPoint + 5.0 * appTime。这样,无论帧速率是多少,x 都将以 5.0/秒的速度变化。

    您可以看到,我如何使用该方法更改了相机的位置:

    new Vector3(5f * (float)(Math.Sin(appTime/10.0)), .5f, 5f * (float)(Math.Cos(appTime/10.0)))

    appTime 由框架维护,并传递给 OnFrameMove 函数。

    对于该动画,您希望每 N 秒更改一次装饰物的颜色,其中 N 为用户能够在 Options 对话框中选择的值。目前还没有 Options 对话框,因此这只是一个变量。您还应该允许用户禁用动画,因此应该为该决定提供一个布尔变量。

    请添加以下声明:

    //decoration animation control variables
    private bool animateDecorations = false;
    private double animateDecorationsTime = 0.2;
    private double lastAnimationTickTime = 0.0;
    private Color[] decorationColors =
    new Color[] {Color.Red, Color.Blue, Color.BlanchedAlmond, Color.DarkOrange, Color.HotPink};

    动画很短。如果在上次变化后经过了足够长的时间,则遍历所有装饰物,并在 OnFrameMove 函数中为每个装饰物指定一个随机的新颜色:

    if (animateDecorations)
    {
        if (appTime - lastAnimationTickTime >; animateDecorationsTime)
        {
            lastAnimationTickTime = appTime;
            Random randomColor = new Random();
            foreach (GameObject decoration in decorations)
            {
                decoration.Color = decorationColors[randomColor.Next(5)];
            }
        }
    }

    如果所有事情都按计划执行,您应该在运行时看到装饰灯的变化:

    按此在新窗口浏览图片


    再见,Frosty
    这个标题可能只对那些在美国度过假期的人有意义。[URL=http://www.imdb.com/title/tt0064349/]Frosty the Snowman[/URL] 是一个传统的美国假日卡通片,片中 Frosty 走到了太阳底下并承受了一系列的后果。我无法制作像 [URL=http://www.pixar.com/]Pixar[/URL] 那样出色的动画,但是在该场景中融化雪人非常简单,只需更改身体部分的坐标以使其滑到“地面”以下即可。将帽子留在一堆雪上是一个不错的动画结尾。要制作成动画的 GameObjectOnCreateDevice 中当前的局部变量,因此将它们的范围扩展到整个类,然后仅将它们的实例留在 OnCreateDevice 中:

    private GameObject lowerBodyNode;
    private GameObject upperBodyNode;
    private GameObject headNode;
    //melting animation control variables
    private bool animateSnowman = true;
    private double animateSnowmanSpeed = 120.0f;
    //2 minutes change to a lower number if you are not patient!
    //replace the similar lines in OnCreateDevice with these
    lowerBodyNode = new GameObject(body, Color.White, new Vector3(0f, .15f, 0f), Vector3.Empty);
    upperBodyNode = new GameObject(body, Color.White, new Vector3(0f, .3f, 0f), Vector3.Empty);
    headNode = new GameObject(head, Color.White, new Vector3(0f, .25f, 0f), Vector3.Empty);
    要制作这三个形状的动画,使之向地面运动,您只需在一段适当的时间内在 OnFrameMove 函数中减少位置的 y 值。

    if (animateSnowman)
    {
        if (appTime <; animateSnowmanSpeed)
        {
            Vector3 position = lowerBodyNode.Position;
            position.Y = (float)(0.15 - 0.35 * (appTime / animateSnowmanSpeed));
            lowerBodyNode.Position = position;

            position = upperBodyNode.Position;
            position.Y = (float)(0.3 - 0.2 * (appTime / animateSnowmanSpeed));
            upperBodyNode.Position = position;

            position = headNode.Position;
            position.Y = (float)(0.25 - 0.15 * (appTime / animateSnowmanSpeed));
            headNode.Position = position;
        }
    }

    请注意,由于所有物体都是相对的,您不必考虑制作帽子、眼睛或鼻子的动画。由于它们与头部关联,因此只需随着头部移动即可。雪人必须在某一点停止融化,因此一旦应用程序时间超过融化时间,该动画就会停止。雪人的每个部分都根据当前的应用程序时间和可以更改的速度因素来更改位置。

    按此在新窗口浏览图片


    清理屏幕保护程序
    现在,屏幕保护程序的图形工作已经完成了。如果这就是您感兴趣的全部内容,那么咱们可以下次再见。本文剩余的一小部分内容是,添加屏幕保护程序控件并移除您在开始使用的 RSS 屏幕保护程序的残余物。

    由于您不需要 RSS 媒体或示例 UI 媒体,因此可以移除 media 文件夹。您还可以删除 documentation 文件夹和 RSS 文件夹,因为您不将再需要它们。删除 ScreenSaverForm.cs,然后移除仍然引用 RSS 对象的两行代码。最简单的方法是编译并移除有错误的行。

    最后,您需要使用映射到动画中所使用变量的控件来更新 OptionsForm。由于这里不是 Windows 窗体专栏,因此我不会逐步演练这些步骤,但是您可以下载示例代码来查看它的工作方式。这些值保存在配置文件中(与屏幕保护程序初学者工具包一样),并在屏幕保护程序启动时读取。最终代码可以从本文开头处的链接下载。

    安装屏幕保护程序比第一篇文章更简单,因为不需要考虑媒体。只需将 .EXE 重命名为 .SCR,然后将它和配置文件一起复制到 system32 目录中。如果您需要更多详细说明,请参阅本系列的第一部分。

    现在,您已经使用示例框架完成了托管 DirectX 屏幕保护程序,创建并使用了简单的场景图形,此外了解了如何在场景中制作物体的动画。请记住,就像我在第一部分中描述的那样,没有人会真正使用大量球形和圆柱形来创建图形。这只是一个介绍场景图形和动画效果的简便方式,而不必考虑复杂的模型。我希望,以“乐高”方式考虑根据其他形状来构建形状也很有趣。

    家庭作业
    我确定,大多数人对代码所作的第一件事就是更改树的密度,以使场景中具有更多的树,我还确定您会对屏幕保护程序性能的下降速度感到震惊。毕竟,您可能花费了几百美元来购买一个每秒可以绘制数百万个三角形的图形卡,但是出于某些原因,包含较少三角形的几百棵树却使性能下降了。这些问题的解决方案将在未来的 Ask The Zman 专栏中提供,但现在,对于在功能如此强大的硬件上运行速度如此之慢这一事实,我只想听取您的看法。

    本文中的代码使用了 [URL=http://msdn.microsoft.com/vstudio/express/visualCsharp/default.aspx]C# 2005 Express Edition[/URL] 和 [URL=http://www.microsoft.com/downloads/details.aspx?FamilyId=1C8DC451-2DBE-4ECC-8C57-C52EEA50C20A]October 2005 DirectX SDK[/URL]。

    ZMan 将在这里解决托管 DirectX 编程问题。如果您有问题,请发送到 [URL=mailto:zman@thezbuffer.com]zman@thezbuffer.com[/URL]。

    鸣谢:

    感谢

    [URL=mailto:web@carlosag.net]Carlos Aguilar[/URL] 提供了[URL=http://www.carlosag.net/Tools/CodeColorizer/Default.aspx]代码着色程序[/URL]

    版权所有 (c) 2005 TheZBuffer.com

    ZMan 创建了第一个托管 DirectX 社区 Web 站点 [URL=http://www.thezbuffer.com/]http://www.thezbuffer.com[/URL]。最近,他脱离了 Microsoft 的工作任务,以便专注于游戏技术和 TheZBuffer。您可以在他的[URL=http://www.indiegameguy.com/blogs/zman/default.aspx]网络日记[/URL]上看到他的工作进度。

    [URL=http://msdn.microsoft.com/coding4fun/holiday/directxmas3/default.aspx]转到原英文页面[/URL]

    ----------------------------------------------
    事业是国家的,荣誉是单位的,成绩是领导的,工资是老婆的,财产是孩子的,错误是自己的。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2008/2/1 15:28:00
     
     GoogleAdSense
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 Dot NET,C#,ASP,VB 』的所有贴子 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2024/4/27 17:22:32

    本主题贴数4,分页: [1]

    管理选项修改tag | 锁定 | 解锁 | 提升 | 删除 | 移动 | 固顶 | 总固顶 | 奖励 | 惩罚 | 发布公告
    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    183.594ms