zoukankan      html  css  js  c++  java
  • 管理Shader——将Shader导入到您的引擎

    将Shader导入到您的引擎

    感谢DirectX和Xna,加载fx文件和设置所有必需的参数是很容易的(上一章在绘制2D直线和3D直线时你已经这样做) 。在下一章您会使用一个更一般的类,它可以接受许多不同的shader,并采用了更优化的方式设置所有必需的参数,但对于这一章在引擎中只使用simpleshader.fx文件。

    与以往一样,你通过定义单元测试开始。在命名空间Shader中创建一个新文件simpleshader.cs并写入以下代码:

    #region Unit Testing
    public static void TestSimpleShader()
    {
      SimpleShader shader = null;
      Model testModel = null;
      Texture testTexture = null;
      TestGame.Start("Test SimpleShader.fx",
        delegate
        {
          shader = new SimpleShader();
          testModel = new Model("Apple");
          testTexture = new Texture("Marble");
        },
        delegate
        {
          // Render model
          shader.RenderModel(testModel.XnaModel, testTexture);
        });
    } // TestSimpleShader
    #endregion

    该单元测试还没有Rendermodel的方法,让我们创建它:

    public void RenderModel(XnaModel someModel, Texture texture)
    {
    } // RenderModel(someModel, texture)

    现在,编译后您就可以看到一个空白屏幕。

    编译Shader

    此单元测试中要在屏幕上显示点什么,你首先必须加载并编译Shader。正如你加载模型和纹理一样,您使用xna的内容管道(content pipeline)。只需将.fx文件拖动到您的项目(同其他材质和模型一样放至正确的目录),这样在项目创建时会自动编译shader,如果Shader并没有编译你还会获取Shader编译的错误信息。同时,你也要确保marble.dds纹理被正确加载,因为您的单元测试中要用到。

    加载effect和加载纹理一样简单,只需定义一个effect变量,并在simpleshader构造函数中加载它:

    #region Variables
    Effect effect = null;
    #endregion
    
    #region Constructor
    public SimpleShader()
    {
      effect = BaseGame.Content.Load<Effect>(
        Path.Combine(Directories.ContentDirectory, "SimpleShader"));
    } // SimpleShader()
    #endregion

    如果您是在Windows平台上,您也可以动态加载shader,对于在游戏中测试和改变shader是很有用的。我通常使用un-compiled shader文件,我不想改变shader了。下面的代码是用来编译和加载shader的。

    请注意,这些类和方法只适用于在Windows平台上。如果您想对Xbox 360使用这些代码,请将#if !XBOX360 #endif围绕这些程序行。

    CompiledEffect compiledEffect = Effect.CompileEffectFromFile(
      Path.Combine("Shaders", shaderContentName + ".fx"),
      null, null, CompilerOptions.None,
      TargetPlatform.Windows);
    
    effect = new Effect(BaseGame.Device,
      compiledEffect.GetEffectCode(), CompilerOptions.None, null);
    使用参数

    在你学习建立Shader的过程中,世界,观察和投影矩阵对转换三维数据,并在屏幕得到正确显示是非常重要的。你应在rendermodel方法设置所有这些Shader的参数,并由您的单元测试调用。

    BaseGame.WorldMatrix =
      Matrix.CreateScale(0.25f, 0.25f, 0.25f);
    
    effect.Parameters["worldViewProj"].SetValue(
      BaseGame.WorldMatrix *
      BaseGame.ViewMatrix *
      BaseGame.ProjectionMatrix);
    effect.Parameters["world"].SetValue(
      BaseGame.WorldMatrix);
    effect.Parameters["viewInverse"].SetValue(
      BaseGame.InverseViewMatrix);
    effect.Parameters["lightDir"].SetValue(
      BaseGame.LightDirection);
    effect.Parameters["diffuseTexture"].SetValue(
      texture.XnaTexture);

    在此代码中首先是设置世界矩阵。这是非常重要的,因为如果世界矩阵没有设置,也许从以往的操作中会产生无法预料的数值,你当然不希望三维模型处于一些随机的位置。因为您的apple.x是相当大的,你应该把它缩放得小一些以适合您上一章建立的simplecamera类。

    然后计算worldviewproj矩阵和设置的其他矩阵,lightdir、diffusetexture也很重要,因为FX Composer中会自动加载贴图,而程序中不会,你必须自己设置。如果你从内容管道加载模型,Xna会从模型数据自动加载所有使用的纹理。所以在单元测试中,应加载marble.dds纹理。

    顶点格式

    在您渲染你的3D苹果模型前必须确保您的程序和shader知道使用哪种顶点格式。在DirectX可以使用一个预定义的固定功能顶点格式,但在xna中不行。你必须用类似于shader中vertexinput结构的方式定义顶点声明。因为你使用内置的vertexpositionnormaltexture结构,所以没无需定义每个值,但在下一章你将学到如何使用您自定义的tangentvertex格式。

    // Use the VertexPositionNormalTexture vertex format in SimpleShader.fx
    BaseGame.Device.VertexDeclaration =
      new VertexDeclaration(BaseGame.Device,
      VertexPositionNormalTexture.VertexElements);

    您无需在每次调用rendermodel时创建一个新的顶点声明,但为了保持简洁,你每次调用都建立一个新的顶点声明。它以图形设备作为第一个参数,顶点元素的数组vertexpositionnormaltexture结构作为第二个参数。如需详细资讯请参阅第7章。

    使用Shader渲染

    现在要渲染苹果,您首先应指定您想要使用的technique(默认设置为第一个technique,但最好知道如何设置technique)。你将一直使用effect类的currenttechnique属性。您为每一个technique中的pass(正如我以前说过,通常只要有一个pass)渲染三维数据。渲染苹果并不容易,因为xna只提供一个mesh.draw的方法,可参见model类的代码。

    XNA Framework另一个缺少的功能是创造盒,球,或茶壶的mesh辅助类。你也会注意到大部分的direct3dx名字空间的功能在xna中不存在。当您处理自己的content processor时只能使用部分的方法,对您的引擎或测试模型,网格,或shader并没有帮助。因为所有的顶点和索引缓冲区是只写的,所以加载模型时您也不能处理任何的顶点或索引的数据,这虽然有利于快速硬件访问,但不够灵活。在您代码中你只是模拟的mesh.draw方法,只是使用了自己的effect类。

    effect.CurrentTechnique = effect.Techniques["SpecularPerPixel"];
    
    effect.Begin();
    foreach (EffectPass pass in effect.CurrentTechnique.Passes)
    {
      pass.Begin();
      // Render all meshes
      foreach (ModelMesh mesh in someModel.Meshes)
      {
        // Render all mesh parts
        foreach (ModelMeshPart part in mesh.MeshParts)
        {
          // Render data our own way
          BaseGame.Device.Vertices[0].SetSource(
            mesh.VertexBuffer, part.StreamOffset, part.VertexStride);
          BaseGame.Device.Indices = mesh.IndexBuffer;
    
          // And render
          BaseGame.Device.DrawIndexedPrimitives(
            PrimitiveType.TriangleList,
            part.BaseVertex, 0, part.NumVertices,
            part.StartIndex, part.PrimitiveCount);
        } // foreach
      } // foreach
      pass.End();
    } // for
    effect.End();

    详细地说这意味着你遍历了每一个pass(这里只有一个)渲染所有网格(meshes)(这里也只有一个),然后你制定的所有mesh parts(仍然又只有一个)最后,您调用drawindexedprimitives方法渲染所有shader中的顶点。然后pass和shader关闭,你终于可以看到带有marble.dds纹理的苹果显示在屏幕上 。(见图6-16)

    1
    图 6-16

    测试Shader

    现在程序可以运行了,你可以试着测试一下其他贴图,材质或渲染模式。

    比如要实现线框的效果,你可以看一下前面的图6-5,您可以在Shader开始前改变Fillmode:

    BaseGame.Device.RenderState.FillMode = FillMode.WireFrame;

    或者可以载入的另一个纹理或另一个模型,Shader类允许这样做。最简单的方式是修改环境光,散射光和镜面光的值来渲染一个怪苹果(见图6-17)。

    2
    图 6-17

    effect.Parameters["ambientColor"].SetValue(
      Color.Blue.ToVector4());
    effect.Parameters["diffuseColor"].SetValue(
      Color.Orange.ToVector4());
    effect.Parameters["specularColor"].SetValue(
      Color.Orchid.ToVector4());

    请注意,您必须将颜色值转换为vector4。当您在effect.Begin()和effect.Ene()之间设定shader参数,你必须调用Effect.Commitchanges()方法,以确保将您所做的更改发送给GPU,但如果象在simpleshader类中的一样,设置参数后才开始effect.Begin()就无需如此。

    如果你通过shader参数名去设置参数,当你不当心拼写错参数很容易发生错误,在下一章我们将学习如何更有效率地去设置参数。

  • 相关阅读:
    48. Rotate Image
    47. Permutations II
    46. Permutations
    45. Jump Game II
    44. Wildcard Matching
    43. Multiply Strings
    42. Trapping Rain Water
    41. First Missing Positive
    40. Combination Sum II
    39. Combination Sum
  • 原文地址:https://www.cnblogs.com/AlexCheng/p/2120250.html
Copyright © 2011-2022 走看看