How to get shader source for an actor

I know from the nightly build documentation for 9.1 that I can use the GetSource function of vtkShader to retrieve the source (as a string) for the shader. Or, at least I hope that I can.

I also know from the examples that I can get a vtkActor’s vtkShaderProperty with the GetShaderProperty function.

What I can’t figure out is how to connect the two so that I can view the shader’s source in its entirety for the actor that I’m using.

Can someone point out a way to do this?

Hi,

vtkShaderProperty has several methods called Get*SourceCode(), reflecting the fact that you don’t have the shader, but several ones that serve to customize a given procedure (e.g. geometry) in the rendering workflow in the graphics card.

take care,

Paulo

Hi Paul,

More context would be helpful here.

I don’t see Get*Source routines listed in the vtkShaderProperty class reference page, but there are several Get*ShaderCode function. Not trying to be picky, but this might make it clearer for others reading this. I will assume that you meant Get*ShaderCode.

I saw these functions before, but was uncertain as to how to handle the fact that the return type is a “virtual char *”; that is why I was trying to work with the vtkShader function that returned a string.

Could you educate us less knowledgeable C++ programmers; how do you use a function that returns a virtual pointer? Do I just ignore the virtual part and assign it to a char * in this case?

Is there some vkt test code / case that uses this function that one could study?

I appreciate the help.

BK

Hello, Roger,

Fixed.

The virtual keyword only means that the function’s behavior may change depending on the object’s class. It doesn’t have to do with the returned data type, which is char *. Suppose the Animal class that has a virtual string talk(){return "";}. You can have a Dog and a Cat classes that extend Animal. These subclasses may override Animal::talk() to implement behavior especific to them: virtual string Dog::talk(){ return "woof woof!";} and virtual string Cat::talk(){ return "meow!";}. So if you declare an Animal object like this: Animal* a = new Dog(); and if you call its talk() method, it’ll return “woof woof!”. Without the virtual keyword, it would return an empty string, since a belongs to the Animal class. The virtual keyword is one way in C++ to implement polymorphism, that is, variable behavior for seemingly the same thing.

char means a single character. * means that it is a pointer to the given data type. So char * is a variable that has the memory address to a singe character. Normally, a char * denotes the pointer to the first character of a string, which ends with the first of the following characters that has the null character (\0) or the 8-bit integer value of zero. These are the so-called "C-string"s. In practice, C++ programmers use some high level string class such as std::string. You can just create a std::string object from a char * by doing, for example, std::string myString( shaderProperty->GetFragmentShaderCode() );.

Please, take a look at some examples of shader usage here: Any example or tips to render sea surface(shader+FFT)? - #2 by Paulo_Carvalho .

take care,

Paulo

Hi Paulo,

Your reply filled in a number of gaps.

When I tried what you suggested – std::string myString( sp->GetFragmentShaderCode() ); – , the code crashed with this error:

terminate called after throwing an instance of ‘std::logic_error’
what(): basic_string::_S_construct null not valid

When I looked at this in the debugger, a null was truly returned by GetFragmentShaderCode.

So, what am I missing here?

I created an actor from vtkPolyDataMapper – zActor1->SetMapper( zMapper );. Isn’t the default shader assigned by the time the actor is created and mapper assigned?

Thanks.

BK

Hello,

Pointers are just integer numbers. In C/C++ standard, a null pointer is a pointer with zero value since the memory address zero is not accessible to a user program. So, you have to test for that before doing something with the pointer. For example, a simple

 char* szCode = sp->GetFragmentShaderCode();
 if( code ) { 
     std::string strCode( szCode );
     //do something with strCode
 } else { 
     printf('WARNING: fragment shader code not set.'); 
 }

does it.

From what I know, you need set the shader code somewhere beforehand. You won’t get whatever the mapper uses by default by calling Get*ShaderCode() on vtkShaderProperty. But maybe someone out there with more experience in using shaders in VTK knows a hack.

regards,

Paulo

Paulo,

Most of the VTK shader examples talk about changing the shader code. But, I need to see the code that is being used in order to know how to change it! Can’t change what I don’t know and need to avoid conflicts. That and one needs to know what the variables are named that the other parts of VTK are passing into the shader (e.g. normals, colors, vertices, etc.)

Thanks for you help so far.

BK

Hello,

If your mapper is a vtkOpenGLPolyDataMapper, then you can call Get[Fragment|Geometry|Vertex]ShaderCode() on it:

vtkOpenGLPolyDataMapper* mapperGL = std::dynamic_cast<vtkOpenGLPolyDataMapper*>( zMapper );
if( mapperGL ){
   char* szFragShaderCode = mapperGL->GetFragmentShaderCode();
   if( szFragShaderCode ){
      printf( "Fragment shader code:\n%s\n", szFragShaderCode);
   } else {
      printf( "Fragment shader code not set.\n" );
   }
} else {
   printf( "Mapper is not a vtkOpenGLPolyDataMapper.\n" );
}

take care,

Paulo

Hello Roger,

In order to get access to the real shader, you can use a trick to make it crash. Then the shader will be printed in the console !
For exemple you can call

mapperGL->AddShaderReplacement( vtkShader::Fragment, "//VTK::", true, "crap_text", false );              

It will replace the first token “//VTK::” by another text and won’t be able to compile.
Just some ugly trick, but it should work.

Simon

2 Likes

That’s a cool trick @simonesneault :+1:

You can also get a lot of insight using renderdoc. Spoiler alert: it’s hard to work on the vtk shaders because handle a lot of different options through code generation in C++.

1 Like

Hi All,

Pardon this for its length. There are a few things to address.

First, thanks to all the responders.

I couldn’t get the dynamic_cast to work in Paulo’s suggesting because I had declared the mapper as: vtkNew<vtkOpenGLPolyDataMapper> zMapper
But, I was able to work around it by using
char *szFragShaderCode = zMapper->GetFragmentShaderCode();
I’ll attribute this to the old way of dong things vs the new way (please somebody correct me if this is wrong).

Simon’s tip was kind of cool. Got to see the code!

Will explore renderdoc when I get a minute. Thanks Steve.

I can appreciate that VTK is doing a lot with shaders under the hood, setting things with C++ code, etc. The reason I started looking at the shaders is that I have them working the way I wanted in some other OpenGL code that I wrote. But, I ran into some problems with trying to get VTK to do what I consider to be a very simple thing. I posted this as a bug on VTK discourse development. I wanted to do the backfaces with a different color and it was taking the outside of a sphere as the backfaces and not the inside. However, if I use culling on the sphere, the backfaces ARE the inside. See: Backface bug in vtkActor
Am totally confused on this and would appreciate any help.

BK

Hi, Roger,

I didn’t know zMapper was a smartpointer to a vtkOpenGLPolyDataMapper. That’s why it’s important to post the code you’re using.

It’s kind of cool but do be careful where you do that. Intentionally crashing the graphics driver/cards in a hired cloud server, for example, will likely put you in hot water.

regards,

Paulo

HI Paulo,

Both points noted.
Thanks.

BK

2 Likes

You can also print the shaders by adding a print at the very bottom of vtkOpenGLPolyDataMapper::BuildShaders or vtkOpenGLGPUVolumeRayCastMapper::BuildShader.

That’s where all the dynamic shader code is resolved before compiling the shader program.

Something like

std::cout << shaders[vtkShader::Vertex]->GetSource() << std::endl;
std::cout << shaders[vtkShader::Fragment]->GetSource() << std::endl;
std::cout << shaders[vtkShader::Geometry]->GetSource() << std::endl;
2 Likes