Shader Bindings...?

Hey guys,



I’m trying to figure out how shaders are bound to materials during runtime. In my game, Gentrieve 2, rooms are made up of platforms, boxes & doors. All of these objects use the same SimpleLighting.j3md material definition. There is a new material object created from that definition for each different texture used in the room. I noticed that all of my platforms for the room end up sharing the same shader object, while my doors and boxes do not. I’m curious as to why the doors and boxes are not using the same shader, even though they use the same material definition? Is there a way I can assure my other materials use the same shader, much like all of my platforms do? I find the shader I am using now by doing this:



[java] public void Update(float tpf) {

if( simpleShader == null ) {

try {

simpleShader = Main.CurrentWorld.GetCurrentRoom().GetParts().get(0).GetPart().GetMaterial().getActiveTechnique().getShader();

} catch(Exception e) { }

} else {

GDynamicLights.UpdateAllLights(simpleShader);

}

}[/java]



… which, as I said, works great for all of the other parts in the room (which may use different material objects). I presume I could solve this by grabbing all of the other shaders used for boxes and doors and update those, but it’d be nice for performance if all of my SimpleLighting.j3md materials used the same shader (then I only need to update this one shader).

No you cannot, it depends on the ordering of the geometry. Basically the shader is changed each time the geometry queue changes the shader, regardless if its been used before afaik. Why is this of interest for you?

“Basically the shader is changed each time the geometry queue changes the shader”



Ahhhh OK.



“Why is this of interest for you?”



I want to pass on all of my light positions and colors to my lighting shader – I was doing it by getting the shader object and then setting uniform variables. I realized all of my platforms had the same shader object, so setting light information for just that shader worked for everything but boxes and doors (probably because the geometry queue changes when moving on to doors & boxes!). Anyway, this is clearly the wrong way to go about it… I’m going to try using static Vector4f[] and Vector3f[] parameters passed to the materials instead.

1 Like

:o

Yes that would be the way, just set the new parameters on the materials as you instance them, thats your “connection” to the shader. Though you’d need a param per light or a map/mesh (there is no real arrays that can be transferred) to transfer this data. And if this amount of data being transferred all the time is too large it will probably bog down your performance a lot anyway :confused: Maybe theres a simpler way involving less data transfer by doing more on the shader / java side?

OK, so my fall-back method will be to have a param per light, which will be a little messy, but it should definitely work. My Java code positions the projectiles (which gives off the light), and the shader code brightens up each fragment, so I think some sharing will have to happen :confused: However, I’m giving another stab at some crazy method that I have no idea should work, but it looks like it could work:


  1. Have my lights stored as static Vector4f[] (colors) and Vector3f[] (positions) arrays
  2. Grab the shader object in Java, which has the vec4 g_LightColors[] and vec3 g_LightPos[] uniform arrays defined
  3. Set those uniforms via a SetValue call like so:



    [java] public static void SetLights(Shader shader) {

    if( shader == null ) return;

    Uniform lightColor = shader.getUniform(“g_LightColor”);

    Uniform lightPos = shader.getUniform(“g_LightPosition”);

    //lightColor.setVector4Length(NUM_LIGHTS);

    //lightPos.setVector4Length(NUM_LIGHTS);

    lightColor.setValue(VarType.Vector4Array, LightColors);

    lightPos.setValue(VarType.Vector3Array, LightPositions);

    }[/java]



    … I get no exceptions, but the jME3 Rendering Thread gets stuck in Uniform.clearValue(), in the while(multiData.remaining() > 0) loop:



    [java] public void clearValue(){

    updateNeeded = true;



    if (multiData != null){

    ZERO_BUF.clear();

    multiData.clear();



    while (multiData.remaining() > 0){

    ZERO_BUF.limit( Math.min(multiData.remaining(), 16) ); // gets stuck in here

    multiData.put(ZERO_BUF);

    }



    multiData.clear();



    return;

    } …[/java]



    Not sure if this can be made to work, but that’d be sweet (then I could share arrays in Java code with shaders pretty easily). Otherwise, I’ll do a param-per-light.

As said, none of the jme shaders use arrays while for some it would be nice (e.g. Terrain) hence I don’t think its possible.

OK :confused: However, I’m curious as to what VarType.Vector*Array uniform types are used for then?



Also, another similar question – say I used a material parameter for each light, these would all be uploaded to the shader / graphics card each rendered frame? So, would it be faster to do my own Uniform.setValue(…) calls on the shaders when only lights have changed instead?

You could encode up to 64 vec4’s in a 8*8 ARGB Texture…

At least this is the way i would go. If you do not like that you could also use unused buffers in the Mesh to transfer some data to the shader…

Yes they would go via the bus each time. And yes, maybe mesh buffers are a good way to get vectors in…

Why are you using shaders directly? why don’t you use the material.setXXXX methods?

@nehon said:
Why are you using shaders directly? why don't you use the material.setXXXX methods?


because:

Yes [the light data] would go via the bus each time.


so, instead, I can just check when lighting data updates, and then send it to the shader myself.

If I used mesh buffers, then I'd have to send all the other mesh data every frame, or is that sent every frame anyway? I have my meshes set to "static", if that makes any difference?
@phr00t said:
...

... I get no exceptions, but the jME3 Rendering Thread gets stuck in Uniform.clearValue(), in the while(multiData.remaining() > 0) loop:

...


@phr00t I had the same effect during my development of the skinning shader where I too pass arrays. I found out this can happen when your uniform is not used and has a location of -1, but is populated Then it froze at the same location, or more or less was in an endless loop. So I added

[java]
if (location == LOC_NOT_DEFINED) {
return;
}
[/java]

at the start.

And you could also check if your shader array is correctly defined in your shader and the material definition, to mitigate the uniform not used problem. This can also happen when you write shader code which does nothing, and thus it gets optimized out (you can google for a more decent explanations on this).
1 Like

@ghoust, you did get arrays to pass to shaders then? Doesn’t that conflict with @normen’s explanation saying arrays can’t be used or transferred…? :?



I have my shader arrays defined like so:



[java]uniform vec4 g_LightPosition[NUM_LIGHTS];

uniform vec4 g_LightColor[NUM_LIGHTS];[/java]



…where I have “#define NUM_LIGHTS 12” earlier.



You added that check at the start of Uniform.clearValue(), or some of your code where you set the arrays?



EDIT: Upon pasting this code above, I realized my g_LightPosition was defined as a vec4, and not a vec3. Maybe this broke my SetValue(VarType.Vector3Array, LightPositions) call, causing the lockup? Oo

@phr00t said:
because:

Yes [the light data] would go via the bus each time.


It's not because you don't change the uniform that the data is not sent through the bus....
....shaders are stateless...if you don't send data...there is no data...so all the uniforms are sent on each draw call...whatever you do

Doing this you only gain 4 moves on the java side...and a big headache...
It doesn't worth it...
@phr00t said:
@ghoust, you did get arrays to pass to shaders then? Doesn't that conflict with @normen's explanation saying arrays can't be used or transferred..? :?


Well I guess the array stuff is not fully tested, that's how I interpret normans explanation. Technically this is possible and used by several other engines. Though it has limitations based on vertex shader constants in vs1.1 and 2.+



I have my shader arrays defined like so:

[java]
uniform vec4 g_LightPosition[NUM_LIGHTS];
uniform vec4 g_LightColor[NUM_LIGHTS];[/java]

...where I have "#define NUM_LIGHTS 12" earlier.


aehm is g_LightColor not a global used by jme to be filled in? I would suggest a look at the material documentation and perhaps Lighting.glsllib for inspiration. And ev. Material.updateLightListUniforms() and Material.renderMultipassLighting()


You added that check at the start of Uniform.clearValue(), or some of your code where you set the arrays?

yes at the start of clearValue but this is not your problem, it's a symptom


EDIT: Upon pasting this code above, I realized my g_LightPosition was defined as a vec4, and not a vec3. Maybe this broke my SetValue(VarType.Vector3Array, LightPositions) call, causing the lockup? Oo


please check you do not screw it up by changing stuff jme expects to be different ;)