When can EffectParameter.SetValue() be called?

Hello:

Im triying to create an ilumination system for a 2D game. For that, I draw each light into a separated buffer. In order to make the maximum amount of light in the buffer exceed when necessary (1,1,1), I draw each light with a shader in a RenderTarget configured with HalfVector4 surface mode:

float3 Bright=float3(1.0,1.0,1.0); /*<-Setted parameter*/
[...]
float4 MainPS(VertexShaderOutput input) : COLOR
{
	float4 baseColor = tex2D(SpriteTextureSampler,input.TextureCoordinates) * input.Color;
	return float4(baseColor.x*Bright.x, baseColor.y*Bright.y, baseColor.z*Bright.z, baseColor.w);
}

At first it worked fine, just as I expected but after some not sustantial changes, it stopped doing so.

I suspect it is because the Bright EffectParamter is being updated whitout starting a new SpriteBatch.Begin cycle. Currently, my code only changes the draw params when necessary in this way:

SpriteBatchDrawParams lightParams = new(){blendState=BlendState.Additive, effect=LightsShader};
            SpriteBatchDrawParams? drawParams = null;
            for(int i = 0; i<ToDrawInBufferElsCount; i++)
            {
                GameObject obj = ToDrawInBuffer[i];
                SpriteBatchDrawParams newParams = obj is LightObject?lightParams:GetObjectDrawParams(obj as IDrawnObject);
                if (drawParams != newParams /*|| true*/)/*Note that when the '|| true' is uncommented, it always works at the expense of more draw calls.*/
                {
                    if (drawParams != null) spriteBatch.End();
                    drawParams = newParams;
                    newParams.StartSpriteBatch(spriteBatch);
                }
                if (obj is LightObject light)
                {
                    LightsShader.Parameters["Bright"].SetValue(light.LightColor);
                    Console.WriteLine(drawParams?.effect);
                    light.DrawLights(spriteBatch);
                } else if (obj is IDrawnObject drawnObject)
                {
                    drawnObject.Draw(DrawItDataByObj[obj]);
                }
            }

So if it has to draw multiple lights back to back, it doesn’t change of drawing begin/end, but if there is any non-light object in between the light calls, it changes to the proper parameters (that make the obj being drawn in black with a custom BlendState, since is the Lights Buffer, not the main render.)

The thing is that even if I uncomment the || true, the EffectParameter.SetValue is still being called after the SpriteBatch.Begin.

So:

When should EffectParameter.SetValue be called?

How Do You Use MonoGame Effects? A Practical Shader Guide

Gist:

LightsShader.Parameters["Bright"].SetValue(lightColor);

spriteBatch.Begin(effect: LightsShader);
spriteBatch.Draw(...);
spriteBatch.End();

I’d set the EffectParameter before `SpriteBatch.Begin()` so the effect starts the batch with the value you want.

You should dig into the source to really see for yourself exactly when/how shader params are used within SpriteBatch, but if memory serves it’s something like if you set the SpriteSortMode to Immediate (when calling Begin) then they’re used anytime you call Draw, else they’re only used when you call End. Because with the other modes, all sprites are drawn at once during End, thus “batched”. So then changes to the parameters between Draw calls would be irrelevant and only the last value set will be used.

1 Like