Advanced replacement shaders

Hello -

I have some shader code that I am able to use in Shader Replacements. It seems to run without any errors, but it doesn’t do anything either. It contains a bunch of uniform variables and I can not figure how to set or change them. I think something else is preventing the shader from affecting the render, but changing the uniforms will be needed regardless.

So how do I make runtime changes to values of uniforms in GLSL replacement shaders in Paraview?

Thanks you.

If you need to debug shaders, I’ve found renderdoc useful. I have not used ParaView’s shader replacements, so I cannot be much help with that side of things.

1 Like

Thanks David! That looks really nice. I installed it locally on the computer. I think it wil be very helpful. However I know this shader works, as I have run it with another porgram.

Update: I have a VTK file that renders a cube and uses a shader embedded in JSON strings to color the surface based on the location. If I load that VTK file into Paraview, it looks exactly as expected. I loaded a Sources > Geometric Shapes > Box and pasted the JSON string(s?) from the VTK file into the shader replacement window. It doesn’t change anything. As a test, I pasted it into the replacement shader window of the VTK colored box. It did not change the color. So its not setting the color to a default. I’ll keep plugging away at it…

Update #2: Back to square one. I realized the VTK file has a lookup table that does the coloring. So the shader is doing nothing in VTK file or the replacement shader dialog.

If you look at the short bit of text below this figure, you’ll see how the JSON for shader replacements is expected to be formatted.

The //VTK::Light::Impl is a comment indicating the section of VTK’s shader code you wish to replace. Are you sure you have the proper name for that? The names of comments are not terribly well documented, but you can see them in the VTK shader source and see how they’re used in the various mappers in the directory above. Once you are sure the comment matches one from VTK, you should be able to use renderdoc to inspect the actual shader source sent to OpenGL and verify the replacement occurred as you directed.

That readme is very helpful! I didn’t realize the input and output variable names had to follow a protocol,

I somehow end up with some odd casing in the original and figured it out. And I saw another question and the answer clued me into the declaration vs. implementation.

In my case, I am almost variable names are what’s causing my trouble. I’ll change my code and test it.

Q: Is there a way to add the functionality of a custom shader to the original vs. replacing it?

Thanks again. This is a big help.

PS: I also didn’t know about //VTK::Output::Dec.

Q2: Can one have both vertex and geometry shaders? Automatically renaming VSOutput variables to GSOutput would seem to preclude that.

Yes, you can have both vertex and geometry shaders. I’m not sure what you mean about automatically renaming VSOutput variables to GSOutput.

Maybe I’m misinterpreting:

“If a geometry shader is present VTK will rename the fragment
shader inputs from VSOutput to GSOutput automatically.”

From the readme.

This renaming is done in the geometry shader, effectively adding a new variable accessible to fragment shaders. See here for example. The “rename” adds variables in the geometry-shader and then fragment shaders are made to use those instead of the VSOutput versions so they take into account geometry shader work.

I’m stuck. I feel like I am missing something simple but with the information I can find, I can’t figure out what it is. I did discover that if I use the same code to replace different tags, I get different errors. Is there any documentation about the different shaders in the Paraview shader program, like what they do, how to figure out which tag to replace, etc? Is there a way to see the final result after the shader replacements? I saw a response to a VTK user on Andriod suggesting to uncomment vtkOpenGL2PolyDataMapper.cxx somewhere, but couldn’t figure out how to do the equivelant in Paraview

Below are the replacement shader strings I am trying to use in Paraview. These are in a file with a .json extension that I load with the file selector in Properties > Display > Miscellaneous > Use Shader Replacements:

    {
        "type":"fragment",
        "original":"//VTK::Normal::Dec",
        "replacement":"
      //VTK::System::Dec
      float df;
      float sf;
      vec3 diffuse;
      vec3 specular;
    "
    }

    {

        "type":"fragment",
        "original":"//VTK::Normal::Impl",
        "replacement":"
      //VTK::System::Dec
      df = max(0.0, normalVCVSOutput.z);
      sf = pow(df, 2.0);
      diffuse = df * vec3(0.2, 0.5, 0.3) //diffuseNormalUniform;
      specular = sf * vec3(0.4,0.4,0.4);
      gl_FragData[0] = vec4(0.3*abs(normalVCVSOutput) + 0.7*diffuse + specular, 1.0);
    "
    }

    {
        "type":"vertex",
        "original":"//VTK::Normal::Dec",
        "replacement":"
      //VTK::Output::Dec
      vec4 tmpPosDC;
    "
    }

    {

        "type":"vertex",
        "original":"//VTK::Normal::Impl",
        "replacement":"
      //VTK::System::Dec
      //VTK::Normal::Dec
      tmpPosDC = MCDCMatrix * vertexMC;
      normalVCVSOutput = normalMatrix * normalMC;
      gl_Position = tmpPosDC*vec4(0.2+0.8*abs(tmpPosDC.x),0.2+0.8*abs(tmpPosDC.y),1.0,1.0);
    "
    }

When I load it, the geometry I have in the viewer, a sphere geometrical shape source, turns black. Then, after a 94 line listing of vtkShaderProgram, I get the following errors. The vtkShaderProgram is mostly tags and comments with my code sprinkled in. I think the 3rd and 5th are important, but I can’t figure out what to do:

ERROR: In vtkShaderProgram.cxx, line 1145
vtkShaderProgram (0xfae02b0):

ERROR: In vtkOpenGLPolyDataMapper.cxx, line 2645
vtkOpenGLBatchedPolyDataMapper (0x10e36050): Could not set shader program

ERROR: In vtkOpenGLVertexArrayObject.cxx, line 262
vtkOpenGLVertexArrayObject (0x10e36c90): attempt to add attribute without a bound program for attribute normalMC

ERROR: In vtkOpenGLVertexBufferObjectGroup.cxx, line 269
vtkOpenGLVertexBufferObjectGroup (0xff78cf0): Error setting ‘normalMC’ in shader VAO.

ERROR: In vtkOpenGLVertexArrayObject.cxx, line 262
vtkOpenGLVertexArrayObject (0x10e36c90): attempt to add attribute without a bound program for attribute vertexMC

ERROR: In vtkOpenGLVertexBufferObjectGroup.cxx, line 269
vtkOpenGLVertexBufferObjectGroup (0xff78cf0): Error setting ‘vertexMC’ in shader VAO.

You’ve a //VTK::System::Dec in your fragment shader’s replacement for //VTK::Normal::Impl. This causes your final shader to be malformed. How about this?

{

    "type":"fragment",
    "original":"//VTK::Normal::Impl",
    "replacement":"
  df = max(0.0, normalVCVSOutput.z);
  sf = pow(df, 2.0);
  diffuse = df * vec3(0.2, 0.5, 0.3) //diffuseNormalUniform;
  specular = sf * vec3(0.4,0.4,0.4);
  gl_FragData[0] = vec4(0.3*abs(normalVCVSOutput) + 0.7*diffuse + specular, 1.0);
"
}

Hi @tjggriffin,

Changing the values of uniforms or adding new uniforms isn’t supported at all, I’m afraid. Technically it would be possible to some extent, but it currently is not.

As you are finding, shader replacements are quite difficult to get right. That’s because VTK is dynamically writing vertex, geometry, and fragment shaders on the fly, and depending on which visualization options you have, those scripts and the uniforms and variables defined in them can change substantially. As a result, a shader replacement JSON string that works in one context may suddenly fail, e.g., when switching from Surface representation to Surface with Edges. ParaView’s shader replacement mechanism unfortunately doesn’t let you supply different replacements for these different contexts. Technically, it could, but in the present implementation it does not. For that reason, shader replacements are somewhat limited to very specific changes that are valid in the context of a particular set of representation and view properties.

There really isn’t much documentation on shader replacements because it’s very complicated, and any written guide would need to be complicated to a degree that is similar to reading the source code of vtkOpenGLPolyDataMapper. Unfortunately, one of the best ways to learn is to look at the source code and look at the final results of all the replacements to see how the GLSL is structured and what rendering options affect the output. It’s not easy stuff.

You can uncomment the three lines in https://gitlab.kitware.com/vtk/vtk/-/blob/master/Rendering/OpenGL2/vtkOpenGLPolyDataMapper.cxx#L2448

  // cout << "VS: " << shaders[vtkShader::Vertex]->GetSource() << endl;
  // cout << "GS: " << shaders[vtkShader::Geometry]->GetSource() << endl;
  // cout << "FS: " << shaders[vtkShader::Fragment]->GetSource() << endl;

Hi @tjggriffin

One way to debug the generated shader code is to make vtk print it out. I recommend, introducing an intentional syntax error. I’ve described this method with an example here: What does "//VTK::Light::Impl" mean? - #2 by sankhesh - Support - VTK

For the most part, the shader replacement tags are considered an internal implementation detail that can change at any time but Shaders in VTK is an old wiki document that describes the logic of the implementation.

Finally, it is possible to introduce new uniforms and set their values in VTK. See TestUserShader2 for an example. Specifically, it introduces a new diffuseColorUniform on https://gitlab.kitware.com/vtk/vtk/-/blob/76bfd3d04646af543af72b1c228a977ee2b00a44/Rendering/OpenGL2/Testing/Cxx/TestUserShader2.cxx#L147 and adds an observer to the UpdateShaderEvent to be able to set its value (https://gitlab.kitware.com/vtk/vtk/-/blob/76bfd3d04646af543af72b1c228a977ee2b00a44/Rendering/OpenGL2/Testing/Cxx/TestUserShader2.cxx#L159).

This ability to add an observer to the UpdateShaderEvent is missing in the ParaView GUI but can be done with custom python code.

I did that because the readme in the VTK repo, in the part about rendering OpenGL2 GLSL siad that it was require for every shader, but I wasn’t sure where it should go. After reading your reply, I tried all combinations of frag, vert, impl and dec, include not have it at all. I got the exact same errors. I suspect it has to do with the context of the shader I chose to replace. I might need to replace a different shader or add something to the shader code.

Thanks!

Will that cause Paraview to output the shaders? Or would I need to load the test shaders with VTK? Thanks!

Yes, ParaView will print the shaders into the console. On windows, I think you will need to run ./bin/paraview.exe > output.txt to capture cout

Yes I saw that in a replay to another question. When I tried it, the output was mostly tags with my code sprinkled in. I think the way to get the full shader codes is to do what Jaswant suggested.

This looks very helpful. I’ll see what I can gleam from vtkOpenGLPolyDataMapper.cxx.

Thinks!