Commit c0c0e7a7 authored by Ken Martin's avatar Ken Martin

Add an ability for user code to modify the default VTK shaders

Add a method so that users can modify the existing VTK shaders
if desired. In the future will also need to add a method for
users to set uniforms as well and maybe eventually map data arrays
to attributes.
parent e2478fb6
......@@ -2,6 +2,7 @@ vtk_add_test_cxx(${vtk-module}CxxTests tests
#TestRenderWidget.cxx # Very experimental, fails, does nothing useful yet.
TestPointGaussianMapper.cxx
TestPointGaussianMapperOpacity.cxx
TestUserShader.cxx
TestVBOPLYMapper.cxx
TestVBOPointsLines.cxx
TestGaussianBlurPass.cxx
......
/*=========================================================================
Program: Visualization Toolkit
Copyright (c) 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.
=========================================================================*/
#include "vtkCamera.h"
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkActor.h"
#include "vtkOpenGLPolyDataMapper.h"
#include "vtkPLYReader.h"
#include "vtkNew.h"
#include "vtkProperty.h"
#include "vtkPolyDataNormals.h"
#include "vtkRegressionTestImage.h"
#include "vtkTestUtilities.h"
#include "vtkRenderWindowInteractor.h"
//----------------------------------------------------------------------------
int TestUserShader(int argc, char *argv[])
{
vtkNew<vtkActor> actor;
vtkNew<vtkRenderer> renderer;
vtkNew<vtkOpenGLPolyDataMapper> mapper;
renderer->SetBackground(0.0, 0.0, 0.0);
vtkNew<vtkRenderWindow> renderWindow;
renderWindow->SetSize(400, 400);
renderWindow->AddRenderer(renderer.Get());
renderer->AddActor(actor.Get());
vtkNew<vtkRenderWindowInteractor> iren;
iren->SetRenderWindow(renderWindow.Get());
const char* fileName = vtkTestUtilities::ExpandDataFileName(argc, argv,
"Data/dragon.ply");
vtkNew<vtkPLYReader> reader;
reader->SetFileName(fileName);
reader->Update();
vtkNew<vtkPolyDataNormals> norms;
norms->SetInputConnection(reader->GetOutputPort());
norms->Update();
mapper->SetInputConnection(norms->GetOutputPort());
actor->SetMapper(mapper.Get());
actor->GetProperty()->SetAmbientColor(0.2, 0.2, 1.0);
actor->GetProperty()->SetDiffuseColor(1.0, 0.65, 0.7);
actor->GetProperty()->SetSpecularColor(1.0, 1.0, 1.0);
actor->GetProperty()->SetSpecular(0.5);
actor->GetProperty()->SetDiffuse(0.7);
actor->GetProperty()->SetAmbient(0.5);
actor->GetProperty()->SetSpecularPower(20.0);
actor->GetProperty()->SetOpacity(1.0);
// Modify the shader to color based on model normal
// To do this we have to modify the vertex shader
// to pass the normal in model coordinates
// through to the fragment shader. By default the normal
// is converted to View coordinates and then passed on.
// We keep that, but add a varying for the original normal.
// Then we modify the fragment shader to set the diffuse color
// based on that normal. First lets modify the vertex
// shader
mapper->AddShaderReplacement(
vtkShader::Vertex,
"//VTK::Normal::Dec", // replace the normal block
true, // before the standard replacements
"//VTK::Normal::Dec\n" // we still want the default
" varying vec3 myNormalMCVSOutput;\n", //but we add this
false // only do it once
);
mapper->AddShaderReplacement(
vtkShader::Vertex,
"//VTK::Normal::Impl", // replace the normal block
true, // before the standard replacements
"//VTK::Normal::Impl\n" // we still want the default
" myNormalMCVSOutput = normalMC;\n", //but we add this
false // only do it once
);
// now modify the fragment shader
mapper->AddShaderReplacement(
vtkShader::Fragment, // in the fragment shader
"//VTK::Normal::Dec", // replace the normal block
true, // before the standard replacements
"//VTK::Normal::Dec\n" // we still want the default
" varying vec3 myNormalMCVSOutput;\n", //but we add this
false // only do it once
);
mapper->AddShaderReplacement(
vtkShader::Fragment, // in the fragment shader
"//VTK::Normal::Impl", // replace the normal block
true, // before the standard replacements
"//VTK::Normal::Impl\n" // we still want the default calc
" diffuseColor = abs(myNormalMCVSOutput);\n", //but we add this
false // only do it once
);
renderWindow->Render();
renderer->GetActiveCamera()->SetPosition(-0.2,0.4,1);
renderer->GetActiveCamera()->SetFocalPoint(0,0,0);
renderer->GetActiveCamera()->SetViewUp(0,1,0);
renderer->ResetCamera();
renderer->GetActiveCamera()->Zoom(1.3);
renderWindow->Render();
int retVal = vtkRegressionTestImage( renderWindow.Get() );
if ( retVal == vtkRegressionTester::DO_INTERACTOR)
{
iren->Start();
}
return EXIT_SUCCESS;
}
......@@ -190,6 +190,44 @@ bool vtkOpenGLPolyDataMapper::IsShaderVariableUsed(const char *name)
this->ShaderVariablesUsed.end(), name);
}
void vtkOpenGLPolyDataMapper::AddShaderReplacement(
vtkShader::Type shaderType, // vertex, fragment, etc
std::string originalValue,
bool replaceFirst, // do this replacement before the default
std::string replacementValue,
bool replaceAll)
{
vtkOpenGLPolyDataMapper::ReplacementSpec spec;
spec.ShaderType = shaderType;
spec.OriginalValue = originalValue;
spec.ReplaceFirst = replaceFirst;
vtkOpenGLPolyDataMapper::ReplacementValue values;
values.Replacement = replacementValue;
values.ReplaceAll = replaceAll;
this->UserShaderReplacements[spec] = values;
}
void vtkOpenGLPolyDataMapper::ClearShaderReplacement(
vtkShader::Type shaderType, // vertex, fragment, etc
std::string originalValue,
bool replaceFirst)
{
vtkOpenGLPolyDataMapper::ReplacementSpec spec;
spec.ShaderType = shaderType;
spec.OriginalValue = originalValue;
spec.ReplaceFirst = replaceFirst;
typedef std::map<const vtkOpenGLPolyDataMapper::ReplacementSpec,
vtkOpenGLPolyDataMapper::ReplacementValue>::const_iterator RIter;
RIter found = this->UserShaderReplacements.find(spec);
if (found == this->UserShaderReplacements.end())
{
this->UserShaderReplacements.erase(found);
}
}
//-----------------------------------------------------------------------------
void vtkOpenGLPolyDataMapper::BuildShaders(
std::map<vtkShader::Type, vtkShader *> shaders,
......@@ -197,7 +235,42 @@ void vtkOpenGLPolyDataMapper::BuildShaders(
{
this->ShaderVariablesUsed.clear();
this->GetShaderTemplate(shaders, ren, actor);
typedef std::map<const vtkOpenGLPolyDataMapper::ReplacementSpec,
vtkOpenGLPolyDataMapper::ReplacementValue>::const_iterator RIter;
// user specified pre replacements
for (RIter i = this->UserShaderReplacements.begin();
i != this->UserShaderReplacements.end(); i++)
{
if (i->first.ReplaceFirst)
{
std::string ssrc = shaders[i->first.ShaderType]->GetSource();
vtkShaderProgram::Substitute(ssrc,
i->first.OriginalValue,
i->second.Replacement,
i->second.ReplaceAll);
shaders[i->first.ShaderType]->SetSource(ssrc);
}
}
this->ReplaceShaderValues(shaders, ren, actor);
// user specified post replacements
for (RIter i = this->UserShaderReplacements.begin();
i != this->UserShaderReplacements.end(); i++)
{
if (!i->first.ReplaceFirst)
{
std::string ssrc = shaders[i->first.ShaderType]->GetSource();
vtkShaderProgram::Substitute(ssrc,
i->first.OriginalValue,
i->second.Replacement,
i->second.ReplaceAll);
shaders[i->first.ShaderType]->SetSource(ssrc);
}
}
std::sort(this->ShaderVariablesUsed.begin(),this->ShaderVariablesUsed.end());
}
......
......@@ -84,7 +84,7 @@ public:
vtkPolyData *CurrentInput;
// Description:
// By default, this painters uses the dataset's point and cell ids during
// By default, this class uses the dataset's point and cell ids during
// rendering. However, one can override those by specifying cell and point
// data arrays to use instead. Currently, only vtkIdType array is supported.
// Set to NULL string (default) to use the point ids instead.
......@@ -94,14 +94,14 @@ public:
vtkGetStringMacro(CellIdArrayName);
// Description:
// If the painter should override the process id using a data-array,
// If this class should override the process id using a data-array,
// set this variable to the name of the array to use. It must be a
// point-array.
vtkSetStringMacro(ProcessIdArrayName);
vtkGetStringMacro(ProcessIdArrayName);
// Description:
// Generally, vtkCompositePainter can render the composite id when iterating
// Generally, this class can render the composite id when iterating
// over composite datasets. However in some cases (as in AMR), the rendered
// structure may not correspond to the input data, in which case we need
// to provide a cell array that can be used to render in the composite id in
......@@ -111,6 +111,24 @@ public:
vtkSetStringMacro(CompositeIdArrayName);
vtkGetStringMacro(CompositeIdArrayName);
// Description:
// This function enables you to apply your own substitutions
// to the shader creation process. The shader code in this class
// is created by applying a bunch of string replacements to a
// shader template. Using this function you can apply your
// own string replacements to add features you desire.
void AddShaderReplacement(
vtkShader::Type shaderType, // vertex, fragment, etc
std::string originalValue,
bool replaceFirst, // do this replacement before the default
std::string replacementValue,
bool replaceAll);
void ClearShaderReplacement(
vtkShader::Type shaderType, // vertex, fragment, etc
std::string originalValue,
bool replaceFirst);
// the following is all extra stuff to work around the
// fact that gl_PrimitiveID does not work correctly on
// Apple devices with AMD graphics hardware. See apple
......@@ -315,6 +333,34 @@ protected:
int TextureComponents;
class ReplacementSpec
{
public:
std::string OriginalValue;
vtkShader::Type ShaderType;
bool ReplaceFirst;
bool operator<(const ReplacementSpec &v1) const
{
if (this->OriginalValue != v1.OriginalValue) { return this->OriginalValue < v1.OriginalValue; }
if (this->ShaderType != v1.ShaderType) { return this->ShaderType < v1.ShaderType; }
return (this->ReplaceFirst < v1.ReplaceFirst);
}
bool operator>(const ReplacementSpec &v1) const
{
if (this->OriginalValue != v1.OriginalValue) { return this->OriginalValue > v1.OriginalValue; }
if (this->ShaderType != v1.ShaderType) { return this->ShaderType > v1.ShaderType; }
return (this->ReplaceFirst > v1.ReplaceFirst);
}
};
class ReplacementValue
{
public:
std::string Replacement;
bool ReplaceAll;
};
std::map<const ReplacementSpec,ReplacementValue> UserShaderReplacements;
private:
vtkOpenGLPolyDataMapper(const vtkOpenGLPolyDataMapper&); // Not implemented.
void operator=(const vtkOpenGLPolyDataMapper&); // Not implemented.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment