How Use array in fx file?

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.

Just a note:

If you render all lights in one loop (forward lighting) you can simply have an array with let’s say size 40 but only loop through the amount of lights you actually use.
Not sure if I’m way too late here, but yeah.

#define MAXLIGHT 20

float3  PointLightPosition[MAXLIGHT];
float4  PointLightColor[MAXLIGHT];
float   PointLightIntensity[MAXLIGHT];
float3  PointLightDirection[MAXLIGHT];
float   PointLightRadius[MAXLIGHT];
int     MaxLightsRendered = 0;

then in the pixel or vertex shader

 [loop]
    for (int i = 0; i < MaxLightsRendered; i++)
    {
        float3 DirectionToLight = PointLightPosition[i] - worldPos;
                           
        float DistanceSq = lengthSquared(DirectionToLight);

        float radius = PointLightRadius[i];
             
        [branch]
        if (DistanceSq < abs(radius*radius))
        {
            //calculate lighting
etc...

in the c# code to pass arrays I use…

private static readonly Vector3[] PointLightPosition = new Vector3[MaxLightsGpu];
private static readonly Vector4[] PointLightColor = new Vector4[MaxLightsGpu];
private static readonly float[] PointLightIntensity = new float[MaxLightsGpu];
private static readonly float[] PointLightRadius = new float[MaxLightsGpu];
private static readonly Vector3[] PointLightDirection = new Vector3[MaxLightsGpu];

public static void Initialize(Effect lightingEffect)
        {
            //POINTLIGHTS
            _lightingEffectPointLightPosition = lightingEffect.Parameters["PointLightPosition"];
            _lightingEffectPointLightColor = lightingEffect.Parameters["PointLightColor"];
            _lightingEffectPointLightIntensity = lightingEffect.Parameters["PointLightIntensity"];
            _lightingEffectPointLightDirection = lightingEffect.Parameters["PointLightDirection"];
            _lightingEffectPointLightRadius = lightingEffect.Parameters["PointLightRadius"];
            _lightingEffectMaxLightsRendered = lightingEffect.Parameters["MaxLightsRendered"];

            //LightTiles = new int[LightTilesHeight*LightTilesWidth];
        }

blabla calculations - is our light actually in the scene etc.

if(currentIndex>0)
            {
                _lightingEffectPointLightPosition.SetValue(PointLightPosition);
                _lightingEffectPointLightColor.SetValue(PointLightColor);
                _lightingEffectPointLightIntensity.SetValue(PointLightIntensity);
                _lightingEffectPointLightRadius.SetValue(PointLightRadius);
                _lightingEffectPointLightDirection.SetValue(PointLightDirection);
            }
            _lightingEffectMaxLightsRendered.SetValue(currentIndex);

I post this just as a short comprehensive post of how to pass arrays, not saying this lighting set up is ideal.

Thx all.

Solution proposed by @Ravendarke works very well. But I need to pass light effect on my player and few layers.
With my player animation I had to modifie frametime, and it was became a bad solution in my case.
Firthermore cause of additive blendstate, I had problem with my background.

@kosmonautgames thx, its a better solution for me.

[quote=“kosmonautgames, post:21, topic:8037”]
post this just as a short comprehensive post of how to pass arrays, not saying this lighting set up is ideal.
[/quote]al.

lol, no problem, I want to know how use array in a fx file and its ok now, thx :wink:

Wait, why I was whole time operating with assumption that you are using deferred rendering? What confused me? Nevermind.

Most probably because of the name of the method:
DeferredNormalPS
I thought the same thing

1 Like