Is it possible to set per-object GLSL uniforms in Blender Game Engine?

I am using a simple procedural shader in Blender Game Engine, which basically outlines an object’s constituent polygons using a single color per object. The source code is as follows:


import bge
 
cont = bge.logic.getCurrentController()
own = cont.owner
 
VertexShader = """
   varying vec4 texCoords; // texture coordinates at this vertex
 
   void main()
   {
      texCoords = gl_MultiTexCoord0; // in this case equal to gl_Vertex
 
      gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
   }
"""
 
FragmentShader = """
   varying vec4 texCoords; 


    uniform float texMultiplier; // Used to specify number of texture repetitions
    uniform vec4 onColor;
    uniform vec4 offColor;
 
   void main()
   {
      vec2 mulCoords = fract(texCoords.xy * texMultiplier);
      
      gl_FragColor = (abs(mulCoords.x - 0.5) > 0.45 || abs(mulCoords.y - 0.5) > 0.45) ? onColor : offColor;
   }
"""
 
mesh = cont.owner.meshes[0]
for mat in mesh.materials:
    shader = mat.getShader()
    if shader != None:
        if not shader.isValid():
            shader.setSource(VertexShader, FragmentShader, 1)

            # Default value for texMultiplier is 5.0
            shader.setUniform1f("texMultiplier", (own.get("texMultiplier", 5.0)))

            # Default color for onColor is purple
            shader.setUniform4f("onColor", (own.get("onRedVal", 1.0)), (own.get("onGreenVal", 0.0)), (own.get("onBlueVal", 1.0)), 1.0)

            # Default color for offColor is black
            shader.setUniform4f("offColor", (own.get("offRedVal", 5.0)), (own.get("offGreenVal", 0.0)), (own.get("offBlueVal", 0.0)), 1.0)

My intention was for multiple objects to share this shader, but I can’t find any way of setting the uniforms on a per-object basis. I tried executing the following code once per frame per object, but it didn’t work, as the uniform values ended up being the same for all objects:


import bge
 
cont = bge.logic.getCurrentController()
own = cont.owner
mesh = cont.owner.meshes[0]
for mat in mesh.materials:
    shader = mat.getShader()
    if shader.isValid():
        shader.setUniform1f("texMultiplier", (own.get("texMultiplier", 5.0)))

        ...
        
        # Omitted similar code setting the onColor and offColor uniforms


Any ideas as to whether per-object uniforms are possible in BGE where those objects share a shader? Or am I flogging a dead horse?

Thanks!

I think the last line may need to be more like this.


   shader.setUniform1f("texMultiplier", own['prop'])

Where ‘prop’ is the property you would create (in the property panel) to control the shader.

Thanks for the reply!

After experimenting, I’ve come to the conclusion that, while it’s possible to set a uniform using the technique described above, you can’t have per-object uniforms where those objects share the same material. (It seems that the value of the uniform for all objects is the value set in the script which happens to run last for a given frame.)

It sounds like separate materials will therefore be necessary, which presumably duplicates the shaders as well (leading to an increased number of state changes and therefore reduced performance)?

^ That sounds like a logical result given the information you have; not sure if it’s true or not. I think custom GLSL shaders are a bit broken at the moment, where multiple objects can’t share a shader correctly (though I’m unsure if this was always the case, or just “recently”). Just wanted to put that out there for anyone who might come along and want to know.