Changing colors in TCastleScene

How can I easily change colors in TCastleScene?

In my old code with TSprite I could change color with:
var rgb1, rgb2, rgb3: single;
Location.BackGround.Color := Vector4(rgb1, rgb2, rgb3, 1); // fade background to night

if Event.IsKey(keySpace)then
rgb1 := rgb1-0.004;
rgb2 := rgb2-0.004;
rgb3 := rgb3-0.004;

As TCastleScene can contain a lot of stuff inside, it’s not so easy to change “its color”. You will have to find all components inside (even if there is only one), and change each of their colors. See e.g. Castle Game Engine: X3DNodes: Class TMaterialNode

However, in your case you most likely wouldn’t want to do that - a better idea would be changing the color (tint) of the whole construction through shader. See examples\3d_rendering_processing\shader_effects. It practically does exactly what you need.

I just experimented with this example but found it still too difficult to implement in my 2D setup.
I only want to gradually darken all colors so eventually it becomes “night”. This should apply to all my scenes (background, player and npc).

The other path might look simpler (manipulating color for every texture) because it uses the same logic as “just images”, but actually it isn’t simple. So, I’d still highly recommend you to invest some time and learn how to work with shaders - later that will enable you to implement much more sophisticated effects than just changing the screen tint.

However, if you want just “fade all the screen” - you can try a bit simpler approach - examples\screen_effects_demo - you can apply those to groups of UI elements - i.e. this way you can dim both Player, Background and objects on the screen in one go. But in the end those are the same GLSL shaders, just a little bit differently formatted.

In case of sprite sheets, it is a bit easier, as you actually have only one material instance inside, and all sprite sheets should have exactly 1 material, no more no less. So you can do

  MyMaterial: TUnlitMateriaNode;
  MyMaterial := Scene.RootNode.FindNode(TUnlitMaterialNode, false); // will raise exception if not found
  MyMaterial.EmissiveColor := Vector3(1, 1, 0); // change color to yellow
1 Like

That would be great.
However I get an error trying this?

Make a typecast:

MyMaterial := Scene.RootNode.FindNode(TUnlitMaterialNode, false) as TUnlitMaterialNode;

Wow, this is really spectacular!

Now that this works for the background I can apply it to all character scenes also.

Update: At least… that’s what I thought, but I can get the effect only for 1 scene.
If I, for instance, want to fade all scenes that are within the viewport at once, is this possible? Maybe by placing a ‘filtering’ rectangle over the world?
Because when I eventually have a background with objects and 20 sprites walking around it would be very much work to apply the effect one by one for all scenes.

There are screen effects: Screen Effects | Castle Game Engine , demo e.g. in examples/screen_effects_demo/. While I heavily advise learning to use them, it requires understanding GLSL a bit. If this is too much then you can use simpler solution instead and just iterate over your TCastleScenes.

Reviving an old topic, I know. But I have 3 questions:

I currently use about 10 different scenes (sprite screens) per Transform.
Is it possible to apply color changes to a Transform directly instead of individually all scenes that are part of it?

MyMaterial := Player.StandIdleScene.RootNode.FindNode(TUnlitMaterialNode, false) as TUnlitMaterialNode;
MyMaterial := Player.WalkLeftScene.RootNode.FindNode(TUnlitMaterialNode, false) as TUnlitMaterialNode;
MyMaterial := Player.WalkRightScene.RootNode.FindNode(TUnlitMaterialNode, false) as TUnlitMaterialNode;
MyMaterial.EmissiveColor := Vector3(0.25, 0.25, 0.25);  

This only changes the colors in Player.WalkRightScene, so should I make an array of MyMaterial for all scenes or is there a more convenient way?

I use TCastleImageControl for background. How can I apply the same colors change to it as in the example above? (Vector3(0.25, 0.25, 0.25). Or do I have to make background a scene also?
TCastleImageControl has the TCastleColor property; can I use this to alter rgb values (and how?)

AD 1, 2: Each scene has it’s own set of materials. So you have to change each one separately.

We plan to expose material editing in CGE editor later this year, then you’d be able to have one central TCastleUnlitMaterial component in the design, assign it to all scenes, and then changing one TCastleUnlitMaterial.EmissiveColor would change it in all scenes. But this is not ready yet.

AD 3 - Just assign to TCastleImageControl.Color, there’s not much to it :slight_smile: It’s a 4D vector because the 4th component is an alpha (opacity), set it to 1.0 if you don’t want to change it. So like MyImageControl.Color := Vector4(0.25, 0.25, 0.25, 1).

Note that you can also use TCastleImageTransform to put image in a viewport. Depends on how you want to use the image (should it move, or not move, when the camera in viewport changes? when viewport size changes?) this may be suitable. It has equivalent property, TCastleImageTransform.Color.


Now I don’t understand why the correct color is not always displayed, when changing for instance
MyMaterial.EmissiveColor := Vector3(128, 0, 255);

results in


I see that color you posted is RGB (255,0,255) instead of (128,0,255). Don’t know yet about
“why”, there could be various reasons (e.g. lighting). Can you submit a testcase to reproduce the problem?