Показаны сообщения с ярлыком shader. Показать все сообщения
Показаны сообщения с ярлыком shader. Показать все сообщения

четверг, 30 июня 2011 г.

Ошибка при переносе шейдера из XNA 3.1 в XNA 4.0


Иногда при портировании проектов XNA 3.1 в XNA 4.0 возникают странные ошибки (NaN and infinity literals not allowed by shader model) при компиляции шейдеров. В этой статье я опишу простой способ избежать ошибки.
Я взяла свой старый проект, который реализуется освещение трехмерных объектов по моделям Гуро и Фонга (то есть освещенность в вычисляется в вершинном и пиксельном шейдере соответственно).
Вот такой шейдер я использую:
float4x4 World;
float4x4 View;
float4x4 Projection;

float4 AmbientColor = float4(0.1, 0.1, 0.1, 1);
float ka = 1;

float4 DiffuseColor = float4(1,1,1,1);
float kd = 0.5;

float4 SpecularColor = float4(1,1,1,1);
float ks = 1;
float SpecularPower = 4;

float3 LightPosition = float3(0,1,1);
float3 Eye;

// TODO: add effect parameters here.

struct VertexShaderInput
{
    float4 Position : POSITION0;
    float3 Normal : NORMAL;

    // TODO: add input channels such as texture
    // coordinates and vertex colors here.
};

struct VertexShaderOutput
{
    float4 Position : POSITION0;
    float4 Light : COLOR0;

    // TODO: add vertex shader outputs such as colors and texture
    // coordinates here. These values will automatically be interpolated
    // over the triangle, and provided as input to your pixel shader.
};

VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{
    VertexShaderOutput output;

    float4 worldPosition = mul(input.Position, World);
    float4 viewPosition = mul(worldPosition, View);
    output.Position = mul(viewPosition, Projection);

    float4 Ambient = ka * AmbientColor;

    float3 worldNormal = normalize(mul(input.Normal, World));
    float3 lightDirection = normalize(LightPosition - worldPosition);
    float4 Diffuse = kd * max(0, dot(worldNormal, lightDirection)) * DiffuseColor;

    float3 eyeDirection = normalize(Eye - worldPosition);
    float3 reflectedLight = normalize(reflect(-lightDirection, worldNormal));
    float4 Specular = ks * pow(max(0, dot(eyeDirection, reflectedLight)), SpecularPower) * SpecularColor;
    output.Light = Ambient + Diffuse + Specular;

    // TODO: add your vertex shader code here.

    return output;
}

float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
    // TODO: add your pixel shader code here.

    return float4(1, 0, 1, 1) * input.Light;
}

technique Gourand
{
    pass Pass1
    {
        // TODO: set renderstates here.

        VertexShader = compile vs_1_1 VertexShaderFunction();
        PixelShader = compile ps_1_1 PixelShaderFunction();
    }
}


struct VertexShaderInputPhong
{
    float4 Position : POSITION0;
    float3 Normal : NORMAL;

    // TODO: add input channels such as texture
    // coordinates and vertex colors here.
};

struct VertexShaderOutputPhong
{
    float4 Position : POSITION0;
    float3 WorldPosition : TEXCOORD0;
    float3 Normal : TEXCOORD1;

    // TODO: add vertex shader outputs such as colors and texture
    // coordinates here. These values will automatically be interpolated
    // over the triangle, and provided as input to your pixel shader.
};

VertexShaderOutputPhong VertexShaderFunctionPhong(VertexShaderInputPhong input)
{
    VertexShaderOutputPhong output;

    float4 worldPosition = mul(input.Position, World);
    float4 viewPosition = mul(worldPosition, View);
    float3 worldNormal = normalize(mul(input.Normal, World));
    output.Position = mul(viewPosition, Projection);
    output.WorldPosition = worldPosition;
    output.Normal = worldNormal;

    // TODO: add your vertex shader code here.

    return output;
}

float4 PixelShaderFunctionPhong(VertexShaderOutputPhong input) : COLOR0
{
    // TODO: add your pixel shader code here.
    float3 worldPosition = input.WorldPosition;
    float3 worldNormal = normalize(input.Normal);


    float4 Ambient = ka * AmbientColor;

    float3 lightDirection = normalize(LightPosition - worldPosition);
    float4 Diffuse = kd * max(0, dot(worldNormal, lightDirection)) * DiffuseColor;

    float3 eyeDirection = normalize(Eye - worldPosition);
    float3 reflectedLight = normalize(reflect(-lightDirection, worldNormal));
    float4 Specular = ks * pow(max(0, dot(eyeDirection, reflectedLight)), SpecularPower) * SpecularColor;

    return float4(1, 0, 1, 1) * (Ambient + Diffuse + Specular);
}

technique Phong
{
    pass Pass1
    {
        // TODO: set renderstates here.

        VertexShader = compile vs_1_1 VertexShaderFunctionPhong();
        PixelShader = compile ps_2_0 PixelShaderFunctionPhong();
    }
}

При компиляции получаю следующие ошибки:
Error      1             Errors compiling C: \LightGame\LightGame\Content\light.fx:
C: \LightGame\LightGame\Content\light.fx(132,12): error X4579: NaN and infinity literals not allowed by shader model
error X3539: ps_1_x is no longer supported
C: \LightGame\LightGame\Content\light.fx(77,23): ID3DXEffectCompiler::CompileEffect: There was an error compiling expression
ID3DXEffectCompiler: Compilation failed          C: \LightGame\LightGame\Content\light.fx     132         12           LightGame
Ну, с ошибкой «X3539 ps_1_x is no longer supported» все понятно. Дело в том, что XNA 4.0 больше не поддерживается шейдерные профили 1_x. Нужно просто изменить описание техники следующим образом:
technique Gourand

{

    pass Pass1

    {

        // TODO: set renderstates here.

 

        VertexShader = compile vs_1_1 VertexShaderFunction();

        PixelShader = compile ps_2_0 PixelShaderFunction();

    }

}

А вот со второй ошибкой все сложнее, иногда пропадает сама (бывает и такое), иногда она уходит если поменять местами параметры в указанной строке.
На самом деле сообщение об ошибке указывает на строку в пиксельном шейдере:
float4 PixelShaderFunctionPhong(VertexShaderOutputPhong input) : COLOR0

{

    // TODO: add your pixel shader code here.

    float3 worldPosition = input.WorldPosition;

    float3 worldNormal = normalize(input.Normal);

    

    

    float4 Ambient = ka * AmbientColor;

    

    float3 lightDirection = normalize(LightPosition - worldPosition);

    float4 Diffuse = kd * max(0, dot(worldNormal, lightDirection)) * DiffuseColor;

    

    float3 eyeDirection = normalize(Eye - worldPosition);

    float3 reflectedLight = normalize(reflect(-lightDirection, worldNormal));

    float4 Specular = ks * pow(max(0, dot(eyeDirection, reflectedLight)), SpecularPower) * SpecularColor;

 

    return float4(1, 0, 1, 1) * (Ambient + Diffuse + Specular);

}

Так вот, самый простой способ избавиться от этой ошибки – это заменить 0 на какое-нибудь очень маленькое число. Например, вот так:
float4 PixelShaderFunctionPhong(VertexShaderOutputPhong input) : COLOR0

{

    // TODO: add your pixel shader code here.

    float3 worldPosition = input.WorldPosition;

    float3 worldNormal = normalize(input.Normal);

    

    

    float4 Ambient = ka * AmbientColor;

    

    float3 lightDirection = normalize(LightPosition - worldPosition);

    float4 Diffuse = kd * max(0, dot(worldNormal, lightDirection)) * DiffuseColor;

    

    float3 eyeDirection = normalize(Eye - worldPosition);

    float3 reflectedLight = normalize(reflect(-lightDirection, worldNormal));

    float4 Specular = ks * pow(max(0.000001, dot(eyeDirection, reflectedLight)), SpecularPower) * SpecularColor;

 

    return float4(1, 0, 1, 1) * (Ambient + Diffuse + Specular);

}

XNA Game Studio и движок Xen. Custom Shaders


В этом уроке мы рассмотрим, пожалуй, самую интересную особенность движка Xen – работа с шейдерами.
Дело в том, что, при установке Xen, в Visual Studio ставится надстройка, которая позволяет несколько по-иному работать с шейдерами.



Как Вы видите файл shader.fx как бы состоит теперь из двух частей shader.fx и shader.fx.cs. shader.fx содержит собственно код шейдера на HLSL, а shader.fx.cs содержит автоматически сгенерированный код C# класса, который реализует описанный шейдерный код в виде класса эффекта Xen.
Например, давайте создадим новый файл testEffect.fx и установим параметр Custom Tool равным XenFx.



Если использовать стандартный файл с эффектом, который создается в XNA Game Studio, то получится следующий тип:
testEffect.fx
float4x4 World;

float4x4 View;

float4x4 Projection;

 

// TODO: add effect parameters here.

 

struct VertexShaderInput

{

    float4 Position : POSITION0;

 

    // TODO: add input channels such as texture

    // coordinates and vertex colors here.

};

 

struct VertexShaderOutput

{

    float4 Position : POSITION0;

 

    // TODO: add vertex shader outputs such as colors and texture

    // coordinates here. These values will automatically be interpolated

    // over the triangle, and provided as input to your pixel shader.

};

 

VertexShaderOutput VertexShaderFunction(VertexShaderInput input)

{

    VertexShaderOutput output;

 

    float4 worldPosition = mul(input.Position, World);

    float4 viewPosition = mul(worldPosition, View);

    output.Position = mul(viewPosition, Projection);

 

    // TODO: add your vertex shader code here.

 

    return output;

}

 

float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0

{

    // TODO: add your pixel shader code here.

 

    return float4(1, 0, 0, 1);

}

 

technique Technique1

{

    pass Pass1

    {

        // TODO: set renderstates here.

 

        VertexShader = compile vs_2_0 VertexShaderFunction();

        PixelShader = compile ps_2_0 PixelShaderFunction();

    }

}

testEffect.fx.cs
// XenFX
// Assembly = Xen.Graphics.ShaderSystem.CustomTool, Version=7.0.1.1, Culture=neutral, PublicKeyToken=e706afd07878dfca
// SourceFile = testEffect.fx
// Namespace = Tutorials.Tutorial_03

namespace Tutorials.Tutorial_03.TestEffect
{
         
          ///
 <summary><para>Technique 'Technique1' generated from file 'testEffect.fx'</para><para>Vertex Shader: approximately 12 instruction slots used, 12 registers</para><para>Pixel Shader: approximately 2 instruction slots used, 0 registers</para></summary>
          [System.Diagnostics.DebuggerStepThroughAttribute()]
          [System.CodeDom.Compiler.GeneratedCodeAttribute("Xen.Graphics.ShaderSystem.CustomTool.dll""5040fe79-bab4-4d2f-9335-463d3794d366")]
          public sealed class Technique1 : Xen.Graphics.ShaderSystem.BaseShader
          {
                    ///
 <summary>Construct an instance of the 'Technique1' shader</summary>
                    public Technique1()
                    {
                    }
                    ///
 <summary>Setup shader static values</summary><param name="state"/>
                    private void gdInit(Xen.Graphics.ShaderSystem.ShaderSystemBase state)
                    {
                              
// set the graphics ID
                               Technique1.gd = state.DeviceUniqueIndex;
                               this.GraphicsID = state.DeviceUniqueIndex;
                               Technique1.cid0 = state.GetNameUniqueID("Projection");
                               Technique1.cid1 = state.GetNameUniqueID("View");
                               Technique1.cid2 = state.GetNameUniqueID("World");
                    }
                    ///
 <summary>Bind the shader, 'ic' indicates the shader instance has changed and 'ec' indicates the extension has changed.</summary><param name="state"/><param name="ic"/><param name="ec"/><param name="ext"/>
                    protected override void BeginImpl(Xen.Graphics.ShaderSystem.ShaderSystemBase state, bool ic, bool ec, Xen.Graphics.ShaderSystem.ShaderExtension ext)
                    {
                              
// if the device changed, call Warm()
                               if ((state.DeviceUniqueIndex != Technique1.gd))
                               {
                                         this.WarmShader(state);
                                         ic = true;
                               }
                              
// Force updating if the instance has changed
                               this.vreg_change = (this.vreg_change | ic);
                               if ((this.vreg_change == true))
                               {
                                         Technique1.fx.vs_c.SetValue(this.vreg);
                                         this.vreg_change = false;
                                         ic = true;
                               }
                              
// Finally, bind the effect
                               if ((ic | ec))
                               {
                                         state.SetEffect(thisref Technique1.fx, ext);
                               }
                    }
                    ///
 <summary>Warm (Preload) the shader</summary><param name="state"/>
                    protected override void WarmShader(Xen.Graphics.ShaderSystem.ShaderSystemBase state)
                    {
                              
// Shader is already warmed
                               if ((Technique1.gd == state.DeviceUniqueIndex))
                               {
                                         return;
                               }
                              
// Setup the shader
                               if ((Technique1.gd != state.DeviceUniqueIndex))
                               {
                                         this.gdInit(state);
                               }
                               Technique1.fx.Dispose();
                              
// Create the effect instance
                               state.CreateEffect(out Technique1.fx, Technique1.fxb, 13, 3);
                    }
                    ///
 <summary>True if a shader constant has changed since the last Bind()</summary>
                    protected override bool Changed()
                    {
                               return this.vreg_change;
                    }
                    ///
 <summary>Returns the number of vertex inputs used by this shader</summary>
                    protected override int GetVertexInputCountImpl()
                    {
                               return 1;
                    }
                    ///
 <summary>Returns a vertex input used by this shader</summary><param name="i"/><param name="usage"/><param name="index"/>
                    protected override void GetVertexInputImpl(int i, out Microsoft.Xna.Framework.Graphics.VertexElementUsage usage, out int index)
                    {
                               usage = ((Microsoft.Xna.Framework.Graphics.VertexElementUsage)(Technique1.vin[i]));
                               index = Technique1.vin[(i + 1)];
                    }
                    ///
 <summary>Static graphics ID</summary>
                    private static int gd;
                    ///
 <summary>Static effect container instance</summary>
                    private static Xen.Graphics.ShaderSystem.ShaderEffect fx;
                    ///
 <summary/>
                    private bool vreg_change;
                    ///
 <summary>Return the supported modes for this shader</summary><param name="blendingSupport"/><param name="instancingSupport"/>
                    protected override void GetExtensionSupportImpl(out bool blendingSupport, out bool instancingSupport)
                    {
                               blendingSupport = true;
                               instancingSupport = true;
                    }
                    ///
 <summary>Name ID for 'Projection'</summary>
                    private static int cid0;
                    ///
 <summary>Set the shader value 'float4x4 Projection'</summary><param name="value"/>
                    public void SetProjection(ref Microsoft.Xna.Framework.Matrix value)
                    {
                              this.vreg[8] = new Microsoft.Xna.Framework.Vector4(value.M11, value.M21, value.M31, value.M41);
                              this.vreg[9] = new Microsoft.Xna.Framework.Vector4(value.M12, value.M22, value.M32, value.M42);
                              this.vreg[10] = new Microsoft.Xna.Framework.Vector4(value.M13, value.M23, value.M33, value.M43);
                              this.vreg[11] = new Microsoft.Xna.Framework.Vector4(value.M14, value.M24, value.M34, value.M44);
                               this.vreg_change = true;
                    }
                    ///
 <summary>Assign the shader value 'float4x4 Projection'</summary>
                    public Microsoft.Xna.Framework.Matrix Projection
                    {
                               set
                               {
                                         this.SetProjection(ref value);
                               }
                    }
                    ///
 <summary>Name ID for 'View'</summary>
                    private static int cid1;
                    ///
 <summary>Set the shader value 'float4x4 View'</summary><param name="value"/>
                    public void SetView(ref Microsoft.Xna.Framework.Matrix value)
                    {
                              this.vreg[4] = new Microsoft.Xna.Framework.Vector4(value.M11, value.M21, value.M31, value.M41);
                              this.vreg[5] = new Microsoft.Xna.Framework.Vector4(value.M12, value.M22, value.M32, value.M42);
                              this.vreg[6] = new Microsoft.Xna.Framework.Vector4(value.M13, value.M23, value.M33, value.M43);
                              this.vreg[7] = new Microsoft.Xna.Framework.Vector4(value.M14, value.M24, value.M34, value.M44);
                               this.vreg_change = true;
                    }
                    ///
 <summary>Assign the shader value 'float4x4 View'</summary>
                    public Microsoft.Xna.Framework.Matrix View
                    {
                               set
                               {
                                         this.SetView(ref value);
                               }
                    }
                    ///
 <summary>Name ID for 'World'</summary>
                    private static int cid2;
                    ///
 <summary>Set the shader value 'float4x4 World'</summary><param name="value"/>
                    public void SetWorld(ref Microsoft.Xna.Framework.Matrix value)
                    {
                              this.vreg[0] = new Microsoft.Xna.Framework.Vector4(value.M11, value.M21, value.M31, value.M41);
                              this.vreg[1] = new Microsoft.Xna.Framework.Vector4(value.M12, value.M22, value.M32, value.M42);
                              this.vreg[2] = new Microsoft.Xna.Framework.Vector4(value.M13, value.M23, value.M33, value.M43);
                              this.vreg[3] = new Microsoft.Xna.Framework.Vector4(value.M14, value.M24, value.M34, value.M44);
                               this.vreg_change = true;
                    }
                    ///
 <summary>Assign the shader value 'float4x4 World'</summary>
                    public Microsoft.Xna.Framework.Matrix World
                    {
                               set
                               {
                                         this.SetWorld(ref value);
                               }
                    }
                    ///
 <summary>array storing vertex usages, and element indices</summary>
readonly
                    private static int[] vin = new int[] {0,0};
                    ///
 <summary>Vertex shader register storage</summary>
readonly
                    private Microsoft.Xna.Framework.Vector4[] vreg = new Microsoft.Xna.Framework.Vector4[12];
#if XBOX360
                    /// <summary>Static RLE compressed shader byte code (Xbox360)</summary>
                    private static byte[] fxb
                    {
                               get
                               {
                                         return new byte[] {4,188,240,11,207,131,0,1,32,152,0,8,254,255,9,1,0,0,1,172,135,0,1,3,131,0,1,1,131,0,1,224,135,0,1,12,131,0,1,4,131,0,1,1,229,0,0,222,0,0,1,6,1,95,1,118,1,115,1,95,1,99,134,0,0,1,1,131,0,0,1,16,131,0,0,1,4,143,0,0,1,2,131,0,0,1,15,131,0,0,1,4,147,0,0,1,3,131,0,0,1,16,131,0,0,1,4,143,0,0,1,4,131,0,0,1,15,131,0,0,1,4,143,0,0,1,9,1,66,1,108,1,101,1,110,1,100,1,105,1,110,1,103,135,0,0,1,5,131,0,0,1,16,131,0,0,1,4,143,0,0,1,6,131,0,0,1,15,131,0,0,1,4,143,0,0,1,11,1,73,1,110,1,115,1,116,1,97,1,110,1,99,1,105,1,110,1,103,133,0,0,1,7,1,83,1,104,1,97,1,100,1,101,1,114,133,0,0,1,1,131,0,0,1,1,131,0,0,1,9,131,0,0,1,7,131,0,0,1,4,131,0,0,1,32,138,0,0,1,1,1,160,135,0,0,1,3,1,0,1,0,1,1,1,28,135,0,0,1,2,131,0,0,1,92,135,0,0,1,240,131,0,0,1,236,131,0,0,1,93,134,0,0,1,1,1,8,1,0,1,0,1,1,1,4,1,0,1,0,1,1,1,80,135,0,0,1,2,131,0,0,1,92,134,0,0,1,1,1,36,1,0,1,0,1,1,1,32,131,0,0,1,93,134,0,0,1,1,1,60,1,0,1,0,1,1,1,56,1,0,1,0,1,1,1,144,135,0,0,1,2,131,0,0,1,92,134,0,0,1,1,1,100,1,0,1,0,1,1,1,96,131,0,0,1,93,134,0,0,1,1,1,124,1,0,1,0,1,1,1,120,135,0,0,1,6,135,0,0,1,2,132,255,0,131,0,0,1,1,134,0,0,1,1,1,4,1,16,1,42,1,17,132,0,0,1,160,131,0,0,1,100,135,0,0,1,36,131,0,0,1,88,131,0,0,1,128,139,0,0,1,48,131,0,0,1,28,131,0,0,1,35,1,255,1,255,1,3,144,0,0,1,28,1,112,1,115,1,95,1,51,1,95,1,48,1,0,1,50,1,46,1,48,1,46,1,49,1,49,1,54,1,50,1,54,1,46,1,48,1,0,1,171,135,0,0,1,1,139,0,0,1,20,1,1,1,252,1,0,1,16,147,0,0,1,64,131,0,0,1,36,1,16,150,0,0,1,1,176,0,0,1,63,1,128,146,0,0,1,16,1,1,1,196,1,0,1,34,131,0,0,1,20,1,152,1,192,132,0,0,1,108,1,194,1,0,1,0,1,255,147,0,0,1,2,132,255,0,138,0,0,1,2,1,60,1,16,1,42,1,17,1,1,1,0,1,0,1,1,1,112,131,0,0,1,204,135,0,0,1,36,134,0,0,1,1,1,68,138,0,0,1,1,1,28,131,0,0,1,28,1,0,1,0,1,1,1,15,1,255,1,254,1,3,132,0,0,1,1,131,0,0,1,28,134,0,0,1,1,1,8,131,0,0,1,48,1,0,1,2,131,0,0,1,12,133,0,0,1,56,131,0,0,1,72,1,95,1,118,1,115,1,95,1,99,1,0,1,171,1,171,1,0,1,1,1,0,1,3,1,0,1,1,1,0,1,4,1,0,1,12,229,0,0,225,0,0,1,118,1,115,1,95,1,51,1,95,1,48,1,0,1,50,1,46,1,48,1,46,1,49,1,49,1,54,1,50,1,54,1,46,1,48,1,0,1,171,135,0,0,1,204,1,0,1,1,1,0,1,1,143,0,0,1,1,131,0,0,1,1,134,0,0,1,2,1,144,131,0,0,1,3,1,16,1,1,1,16,1,3,1,0,1,0,1,18,1,0,1,194,133,0,0,1,96,1,4,1,96,1,10,1,18,1,0,1,18,136,0,0,1,3,1,196,1,0,1,34,131,0,0,1,5,1,248,132,0,0,1,6,1,136,132,0,0,1,200,1,1,1,0,1,1,1,0,1,167,1,167,1,0,1,175,131,0,0,1,200,1,2,1,0,1,1,1,0,1,167,1,167,1,0,1,175,1,0,1,1,1,0,1,200,1,4,1,0,1,1,1,0,1,167,1,167,1,0,1,175,1,0,1,2,1,0,1,200,1,8,1,0,1,1,1,0,1,167,1,167,1,0,1,175,1,0,1,3,1,0,1,200,1,1,131,0,0,1,167,1,167,1,0,1,175,1,1,1,4,1,0,1,200,1,2,131,0,0,1,167,1,167,1,0,1,175,1,1,1,5,1,0,1,200,1,4,131,0,0,1,167,1,167,1,0,1,175,1,1,1,6,1,0,1,200,1,8,131,0,0,1,167,1,167,1,0,1,175,1,1,1,7,1,0,1,200,1,1,1,128,1,62,1,0,1,167,1,167,1,0,1,175,1,0,1,8,1,0,1,200,1,2,1,128,1,62,1,0,1,167,1,167,1,0,1,175,1,0,1,9,1,0,1,200,1,4,1,128,1,62,1,0,1,167,1,167,1,0,1,175,1,0,1,10,1,0,1,200,1,8,1,128,1,62,1,0,1,167,1,167,1,0,1,175,1,0,1,11,148,0,0,1,1,132,255,0,131,0,0,1,1,134,0,0,1,1,1,4,1,16,1,42,1,17,132,0,0,1,160,131,0,0,1,100,135,0,0,1,36,131,0,0,1,88,131,0,0,1,128,139,0,0,1,48,131,0,0,1,28,131,0,0,1,35,1,255,1,255,1,3,144,0,0,1,28,1,112,1,115,1,95,1,51,1,95,1,48,1,0,1,50,1,46,1,48,1,46,1,49,1,49,1,54,1,50,1,54,1,46,1,48,1,0,1,171,135,0,0,1,1,139,0,0,1,20,1,1,1,252,1,0,1,16,147,0,0,1,64,131,0,0,1,36,1,16,150,0,0,1,1,176,0,0,1,63,1,128,146,0,0,1,16,1,1,1,196,1,0,1,34,131,0,0,1,20,1,152,1,192,132,0,0,1,108,1,194,1,0,1,0,1,255,147,0,0,1,1,132,255,0,138,0,0,1,2,1,60,1,16,1,42,1,17,1,1,1,0,1,0,1,1,1,112,131,0,0,1,204,135,0,0,1,36,134,0,0,1,1,1,68,138,0,0,1,1,1,28,131,0,0,1,28,1,0,1,0,1,1,1,15,1,255,1,254,1,3,132,0,0,1,1,131,0,0,1,28,134,0,0,1,1,1,8,131,0,0,1,48,1,0,1,2,131,0,0,1,12,133,0,0,1,56,131,0,0,1,72,1,95,1,118,1,115,1,95,1,99,1,0,1,171,1,171,1,0,1,1,1,0,1,3,1,0,1,1,1,0,1,4,1,0,1,12,229,0,0,225,0,0,1,118,1,115,1,95,1,51,1,95,1,48,1,0,1,50,1,46,1,48,1,46,1,49,1,49,1,54,1,50,1,54,1,46,1,48,1,0,1,171,135,0,0,1,204,1,0,1,1,1,0,1,1,143,0,0,1,1,131,0,0,1,1,134,0,0,1,2,1,144,131,0,0,1,3,1,16,1,1,1,16,1,3,1,0,1,0,1,18,1,0,1,194,133,0,0,1,96,1,4,1,96,1,10,1,18,1,0,1,18,136,0,0,1,3,1,196,1,0,1,34,131,0,0,1,5,1,248,132,0,0,1,6,1,136,132,0,0,1,200,1,1,1,0,1,1,1,0,1,167,1,167,1,0,1,175,131,0,0,1,200,1,2,1,0,1,1,1,0,1,167,1,167,1,0,1,175,1,0,1,1,1,0,1,200,1,4,1,0,1,1,1,0,1,167,1,167,1,0,1,175,1,0,1,2,1,0,1,200,1,8,1,0,1,1,1,0,1,167,1,167,1,0,1,175,1,0,1,3,1,0,1,200,1,1,131,0,0,1,167,1,167,1,0,1,175,1,1,1,4,1,0,1,200,1,2,131,0,0,1,167,1,167,1,0,1,175,1,1,1,5,1,0,1,200,1,4,131,0,0,1,167,1,167,1,0,1,175,1,1,1,6,1,0,1,200,1,8,131,0,0,1,167,1,167,1,0,1,175,1,1,1,7,1,0,1,200,1,1,1,128,1,62,1,0,1,167,1,167,1,0,1,175,1,0,1,8,1,0,1,200,1,2,1,128,1,62,1,0,1,167,1,167,1,0,1,175,1,0,1,9,1,0,1,200,1,4,1,128,1,62,1,0,1,167,1,167,1,0,1,175,1,0,1,10,1,0,1,200,1,8,1,128,1,62,1,0,1,167,1,167,1,0,1,175,1,0,1,11,149,0,0,132,255,0,131,0,0,1,1,134,0,0,1,1,1,4,1,16,1,42,1,17,132,0,0,1,160,131,0,0,1,100,135,0,0,1,36,131,0,0,1,88,131,0,0,1,128,139,0,0,1,48,131,0,0,1,28,131,0,0,1,35,1,255,1,255,1,3,144,0,0,1,28,1,112,1,115,1,95,1,51,1,95,1,48,1,0,1,50,1,46,1,48,1,46,1,49,1,49,1,54,1,50,1,54,1,46,1,48,1,0,1,171,135,0,0,1,1,139,0,0,1,20,1,1,1,252,1,0,1,16,147,0,0,1,64,131,0,0,1,36,1,16,150,0,0,1,1,176,0,0,1,63,1,128,146,0,0,1,16,1,1,1,196,1,0,1,34,131,0,0,1,20,1,152,1,192,132,0,0,1,108,1,194,1,0,1,0,1,255,148,0,0,132,255,0,138,0,0,1,2,1,60,1,16,1,42,1,17,1,1,1,0,1,0,1,1,1,112,131,0,0,1,204,135,0,0,1,36,134,0,0,1,1,1,68,138,0,0,1,1,1,28,131,0,0,1,28,1,0,1,0,1,1,1,15,1,255,1,254,1,3,132,0,0,1,1,131,0,0,1,28,134,0,0,1,1,1,8,131,0,0,1,48,1,0,1,2,131,0,0,1,12,133,0,0,1,56,131,0,0,1,72,1,95,1,118,1,115,1,95,1,99,1,0,1,171,1,171,1,0,1,1,1,0,1,3,1,0,1,1,1,0,1,4,1,0,1,12,229,0,0,225,0,0,1,118,1,115,1,95,1,51,1,95,1,48,1,0,1,50,1,46,1,48,1,46,1,49,1,49,1,54,1,50,1,54,1,46,1,48,1,0,1,171,135,0,0,1,204,1,0,1,1,1,0,1,1,143,0,0,1,1,131,0,0,1,1,134,0,0,1,2,1,144,131,0,0,1,3,1,16,1,1,1,16,1,3,1,0,1,0,1,18,1,0,1,194,133,0,0,1,96,1,4,1,96,1,10,1,18,1,0,1,18,136,0,0,1,3,1,196,1,0,1,34,131,0,0,1,5,1,248,132,0,0,1,6,1,136,132,0,0,1,200,1,1,1,0,1,1,1,0,1,167,1,167,1,0,1,175,131,0,0,1,200,1,2,1,0,1,1,1,0,1,167,1,167,1,0,1,175,1,0,1,1,1,0,1,200,1,4,1,0,1,1,1,0,1,167,1,167,1,0,1,175,1,0,1,2,1,0,1,200,1,8,1,0,1,1,1,0,1,167,1,167,1,0,1,175,1,0,1,3,1,0,1,200,1,1,131,0,0,1,167,1,167,1,0,1,175,1,1,1,4,1,0,1,200,1,2,131,0,0,1,167,1,167,1,0,1,175,1,1,1,5,1,0,1,200,1,4,131,0,0,1,167,1,167,1,0,1,175,1,1,1,6,1,0,1,200,1,8,131,0,0,1,167,1,167,1,0,1,175,1,1,1,7,1,0,1,200,1,1,1,128,1,62,1,0,1,167,1,167,1,0,1,175,1,0,1,8,1,0,1,200,1,2,1,128,1,62,1,0,1,167,1,167,1,0,1,175,1,0,1,9,1,0,1,200,1,4,1,128,1,62,1,0,1,167,1,167,1,0,1,175,1,0,1,10,1,0,1,200,1,8,1,128,1,62,1,0,1,167,1,167,1,0,1,175,1,0,1,11,140,0,0,1,0};
                              }
                    }
#else
                    ///
 <summary>Static Length+DeflateStream compressed shader byte code (Windows)</summary>
                    private static byte[] fxb
                    {
                               get
                               {
                                         return new byte[] {24,11,0,0,236,189,7,96,28,73,150,37,38,47,109,202,123,127,74,245,74,215,224,116,161,8,128,96,19,36,216,144,64,16,236,193,136,205,230,146,236,29,105,71,35,41,171,42,129,202,101,86,101,93,102,22,64,204,237,157,188,247,222,123,239,189,247,222,123,239,189,247,186,59,157,78,39,247,223,255,63,92,102,100,1,108,246,206,74,218,201,158,33,128,170,200,31,63,126,124,31,63,34,254,197,223,240,127,250,251,210,95,99,248,249,53,127,236,255,254,191,254,166,95,83,126,255,181,241,55,253,255,63,210,239,126,35,250,255,175,163,159,253,127,253,249,245,232,255,191,255,101,243,251,79,127,13,25,207,111,246,107,200,216,252,231,215,162,255,255,166,145,207,205,248,65,159,223,44,242,61,254,142,189,247,99,244,255,39,101,190,156,21,203,11,252,253,235,254,26,241,247,129,91,236,253,223,144,254,127,182,108,218,108,57,101,8,191,62,253,253,122,158,205,242,90,112,194,255,209,7,62,199,187,169,247,238,95,230,205,233,239,164,191,99,124,127,138,126,255,63,209,255,255,59,250,255,159,170,127,255,6,212,230,215,161,255,191,137,180,221,162,207,126,183,95,211,181,253,61,233,247,199,244,255,63,45,210,118,78,159,205,188,182,127,16,253,254,51,218,238,215,147,31,220,254,255,166,199,208,245,143,194,63,191,246,255,253,127,255,95,255,247,111,243,107,156,188,57,126,242,59,209,159,63,174,159,105,19,60,41,62,95,53,191,255,189,223,127,231,215,248,162,152,214,85,83,157,183,233,214,171,59,233,183,159,191,126,158,10,101,210,147,106,177,42,74,250,229,225,120,239,211,241,195,251,123,227,189,131,253,253,95,227,39,152,252,191,233,95,68,24,253,30,30,76,208,240,215,250,53,126,131,223,244,15,250,53,126,141,223,234,47,50,189,25,252,76,155,45,124,240,107,255,95,132,223,79,88,252,126,211,95,83,62,195,143,223,73,154,165,160,225,142,190,255,27,209,191,7,244,243,219,191,134,225,187,191,246,175,253,53,105,54,126,77,154,41,200,213,255,31,158,203,175,57,23,191,139,144,232,15,34,42,254,73,222,239,255,209,143,49,187,254,154,244,251,127,246,39,209,255,255,34,249,251,215,226,191,127,77,251,247,175,195,127,255,90,246,239,223,128,255,254,181,245,239,95,83,222,255,131,126,29,251,55,191,255,7,253,186,246,111,126,255,15,250,245,236,223,252,254,31,244,235,91,120,191,230,127,68,125,253,65,191,129,235,159,255,254,49,215,63,255,157,184,254,249,239,223,144,254,54,28,3,158,232,241,247,175,21,225,239,95,43,202,223,123,95,135,166,113,254,254,131,126,15,199,223,125,252,76,171,223,9,51,240,107,69,248,251,215,250,121,207,223,95,107,46,124,254,254,127,9,79,255,67,29,158,254,135,58,60,253,15,117,120,250,31,234,242,52,158,255,55,243,52,158,31,241,244,205,207,143,120,90,120,230,255,9,0,0,255,255};
                               }
                    }
#endif
                    ///
 <summary>Set a shader attribute of type 'Matrix' by global unique ID, see <see cref="Xen.Graphics.ShaderSystem.ShaderSystemBase.GetNameUniqueID"/> for details.</summary><param name="state"/><param name="id"/><param name="value"/>
                    protected override bool SetAttributeImpl(Xen.Graphics.ShaderSystem.ShaderSystemBase state, int id, ref Microsoft.Xna.Framework.Matrix value)
                    {
                               if ((Technique1.gd != state.DeviceUniqueIndex))
                               {
                                         this.WarmShader(state);
                               }
                               if ((id == Technique1.cid0))
                               {
                                         this.SetProjection(ref value);
                                         return true;
                               }
                               if ((id == Technique1.cid1))
                               {
                                         this.SetView(ref value);
                                         return true;
                               }
                               if ((id == Technique1.cid2))
                               {
                                         this.SetWorld(ref value);
                                         return true;
                               }
                               return false;
                    }
          }
}
На самом деле это очень интересно, обратите внимание на то, что для каждого параметра (World, View, Projection) в классе создалось свойство. Также интересным моментов является то, что прямо в самом классе хранится байт-код шейдера.
Как мы помним, в XNA Game Studio файлы с шейдерами нужно помещать в Content проект, для того чтобы он скомпилировался. Получается, что в Xen этого делать не нужно, так как откомпилированный код шейдера уже содержится в классе и может быть загружен во время выполнения программы.

суббота, 25 июня 2011 г.

Четыре метода использования Вершинных Текстур (Vertex Textures) Часть 9

 

Использование Вершинных Текстур в играх на платформе XNA (перевод)

Catalin Zima
Создание финальной карты смещения
Для финального этапа нашего урока нам необходим еще один файл шейдера, содержащий несколько техник, для получения карты смещения. Как уже мы это делали не раз, в папке Shaders создайте новый файл и назовите его Snow.fx. Для будущих техник нам понадобятся текстуры inputMap, randomMap, snowMap и deformationMap. Текстура inputMap будет использоваться при копировании данных из одной цели визуализации в другую, randomMap будет содержать рандомные данные, snowMap содержать данные смещения, а deformationMap, как понятно данные деформации.
    texture inputMap;
    sampler inputSampler : register(s0)  = sampler_state
    {
        Texture   = <inputMap>;
        
        MipFilter = Linear;
        MinFilter = Linear;
        MagFilter = Linear;
        
        AddressU  = Clamp;
        AddressV  = Clamp;
    };
    
    texture randomMap;
    sampler randomSampler : register(s0)= sampler_state
    {
        Texture   = <randomMap>;
        
        MipFilter = Linear;
        MinFilter = Linear;
        MagFilter = Linear;
        
        AddressU  = mirror;
        AddressV  = mirror;
    };
    
    texture snowMap;
    sampler snowSampler = sampler_state
    {
        Texture   = <snowMap>;
        
        MipFilter = Linear;
        MinFilter = Linear;
        MagFilter = Linear;
        
        AddressU  = Clamp;
        AddressV  = Clamp;
    };
 
    texture deformationMap;
    sampler deformationSampler = sampler_state
    {
        Texture   = <deformationMap>;
        
        MipFilter = Linear;
        MinFilter = Linear;
        MagFilter = Linear;
        
        AddressU  = Clamp;
        AddressV  = Clamp;
    };
 

При инициализации мы устанавливаем высоту снежного покрова в значение 32:
    float4 InitSnowPS(in float2 uv : TEXCOORD0) : COLOR
    {
        return 32;
    }
 

При деформации снежного покрова мы просто вычитаем данные деформации и текущих данных высоты:
    float4 AddDeformationPS(in float2 uv : TEXCOORD0) : COLOR
    {
        float height        = tex2D(snowSampler, uv);
        
        float deformation   = tex2D(deformationSampler, uv);
        
        return clamp(height - deformation, 0, 32);
    }

Для “восстановления” снежного покрова, мы прибавляем к данным высоты случайные значения, которые считываем из текстуры randomMap. Значение randomNumbers меняется на каждом кадре, чтобы распределить данные по всей поверхности.
    float2 randomNumbers;
    float4 AccumulateSnowPS(in float2 uv : TEXCOORD0) : COLOR
    {
            float accumulatedSnow = 0.5 * tex2D(randomSampler,
                                                uv + randomNumbers);
                                                
            float height = tex2D(snowSampler, uv);
            
            return clamp(accumulatedSnow + height, 0, 32);
    }
 

Перед тем как скомбинировать карты деформации и смещения, мы для начала немного “размоем” (blur) карту деформации, что приведет к ее сглаживанию. Этот шейдер будет использован так:
    static float2 blurOffset[8] = 
    {
        float2( 1 / 256.0f ,   1 / 256.0f), 
        float2(-1 / 256.0f ,  -1 / 256.0f), 
        float2(-1 / 256.0f ,   1 / 256.0f), 
        float2( 1 / 256.0f ,  -1 / 256.0f), 
        float2( 1 / 256.0f ,   0.0f), 
        float2(-1 / 256.0f ,   0.0f), 
        float2(0.0f ,          1 / 256.0f), 
        float2(0.0f ,         -1 / 256.0f) 
    };
    
    float4 BlurPS(in float2 uv : TEXCOORD0) : COLOR
    {
        float4 color = tex2D(deformationSampler, uv);
        
        for( int i = 0; i < 8; i++)
        {
            color += tex2D(deformationSampler, 
                           uv + blurOffset[i]);
        }
        
        return color / 9.0f;
    }

Далее приведен пиксельный шейдер для копирования текстур и техники для каждого из шейдеров.
     float4 CopyTexturePS(in float2 uv : TEXCOORD0) : COLOR
    {
        return tex2D(inputSampler, uv);
    }
    
    technique AddDeformation
    {
        pass P0
        {
            pixelShader  = compile ps_3_0 AddDeformationPS();
        }
    }
 
    technique AccumulateSnow
    {
        pass P0
        {
            pixelShader  = compile ps_3_0 AccumulateSnowPS();
        }
    }
    technique BlurDeformation
    {
        pass P0
        {
            pixelShader  = compile ps_3_0 BlurPS();
        }
    }
    technique InitSnow
    {
        pass P0
        {
            pixelShader  = compile ps_3_0 InitSnowPS();
        }
    }
    technique CopyTexture
    {
        pass P0
        {
            pixelShader  = compile ps_3_0 CopyTexturePS();
        }
    }

Возвращаемся в Game1.cs и загружаем шейдер в методе LoadGraphicsContent:
    protected override void LoadGraphicsContent(bool loadAllContent)
    {
        if (loadAllContent)
        {
            [...]
            snowEffect = content.Load<Effect>("Shaders\\Snow");
        }
        [...]
    }
 

Так же как и в уроке с системой частиц нам необходим вспомогательный метод, который будет принимать на вход наименования техник и реализовывать работу с целями визуализации. Мы добавим функцию, которая берёт в качестве параметра строку и цель визуализации, в которую будет помещён результат. Сохраним её старое значение, определим временное, применим желаемую технику, и после этого скопируем результат из временного значения render target в предназначенную render target.
    private void DoSnowTechnique(string technique, 
                                 RenderTarget2D resultTarget)
    {
        RenderTarget2D oldRT = graphics.GraphicsDevice.GetRenderTarget(0) 
                                                            as RenderTarget2D;
                                                            
        DepthStencilBuffer oldDS = graphics.GraphicsDevice.DepthStencilBuffer;
 
        graphics.GraphicsDevice.DepthStencilBuffer = snowDepth;
        graphics.GraphicsDevice.SetRenderTarget(0, temporaryRT);
 
        graphics.GraphicsDevice.Clear(Color.White);
        
        spriteBatch.Begin(SpriteBlendMode.None,
                          SpriteSortMode.Immediate,
                          SaveStateMode.None);
 
        snowEffect.CurrentTechnique = snowEffect.Techniques[technique];
        snowEffect.Begin();
 
        if (isSnowReset)
        {
            snowEffect.Parameters["snowMap"].SetValue(
                                            snowRT.GetTexture());
            
            snowEffect.Parameters["deformationMap"].SetValue(
                                            deformationRT.GetTexture());
        }
        snowEffect.Parameters["randomNumbers"].SetValue(
                                            new Vector2((float)random.NextDouble(), 
                                                        (float)random.NextDouble()));
                                                
        snowEffect.CurrentTechnique.Passes[0].Begin();
        
        spriteBatch.Draw(noiseTexture, 
                         new Rectangle(0, 0, 256, 256), 
                         Color.White);
                         
        snowEffect.CurrentTechnique.Passes[0].End();
        snowEffect.End();
        spriteBatch.End();
 
        graphics.GraphicsDevice.ResolveRenderTarget(0);
 
        graphics.GraphicsDevice.SetRenderTarget(0, resultTarget);
 
        spriteBatch.Begin(SpriteBlendMode.None,
                          SpriteSortMode.Immediate,
                          SaveStateMode.None);
 
        snowEffect.CurrentTechnique = snowEffect.Techniques["CopyTexture"];
        
        snowEffect.Begin();
        snowEffect.CurrentTechnique.Passes[0].Begin();
        
        spriteBatch.Draw(temporaryRT.GetTexture(), 
                         new Rectangle(0, 0, 256, 256), 
                         Color.White);
                         
        snowEffect.CurrentTechnique.Passes[0].End();
        snowEffect.End();
        spriteBatch.End();
 
        graphics.GraphicsDevice.ResolveRenderTarget(0);
        graphics.GraphicsDevice.SetRenderTarget(0, oldRT);
        graphics.GraphicsDevice.DepthStencilBuffer = oldDS;
    }

Даная функция будет вызываться с различными параметрами. Для начала, если данные о снежном покрове должны быть сброшены, мы вызываем ее с указанием техники InitSnow. Затем первичная отрисовка карты деформации, затем ее размытие, затем “спекание” с картой смещения и, наконец, “восстановление” снежного покрова. Все эти вызовы мы помещаем в начало метода Draw(). И только после этого мы можем передать полученную текстуру в параметр displacementMap шейдера VTFSnow.
    void UpdateSnow()
    {
        if (!isSnowReset)
        {
            DoSnowTechnique("InitSnow", snowRT);
            isSnowReset = true;
        }
        
        RenderDeformationMap();
        
        DoSnowTechnique("BlurDeformation", deformationRT);
        DoSnowTechnique("AddDeformation",  snowRT);
        DoSnowTechnique("AccumulateSnow",  snowRT);
    }
 
    protected override void Draw(GameTime gameTime)
    {
        UpdateSnow();
 
        graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
        
        graphics.GraphicsDevice.RenderState.CullMode = CullMode.None;
        graphics.GraphicsDevice.RenderState.DepthBufferEnable = true;
 
        gridEffect.Parameters["displacementMap"].SetValue(
                                                snowRT.GetTexture());
        [...]
    }
 

Запустите приложение и Вы увидите как персонаж, несколько не напрягаясь, преодолевает снежные сугробы J

clip_image002

clip_image004

В данном разделе мы рассмотрели как можно сгенерировать карту деформации путем отрисовки сцены, используя специфичные матрицы, как скомбинировать полученную карту с картой смещения и как получить эффект оставления следов на снегу. Данный эффект можно скомбинировать с алгоритмом генерации нормалей для нормального освещения и с системой частиц для более реалистичного результата. Если Вы решите реализовать систему частиц, то можете реализовать “восстановление” снежного покрова на основе текстуры, содержащей данные о местоположении частиц, и заставить частицы “умирать”, когда они касаются снега, основываясь на данных карты смещения.

Полный код этого раздела Вы можете скачать тут: Chapter4.zip

Заключение и будущие разработки

В данном уроке мы рассмотрели четыре способа использования вершинных текстур, но мы только прикоснулись к этому большому разделу, где их можно использовать. Эффекты, рассмотренные здесь, были реализованы на скорую руку, и если немного подумать, то можно было написать более интересную и более функциональную реализацию.

Для отрисовки ландшафта можно использовать вершинную текстуру с различной плотностью сетки, более высокую плотность использовать в том месте, куда у нас в данный момент смотрит камера, и смещать ее по мере движения камеры по сцене. При таком подходе мы получим автоматическую реализацию LOD (Level Of Detail) ландшафта. Это вполне реализуемо с VTF, так как данные высоты постоянно рассчитываются в вершинном шейдере. Это использовано в технике geometric clipmaps разработанной Hugues Hoppe и Frank Losasso .

Существует большое количество способов для усовершенствования системы частиц. Не так давно поток (течение) Lord Ikon’а на форуме creators.xna.com натолкнул на идею по усовершенствованию системы частиц. Первое, это определение столкновений частиц со всеми объектами сцены. Мы все видели, как это реализовать на примере с ландшафтом, когда у нас была карта высот. Но, немного подумав, я понял, как это реализовать со всеми объектами сцены. Для этого нам надо отрисовать сцену с положения камеры “сверху вниз” и сгенерировать динамическую карту высот для всей сцены вместе со всеми объектами. Так как мы только что закончили четвертую часть урока, не так уж и трудно себе представить, как это сделать, с помощью ортогональной матрицы и специального пиксельного шейдера. Полученную карту вида сверху затем использовать в пиксельном шейдере системы частиц. Это особенно окажется действенным для частиц, которые имеют свойство падать с неба: снег, дождь. Вид камеры может быть немного подправлен, если нам нужен будет не “прямой” дождь, а идущий немного под углом. Еще одно дополнение к системе частиц, это если взглянуть на наш пример и поперемещаться внутри системы, то можно заметить разницу в плотности идущих частиц. Все было бы нормально, если бы мы держали систему частиц оцентрованной относительно положения наблюдателя. Но для этого надо решить одну проблему, надо чтобы частицы не двигались вслед за наблюдателем. Я до сих пор работаю над этим и как только найду решение, дам знать.

Существует и другие эффекты, использующие вершинные текстуры: симуляция ткани (cloth simulation), реалистичный рендеринг воды, который был рассмотрен в GPU Gems 2, и у ATI есть пример сортировки спрайтов, используя R2VB, который можно переписать с использованием вершинных текстур.

Я надеюсь, Вам понравился данный урок, и он подтолкнет Вас на дальнейшие эксперименты с вершинными текстурами и поиск новых путей их использования. Я уверен, что Вы сообщите мне, если придумает что-нибудь интересное. J