Update a custom uniform in a modified shader

I have modified the default vtkOpenGLPolyDataMapper along the lines of https://github.com/dmreagan/vtk-shaders (case 1). I have also added a new uniform, which I can set as it is done for diffuseColor in https://github.com/Kitware/VTK/blob/master/Rendering/OpenGL2/Testing/Python/TestUserShader2.py. However, as far as I can see, that sets a hardcoded fixed value for diffuseColor, and I would like something I can change “live” (with e.g. a slider on a GUI). This works fine for default uniforms like Opacity: I just call actor.GetProperty().SetOpacity(value) and the mapper udates automatically. How can I do the same with a custom uniform?

This was already asked in http://vtk.1045678.n5.nabble.com/Update-Custom-Uniforms-for-Shaders-td5739937.html, but the “solution” is basically the same code as above, it seems to be giving a fixed value to diffuseColor.

In the example from TestUserShader2.py, the value of diffuseColor is set inside the callback, as follows:

@vtk.calldata_type(vtk.VTK_OBJECT)
def vtkShaderCallback(caller, event, calldata):
    program = calldata
    if program is not None:
        diffuseColor = [0.4, 0.7, 0.6]
        program.SetUniform3f("diffuseColorUniform", diffuseColor)

mapper.AddObserver(vtk.vtkCommand.UpdateShaderEvent, vtkShaderCallback)

vtkShaderCallback runs each time an UpdateShaderEvent is observed, and each time diffuseColorUniform is being set to [0.4, 0.7, 0.6]. So you want to assign diffuseColor's value somewhere outside of vtkShaderCallback, like in your GUI slider callback. This way, each time vtkShaderCallback runs, it will fetch the new value for diffuseColor and set the uniform accordingly.

Thanks. I have solved it by subclassing vtkOpenGLPolyDataMapper:

class AOIMapper(vtk.vtkOpenGLPolyDataMapper):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.MyCustomUniform = 0.0
        self.AddShaderReplacement(...)
        self.AddObserver(vtk.vtkCommand.UpdateShaderEvent, self.update_shader)
    @vtk.calldata_type(vtk.VTK_OBJECT)
    def update_shader(self, caller, event, program):
        if program is not None:
            program.SetUniformf("MyCustomUniform", self.MyCustomUniform)

And then in the rest of the code, something like:

actor.GetProperty().SetOpacity(opacity_value)
actor.GetMapper().MyCustomUniform = custom_value

Are you aware of the recently introduced vtkUniforms class that allows you to get/set custom uniforms anytime, anywhere, without worrying about adding callback functions, etc. Uniforms are accessible via vtkShaderProperty::Get…CustomUniforms() methods.

@Simon are there some simple standalone examples?

1 Like

How recently? I need it to work with VTK 8.1.2 (pip version). I remember I’ve seen the stuff, but either it wasn’t working or I couldn’t figure out how.

It was added a few months ago, so it is not included in VTK 8.1.2.

If you need a Python distribution that contains recent VTK then you can use Python included in a recent Preview Release of 3D Slicer. VTK is already bundled and you can pip install any other packages.