Skip to content

MarbleShaderDemo

Repository source: MarbleShaderDemo

Description

Makes solid marble texture with strong veins. The "veincolor" parameter controls the color of the veins. The background color is given by the surface's DiffuseColor.

Explore the parameter space with the vtkSliderWidget.

The file for the shader code is src/Testing/Data/Shaders/PerlinNoise.glsl.

Parameters

  • veinfreq - controls the lowest frequency of the color veins e.g. 10
  • veinlevels - how many "levels" of vein tendrils it has e.g. 2
  • warpfreq - lowest frequency of the turbulent warping in the marble e.g. 10
  • warping - controls how much turbulent warping there will be e.g. 0.5
  • veincolor - the color of the veins e.g. white: 1 1 1
  • sharpness - controls how sharp or fuzzy the veins are (higher = sharper) e.g. 8

Cite

This shader is inspired by Larry Gritz's veined marble shader.

Other languages

See (PythonicAPI)

Question

If you have a question about this example, please use the VTK Discourse Forum

Code

MarbleShaderDemo.cxx

#include <vtkActor.h>
#include <vtkCamera.h>
#include <vtkCameraOrientationWidget.h>
#include <vtkNamedColors.h>
#include <vtkNew.h>
#include <vtkOpenGLPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkProperty2D.h>
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <vtkShaderProgram.h>
#include <vtkSliderRepresentation2D.h>
#include <vtkSliderWidget.h>
#include <vtkSmartPointer.h>
#include <vtkTextProperty.h>
#include <vtkVersion.h>

#include <vtkInteractorStyleTrackballCamera.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkTransform.h>
#include <vtkTransformPolyDataFilter.h>
#include <vtkTriangleFilter.h>
#include <vtkTriangleMeshPointNormals.h>

#include <vtkBYUReader.h>
#include <vtkOBJReader.h>
#include <vtkPLYReader.h>
#include <vtkPolyDataReader.h>
#include <vtkSTLReader.h>
#include <vtkSphereSource.h>
#include <vtkXMLPolyDataReader.h>
#include <vtksys/SystemTools.hxx>

#if VTK_VERSION_NUMBER >= 90020210809ULL
#define VTK_HAS_COW 1
#endif

#if VTK_HAS_COW
#include <vtkCameraOrientationWidget.h>
#endif

#if VTK_VERSION_NUMBER >= 89000000000ULL
#define USE_SHADER_PROPERTIES 1
#include <vtkShaderProperty.h>
#endif

#include <array>
#include <chrono>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <thread>

// Delay time in milliseconds.
// #define DELAY 200
#define DELAY 20

namespace {

struct ShaderProperties
{
  std::array<double, 3> veincolor{0.0, 0.501961, 0.0};
  float veinfreq{7.5};
  int veinlevels{3};
  float warpfreq{1.5};
  float warping{0.5};
  float sharpness{8.0};
} shaderProperties;

std::string dispParameters();

// Commands for the vertex and fragment shaders.
struct ShaderCommand
{
  std::string p1;
  bool p2;
  std::string p3;
  bool p4;
};

ShaderCommand GetVertexShaderReplacement1();

ShaderCommand GetVertexShaderReplacement2();

ShaderCommand GetFragmentShaderReplacement1(std::string const& shaderCode);

ShaderCommand GetFragmentShaderReplacement2();

ShaderCommand GetFragmentShaderReplacement3();

// -----------------------------------------------------------------------
// Update uniform variables in the shader for each render. We do this with
//  a callback for the UpdateShaderEvent.
class ShaderCallback : public vtkCommand
{
public:
  static ShaderCallback* New()
  {
    return new ShaderCallback;
  }

  ShaderCallback()
  {
    this->veincolor = shaderProperties.veincolor;
    this->veinfreq = shaderProperties.veinfreq;
    this->veinlevels = shaderProperties.veinlevels;
    this->warpfreq = shaderProperties.warpfreq;
    this->warping = shaderProperties.warping;
    this->sharpness = shaderProperties.sharpness;
  }

  void Execute(vtkObject*, unsigned long, void* calldata) override
  {
    vtkShaderProgram* program = reinterpret_cast<vtkShaderProgram*>(calldata);
    if (program)
    {
      program->SetUniform3f("veincolor", shaderProperties.veincolor.data());
      program->SetUniformf("veinfreq", shaderProperties.veinfreq);
      program->SetUniformi("veinlevels", shaderProperties.veinlevels);
      program->SetUniformf("warpfreq", shaderProperties.warpfreq);
      program->SetUniformf("warping", shaderProperties.warping);
      program->SetUniformf("sharpness", shaderProperties.sharpness);
      if (HasChanged())
      {
        std::cout << dispParameters();
        Update();
      }
    }
  }

private:
  void Update()
  {
    this->veincolor = shaderProperties.veincolor;
    this->veinfreq = shaderProperties.veinfreq;
    this->veinlevels = shaderProperties.veinlevels;
    this->warpfreq = shaderProperties.warpfreq;
    this->warping = shaderProperties.warping;
    this->sharpness = shaderProperties.sharpness;
  }

  bool HasChanged()
  {
    if (this->veincolor != shaderProperties.veincolor)
    {
      return true;
    }
    if (this->veinfreq != shaderProperties.veinfreq)
    {
      return true;
    }
    if (this->veinlevels != shaderProperties.veinlevels)
    {
      return true;
    }
    if (this->warpfreq != shaderProperties.warpfreq)
    {
      return true;
    }
    if (this->warping != shaderProperties.warping)
    {
      return true;
    }
    if (this->sharpness != shaderProperties.sharpness)
    {
      return true;
    }

    return false;
  }

public:
  std::array<double, 3> veincolor;
  float veinfreq;
  int veinlevels;
  float warpfreq;
  float warping;
  float sharpness;
};

// These callbacks do the actual work.
// Callbacks for the interactions
class SliderCallbackVeinFreq : public vtkCommand
{
public:
  static SliderCallbackVeinFreq* New()
  {
    return new SliderCallbackVeinFreq;
  }

  virtual void Execute(vtkObject* caller, unsigned long, void*)
  {
    vtkSliderWidget* sliderWidget = reinterpret_cast<vtkSliderWidget*>(caller);

    float value = static_cast<vtkSliderRepresentation2D*>(
                      sliderWidget->GetRepresentation())
                      ->GetValue();
    shaderProperties.veinfreq = value;
    std::this_thread::sleep_for(std::chrono::milliseconds(DELAY));
  }

  SliderCallbackVeinFreq() = default;
};

class SliderCallbackVeinLevels : public vtkCommand
{
public:
  static SliderCallbackVeinLevels* New()
  {
    return new SliderCallbackVeinLevels;
  }

  virtual void Execute(vtkObject* caller, unsigned long, void*)
  {
    vtkSliderWidget* sliderWidget = reinterpret_cast<vtkSliderWidget*>(caller);

    int value = static_cast<vtkSliderRepresentation2D*>(
                    sliderWidget->GetRepresentation())
                    ->GetValue();
    static_cast<vtkSliderRepresentation2D*>(sliderWidget->GetRepresentation())
        ->SetValue(value);
    shaderProperties.veinlevels = value;
    std::this_thread::sleep_for(std::chrono::milliseconds(DELAY));
  }

  SliderCallbackVeinLevels() = default;
};

class SliderCallbackWarpFreq : public vtkCommand
{
public:
  static SliderCallbackWarpFreq* New()
  {
    return new SliderCallbackWarpFreq;
  }

  virtual void Execute(vtkObject* caller, unsigned long, void*)
  {
    vtkSliderWidget* sliderWidget = reinterpret_cast<vtkSliderWidget*>(caller);

    float value = static_cast<vtkSliderRepresentation2D*>(
                      sliderWidget->GetRepresentation())
                      ->GetValue();
    shaderProperties.warpfreq = value;
    std::this_thread::sleep_for(std::chrono::milliseconds(DELAY));
  }

  SliderCallbackWarpFreq() : Shader(0)
  {
  }
  ShaderCallback* Shader;
};

class SliderCallbackWarping : public vtkCommand
{
public:
  static SliderCallbackWarping* New()
  {
    return new SliderCallbackWarping;
  }

  virtual void Execute(vtkObject* caller, unsigned long, void*)
  {
    vtkSliderWidget* sliderWidget = reinterpret_cast<vtkSliderWidget*>(caller);

    float value = static_cast<vtkSliderRepresentation2D*>(
                      sliderWidget->GetRepresentation())
                      ->GetValue();
    shaderProperties.warping = value;
    std::this_thread::sleep_for(std::chrono::milliseconds(DELAY));
  }

  SliderCallbackWarping() = default;
};

class SliderCallbackSharpness : public vtkCommand
{
public:
  static SliderCallbackSharpness* New()
  {
    return new SliderCallbackSharpness;
  }

  virtual void Execute(vtkObject* caller, unsigned long, void*)
  {
    vtkSliderWidget* sliderWidget = reinterpret_cast<vtkSliderWidget*>(caller);

    float value = static_cast<vtkSliderRepresentation2D*>(
                      sliderWidget->GetRepresentation())
                      ->GetValue();
    shaderProperties.sharpness = value;
    std::this_thread::sleep_for(std::chrono::milliseconds(DELAY));
  }

  SliderCallbackSharpness() = default;
};

vtkSmartPointer<vtkPolyData> ReadPolyData(const char* fileName);

struct SliderProperties
{
  // Set up the sliders
  double tubeWidth{0.008};
  double sliderLength{0.075};
  double sliderWidth{0.025};
  double endCapLength{0.025};
  double endCapWidth{0.025};
  double titleHeight{0.025};
  double labelHeight{0.020};

  double minimumValue{0.0};
  double maximumValue{1.0};
  double initialValue{0.0};

  std::array<double, 2> p1{0.02, 0.1};
  std::array<double, 2> p2{0.18, 0.1};

  std::string title{""};

  std::string titleColor{"Black"};
  std::string labelColor{"Black"};
  std::string valueColor{"DarkSlateGray"};
  std::string sliderColor{"BurlyWood"};
  std::string selectedColor{"Lime"};
  std::string barColor{"Black"};
  std::string barEndsColor{"Indigo"};
};

vtkNew<vtkSliderWidget> MakeSliderWidget(SliderProperties const& properties,
                                         vtkRenderer* currentRenderer,
                                         vtkRenderWindowInteractor* interactor);

} // namespace

//----------------------------------------------------------------------------
int main(int argc, char* argv[])
{
  if (argc < 2)
  {
    std::cout << "Usage: " << argv[0] << " PerlnNoise.glsl "
              << "[polydataFile] " << "[veincolor(0.0, 0.501961, 0.0)] "
              << "[veinfreq(7.5)] " << "[veinlevels(3)] "
              << "[warpfreq(1.5)] " << "[warping(0.5)] "
              << "[sharpness(8.0)]" << std::endl;
    std::cout << "If the polydata file is not specified,"
                 " a sphere is used instead."
              << std::endl;
    return EXIT_FAILURE;
  }

  vtkNew<vtkNamedColors> colors;

  // Set the background color.
  std::array<unsigned char, 4> bkg{{26, 51, 102, 255}};
  colors->SetColor("BkgColor", bkg.data());

  auto polyData = ReadPolyData(argc > 2 ? argv[2] : "");

  std::ifstream shaderFile(argv[1]);
  std::ostringstream shaderCode;
  shaderCode << shaderFile.rdbuf();

  // This is where we change the defaultshader properties.
  // Defaulting to green.
  shaderProperties.veincolor[0] = colors->GetColor3d("Green").GetData()[0];
  shaderProperties.veincolor[1] = colors->GetColor3d("Green").GetData()[1];
  shaderProperties.veincolor[2] = colors->GetColor3d("Green").GetData()[2];

  // Create a transform to rescale the model.
  double center[3];
  polyData->GetCenter(center);
  double bounds[6];
  polyData->GetBounds(bounds);
  double maxBound =
      std::max(std::max(bounds[1] - bounds[0], bounds[3] - bounds[2]),
               bounds[5] - bounds[4]);

  // Build the pipeline.
  // ren1 is for the slider rendering,
  // ren2 is for the object rendering.
  vtkNew<vtkRenderer> ren1;
  ren1->SetBackground(colors->GetColor3d("Snow").GetData());
  vtkNew<vtkRenderer> ren2;
  vtkNew<vtkOpenGLPolyDataMapper> mapper;
  ren2->SetBackground(colors->GetColor3d("SlateGray").GetData());

  vtkNew<vtkRenderWindow> renderWindow;
  renderWindow->SetSize(800, 480);
  renderWindow->SetWindowName("MarbleShaderDemo");

  // The order here is important.
  // This ensures that the sliders will be in ren1.
  renderWindow->AddRenderer(ren2);
  renderWindow->AddRenderer(ren1);
  ren1->SetViewport(0.0, 0.0, 0.2, 1.0);
  ren2->SetViewport(0.2, 0.0, 1, 1);

  vtkNew<vtkRenderWindowInteractor> interactor;
  interactor->SetRenderWindow(renderWindow);
  vtkNew<vtkInteractorStyleTrackballCamera> style;
  interactor->SetInteractorStyle(style);

  // Rescale the polydata to [-1,1].
  vtkNew<vtkTransform> userTransform;
  userTransform->Translate(-center[0], -center[1], -center[2]);
  userTransform->Scale(1.0 / maxBound, 1.0 / maxBound, 1.0 / maxBound);
  vtkNew<vtkTransformPolyDataFilter> transform;
  transform->SetTransform(userTransform);
  transform->SetInputData(polyData);

  vtkNew<vtkTriangleFilter> triangles;
  triangles->SetInputConnection(transform->GetOutputPort());

  vtkNew<vtkTriangleMeshPointNormals> norms;
  norms->SetInputConnection(triangles->GetOutputPort());

  mapper->SetInputConnection(norms->GetOutputPort());
  mapper->ScalarVisibilityOff();

  vtkNew<vtkActor> actor;

  actor->SetMapper(mapper);
  actor->GetProperty()->SetDiffuse(1.0);
  actor->GetProperty()->SetDiffuseColor(colors->GetColor3d("Wheat").GetData());
  actor->GetProperty()->SetSpecular(.5);
  actor->GetProperty()->SetSpecularPower(50);

  ren2->AddActor(actor);

  // Modify the vertex shader to pass the position of the vertex.
  auto cmd = GetVertexShaderReplacement1();
#if USE_SHADER_PROPERTIES
  vtkShaderProperty* sp = actor->GetShaderProperty();
  sp->AddVertexShaderReplacement(cmd.p1, cmd.p2, cmd.p3, cmd.p4);
#else
  mapper->AddShaderReplacement(vtkShader::Vertex, cmd.p1, cmd.p2, cmd.p3,
                               cmd.p4);
#endif

  cmd = GetVertexShaderReplacement2();
#if USE_SHADER_PROPERTIES
  sp->AddVertexShaderReplacement(cmd.p1, cmd.p2, cmd.p3, cmd.p4);
#else
  mapper->AddShaderReplacement(vtkShader::Vertex, cmd.p1, cmd.p2, cmd.p3,
                               cmd.p4);
#endif

  // Add the code to generate noise.
  // These functions need to be defined outside of main.
  // Use the System::Dec to declare and implement.
  cmd = GetFragmentShaderReplacement1(shaderCode.str());
#if USE_SHADER_PROPERTIES
  sp->AddFragmentShaderReplacement(cmd.p1, cmd.p2, cmd.p3, cmd.p4);
#else
  mapper->AddShaderReplacement(vtkShader::Fragment, // in the fragment shader
                               cmd.p1, cmd.p2, cmd.p3, cmd.p4);
#endif

  // Define varying and uniforms for the fragment shader here.
  cmd = GetFragmentShaderReplacement2();
#if USE_SHADER_PROPERTIES
  sp->AddFragmentShaderReplacement(cmd.p1, cmd.p2, cmd.p3, cmd.p4);
#else
  mapper->AddShaderReplacement(vtkShader::Fragment, // in the fragment shader
                               cmd.p1, cmd.p2, cmd.p3, cmd.p4);
#endif

  cmd = GetFragmentShaderReplacement3();
#if USE_SHADER_PROPERTIES
  sp->AddFragmentShaderReplacement(cmd.p1, cmd.p2, cmd.p3, cmd.p4);
#else
  mapper->AddShaderReplacement(vtkShader::Fragment, // in the fragment shader
                               cmd.p1, cmd.p2, cmd.p3, cmd.p4);
#endif

  std::cout << "------------" << std::endl;
  std::cout << "Input: " << (argc > 2 ? argv[2] : "Generated Sphere")
            << std::endl;
  std::cout << "------------" << std::endl;
  std::cout << dispParameters();
  vtkNew<ShaderCallback> myCallback;
  mapper->AddObserver(vtkCommand::UpdateShaderEvent, myCallback);

  // Setup a slider widget and callback for each varying parameter.

  auto stepSize = 1.0 / 5.0;
  auto posY = 0.1;
  auto posX0 = 0.1;
  auto posX1 = 0.9;

  auto slwP = SliderProperties();

  // Slider values
  slwP.minimumValue = 1.0;
  slwP.initialValue = 7.5;
  slwP.maximumValue = 15.0;
  slwP.title = "Vein Frequency";
  //  Screen coordinates.
  slwP.p1[0] = posX0;
  slwP.p1[1] = posY;
  slwP.p2[0] = posX1;
  slwP.p2[1] = posY;

  auto sliderWidgetVeinFreq = MakeSliderWidget(slwP, ren1, interactor);
  vtkNew<SliderCallbackVeinFreq> callbackVeinFreq;
  sliderWidgetVeinFreq->AddObserver(vtkCommand::InteractionEvent,
                                    callbackVeinFreq);

  posY += stepSize;

  // Slider values
  slwP.minimumValue = 1;
  slwP.initialValue = 3;
  slwP.maximumValue = 5;
  slwP.title = "Vein Levels";
  //  Screen coordinates.
  slwP.p1[0] = posX0;
  slwP.p1[1] = posY;
  slwP.p2[0] = posX1;
  slwP.p2[1] = posY;

  auto sliderWidgetVeinLevels = MakeSliderWidget(slwP, ren1, interactor);
  vtkNew<SliderCallbackVeinLevels> callbackVeinLevels;
  sliderWidgetVeinLevels->AddObserver(vtkCommand::EndInteractionEvent,
                                      callbackVeinLevels);

  posY += stepSize;

  // Slider values
  slwP.minimumValue = 1.0;
  slwP.initialValue = 1.5;
  slwP.maximumValue = 2.0;
  slwP.title = "Warp Frequency";
  //  Screen coordinates.
  slwP.p1[0] = posX0;
  slwP.p1[1] = posY;
  slwP.p2[0] = posX1;
  slwP.p2[1] = posY;

  auto sliderWidgetWarpFreq = MakeSliderWidget(slwP, ren1, interactor);
  vtkNew<SliderCallbackWarpFreq> callbackWarpFreq;
  sliderWidgetWarpFreq->AddObserver(vtkCommand::InteractionEvent,
                                    callbackWarpFreq);

  posY += stepSize;

  // Slider values
  slwP.minimumValue = 0.0;
  slwP.initialValue = 0.5;
  slwP.maximumValue = 1.0;
  slwP.title = "Warping";
  //  Screen coordinates.
  slwP.p1[0] = posX0;
  slwP.p1[1] = posY;
  slwP.p2[0] = posX1;
  slwP.p2[1] = posY;

  auto sliderWidgetWarping = MakeSliderWidget(slwP, ren1, interactor);
  vtkNew<SliderCallbackWarping> callbackWarping;
  sliderWidgetWarping->AddObserver(vtkCommand::InteractionEvent,
                                   callbackWarping);

  posY += stepSize;

  // Slider values
  slwP.minimumValue = 0.01;
  slwP.initialValue = 8.0;
  slwP.maximumValue = 10.0;
  slwP.title = "Sharpness";
  //  Screen coordinates.
  slwP.p1[0] = posX0;
  slwP.p1[1] = posY;
  slwP.p2[0] = posX1;
  slwP.p2[1] = posY;

  auto sliderWidgetSharpness = MakeSliderWidget(slwP, ren1, interactor);
  vtkNew<SliderCallbackSharpness> callbackSharpness;
  sliderWidgetSharpness->AddObserver(vtkCommand::InteractionEvent,
                                     callbackSharpness);

  renderWindow->Render();

#if VTK_HAS_COW
  vtkNew<vtkCameraOrientationWidget> camOrientManipulator;
  camOrientManipulator->SetParentRenderer(ren2);
  // Enable the widget.
  camOrientManipulator->On();
#else
  vtkNew<vtkAxesActor> axes;

  vtkNew<vtkOrientationMarkerWidget> widget;
  double rgba[4]{0.0, 0.0, 0.0, 0.0};
  colors->GetColor("Carrot", rgba);
  widget->SetOutlineColor(rgba[0], rgba[1], rgba[2]);
  widget->SetOrientationMarker(axes);
  widget->SetInteractor(interactor);
  widget->SetViewport(0.0, 0.0, 0.2, 0.2);
  widget->EnabledOn();
  widget->InteractiveOn();
#endif

  ren2->ResetCamera();
  ren2->GetActiveCamera()->Zoom(1.0);
  renderWindow->Render();
  renderWindow->Render();
  interactor->Start();

  transform->GetOutput()->GetBounds(bounds);
  std::cout << "------------" << std::endl;
  std::cout << "Range: " << " x " << bounds[1] - bounds[0] << " y "
            << bounds[3] - bounds[2] << " z " << bounds[5] - bounds[4]
            << std::endl;
  std::cout << "------------" << std::endl;

  return EXIT_SUCCESS;
}

namespace {

std::string dispParameters()
{
  std::stringstream ss;
  ss << "------------" << std::endl;
  ss << "veincolor: " << shaderProperties.veincolor[0] << ", "
     << shaderProperties.veincolor[1] << ", " << shaderProperties.veincolor[2]
     << std::endl;
  ss << "veinfreq: " << shaderProperties.veinfreq << std::endl;
  ss << "veinlevels: " << shaderProperties.veinlevels << std::endl;
  ss << "warpfreq: " << shaderProperties.warpfreq << std::endl;
  ss << "warping: " << shaderProperties.warping << std::endl;
  ss << "sharpness: " << shaderProperties.sharpness << std::endl;
  ss << "------------" << std::endl;
  return ss.str();
}

vtkSmartPointer<vtkPolyData> ReadPolyData(const char* fileName)
{
  vtkSmartPointer<vtkPolyData> polyData;
  std::string extension =
      vtksys::SystemTools::GetFilenameExtension(std::string(fileName));
  if (extension == ".ply")
  {
    vtkNew<vtkPLYReader> reader;
    reader->SetFileName(fileName);
    reader->Update();
    polyData = reader->GetOutput();
  }
  else if (extension == ".vtp")
  {
    vtkNew<vtkXMLPolyDataReader> reader;
    reader->SetFileName(fileName);
    reader->Update();
    polyData = reader->GetOutput();
  }
  else if (extension == ".obj")
  {
    vtkNew<vtkOBJReader> reader;
    reader->SetFileName(fileName);
    reader->Update();
    polyData = reader->GetOutput();
  }
  else if (extension == ".stl")
  {
    vtkNew<vtkSTLReader> reader;
    reader->SetFileName(fileName);
    reader->Update();
    polyData = reader->GetOutput();
  }
  else if (extension == ".vtk")
  {
    vtkNew<vtkPolyDataReader> reader;
    reader->SetFileName(fileName);
    reader->Update();
    polyData = reader->GetOutput();
  }
  else if (extension == ".g")
  {
    vtkNew<vtkBYUReader> reader;
    reader->SetGeometryFileName(fileName);
    reader->Update();
    polyData = reader->GetOutput();
  }
  else
  {
    vtkNew<vtkSphereSource> source;
    source->SetPhiResolution(25);
    source->SetThetaResolution(25);
    source->Update();
    polyData = source->GetOutput();
  }
  return polyData;
}

ShaderCommand GetVertexShaderReplacement1()
{
  ShaderCommand sc;

  sc.p1 = "//VTK::Normal::Dec";  // replace the normal block
  sc.p2 = true;                  // before the standard replacements
  sc.p3 = "//VTK::Normal::Dec\n" // we still want the default
          "  out vec4 myVertexMC;\n";
  sc.p4 = false;                 // only do it once

  return sc;
}

ShaderCommand GetVertexShaderReplacement2()
{
  ShaderCommand sc;

  sc.p1 = "//VTK::Normal::Impl";  // replace the normal block
  sc.p2 = true;                   // before the standard replacements
  sc.p3 = "//VTK::Normal::Impl\n" // we still want the default
          "  myVertexMC = vertexMC;\n";
  sc.p4 = false;                  // only do it once

  return sc;
}

ShaderCommand GetFragmentShaderReplacement1(std::string const& shaderCode)
{
  ShaderCommand sc;

  sc.p1 = "//VTK::System::Dec";
  sc.p2 = false; // before the standard replacements
  sc.p3 = shaderCode;
  sc.p4 = false; // only do it once

  return sc;
}

ShaderCommand GetFragmentShaderReplacement2()
{
  ShaderCommand sc;

  sc.p1 = "//VTK::Normal::Dec";  // replace the normal block
  sc.p2 = false;                 // before the standard replacements
  sc.p3 = "//VTK::Normal::Dec\n" // we still want the default
          "  varying vec4 myVertexMC;\n"
          "  uniform vec3 veincolor = vec3(1.0, 1.0, 1.0);\n"
          "  uniform float veinfreq = 10.0;\n"
          "  uniform int veinlevels = 2;\n"
          "  uniform float warpfreq = 1;\n"
          "  uniform float warping = .5;\n"
          "  uniform float sharpness = 8.0;\n";
  sc.p4 = false;                 // only do it once

  return sc;
}

ShaderCommand GetFragmentShaderReplacement3()
{
  ShaderCommand sc;

  sc.p1 = "//VTK::Light::Impl",  // replace the light block
      sc.p2 = false;             // after the standard replacements
  sc.p3 = "//VTK::Light::Impl\n" // we still want the default calc
          "\n"
          "#define pnoise(x) ((noise(x) + 1.0) / 2.0)\n"
          "#define snoise(x) (2.0 * pnoise(x) - 1.0)\n"
          "  vec3 Ct;\n"
          "  int i;\n"
          "  float turb, freq;\n"
          "  float turbsum;\n"
          "  /* perturb the lookup */\n"
          "  freq = 1.0;\n"
          "  vec4 offset = vec4(0.0,0.0,0.0,0.0);\n"
          "  vec4 noisyPoint;\n"
          "  vec4 myLocalVertexMC = myVertexMC;\n"
          "\n"
          "    for (i = 0;  i < 6;  i += 1) {\n"
          "      noisyPoint[0] = snoise(warpfreq * freq * myLocalVertexMC);\n"
          "      noisyPoint[1] = snoise(warpfreq * freq * myLocalVertexMC);\n"
          "      noisyPoint[2] = snoise(warpfreq * freq * myLocalVertexMC);\n"
          "      noisyPoint[3] = 1.0;\n"
          "      offset += 2.0 * warping * (noisyPoint - 0.5)  / freq;\n"
          "      freq *= 2.0;\n"
          "    }\n"
          "    myLocalVertexMC.x += offset.x;\n"
          "    myLocalVertexMC.y += offset.y;\n"
          "    myLocalVertexMC.z += offset.z;\n"
          "\n"
          "    /* Now calculate the veining function for the lookup area */\n"
          "    turbsum = 0.0;  freq = 1.0;\n"
          "    myLocalVertexMC *= veinfreq;\n"
          "    for (i = 0;  i < veinlevels;  i += 1) {\n"
          "      turb = abs (snoise (myLocalVertexMC));\n"
          "      turb = pow (smoothstep (0.8, 1.0, 1.0 - turb), sharpness) / "
          "freq;\n"
          "      turbsum += (1.0-turbsum) * turb;\n"
          "      freq *= 1.5;\n"
          "      myLocalVertexMC *= 1.5;\n"
          "    }\n"
          "\n"
          "    Ct = mix (diffuseColor, veincolor, turbsum);\n"
          "\n"
          "  fragOutput0.rgb = opacity * (ambientColor + Ct + specular);\n"
          "  fragOutput0.a = opacity;\n";
  sc.p4 = false;                 // only do it once

  return sc;
}

vtkNew<vtkSliderWidget> MakeSliderWidget(SliderProperties const& properties,
                                         vtkRenderer* currentRenderer,
                                         vtkRenderWindowInteractor* interactor)
{
  vtkNew<vtkNamedColors> colors;
  vtkNew<vtkSliderRepresentation2D> slider;

  slider->SetMinimumValue(properties.minimumValue);
  slider->SetMaximumValue(properties.maximumValue);
  slider->SetValue(properties.initialValue);
  slider->SetTitleText(properties.title.c_str());

  slider->GetPoint1Coordinate()->SetCoordinateSystemToNormalizedViewport();
  slider->GetPoint1Coordinate()->SetValue(properties.p1[0], properties.p1[1]);
  slider->GetPoint2Coordinate()->SetCoordinateSystemToNormalizedViewport();
  slider->GetPoint2Coordinate()->SetValue(properties.p2[0], properties.p2[1]);

  slider->SetTubeWidth(properties.tubeWidth);
  slider->SetSliderLength(properties.sliderLength);
  slider->SetSliderWidth(properties.sliderWidth);
  slider->SetEndCapLength(properties.endCapLength);
  slider->SetEndCapWidth(properties.endCapWidth);
  slider->SetTitleHeight(properties.titleHeight);
  slider->SetLabelHeight(properties.labelHeight);

  // Set the color properties
  // Change the color of the title.
  slider->GetTitleProperty()->SetColor(
      colors->GetColor3d(properties.titleColor).GetData());
  // Change the color of the label.
  slider->GetLabelProperty()->SetColor(
      colors->GetColor3d(properties.labelColor).GetData());
  // Change the color of the bar.
  slider->GetTubeProperty()->SetColor(
      colors->GetColor3d(properties.barColor).GetData());
  // Change the color of the ends of the bar.
  slider->GetCapProperty()->SetColor(
      colors->GetColor3d(properties.barEndsColor).GetData());
  // Change the color of the knob that slides.
  slider->GetSliderProperty()->SetColor(
      colors->GetColor3d(properties.sliderColor).GetData());
  // Change the color of the knob when the mouse is held on it.
  slider->GetSelectedProperty()->SetColor(
      colors->GetColor3d(properties.selectedColor).GetData());
  // Change the color of the text displaying the value.
  slider->GetLabelProperty()->SetColor(
      colors->GetColor3d(properties.valueColor).GetData());

  vtkNew<vtkSliderWidget> sliderWidget;
  sliderWidget->SetCurrentRenderer(currentRenderer);
  sliderWidget->SetInteractor(interactor);
  sliderWidget->SetRepresentation(slider);
  sliderWidget->SetAnimationModeToAnimate();
  sliderWidget->SetNumberOfAnimationSteps(10);
  sliderWidget->EnabledOn();

  return sliderWidget;
}

} // namespace

CMakeLists.txt

cmake_minimum_required(VERSION 3.12 FATAL_ERROR)

project(MarbleShaderDemo)

find_package(VTK COMPONENTS 
  CommonColor
  CommonCore
  CommonTransforms
  FiltersCore
  FiltersGeneral
  FiltersSources
  IOGeometry
  IOLegacy
  IOPLY
  IOXML
  InteractionStyle
  InteractionWidgets
  RenderingContextOpenGL2
  RenderingCore
  RenderingFreeType
  RenderingGL2PSOpenGL2
  RenderingOpenGL2
)

if (NOT VTK_FOUND)
  message(FATAL_ERROR "MarbleShaderDemo: Unable to find the VTK build folder.")
endif()

# Prevent a "command line is too long" failure in Windows.
set(CMAKE_NINJA_FORCE_RESPONSE_FILE "ON" CACHE BOOL "Force Ninja to use response files.")
add_executable(MarbleShaderDemo MACOSX_BUNDLE MarbleShaderDemo.cxx )
  target_link_libraries(MarbleShaderDemo PRIVATE ${VTK_LIBRARIES}
)
# vtk_module_autoinit is needed
vtk_module_autoinit(
  TARGETS MarbleShaderDemo
  MODULES ${VTK_LIBRARIES}
)

Download and Build MarbleShaderDemo

Click here to download MarbleShaderDemo and its CMakeLists.txt file. Once the tarball MarbleShaderDemo.tar has been downloaded and extracted,

cd MarbleShaderDemo/build

If VTK is installed:

cmake ..

If VTK is not installed but compiled on your system, you will need to specify the path to your VTK build:

cmake -DVTK_DIR:PATH=/home/me/vtk_build ..

Build the project:

make

and run it:

./MarbleShaderDemo

WINDOWS USERS

Be sure to add the VTK bin directory to your path. This will resolve the VTK dll's at run time.