Strange Shader Behavior With VTK and WebAssembly

I have some code that reads a .vtp file with vtkXMLPolyDataReader that contains lines. I can run the code and see the lines with my desktop app, but when I compile it to WebAssembly I get the following error when running in the browser:

ERROR: In /work/src/Rendering/OpenGL2/vtkShaderProgram.cxx, line 498

put_char @ SwisRadar.js:2270

SwisRadar.js:2270 vtkShaderProgram (0x705458): Links failed: FRAGMENT varying clipDistancesGSOutput does not match any VERTEX varying

put_char @ SwisRadar.js:2270

I have attached the .vtp file I am using as a zipped file.koax_colines.zip (3.5 MB)

I have discovered that if I don’t add my clipping planes collection (2 clipping planes) to the vtkPolyDataMapper the lines render correctly. Looks like a problem with using clipping planes in WebAssembly VTK.

Firefox is giving a little more descriptive error:

ERROR: In /work/src/Rendering/OpenGL2/vtkShaderProgram.cxx, line 498 SwisRadar.js:2270:16
WebGL warning: linkProgram: Varying clipDistancesGSOutput has static-use in the frag shader, but is undeclared in the vert shader. SwisRadar.js:11104:13
vtkShaderProgram (0x70f4e0): Links failed: Varying clipDistancesGSOutput has static-use in the frag shader, but is undeclared in the vert shader.

In the source code for vtkOpenGLPolyDataMapper.cxx starting at line 1913:

// geometry shader impl
if (GSSource.length())
{
vtkShaderProgram::Substitute(VSSource, “//VTK::Clip::Dec”,
“out vec4 clipVertexMC;”);
vtkShaderProgram::Substitute(VSSource, “//VTK::Clip::Impl”,
" clipVertexMC = vertexMC;\n");
vtkShaderProgram::Substitute(GSSource,
“//VTK::Clip::Dec”,
“uniform int numClipPlanes;\n”
“uniform vec4 clipPlanes[6];\n”
“in vec4 clipVertexMC[];\n”
“out float clipDistancesGSOutput[6];”);
vtkShaderProgram::Substitute(GSSource,
“//VTK::Clip::Impl”,
“for (int planeNum = 0; planeNum < numClipPlanes; planeNum++)\n”
" {\n"
" clipDistancesGSOutput[planeNum] = dot(clipPlanes[planeNum], clipVertexMC[i]);\n"
" }\n");
}
else // vertex shader impl
{
vtkShaderProgram::Substitute(VSSource, “//VTK::Clip::Dec”,
“uniform int numClipPlanes;\n”
“uniform vec4 clipPlanes[6];\n”
“out float clipDistancesVSOutput[6];”);
vtkShaderProgram::Substitute(VSSource, “//VTK::Clip::Impl”,
“for (int planeNum = 0; planeNum < numClipPlanes; planeNum++)\n”
" {\n"
" clipDistancesVSOutput[planeNum] = dot(clipPlanes[planeNum], vertexMC);\n"
" }\n");
}

vtkShaderProgram::Substitute(FSSource, "//VTK::Clip::Dec",
  "uniform int numClipPlanes;\n"
  "in float clipDistancesVSOutput[6];");
vtkShaderProgram::Substitute(FSSource, "//VTK::Clip::Impl",
  "for (int planeNum = 0; planeNum < numClipPlanes; planeNum++)\n"
  "    {\n"
  "    if (clipDistancesVSOutput[planeNum] < 0.0) discard;\n"
  "    }\n");

}

It looks like the fragment shader is also expecting clipDistancesVSOutput as an input, but if a geometry shader is used it is outputting clipDistancesGSOutput. I am going to try to modify the code so the geometry shader also outputs clipDistancesVSOutput instead of clipDistancesGSOutput.

Can anybody else verify if this seems like a bug in VTK?

I have tried modifying the code in the vktOpenGLPolyDataMapper without any changes in the result.

After further research, it looks like I need to try and use the new vtkShaderProperty class with the actor to try and find where this possible bug is.

Below is the shader code that is causing the error. Vertex shader is first, followed by geometry shader, and then the fragment shader. Can anybody that knows glsl better than me figure this out?

VS: //VTK::System::Dec

/=========================================================================

Program: Visualization Toolkit
Module: vtkPolyDataVS.glsl

Copyright © Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.

=========================================================================
/

in vec4 vertexMC;

// frag position in VC
//VTK::PositionVC::Dec

// optional normal declaration
//VTK::Normal::Dec

// extra lighting parameters
//VTK::Light::Dec

// Texture coordinates
//VTK::TCoord::Dec

// material property values
//VTK::Color::Dec

// clipping plane vars
out vec4 clipVertexMC;

// camera and actor matrix values
uniform mat4 MCDCMatrix;

// Apple Bug
//VTK::PrimID::Dec

// Value raster
//VTK::ValuePass::Dec

// picking support
//VTK::Picking::Dec

void main()
{
//VTK::Color::Impl

//VTK::Normal::Impl

//VTK::TCoord::Impl

clipVertexMC = vertexMC;

//VTK::PrimID::Impl

gl_Position = MCDCMatrix * vertexMC;

//VTK::ValuePass::Impl

//VTK::Light::Impl

//VTK::Picking::Impl
}

GS: //VTK::System::Dec

/=========================================================================

Program: Visualization Toolkit
Module: vtkPolyDataWideLineGS.glsl

Copyright © Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.

=========================================================================
/
// Template for the polydata mappers geometry shader

// VC position of this fragment
//VTK::PositionVC::Dec

// primitiveID
//VTK::PrimID::Dec

// optional color passed in from the vertex shader, vertexColor
//VTK::Color::Dec

// optional surface normal declaration
//VTK::Normal::Dec

// extra lighting parameters
//VTK::Light::Dec

// Texture coordinates
//VTK::TCoord::Dec

// picking support
//VTK::Picking::Dec

// Depth Peeling Support
//VTK::DepthPeeling::Dec

// clipping plane vars
uniform int numClipPlanes;
uniform vec4 clipPlanes[6];
in vec4 clipVertexMC[];
out float clipDistancesGSOutput[6];

// the output of this shader
//VTK::Output::Dec

uniform vec2 lineWidthNVC;

layout(lines) in;
layout(triangle_strip, max_vertices = 4) out;

void main()
{
// compute the lines direction
vec2 normal = normalize(
gl_in[1].gl_Position.xy/gl_in[1].gl_Position.w -
gl_in[0].gl_Position.xy/gl_in[0].gl_Position.w);

// rotate 90 degrees
normal = vec2(-1.0normal.y,normal.x);

//VTK::Normal::Start

for (int j = 0; j < 4; j++)
{
int i = j/2;

gl_PrimitiveID = gl_PrimitiveIDIn;

for (int planeNum = 0; planeNum < numClipPlanes; planeNum++)
{
clipDistancesGSOutput[planeNum] = dot(clipPlanes[planeNum], clipVertexMC[i]);
}

//VTK::Color::Impl

//VTK::Normal::Impl

//VTK::Light::Impl

//VTK::TCoord::Impl

//VTK::DepthPeeling::Impl

//VTK::Picking::Impl

// VC position of this fragment
//VTK::PositionVC::Impl

gl_Position = vec4(
gl_in[i].gl_Position.xy + (lineWidthNVC
normal)*((j+1)%2 - 0.5)*gl_in[i].gl_Position.w,
gl_in[i].gl_Position.z,
gl_in[i].gl_Position.w);
EmitVertex();
}
EndPrimitive();
}

FS: //VTK::System::Dec

/=========================================================================

Program: Visualization Toolkit
Module: vtkPolyDataFS.glsl

Copyright © Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.

=========================================================================
/
// Template for the polydata mappers fragment shader

uniform int PrimitiveIDOffset;

// VC position of this fragment
//VTK::PositionVC::Dec

// Camera prop
uniform int cameraParallel;

// optional color passed in from the vertex shader, vertexColor
uniform float ambientIntensity; // the material ambient
uniform float diffuseIntensity; // the material diffuse
uniform float opacityUniform; // the fragment opacity
uniform vec3 ambientColorUniform; // ambient color
uniform vec3 diffuseColorUniform; // diffuse color

// optional surface normal declaration
//VTK::Normal::Dec

// extra lighting parameters
uniform vec3 lightColor0;

// Texture maps
//VTK::TMap::Dec

// Texture coordinates
//VTK::TCoord::Dec

// picking support
//VTK::Picking::Dec

// Depth Peeling Support
//VTK::DepthPeeling::Dec

// clipping plane vars
uniform int numClipPlanes;
in float clipDistancesVSOutput[6];

// the output of this shader
//VTK::Output::Dec

// Apple Bug
//VTK::PrimID::Dec

// handle coincident offsets
//VTK::Coincident::Dec

// Value raster
//VTK::ValuePass::Dec

// surface with edges
//VTK::Edges::Dec

void main()
{
// VC position of this fragment. This should not branch/return/discard.
//VTK::PositionVC::Impl

// Place any calls that require uniform flow (e.g. dFdx) here.
//VTK::UniformFlow::Impl

// Set gl_FragDepth here (gl_FragCoord.z by default)
//VTK::Depth::Impl

// Early depth peeling abort:
//VTK::DepthPeeling::PreColor

// Apple Bug
//VTK::PrimID::Impl

for (int planeNum = 0; planeNum < numClipPlanes; planeNum++)
{
if (clipDistancesVSOutput[planeNum] < 0.0) discard;
}

//VTK::ValuePass::Impl

vec3 ambientColor = ambientIntensity * ambientColorUniform;
vec3 diffuseColor = diffuseIntensity * diffuseColorUniform;
float opacity = opacityUniform;

//VTK::Edges::Impl

// Generate the normal if we are not passed in one
//VTK::Normal::Impl

gl_FragData[0] = vec4(ambientColor + diffuseColor, opacity);
//VTK::Light::Impl

//VTK::TCoord::Impl

if (gl_FragData[0].a <= 0.0)
{
discard;
}

gl_FragData[0] = vec4(gl_FragData[0].rgb*gl_FragData[0].a, gl_FragData[0].a);
gl_FragData[1].r = gl_FragData[0].a;

//VTK::Picking::Impl

// handle coincident offsets
//VTK::Coincident::Impl
}

Ahhhhh. Sorry for the repeated posts, but I just discovered that WebGL doesn’t support geometry shaders! Is there an easy way to tell the vtkOpenGLPolyDataMapper to not use geometry shaders?

Ok, I finally got it to work. I put an #ifdef check for Emscripten and write an empty string to the geometry shader if it is defined.

I then ran into a problem with the vertex and fragment shader code not having the uniform numClipPlanes at the same precision. After some research I discovered that in GLES, if the uniform in the fragment shader is defined as an int, then it must be prefixed with highp.

Not sure if my changes should be included in the main VTK source or not?

I’ll try catching up on this next week. We do want to incorporate fixes for shader code to make it work with WebGL (usually webgl is a bit more picky but the fixes tend to be easy)