MonoGame / MonoGame

One framework for creating powerful cross-platform games.
http://www.monogame.net
Other
11.31k stars 2.9k forks source link

Array of float/float3 in HLSL translates as an array of zeros when running app #6250

Open MarcVador opened 6 years ago

MarcVador commented 6 years ago

What version of MonoGame does the bug occur on:

What operating system are you using:

What MonoGame platform are you using:

I don't know if https://github.com/MonoGame/MonoGame/issues/6184 is related to this or if I set up my array in a wrong way ? I'm wondering why no one has noticed this before ?

The shader's code:

#if OPENGL
    #define SV_POSITION POSITION
    #define VS_SHADERMODEL vs_3_0
    #define PS_SHADERMODEL ps_3_0
#else
    #define VS_SHADERMODEL vs_4_0
    #define PS_SHADERMODEL ps_4_0
#endif

//-----------------------------------------
// Textures
texture DepthBuffer;
sampler2D depthSampler = sampler_state
{
    Texture = < DepthBuffer > ;
    MipFilter = LINEAR;
    MagFilter = LINEAR;
    MinFilter = LINEAR;
    AddressU = Clamp;
    AddressV = Clamp;
};

texture NormalBuffer;
sampler2D normalSampler = sampler_state
{
    Texture = < NormalBuffer > ;
    MipFilter = LINEAR;
    MagFilter = LINEAR;
    MinFilter = LINEAR;
    AddressU = Clamp;
    AddressV = Clamp;
};

texture RandomMap;
sampler2D randomSampler = sampler_state
{
    Texture = < RandomMap > ;
    MipFilter = POINT;
    MagFilter = POINT;
    MinFilter = POINT;
    AddressU = WRAP ;
    AddressV = WRAP ;
};

//-------------------------------
// Structs
//-------------------------------
struct VertexShaderInput
{
    float4 Position : SV_POSITION;
    float2 TexCoord : TEXCOORD0;
};

struct VertexShaderOutput
{
    float4 Position : POSITION0;
    float2 Tex0 : TEXCOORD0;
    //float2 TexCoordHalfBuffer : TEXCOORD1;
};

float3 normal_from_depth(float depth, float2 texcoords)
{
    const float2 offset1 = float2(0.0,0.001);
    const float2 offset2 = float2(0.001,0.0);

    float depth1 = tex2D(depthSampler, texcoords + offset1).r;
    float depth2 = tex2D(depthSampler, texcoords + offset2).r;

    float3 p1 = float3(offset1, depth1 - depth);
    float3 p2 = float3(offset2, depth2 - depth);

    float3 normal = cross(p1, p2);
    normal.z = -normal.z;

    return normalize(normal);
}

float4 ps_ssao(VertexShaderOutput input): COLOR
{ 
    //const float total_strength = 1.0;
    const float total_strength = 01.750;
    const float base = 0.2;

    const float area = 0.0075;
    const float falloff = 0.000001;

    const float radius = 0.0002;

    const int samples = 16;
    float3 sample_sphere[samples] = {
        float3( 0.5381, 0.1856,-0.4319), float3( 0.1379, 0.2486, 0.4430),
        float3( 0.3371, 0.5679,-0.0057), float3(-0.6999,-0.0451,-0.0019),
        float3( 0.0689,-0.1598,-0.8547), float3( 0.0560, 0.0069,-0.1843),
        float3(-0.0146, 0.1402, 0.0762), float3( 0.0100,-0.1924,-0.0344),
        float3(-0.3577,-0.5301,-0.4358), float3(-0.3169, 0.1063, 0.0158),
        float3( 0.0103,-0.5869, 0.0046), float3(-0.0897,-0.4940, 0.3287),
        float3( 0.7119,-0.0154,-0.0918), float3(-0.0533, 0.0596,-0.5411),
        float3( 0.0352,-0.0631, 0.5460), float3(-0.4776, 0.2847,-0.0271)
    };

    float3 random = normalize( tex2D(randomSampler, input.Tex0 * 4.0).rgb );

    float depth = tex2D(depthSampler, input.Tex0).r;

    float3 position = float3(input.Tex0.x, input.Tex0.y, depth);
    float3 normal = normal_from_depth(depth, input.Tex0);

    float radius_depth = radius / depth;
    float occlusion = 0.0;
    for(int i=0; i < samples; i++) {

        float3 ray = radius_depth * reflect(sample_sphere[i], random);
        float3 hemi_ray = position + sign(dot(ray,normal)) * ray;

        float occ_depth = tex2D(depthSampler, saturate(hemi_ray.xy)).r;
        float difference = depth - occ_depth;

        occlusion += step(falloff, difference) * (1.0-smoothstep(falloff, area, difference));
    }

    float ao = 1.0 - total_strength * occlusion * (1.0 / samples);
    float finalvalue = saturate(ao + base);

    return float4(finalvalue, finalvalue, finalvalue, 1.0f);
}

technique BasicColorDrawing
{
    pass P0
    {
        PixelShader = compile PS_SHADERMODEL ps_ssao();
    }
};

(I know I'm not yet using NormalBuffer nor NormalBuffer. But they are out of the scope of the problem.)

If I store the sample_sphere array out of the ps_ssao method, the resulting shader draws as if the values sampled from it were float3(0.0f, 0.0f, 0.0f), ie: if the array was filled with float3(0.0f, 0.0f, 0.0f)

Is it better to store an array in a method or outside ? (I would say the 2nd is better but I'm no expert)

MarcVador commented 6 years ago

The shader with the array in out of the method:

#if OPENGL
    #define SV_POSITION POSITION
    #define VS_SHADERMODEL vs_3_0
    #define PS_SHADERMODEL ps_3_0
#else
    #define VS_SHADERMODEL vs_4_0
    #define PS_SHADERMODEL ps_4_0
#endif

//-----------------------------------------
// Textures
texture DepthBuffer;
sampler2D depthSampler = sampler_state
{
    Texture = < DepthBuffer > ;
    MipFilter = LINEAR;
    MagFilter = LINEAR;
    MinFilter = LINEAR;
    AddressU = Clamp;
    AddressV = Clamp;
};

texture NormalBuffer;
sampler2D normalSampler = sampler_state
{
    Texture = < NormalBuffer > ;
    MipFilter = LINEAR;
    MagFilter = LINEAR;
    MinFilter = LINEAR;
    AddressU = Clamp;
    AddressV = Clamp;
};

texture RandomMap;
sampler2D randomSampler = sampler_state
{
    Texture = < RandomMap > ;
    MipFilter = POINT;
    MagFilter = POINT;
    MinFilter = POINT;
    AddressU = WRAP ;
    AddressV = WRAP ;
};

const int samples = 16;
float3 sample_sphere[16] = {
        float3( 0.5381, 0.1856,-0.4319), float3( 0.1379, 0.2486, 0.4430),
        float3( 0.3371, 0.5679,-0.0057), float3(-0.6999,-0.0451,-0.0019),
        float3( 0.0689,-0.1598,-0.8547), float3( 0.0560, 0.0069,-0.1843),
        float3(-0.0146, 0.1402, 0.0762), float3( 0.0100,-0.1924,-0.0344),
        float3(-0.3577,-0.5301,-0.4358), float3(-0.3169, 0.1063, 0.0158),
        float3( 0.0103,-0.5869, 0.0046), float3(-0.0897,-0.4940, 0.3287),
        float3( 0.7119,-0.0154,-0.0918), float3(-0.0533, 0.0596,-0.5411),
        float3( 0.0352,-0.0631, 0.5460), float3(-0.4776, 0.2847,-0.0271)
    };

//-------------------------------
// Structs
//-------------------------------
struct VertexShaderInput
{
    float4 Position : SV_POSITION;
    float2 TexCoord : TEXCOORD0;
};

struct VertexShaderOutput
{
    float4 Position : POSITION0;
    float2 Tex0 : TEXCOORD0;
    //float2 TexCoordHalfBuffer : TEXCOORD1;
};

float3 normal_from_depth(float depth, float2 texcoords)
{
    const float2 offset1 = float2(0.0,0.001);
    const float2 offset2 = float2(0.001,0.0);

    float depth1 = tex2D(depthSampler, texcoords + offset1).r;
    float depth2 = tex2D(depthSampler, texcoords + offset2).r;

    float3 p1 = float3(offset1, depth1 - depth);
    float3 p2 = float3(offset2, depth2 - depth);

    float3 normal = cross(p1, p2);
    normal.z = -normal.z;

    return normalize(normal);
}

float4 ps_ssao(VertexShaderOutput input): COLOR
{ 
    //const float total_strength = 1.0;
    const float total_strength = 01.750;
    const float base = 0.2;

    const float area = 0.0075;
    const float falloff = 0.000001;

    const float radius = 0.0002;

    /*array was here*/

    float3 random = normalize( tex2D(randomSampler, input.Tex0 * 4.0).rgb );

    float depth = tex2D(depthSampler, input.Tex0).r;

    float3 position = float3(input.Tex0.x, input.Tex0.y, depth);
    float3 normal = normal_from_depth(depth, input.Tex0);

    float radius_depth = radius / depth;
    float occlusion = 0.0;
    for(int i=0; i < samples; i++) {

        float3 ray = radius_depth * reflect(sample_sphere[i], random);
        float3 hemi_ray = position + sign(dot(ray,normal)) * ray;

        float occ_depth = tex2D(depthSampler, saturate(hemi_ray.xy)).r;
        float difference = depth - occ_depth;

        occlusion += step(falloff, difference) * (1.0-smoothstep(falloff, area, difference));
    }

    float ao = 1.0 - total_strength * occlusion * (1.0 / samples);
    float finalvalue = saturate(ao + base);

    return float4(finalvalue, finalvalue, finalvalue, 1.0f);
}

technique BasicColorDrawing
{
    pass P0
    {
        PixelShader = compile PS_SHADERMODEL ps_ssao();
    }
};

And the effect read in game: image

My test projects with references to Monogam's sources ____TestShadersArrays 180314 133250.zip

When I stop in private static EffectParameterCollection ReadParameters(BinaryReader reader), Line 402, (count = 16) each float3 gives:

class_ = Vector type = Single name = "sample_sphere" semantic = "" //annotations is empty rowCount = 1 columnCount = 3

elements: image

YTN0 commented 6 years ago

I might be running into a similar / related issue with arrays in my shader.

I posted a comment about it in the Monogame community forums:

http://community.monogame.net/t/arrays-in-shader-fx-not-working-for-opengl/10502

cpt-max commented 1 year ago

This is a bug in MGFXC. It doesn't store default values for arrays properly. You can initialize the array from C# yourself though.

If you are not planning to set the array values from C#, you should turn this into a static array, which will solve the problem: static float3 sample_sphere[samples] = ... This will make it a real HLSL constant, rather than a shader parameter that get's initialized by MonoGame.

I don't want to spend the time to fix this right now, but I can point out the basic problem. This line will only allocate data for the first element in the array: https://github.com/MonoGame/MonoGame/blob/2911bbb3bcc412aedc00c109f3a96bb2480b255f/Tools/MonoGame.Effect.Compiler/Effect/ConstantBufferData.sharpdx.cs#L31 The total byte size of the array is vdesc.Description.Size. But even if you fix this here, by making the data array large enough, this data will still not be written to the xnb file. The writer doesn't write the entire array at once, it wants to write each element individually. Parameters for the individual elements are being created, but the default value data doesn't get filled in:
https://github.com/MonoGame/MonoGame/blob/2911bbb3bcc412aedc00c109f3a96bb2480b255f/Tools/MonoGame.Effect.Compiler/Effect/ConstantBufferData.sharpdx.cs#L121

So the solution is to either make the writer write the entire array at once, or make sure the individual elements get the default value data filled in.