четверг, 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);

}

Комментариев нет:

Отправить комментарий