How Use array in fx file?

Hi guys

I am new in monogame and sorry for my bad english level.

I want to transform all my XNA project but fx files have problems.

First it’s not possible to declare array struct, “unsupported parameter class”.

So I had make a new effect file with variable array but update postion doesn’t work.

Can you help me?
What is a difference between ->


float4 position;
float4 color;
float invRadius;

float4 ambientColor;
int screenWidth;
int screenHeight;

sampler ColorMap : register(s0);

sampler NormalMap = sampler_state 
{
    Texture = <normaltexture>;   
}; 


float4 DeferredNormalPS(float4 Position : SV_Position, float4 Color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
{
    float4 base = tex2D(ColorMap, texCoord);

    float3 normal = normalize(tex2D(NormalMap, texCoord) * 2.0f - 1.0f);

    float3 PixelPosition = float3(screenWidth * texCoord.x, screenHeight * texCoord.y,0);
    
    float2 Direction;
    float Distance;

    float4 finalresult = 0;
    
    float modifer = 1; //max((1 - atten), 0);

    Direction = position * normal - PixelPosition*normal;

    invRadius = 1 / invRadius;

    Distance = saturate(1 / length(Direction) * invRadius);

    finalresult = (Distance * color * 1);

    return base * ((ambientColor * 1) + finalresult);// *((ambientColor * 1) + (Distance * color[0] * 1));
}

    
technique Deferred
{
    pass Pass0
    {
        PixelShader = compile ps_4_0_level_9_1 DeferredNormalPS();
    }
}

and this


float4 position [1];
float4 color [1];
float invRadius [1];

int NUMBEROFLIGHT;

float4 ambientColor;
int screenWidth;
int screenHeight;

sampler ColorMap : register(s0);

sampler NormalMap = sampler_state 
{
    Texture = <normaltexture>;
}; 


float4 DeferredNormalPS(float4 Position : SV_Position, float4 Color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
{
    float4 base = tex2D(ColorMap, texCoord);

    float3 normal = normalize(tex2D(NormalMap, texCoord) * 2.0f - 1.0f);

    float3 PixelPosition = float3(screenWidth * texCoord.x, screenHeight * texCoord.y,0);
    
    float2 Direction [1];
    float Distance [1];

    float4 finalresult = 0;
    
  
    for(int i =0; i<= NUMBEROFLIGHT; i++)
    {
        Direction[i] = position[i] * normal - PixelPosition*normal;

        invRadius[i] = 1 / invRadius[i];

        Distance[i] = saturate(1 / length(Direction[i]) * invRadius[i]);

        finalresult += (Distance[i] * color[i] * 1);
    }

    return base * ((ambientColor * 1) + finalresult);// *((ambientColor * 1) + (Distance * color[0] * 1));
}

    
technique Deferred
{
    pass Pass0
    {
        PixelShader = compile ps_4_0_level_9_1 DeferredNormalPS();
    }
}

Thank you in advance

I don’t understand why its not a same thing, I see all lights but no update.

Wait Monogame 3.6?

Hi !

NUMBEROFLIGHT is not define in the second part. Does the pipeline tool give any error/warning ?

Hi,

Thx for reply :wink:

Yes error copy/past, sorry.

Its not that. I don’t have error, its work fine, I see my light source but no update position or color :frowning:

Ok, thx

I have a soultion, very fastidious but I see only it for my level.


float3 position_1;
float3 color_1;
float invRadius_1;

float3 position_2;
float3 color_2;
float invRadius_2;1

float4 ambientColor;
int screenWidth;
int screenHeight;

sampler ColorMap : register(s0);

sampler NormalMap : register(s1)
{
    Texture = <normaltexture>;
}; 


float4 DeferredNormalPS(float4 Position : SV_Position, float4 Color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
{
    float4 base = tex2D(ColorMap, texCoord);

    float3 normal = normalize(tex2D(NormalMap, texCoord) * 2.0f - 1.0f);

    float3 PixelPosition = float3(screenWidth * texCoord.x, screenHeight * texCoord.y,0);        
   
    float3 finalresult = 0;

    float3 Direction_1;
    float Distance_1;

    float3 Direction_2;
    float Distance_2;
    
   
    Direction_1 = position_1 - PixelPosition;

    float lenght_1 = length(Direction_1);

    Direction_1 /= lenght_1;

    lenght_1 *= invRadius_1;

    float modifer_1 = max((1 - lenght_1), 0);

    float amount_1 =  max(dot(normal, Direction_1), 0);

    invRadius_1 = 1 / invRadius_1;

    Distance_1 = saturate(length(Direction_1) * invRadius_1);

    finalresult = (color_1 * amount_1 * modifer_1 * Distance_1);

    //////////////////////////////////////////////////////////////////////////////////

    Direction_2 = position_2 - PixelPosition;

    float lenght_2 = length(Direction_2);

    Direction_2 /= lenght_2;

    lenght_2 *= invRadius_2;

    float modifer_2 = max((1 - lenght_2), 0);

    float amount_2 = max(dot(normal, Direction_2), 0);

    //Direction *= normal;
    invRadius_2 = 1 / invRadius_2;

    Distance_2= saturate(length(Direction_2) * invRadius_2);

    finalresult += (color_2 * amount_2 * modifer_2 * Distance_2);
 

    return float4(((ambientColor * 1) + finalresult) * base.rgb ,base.a);
}

    
technique Deferred
{
    pass Pass0
    {
        PixelShader = compile ps_4_0_level_9_1 DeferredNormalPS();
    }
}

For 2 light source its not a problem, but with 50 light source its nonsense

Can you help me to use array in fx file ?

Thx

Up

please, I need help. I know my english level is so bad. If you don’t understand ask me. I will try again and again.

With deferred rendering, you should not have to pass an array of lights to the shader. At least it’s not what I do.
But what do you mean by “I see my light source but no update position or color” ? Does the program enters the update method ?
When I need to pass an array of something to a shader, I often use a vector, or a matrix if more than 4 floats are needed. But an array of something I don’t know…

Hi thx for your reply.

I have combined the 2 fx file. You can see a problem in this video.
If I want multiple light source, different position and color and radius…, I must have array?
I want to make this for my level editor.

With vector its a same thing. No update :frowning:

Video with and without array declaration

Thx

I draw an array of lights, but the “loop” is made in the draw method, not in the shader in my engine. So the code can be used without using the VRAM (I can display up to 2000 pointlights without problem, maybe more). Both method should work though.
I remember of an XNA example on microsoft education showing your method of using multiple lights. (the material one maybe ?) It might be this one.
http://xbox.create.msdn.com/en-US/education/catalog/sample/shader_series_5

Have you tried, instead of drawing the light, make the shader draw a pixel in blue for ex, with the coordinates you send to the shader ? It may help to see what’s going on with the coordinates.
It may also come from
float3 PixelPosition = float3(screenWidth * texCoord.x, screenHeight * texCoord.y,0);
where an int is multiplied by a float, it may be truncated to 0 if a cast to int is done before the multiply ? cast to float to confirm it’s not the problem.

I had see this tutotial, but they use array struct, its not possible with monogame.

I have this error, “unsupported parameter class”.

Yes I have tried but position not change with array using.
I have change a cast too, but same issue.

If I use array, all variable stay in initialisation state.

I look for great tuto but they are rare for beginners

thx

Beware, batching lights in deferred rendering can improve performance quite a bit. Personally I got about 14% performance increase when I started batching lights by 4. Granted, it was specific case, my point is that using batching lights in deferred might be legit approach.

As far as arrays goes:

You can definitely do this:

float3 LightColor[4];
float3 LightPosition[4];

Only thing is that array in shader can´t be dynamic in anyway.

I use another trick to improve performance, as well as LOD when lights are really far as it will be used in a space game where you can see really far with the right developed technology by the player. What do you mean with batching ? Send lights as a group of 4, or 8 for ex ?

Yeah, in my case I am sending group of 4.

Btw I am using Schlick approximation, originally I was using Cook-Torrance but I wasn´t quite happy with results. Anyway while batching there are few things to consider, for instance how good is your limiting of any possible overdraw (what 2D geometry do you use when resolving light).

In your case, as Alkher said, just accumulate lights in rendering loop. You would hit instruction limit at some point anyway (if you would be trying to render all lights in single drawcall).

You will want to use additive blending mode.

Thanx guys

Ok, I understand now. But if I want a lot of dynamic source light, How make this?

Yes I want that. I have 6 layers in my game project 2D, I want to apply light (defferd normal) in single layer.

Declaration

LightEmmitter[] lights = new LightEmmitter[2];

Here in a LoadContent method

lights[0] = new LightEmmitter();
            lights[0].position = new Vector3(20, 30, 0);
            lights[0].radius = lightRadius;
            lights[0].corrector = lightC;
            lights[0].color = new Vector3(0.5f, 1f, 1f);

            lights[1] = new LightEmmitter();
            lights[1].position = new Vector3(20, 30, 0);
            lights[1].radius = lightRadius;
            lights[1].corrector = lightC;
            lights[1].color = new Vector3(1f, 0f, 0f);

Update methode

 lights[0].position = new Vector3(Mouse.GetState().X, Mouse.GetState().Y, lightZ);
            lights[0].radius = lightRadius;
            lights[0].corrector = lightC;

            lights[1].position = new Vector3(800 - Mouse.GetState().X, 480 - Mouse.GetState().Y, lightZ);
            lights[1].radius = lightRadius;
            lights[1].corrector = lightC;

And Draw method

deferred.CurrentTechnique = deferred.Techniques["Deferred"];
deferred.Parameters["screenWidth"].SetValue(GraphicsDevice.Viewport.Width);
deferred.Parameters["screenHeight"].SetValue(GraphicsDevice.Viewport.Height);
deferred.Parameters["ambientColor"].SetValue(new Vector3(0, 1, 0) * 0.0f);

for (int i = 0; i < lights.Length; i++)
                {                   
                    lights[i].UpdateEffect(deferred);
                }


foreach (EffectPass pass in deferred.CurrentTechnique.Passes)
             {
                           pass.Apply();
                           spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied, SamplerState.LinearClamp, DepthStencilState.None, RasterizerState.CullCounterClockwise);

                           spriteBatch.Draw(textureNormal, new Rectangle(0,0, 800, 480), Color.White);     
        
                           spriteBatch.end();

            }

With this draw method I see only the last light in a array;

So I had tested this


spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied, SamplerState.LinearClamp, DepthStencilState.None, RasterizerState.CullCounterClockwise);

           //First Light
             for (int i = 0; i < 1; i++)
              {
                    lights[i].UpdateEffect(deferred);                   
              }          


            foreach (EffectPass pass in deferred.CurrentTechnique.Passes)
             {
                pass.Apply();
            }

          //Second Light
             for (int i = 1; i < 2; i++)
              {
                  lights[i].UpdateEffect(deferred);
              }
           
            foreach (EffectPass pass in deferred.CurrentTechnique.Passes)
            {
                pass.Apply();
               
            }
            spriteBatch.Draw(textureNormal, new Rectangle(0, 0, 800, 480), Color.White);


            spriteBatch.End();

FX file

float3 position_;
float3 color_;
float invRadius_;

float4 ambientColor;
float screenWidth;
float screenHeight;

sampler ColorMap : register(s0);

sampler NormalMap : register(s1)
{
    Texture = (normaltexture);
}; 

float4 DeferredNormalPS(float4 Position : SV_Position, float4 Color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0 
{
    foat4 base = tex2D(ColorMap, texCoord);

    float3 normal = normalize(tex2D(NormalMap, texCoord) * 2.0f - 1.0f);

    float3 PixelPosition = float3((screenWidth) * texCoord.x, screenHeight * texCoord.y,0);
         
    float3 finalresult = 0;
    
    float3 Direction_;
    float Distance_;

    Direction_ = (position_ - PixelPosition)* normal;
    float lenght = length(Direction_);
    Direction_ /= lenght;
    lenght *= invRadius_;       
    invRadius_ =  1/invRadius_;
    Distance_ = saturate(length(Direction_)*invRadius_);

    finalresult += (color_* Distance_);

}
technique Deferred
{
    pass Pass0
    {
        AlphaBlendEnable = TRUE;
        //DestBlend = INVSRCALPHA;
        //SrcBlend = SRCALPHA;
        PixelShader = compile ps_4_0 DeferredNormalPS();
    }
}

And lightemmitter update method

internal void UpdateEffect(Effect effectParameter)
        {           
                effectParameter.Parameters["position_"].SetValue(position);
                effectParameter.Parameters["color_"].SetValue(color * corrector);
                effectParameter.Parameters["invRadius_"].SetValue(1f / radius);
           

        }

It is a same thing. I see only the last light. Bad way?

Thx

I think I make this but it would seem to make me wrong.

Not specially, why?

You can´t render high amount of lights in single drawcall, this is whole point, you need to accumulate them as you were told.

//somewhere outside draw loop asign normal render target and whatever else you use when resolving lights (custom depth buffer, etc...)
light_effect.Parameters["NormalMap"].SetValue... 
light_effect.Parameters["DepthBuffer"].SetValue...

//then in rendering loop
batch.Begin(SpriteSortMode.Immediate, BlendState.Additive, SamplerState.PointClamp, null, null, light_effect);
foreach light in lightbag
{
   light_effect.Parameters["Intensity"].SetValue....... //send all parameter for single light you are rendering
   light_effect.Parameters["Color"].SetValue...
   ...

   batch.Draw(diffuseRT, Vector2.Zero, Color.White); //every light renders Diffuse * Light + Specularity
}
batch.end

This specific example wont require arrays in fx file at all.

Thx

Sry, I thought it was good.

But I don’t understand why it’s working for your all lights because me I see always one light.

If I follow your instruction, its done that :


 spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Additive, SamplerState.LinearClamp, null, null, deferred);
 foreach (LightEmmitter light in lights)
            {
                deferred.Parameters["position_"].SetValue(light.position);
                deferred.Parameters["color_"].SetValue(light.color * light.corrector);
                deferred.Parameters["invRadius_"].SetValue(1f / light.radius);

                spriteBatch.Draw(texture, new Rectangle(0, 0, 800, 480), Color.White);
            }

spriteBatch.End();

And outside draw loop


deferred.CurrentTechnique = deferred.Techniques["Deferred"];
deferred.Parameters["screenWidth"].SetValue((float)GraphicsDevice.Viewport.Width);
deferred.Parameters["screenHeight"].SetValue((float)GraphicsDevice.Viewport.Height);
deferred.Parameters["ambientColor"].SetValue(new Vector3(1, 1, 1) * 1.0f);
deferred.Parameters["normaltexture"].SetValue(textureNormal);

I do well, no?

Yeah, this looks correct so far. In your original code you were using “nonpremultiplied” as blending mode so you could see only last light as it was rendered on top. Do you have any issues with current code?

It’s my fault, sorry It’s worrking very well thank you.

I had put “AlphaBlendEnable = TRUE;” in my pass, if “AlphaBlendEnable = FALSE;” same thing, but if I delete this it works.

But I need to change “ambientcolor” parameters by


deferred.Parameters["ambientColor"].SetValue(new Vector3(1, 1, 1) * FLOAT);

FLOAT = 1/NumberOfLight.

Thx a lot.