Pass array of integer to TEffectNode?

Trying to make my splat map, I have prototyped an array of integers of 0..3 in the glsl and entered values and use it to paint the image ontop of the default terrain display. Currently just prototyping with the same 5x5 grid per tile. It works. But I can’t figure out a good way to get the splat index data into the glsl? Ideally it would be an array of bytes or nibbles… but it looks like glsl I have to use full int. But I don’t see how to pass an array? Passing each value individually with a name seems like a lot of overhead.

The sandy squares are the ‘upper left’ cell of the 5x5 splatmap that is used on every tile. Then it has brown muddy cells to the lower right of it. I have the same on each tile, so you can see the size of a tile…


“Grandma’s topographical quilt” (now 6x6 splattermap)…

  int a[36] = int[36](0, 1, 0, 1, 0, 1,
                      1, 0, 1, 0, 1, 0,
					  0, 1, 0, 3, 0, 1, 
					  1, 0, 1, 0, 1, 0,
					  0, 1, 0, 1, 0, 1,
					  2, 0, 1, 0, 1, 0 ); 

This isn’t aligned to the tiles but is scaled to what seems like it would be a good size splatcell (here 2 world units, ie 10m at my scale). That would make the ideal splatmap size 60x60 for my 120x120 tiles. (120x120 would be ideal, but that seems greedy).

Maybe 600x600 would be ideal. That would be at 1m in the game. the terrain is at 5m. Here is an ode to CGE. My splatmap is 10x10 with a palette of 20 entries. I still haven’t figure out a good way to pass the data.

I figure it out using

splatmap := TMFLong.Create( Result, true, 'splatmap', [] );
   splatmap.items.Count := 3600;
   for i := 0 to splatmap.items.Count - 1 do
      splatmap.items[i] := random(20);
   Result.AddCustomField(splatmap);

This is giving me a 60x60 splat grid per 120x120 tile. So 10m in my world. Now the splat maps are separate per tile. So here each position at each point on all the splatmaps is unique and has 20 current possibities. Here it is randomized. If I go higher splat resolution (120x120 is 14400) it crashes.

Since I have to pass integer.. I have packed 4 bit rgba color in to the low 16 bits and then the texture id 0..3 with its 4 bit alpha into the next 8 bits. I have room to add another image. I have made some effort to blend colors between splatcells, but haven’t done so for texture yet. Here is random color+alpha with random texture+alpha overlayed onto the standard terrain coloring.

I was feeling constrained by the buffer limit for glsl limiting the resolution of my splat map. I started to combine neighboring cells… and voila … virtual increase so now it looks like a splat resolution of 120 per tile to match the tile resolution. Even though the real splat resolution is 60 per tile (outlined by the red squares in the grid).

Taking that a step further, by combining with neighbors I can produce a virtual 3x3 splat from each splat cell. So now it is like having 180x180 splat cells for 120x120 vertex cell in a tile even though the splat resolution is 60x60. This is visual resolution of 3.33m in my game scale. I also fixed some bugs in how each splat cell mixes with its neighbors.

Sorry for delay (I’m catching up with forum threads where I had given no answer). I see you found the solution – indeed TMFLong (equivalent to TMFInt32) is a type for attribute that will get passed as integer array to GLSL.

We admittedly don’t document it nicely, the Programmable shaders component | Castle Game Engine just says

You can use VRML/X3D multiple-value fields to set GLSL array types. We support all mappings between VRML/X3D and GLSL types for uniform values (that are mentioned in X3D spec).

… which means that you can look at X3D spec: OpenGL shading language (GLSL) binding to learn all the possibilities.

They are tested in our demo-models,

Various strategies to “pack” are also possible, as I see you explored. You can use 4d vectors, you can use floats, if you have a predetermined range/resolution that you can possibly squeeze more values into 1. You can also use textures with RGBA colors meaning… well, anything you want :slight_smile:

It seems like passing arrays of values is more limited by glsl buffer size than images? I am limited to about 60x60 integers in the MFInt32 I pass for the splat map but texture images can be much much much bigger? I guess images are managed differently?

Yes the size of vertex and fragment uniform variable storage is limited, with OpenGL specifications guaranteeing a minimum of 1024. My new RTX 4060Ti card reports a size of 4096, which quite aligns with your 60x60 limit.

You can query for the size by calling glGetIntergerv() with GL_MAX_VERTEX_UNIFORM_COMPONENTS and GL_MAX_FRAGMENT_UNIFORM_COMPONENTS

Uniform arrays are stored in uniform variable storage. Traditional way to deal with this issue is to just pass large data as texture, then access it via texelFetch()

1 Like

Thanks! So the uniform variable storage limit varies by card? So my 60x60 splat maps might be too much for some? I’ll have to play around. When you query the card, how are you getting the info out of the glsl?

The glGetIntergerv queries mentioned by @kagamma are OpenGL commands that you can just perform from Pascal. They inform you about GLSL limits.

So, in your Pascal code, just do something like this:

uses ... {$ifdef OpenGLES} CastleGLES {$else} CastleGL {$endif};
var
  MaxVertexUniformComponents: TGLint;
begin
  glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, @MaxVertexUniformComponents);
  WritelnLog('GL_MAX_VERTEX_UNIFORM_COMPONENTS: %d', [MaxVertexUniformComponents]);
  // .. and potentially use MaxVertexUniformComponents to activate a different rendering, 
  // different shaders etc.,
  // if it seems the number is too low for your full shader.
end;

Warning: You can do these queries only once the rendering context is initialized. So anywhere from the moment Application.OnInitialize is called, so practically almost anywhere is your code is good. But do not call this in initialization of some unit.

1 Like

Cool, thanks my limit is 8000. It seems like I should try to figure out how to use texture images to encode my splatmaps instead of worrying about supporting lower limits.

1 Like