BozoShaderDemo
VTKExamples/Cxx/Shaders/BozoShaderDemo
Code¶
BozoShaderDemo.cxx
#include <vtkActor.h> #include <vtkActor2D.h> #include <vtkCamera.h> #include <vtkNamedColors.h> #include <vtkOpenGLPolyDataMapper.h> #include <vtkPolyDataNormals.h> #include <vtkProperty.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> #include <vtkRenderer.h> #include <vtkShaderProgram.h> #include <vtkSmartPointer.h> #include <vtkTextMapper.h> #include <vtkTextProperty.h> #include <vtkTransform.h> #include <vtkTransformPolyDataFilter.h> #include <vtkTriangleFilter.h> #include <vtkTriangleMeshPointNormals.h> #include <vtkVersion.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 >= 89000000000ULL #define USE_SHADER_PROPERTIES 1 #include <vtkShaderProperty.h> #endif #include <fstream> #include <sstream> namespace { // ----------------------------------------------------------------------- // Update a uniform in the shader for each render. We do this with a // callback for the UpdateShaderEvent class vtkShaderCallback : public vtkCommand { public: static vtkShaderCallback* New() { return new vtkShaderCallback; } vtkRenderer* Renderer; float k; void Execute(vtkObject*, unsigned long, void* calldata) override { vtkShaderProgram* program = reinterpret_cast<vtkShaderProgram*>(calldata); if (program) { program->SetUniformf("k", k); } } void Print(std::ostream& os) { os << "k: " << k << std::endl; } vtkShaderCallback() { this->Renderer = nullptr; this->k = 5; } }; } // namespace namespace { vtkSmartPointer<vtkPolyData> ReadPolyData(const char* fileName); } //---------------------------------------------------------------------------- int main(int argc, char* argv[]) { if (argc < 2) { std::cout << "Usage: " << argv[0] << " PerlnNoise.glsl " << "[polydataFile] " << "[k(5)]" << std::endl; return EXIT_FAILURE; } auto colors = vtkSmartPointer<vtkNamedColors>::New(); auto renderWindow = vtkSmartPointer<vtkRenderWindow>::New(); // Create one text property for all auto textProperty = vtkSmartPointer<vtkTextProperty>::New(); textProperty->SetFontSize(20); // textProperty->SetJustificationToCentered(); textProperty->SetColor(0.3, 0.3, 0.3); std::vector<vtkSmartPointer<vtkRenderer>> renderers; for (int i = 2; i < argc; ++i) { std::cout << "Reading file: " << argv[i] << std::endl; auto polyData = ReadPolyData(argv[i]); std::ifstream shaderFile(argv[1]); std::ostringstream shaderCode; shaderCode << shaderFile.rdbuf(); shaderFile.close(); // Create a transform to rescale 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]); // Create textActors auto textMapper = vtkSmartPointer<vtkTextMapper>::New(); textMapper->SetTextProperty(textProperty); textMapper->SetInput(vtksys::SystemTools::GetFilenameName(argv[i]).c_str()); auto textActor = vtkSmartPointer<vtkActor2D>::New(); textActor->SetMapper(textMapper); textActor->SetPosition(20, 20); auto actor = vtkSmartPointer<vtkActor>::New(); auto renderer = vtkSmartPointer<vtkRenderer>::New(); auto mapper = vtkSmartPointer<vtkOpenGLPolyDataMapper>::New(); renderer->SetBackground(colors->GetColor3d("Burlywood").GetData()); renderers.push_back(renderer); renderWindow->AddRenderer(renderer); renderer->AddActor(actor); renderer->AddActor(textActor); // Rescale polydata to [-1,1] auto userTransform = vtkSmartPointer<vtkTransform>::New(); userTransform->Translate(-center[0], -center[1], -center[2]); userTransform->Scale(1.0 / maxBound, 1.0 / maxBound, 1.0 / maxBound); auto transform = vtkSmartPointer<vtkTransformPolyDataFilter>::New(); transform->SetTransform(userTransform); transform->SetInputData(polyData); auto triangles = vtkSmartPointer<vtkTriangleFilter>::New(); triangles->SetInputConnection(transform->GetOutputPort()); auto norms = vtkSmartPointer<vtkTriangleMeshPointNormals>::New(); norms->SetInputConnection(triangles->GetOutputPort()); mapper->SetInputConnection(norms->GetOutputPort()); mapper->ScalarVisibilityOff(); actor->SetMapper(mapper); actor->GetProperty()->SetAmbientColor(0.2, 0.2, 0.2); actor->GetProperty()->SetDiffuseColor(1.0, 1.0, 1.0); actor->GetProperty()->SetSpecularColor(1.0, 1.0, 1.0); actor->GetProperty()->SetSpecular(0.5); actor->GetProperty()->SetDiffuse(0.7); actor->GetProperty()->SetAmbient(0.1); actor->GetProperty()->SetSpecularPower(100.0); actor->GetProperty()->SetOpacity(1.0); renderer->ResetCamera(); renderer->GetActiveCamera()->Zoom(1.2); // Modify the vertex shader to pass the position of the vertex #if USE_SHADER_PROPERTIES vtkShaderProperty* sp = actor->GetShaderProperty(); sp->AddVertexShaderReplacement( "//VTK::Normal::Dec", // replace the normal block true, // before the standard replacements "//VTK::Normal::Dec\n" // we still want the default " out vec4 myVertexMC;\n", false // only do it once ); #else 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 " out vec4 myVertexMC;\n", false // only do it once ); #endif #if USE_SHADER_PROPERTIES sp->AddVertexShaderReplacement( "//VTK::Normal::Impl", // replace the normal block true, // before the standard replacements "//VTK::Normal::Impl\n" // we still want the default " myVertexMC = vertexMC;\n", false // only do it once ); #else 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 " myVertexMC = vertexMC;\n", false // only do it once ); #endif // Add the code to generate noise // These functions need to be defined outside of main. Use the System::Dec // to declare and implement #if USE_SHADER_PROPERTIES sp->AddFragmentShaderReplacement("//VTK::System::Dec", false, // before the standard replacements shaderCode.str(), false // only do it once ); #else mapper->AddShaderReplacement(vtkShader::Fragment, "//VTK::System::Dec", false, // before the standard replacements shaderCode.str(), false // only do it once ); #endif // Define varying and uniforms for the fragment shader here #if USE_SHADER_PROPERTIES sp->AddFragmentShaderReplacement( "//VTK::Normal::Dec", // replace the normal block true, // before the standard replacements "//VTK::Normal::Dec\n" // we still want the default " varying vec4 myVertexMC;\n" " uniform float k = 1.0;\n", false // only do it once ); #else 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 vec4 myVertexMC;\n" " uniform float k = 1.0;\n", false // only do it once ); #endif #if USE_SHADER_PROPERTIES sp->AddFragmentShaderReplacement( "//VTK::Light::Impl", // replace the light block false, // after the standard replacements "//VTK::Light::Impl\n" // we still want the default calc "#define pnoise(x) ((noise(x) + 1.0) / 2.0)\n" " vec3 noisyColor;\n" " noisyColor.r = noise(k * 10.0 * myVertexMC);\n" " noisyColor.g = noise(k * 11.0 * myVertexMC);\n" " noisyColor.b = noise(k * 12.0 * myVertexMC);\n" " /* map ranges of noise values into different colors */\n" " int i;\n" " float lowerValue = .3;\n" " float upperValue = .6;\n" " for ( i=0; i<3; i+=1)\n" " {\n" " noisyColor[i] = (noisyColor[i] + 1.0) / 2.0;\n" " if (noisyColor[i] < lowerValue) \n" " {\n" " noisyColor[i] = lowerValue;\n" " }\n" " else\n" " {\n" " if (noisyColor[i] < upperValue)\n" " {\n" " noisyColor[i] = upperValue;\n" " }\n" " else\n" " {\n" " noisyColor[i] = 1.0;\n" " }\n" " }\n" " }\n" " fragOutput0.rgb = opacity * vec3(ambientColor + noisyColor * " "diffuse + specular);\n" " fragOutput0.a = opacity;\n", false // only do it once ); #else mapper->AddShaderReplacement( vtkShader::Fragment, // in the fragment shader "//VTK::Light::Impl", // replace the light block false, // after the standard replacements "//VTK::Light::Impl\n" // we still want the default calc "#define pnoise(x) ((noise(x) + 1.0) / 2.0)\n" " vec3 noisyColor;\n" " noisyColor.r = noise(k * 10.0 * myVertexMC);\n" " noisyColor.g = noise(k * 11.0 * myVertexMC);\n" " noisyColor.b = noise(k * 12.0 * myVertexMC);\n" " /* map ranges of noise values into different colors */\n" " int i;\n" " float lowerValue = .3;\n" " float upperValue = .6;\n" " for ( i=0; i<3; i+=1)\n" " {\n" " noisyColor[i] = (noisyColor[i] + 1.0) / 2.0;\n" " if (noisyColor[i] < lowerValue) \n" " {\n" " noisyColor[i] = lowerValue;\n" " }\n" " else\n" " {\n" " if (noisyColor[i] < upperValue)\n" " {\n" " noisyColor[i] = upperValue;\n" " }\n" " else\n" " {\n" " noisyColor[i] = 1.0;\n" " }\n" " }\n" " }\n" " fragOutput0.rgb = opacity * vec3(ambientColor + noisyColor * " "diffuse + specular);\n" " fragOutput0.a = opacity;\n", false // only do it once ); #endif auto myCallback = vtkSmartPointer<vtkShaderCallback>::New(); myCallback->Renderer = renderer; myCallback->k = 1; mapper->AddObserver(vtkCommand::UpdateShaderEvent, myCallback); } // Setup viewports for the renderers int rendererSize = 250; unsigned int xGridDimensions = 3; unsigned int yGridDimensions = 3; renderWindow->SetSize(rendererSize * xGridDimensions, rendererSize * yGridDimensions); for (int row = 0; row < static_cast<int>(yGridDimensions); row++) { for (int col = 0; col < static_cast<int>(xGridDimensions); col++) { int index = row * xGridDimensions + col; // (xmin, ymin, xmax, ymax) double viewport[4] = {static_cast<double>(col) * rendererSize / (xGridDimensions * rendererSize), static_cast<double>(yGridDimensions - (row + 1)) * rendererSize / (yGridDimensions * rendererSize), static_cast<double>(col + 1) * rendererSize / (xGridDimensions * rendererSize), static_cast<double>(yGridDimensions - row) * rendererSize / (yGridDimensions * rendererSize)}; renderers[index]->SetViewport(viewport); } } auto interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New(); interactor->SetRenderWindow(renderWindow); renderWindow->Render(); renderWindow->SetWindowName("BozoShaderDemo"); renderWindow->Render(); interactor->Start(); return EXIT_SUCCESS; } namespace { vtkSmartPointer<vtkPolyData> ReadPolyData(const char* fileName) { vtkSmartPointer<vtkPolyData> polyData; std::string extension = vtksys::SystemTools::GetFilenameExtension(std::string(fileName)); if (extension == ".ply") { auto reader = vtkSmartPointer<vtkPLYReader>::New(); reader->SetFileName(fileName); reader->Update(); polyData = reader->GetOutput(); } else if (extension == ".vtp") { auto reader = vtkSmartPointer<vtkXMLPolyDataReader>::New(); reader->SetFileName(fileName); reader->Update(); polyData = reader->GetOutput(); } else if (extension == ".obj") { auto reader = vtkSmartPointer<vtkOBJReader>::New(); reader->SetFileName(fileName); reader->Update(); polyData = reader->GetOutput(); } else if (extension == ".stl") { auto reader = vtkSmartPointer<vtkSTLReader>::New(); reader->SetFileName(fileName); reader->Update(); polyData = reader->GetOutput(); } else if (extension == ".vtk") { auto reader = vtkSmartPointer<vtkPolyDataReader>::New(); reader->SetFileName(fileName); reader->Update(); polyData = reader->GetOutput(); } else if (extension == ".g") { auto reader = vtkSmartPointer<vtkBYUReader>::New(); reader->SetGeometryFileName(fileName); reader->Update(); polyData = reader->GetOutput(); } else { auto source = vtkSmartPointer<vtkSphereSource>::New(); source->SetPhiResolution(25); source->SetThetaResolution(25); source->Update(); polyData = source->GetOutput(); } return polyData; } } // namespace
CMakeLists.txt¶
cmake_minimum_required(VERSION 3.3 FATAL_ERROR) project(BozoShaderDemo) find_package(VTK COMPONENTS vtkCommonColor vtkCommonCore vtkCommonTransforms vtkFiltersCore vtkFiltersGeneral vtkFiltersSources vtkIOGeometry vtkIOLegacy vtkIOPLY vtkIOXML vtkInteractionStyle vtkRenderingContextOpenGL2 vtkRenderingCore vtkRenderingFreeType vtkRenderingGL2PSOpenGL2 vtkRenderingOpenGL2 QUIET) if (NOT VTK_FOUND) message("Skipping BozoShaderDemo: ${VTK_NOT_FOUND_MESSAGE}") return () endif() message (STATUS "VTK_VERSION: ${VTK_VERSION}") if (VTK_VERSION VERSION_LESS "8.90.0") # old system include(${VTK_USE_FILE}) add_executable(BozoShaderDemo MACOSX_BUNDLE BozoShaderDemo.cxx ) target_link_libraries(BozoShaderDemo PRIVATE ${VTK_LIBRARIES}) else () # include all components add_executable(BozoShaderDemo MACOSX_BUNDLE BozoShaderDemo.cxx ) target_link_libraries(BozoShaderDemo PRIVATE ${VTK_LIBRARIES}) # vtk_module_autoinit is needed vtk_module_autoinit( TARGETS BozoShaderDemo MODULES ${VTK_LIBRARIES} ) endif ()
Download and Build BozoShaderDemo¶
Click here to download BozoShaderDemo and its CMakeLists.txt file. Once the tarball BozoShaderDemo.tar has been downloaded and extracted,
cd BozoShaderDemo/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:
./BozoShaderDemo
WINDOWS USERS
Be sure to add the VTK bin directory to your path. This will resolve the VTK dll's at run time.