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参数名去设置参数,当你不当心拼写错参数很容易发生错误,在下一章我们将学习如何更有效率地去设置参数。

  • 相关阅读:
    寒假学习5流程控制之ifelse与for循环
    寒假学习4scala数据类型与运算符
    寒假学习13
    寒假学习14
    寒假学习3scala简介与基础1
    sqlserver 定时自动备份
    windows mysql 主从同步
    mysql 连接数 最大并发数
    mysql基础语法
    sqlserver 常见语法
  • 原文地址:https://www.cnblogs.com/AlexCheng/p/2120250.html
Copyright © 2011-2022 走看看