HLSL, Whats is wrong, this time...?

Hello, I am new in HLSL, totally new. I am tring to implement the lights, but every code line is a epic battle.

Now I recive a error, if I delete the line:
if (dist <= lightRadius[i])
all work… but I need the line. Any one know what is happening now?

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

sampler s0;
float4 lightColor[75];
float lightRadius[75];
float2 lightCenter[75];
int lightNumber;
float2 cameraPosition;

float4 PixelShaderFunction(float4 pos : SV_POSITION, float4 color1 : COLOR0, float2 coords : TEXCOORD0) : COLOR0
{
float4 color = tex2D(s0, coords);
float4 end_color = color;
float2 screenPosition = float2(coords.x * 1920, coords.y * 1080) + cameraPosition;
float dist;

for (int i = 0; i < 75; i++)
{
    if (i == lightNumber)
    {
        break;
    }
    else
    {
        dist = distance(screenPosition, lightCenter[i]);

        if (dist <= lightRadius[i])
        {
            //end_color = end_color*lightColor[i];
        }
    }
}

return color;

}

technique Technique1
{
pass Pass1
{
PixelShader = compile ps_4_0_level_9_1 PixelShaderFunction();
}
}

What is the error you receive, and what are you passing to the shader?

Related: Performing so many conditionals on the GPU is very expensive and may not be supported on all cards. You might want to process some of this information on the CPU and pass it into the shader.

I think you mixed your coordiates up … looks like lightCenter is in WorldSpace but your “screenPosition” is in Screenspace. Those do not match.

also you really shouldn’t use hardcoded dimensions - if you need the worldposition in the PixelShader, you can just pass that over in a param like TEXCOORD1 - which is a common way.

anyway - you didn’t tell what error you receive - i would just guess it’sa type mismatch (we can only guess at this point)

The error is always the same error, for everything:

Error 2 The command ““C:\Program Files (x86)\MSBuild\MonoGame\v3.0\Tools\MGCB.exe” /@:“C:\Users\Administrador\Desktop\Inventario Adrian NO BORRAR AUNNNN\Inventario\Content\Content.mgcb” /platform:Windows /quiet /outputDir:“bin\Windows\Content” /intermediateDir:“obj\Windows\Content”” exited with code 1. CraftTale

The coordinates are fine, work perfect for one light , when I use arrays to try extend the number of lights is when everything go crazy.

My game is a 2D game, the player have plenty of freedom, for example, he can colocate unlimited numbers of bonfire in the ground, every bonfire add a new light fount. First I implemented the lights using masks and work fine, but the fps go down a lot.

That’s not an HLSL error message, it just tells you that MGCB.exe exited with an error. Are you maybe not getting HLSL errors because of the /quiet option?

You might be running into pixel shader limitations. ps_4_0_level_9_1 is basically pixel shader 2, which is old and limited. Your loop count is very high. With pixel shader 2 the loop may need to be unrolled, making your shader program too long. Does lowering the loop count help?
If that’s the case, your options are

  • increase the pixel shader version (if your target platform allows it)
  • use less lights. Maybe use the 10 most influential lights for every object, instead of all the lights.
  • somehow divide the shader up into multiple smaller steps
  • use a different lighting model. If you have many small lights you could render them all into a lighting texture first (one quad per light), and then blend the whole scene using this texture.
1 Like

Thanks, I will to try using a more advanced version of pixel shader and limited the lights for object.

Now I render the entiry scene and apply the ligths to the render, but, object by object and limit the numbers of lights sound like a better solution.

If you double-click on the content.mcgb and click the “build” button you should see a more detailed error in there.

1 Like

Exist a way to control to what Draw apply the effect?

I read thath a way is do many Begin/End, but I have only one Begin/End for the entire World, I use Sprite SortMode.FrontToBack and calculate dynamically the Depth for every actor. If I use many of them (Begin/End) I can not handle the order of drawing.

There is a “layer” parameter in the .Draw call - us this for z-sorting (+apropriate sortmode). Call the Begin/End for each effect

SpriteBatch does no drawing until .End is called - that’s why you cannot change effect between Begin/End

I use the “layer” parameter in the draws, for a global Begin/End, but if I use a Begin/End for every actor, the order is not more for z-sorting.

I would say, you need to reorganize it in your code then - as you didn’t tell what you want to achieve, it’s hard to give any recommendation.

you cannot switch the effect used between begin/end (at least not on spritebatch, you could make your own spritebatch which does this I guess, but if it’s worth that work?)

This is a picture of the day. The bonfire not do nothing. All good.

This is other picture, in the nigth, I want the lights in the game will look like this. This is the current implementation.

In this last picture the lights look good, but the fps is low, because the high numbers of ligths.

I do this in a simple way. I draw in a 1920*1080 (virtual resolution) RenderTarget2D, masks of white to black for every light affecting de scene. I pass the final RenderTarget2D of the entire scene and multiply the pixel color in the scene for the color in the big mask of ligths.

I want do this in more fast way.

Not sure, if the shader code in your first post is still what you are using. The way I would do it is as follows (maybe it’s already like you do it)

Render the light-falloff to a texture (this don’t need to be full res, you can do 1/4 because you don’t need the detail anyway) - you could basically just render a texture with a white dot per lightsource on a black-cleared target. Do NOT use any loops in the shader, just pass in as geometry (or with spritebatch as 1 draw per light). This can also be as fast as passing a single Postion + falloff and do all the other calculations in vertexshader (not using spritebatch then, it’s more like a particlesystem in that case)

you then pass that texture to the batch where you draw everything else and in the shader use the position (screenspace: 0-1) as TextureCoordinate on that “lightmap” and multiply with color to get the result - should not have any problems with thousands of lights

There also should be no reason for multiple spritebatches or different effects per sprite <- that’s what you should aim for

1 Like
//////////////////////////////////////

sampler s0;
float4 lightColor[75];
float lightRadius[75];
float2 lightCenter[75];
int lightNumber;
float2 cameraPosition;

float4 PixelShaderFunction(float4 pos : SV_POSITION, float4 color1 : COLOR0, float2 coords : TEXCOORD0) : COLOR0
{
    float4 color = tex2D(s0, coords);
    float4 end_color = color;
    float2 screenPosition = float2(coords.x * 1920, coords.y * 1080) + cameraPosition;
    float dist;

    for (int i = 0; i < 75; i++)
    {
        if (i == lightNumber)
        {
            break;
        }
        else
        {
            dist = distance(screenPosition, lightCenter[i]);

            if (dist <= lightRadius[i])
            {
                //end_color = end_color*lightColor[i];
            }
        }
    }

    return color;
}

technique Technique1
{
    pass Pass1
    {
        PixelShader = compile ps_4_0_level_9_1 PixelShaderFunction();
    }
}

Your variable passed in is float4 color1 : COLOR0

But your pixel shader at no point actually uses this value that is passed in from the vertex shader.
This could cause a error depending on what the rest of the shader looks like.
Which is why its helpful to post the full shader ?

Also the way you are doing it you should have a lightIntensity or lightStrength array that ranges from 0 to 1.

You should for loop each light.
Based on the distance calculate the strength of the light.
Multiply that intensity amount (0 to 1 / distance.) to the rgb color.
Sum that rgb color on each iteration to a final color.
After the loop is completed you should call saturate on the color to ensure it is within a normal color range.
Then pass that value out.

No if else statements are required.
Basically though this is just pseudo code.

// add this
float lightStrength[75];  // intensity 0 to 1;
.
.
float4 PixelShaderFunction(float4 pos : SV_POSITION, float2 coords : TEXCOORD0) : COLOR0
{
float4 uv_texel_color = tex2D(s0, coords);
// im not to confident of this line but im going to assume that you are already made sure this works and tested it.
float2 screenPosition = float2(coords.x * 1920, coords.y * 1080) + cameraPosition;
for (int i = 0; i < 75; i++)
{
    dist = distance(screenPosition, lightCenter[i]);
    // light falloff curve formula is entirely up to you depending on how you want it to feel.
    // I forget how distance works in a shader so you will probably need to experiment with this.
    // The intensity should i think fall off with the square of the distance.
    float intensity = lightStrength[i] * ( lightRadius[i] / ( lightRadius[i] * (dist + 1.0f) ) );
    // make sure the value isn't below zero saturate can be used here as well i think alternately.
    intensity = max(0, intensity);
    // sum each light that illuminates this pixel.
    end_color += uv_texel_color *  float4(intensity, intensity, intensity, 1.0f);
}
end_color = saturate(end_color);
return end_color;
}

Get rid of the color1 input value if you are not using it.

1 Like

Almost 400 lights in the scene and the fps is stable. The low resolution of the render was a big improve to the speed. Thanks for the patience and the help.