Skip to content

Commit

Permalink
Add custom shader option (#1474)
Browse files Browse the repository at this point in the history
  • Loading branch information
Meakk authored Jun 24, 2024
1 parent aa0007b commit 333c320
Show file tree
Hide file tree
Showing 22 changed files with 428 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .lsan.supp
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ leak:libtbb
# forces us to hide all leaks from the tests using a render window
# https://gitlab.kitware.com/vtk/vtk/-/issues/18504
leak:libf3dSDKTests
leak:VTKExtensionsRenderingTests
leak:vtkextPrivateTests
11 changes: 9 additions & 2 deletions _config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -126,18 +126,25 @@ defaults:

-
scope:
path: "doc/user/DESKTOP_INTEGRATION.md"
path: "doc/user/FINAL_SHADER.md"
values:
parent: User Documentation
nav_order: 7

-
scope:
path: "doc/user/DESKTOP_INTEGRATION.md"
values:
parent: User Documentation
nav_order: 8

-
scope:
path: "doc/user/LIMITATIONS_AND_TROUBLESHOOTING.md"
values:
title: Limitations and Troubleshooting
parent: User Documentation
nav_order: 8
nav_order: 9

# libf3d doc
-
Expand Down
1 change: 1 addition & 0 deletions application/F3DOptionsParser.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,7 @@ void ConfigurationOptions::GetOptions(F3DAppOptions& appOptions, f3d::options& o
this->DeclareOption(grp6, "ambient-occlusion", "q", "Enable ambient occlusion providing approximate shadows for better depth perception, implemented using SSAO", options.getAsBoolRef("render.effect.ambient-occlusion"), HasDefault::YES, MayHaveConfig::YES);
this->DeclareOption(grp6, "anti-aliasing", "a", "Enable anti-aliasing, implemented using FXAA", options.getAsBoolRef("render.effect.anti-aliasing"), HasDefault::YES, MayHaveConfig::YES);
this->DeclareOption(grp6, "tone-mapping", "t", "Enable Tone Mapping, providing balanced coloring", options.getAsBoolRef("render.effect.tone-mapping"), HasDefault::YES, MayHaveConfig::YES);
this->DeclareOption(grp6, "final-shader", "", "Execute the final shader at the end of the rendering pipeline", options.getAsStringRef("render.effect.final-shader"), LocalHasDefaultNo, MayHaveConfig::YES, "<GLSL code>");

auto grp7 = cxxOptions.add_options("Testing");
this->DeclareOption(grp7, "ref", "", "Reference", appOptions.Reference, LocalHasDefaultNo, MayHaveConfig::YES, "<png file>");
Expand Down
1 change: 1 addition & 0 deletions application/F3DOptionsParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ struct F3DAppOptions
double CameraZoomFactor = 0.0;
double CameraViewAngle = 0.0;
std::vector<std::string> Plugins;
std::string FinalShader = "";
};

class F3DOptionsParser
Expand Down
4 changes: 4 additions & 0 deletions application/testing/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,10 @@ if(F3D_PLUGIN_BUILD_ALEMBIC AND F3D_PLUGIN_BUILD_ASSIMP)
f3d_test(NAME TestMultiplePluginsLoad DATA cow.vtp ARGS --load-plugins=assimp,alembic NO_BASELINE REGEXP_FAIL "Plugin failed to load")
endif()

f3d_test(NAME TestFinalShaderNegative DATA cow.vtp ARGS --final-shader "vec4 pixel(vec2 uv){return vec4(vec3(1.0) - texture(source, uv).rgb, 1.0)\\\\\\\;}" DEFAULT_LIGHTS)
f3d_test(NAME TestFinalShaderUndefined DATA cow.vtp ARGS --final-shader "undefined" REGEXP "Final shader must define a function" NO_BASELINE)
f3d_test(NAME TestFinalShaderCompilationFailure DATA cow.vtp ARGS --final-shader "vec4 pixel(vec2 uv){}" --verbose REGEXP " build the shader program" NO_BASELINE)

## Interaction Tests
# Test hotkeys
f3d_test(NAME TestInteractionPostFX DATA cow.vtp INTERACTION DEFAULT_LIGHTS) #PQAT
Expand Down
1 change: 1 addition & 0 deletions doc/libf3d/OPTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ render.effect.translucency-support|bool<br>false<br>render|Enable *translucency
render.effect.anti-aliasing|bool<br>false<br>render|Enable *anti-aliasing*. This technique is used to reduce aliasing, implemented using FXAA.|\-\-anti-aliasing
render.effect.ambient-occlusion|bool<br>false<br>render|Enable *ambient occlusion*. This is a technique providing approximate shadows, used to improve the depth perception of the object. Implemented using SSAO|\-\-ambient-occlusion
render.effect.tone-mapping|bool<br>false<br>render|Enable generic filmic *Tone Mapping Pass*. This technique is used to map colors properly to the monitor colors.|\-\-tone-mapping
render.effect.final-shader|string<br>""<br>render|Add a final shader to the output image|\-\-final-shader. See [user documentation](../user/FINAL_SHADER.md).
render.line-width|double<br>1.0<br>render|Set the *width* of lines when showing edges.|\-\-line-width
render.show-edges|bool<br>false<br>render|Show the *cell edges*|\-\-edges
render.point-size|double<br>10.0<br>render|Set the *size* of points when showing vertices and point sprites.|\-\-point-size
Expand Down
75 changes: 75 additions & 0 deletions doc/user/FINAL_SHADER.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Final shaders

It is possible to specify a final full screen shader with the option `--final-shader`.
It is executed as the last shader in the rendering pipeline.

The shader code can be passed directly as a single code line.
It's possible to pass the content of a shader file using the following command:

```sh
f3d model.obj --final-shader "$(cat final.glsl)"
```

Note that the command above works on Linux and bash, and should be adapted depending on the operating system and the shell/terminal used.

## Requirements

It is required to implement the function `vec4 pixel(vec2 uv)`.
The value `uv` is ranging from 0 to 1 in both directions. `(0,0)` is the bottom left corner, and `(1,1)` is the upper right corner.
The first component is the horizontal direction.

## Uniforms

It is possible to access these uniforms:
- `source` (type: `sampler2d`): texture of the image generated by rendering pipeline.
- `resolution` (type: `ivec2`): resolution of the texture `source`.

## Examples

Here are three shader examples to illustrate how an implementation looks like.

### Negative

```glsl
vec4 pixel(vec2 uv)
{
vec3 value = texture(source, uv).rgb;
return vec4(vec3(1.0) - value, 1.0);
}
```

### Vignette

```glsl
vec4 pixel(vec2 uv)
{
float l = clamp(1.0 - 2.0 * length(uv - 0.5), 0.0, 1.0);
return vec4(l*texture(source, uv).rgb, 1.0);
}
```

### Box blur

```glsl
vec4 pixel(vec2 uv)
{
const float radius = 0.03;
const int samples = 20;
const float step = radius / float(samples);
vec3 sum = vec3(0);
for (int i = -samples; i <= samples; i++)
{
for (int j = -samples; j <= samples; j++)
{
sum += texture(source, uv + vec2(i, j) * step).rgb;
}
}
float d = 1.0 + 2.0 * float(samples);
sum /= d * d;
return vec4(sum, 1.0);
}
```
1 change: 1 addition & 0 deletions doc/user/OPTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ Options|Description
-q, \-\-ambient-occlusion|Enable *ambient occlusion*. This is a technique used to improve the depth perception of the object.
-a, \-\-anti-aliasing|Enable *anti-aliasing*. This technique is used to reduce aliasing.
-t, \-\-tone-mapping|Enable generic filmic *Tone Mapping Pass*. This technique is used to map colors properly to the monitor colors.
\-\-final-shader|Add a final shader to the output image. See [dedicated documentation](FINAL_SHADER.md) for more details.

## Testing options

Expand Down
1 change: 1 addition & 0 deletions doc/user/README_USER.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- [The different interactions in F3D.](INTERACTIONS.md)
- [How to use animations in F3D.](ANIMATIONS.md)
- [How to use colormaps in F3D.](COLOR_MAPS.md)
- [How to a use custom final shader in F3D.](FINAL_SHADER.md)
- [How to configure F3D using a configuration file.](CONFIGURATION_FILE.md)
- [How to integrate F3D in your desktop.](DESKTOP_INTEGRATION.md)
- [Limitations and troubleshooting of F3D.](LIMITATIONS_AND_TROUBLESHOOTING.md)
1 change: 1 addition & 0 deletions library/src/options.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ options::options()
this->Internals->init("render.effect.anti-aliasing", false);
this->Internals->init("render.effect.ambient-occlusion", false);
this->Internals->init("render.effect.tone-mapping", false);
this->Internals->init("render.effect.final-shader", std::string());

this->Internals->init("render.hdri.file", std::string());
this->Internals->init("render.hdri.ambient", false);
Expand Down
2 changes: 2 additions & 0 deletions library/src/window_impl.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,8 @@ void window_impl::UpdateDynamicOptions()
this->Internals->Options.getAsBool("render.effect.translucency-support"));
this->Internals->Renderer->SetBackfaceType(
this->Internals->Options.getAsString("render.backface-type"));
this->Internals->Renderer->SetFinalShader(
this->Internals->Options.getAsString("render.effect.final-shader"));

this->Internals->Renderer->SetBackground(
this->Internals->Options.getAsDoubleVector("render.background.color").data());
Expand Down
1 change: 1 addition & 0 deletions library/testing/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ if(VTK_VERSION VERSION_GREATER 9.0.1)
list(APPEND libf3dSDKTests_list
TestSDKCompareWithFile.cxx
TestSDKDropZone.cxx
TestSDKRenderFinalShader.cxx
TestSDKImage.cxx
TestSDKLoadFromMemory.cxx
TestSDKMultiColoring.cxx
Expand Down
57 changes: 57 additions & 0 deletions library/testing/TestSDKRenderFinalShader.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#include <engine.h>
#include <loader.h>
#include <options.h>
#include <window.h>

#include "TestSDKHelpers.h"

int TestSDKRenderFinalShader(int argc, char* argv[])
{
f3d::engine eng(f3d::window::Type::NATIVE_OFFSCREEN);

f3d::window& win = eng.getWindow();
win.setSize(300, 300);

f3d::loader& load = eng.getLoader();
load.loadGeometry(std::string(argv[1]) + "/data/cow.vtp");

win.render();

const std::string negativeShader = R"=(
vec4 pixel(vec2 uv)
{
vec3 value = texture(source, uv).rgb;
return vec4(vec3(1.0) - value, 1.0);
}
)=";

const std::string vignetteShader = R"=(
vec4 pixel(vec2 uv)
{
float l = clamp(1.0 - 2.0 * length(uv - 0.5), 0.0, 1.0);
return vec4(l*texture(source, uv).rgb, 1.0);
}
)=";

f3d::options& options = eng.getOptions();
options.set("render.effect.final-shader", negativeShader);

if (!TestSDKHelpers::RenderTest(win, std::string(argv[1]) + "baselines/", std::string(argv[2]),
"TestSDKRenderFinalShaderNegative", 50))
{
std::cerr << "Negative shader failure";
return EXIT_FAILURE;
}

// change the shader to test the recompilation is triggered
options.set("render.effect.final-shader", vignetteShader);

if (!TestSDKHelpers::RenderTest(win, std::string(argv[1]) + "baselines/", std::string(argv[2]),
"TestSDKRenderFinalShaderVignette", 50))
{
std::cerr << "Vignette shader failure";
return EXIT_FAILURE;
}

return EXIT_SUCCESS;
}
3 changes: 3 additions & 0 deletions testing/baselines/TestFinalShaderNegative.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions testing/baselines/TestSDKRenderFinalShaderNegative.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions testing/baselines/TestSDKRenderFinalShaderVignette.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions vtkext/private/module/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ set(classes
vtkF3DRenderPass
vtkF3DRenderer
vtkF3DRendererWithColoring
vtkF3DUserRenderPass
)

# Needs https://gitlab.kitware.com/vtk/vtk/-/merge_requests/10675
Expand Down
26 changes: 26 additions & 0 deletions vtkext/private/module/Testing/TestF3DRenderPass.cxx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
#include <vtkNew.h>
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <vtkTestUtilities.h>

#include "vtkF3DHexagonalBokehBlurPass.h"
#include "vtkF3DRenderPass.h"
#include "vtkF3DUserRenderPass.h"

#include <iostream>

Expand All @@ -12,6 +15,7 @@ int TestF3DRenderPass(int argc, char* argv[])
pass->Print(std::cout);

vtkNew<vtkF3DHexagonalBokehBlurPass> bokeh;
bokeh->SetDelegatePass(pass);
bokeh->Print(std::cout);

bokeh->SetCircleOfConfusionRadius(30.f);
Expand All @@ -21,5 +25,27 @@ int TestF3DRenderPass(int argc, char* argv[])
return EXIT_FAILURE;
}

vtkNew<vtkF3DUserRenderPass> user;
user->SetDelegatePass(bokeh);
user->Print(std::cout);

vtkNew<vtkRenderer> renderer;
renderer->SetPass(user);

vtkNew<vtkRenderWindow> renWin;
renWin->AddRenderer(renderer);
renWin->OffScreenRenderingOn();

// set valid shader
user->SetUserShader("vec4 pixel(vec2 uv) { return vec4(uv, 0.0, 1.0); }");
renWin->Render();

// change to another valid shader
user->SetUserShader("vec4 pixel(vec2 uv) { return vec4(uv, 1.0, 1.0); }");
renWin->Render();

// render a second time to check if recompilation skipping is working
renWin->Render();

return EXIT_SUCCESS;
}
30 changes: 30 additions & 0 deletions vtkext/private/module/vtkF3DRenderer.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "vtkF3DDropZoneActor.h"
#include "vtkF3DOpenGLGridMapper.h"
#include "vtkF3DRenderPass.h"
#include "vtkF3DUserRenderPass.h"

#include <vtkAxesActor.h>
#include <vtkBoundingBox.h>
Expand Down Expand Up @@ -311,6 +312,25 @@ void vtkF3DRenderer::ConfigureRenderPasses()
renderingPass = fxaaP;
}

if (!this->FinalShader.empty())
{
// basic validation
if (this->FinalShader.find("pixel") != std::string::npos)
{
vtkNew<vtkF3DUserRenderPass> userP;
userP->SetUserShader(this->FinalShader.c_str());
userP->SetDelegatePass(renderingPass);

this->SetPass(userP);
renderingPass = userP;
}
else
{
F3DLog::Print(F3DLog::Severity::Warning,
"Final shader must define a function named \"pixel\"");
}
}

this->SetPass(renderingPass);

#if F3D_MODULE_RAYTRACING
Expand Down Expand Up @@ -1148,6 +1168,16 @@ void vtkF3DRenderer::SetUseSSAOPass(bool use)
}
}

//----------------------------------------------------------------------------
void vtkF3DRenderer::SetFinalShader(const std::string& finalShader)
{
if (this->FinalShader != finalShader)
{
this->FinalShader = finalShader;
this->RenderPassesConfigured = false;
}
}

//----------------------------------------------------------------------------
void vtkF3DRenderer::SetUseFXAAPass(bool use)
{
Expand Down
2 changes: 2 additions & 0 deletions vtkext/private/module/vtkF3DRenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class vtkF3DRenderer : public vtkOpenGLRenderer
void SetBlurCircleOfConfusionRadius(double radius);
void SetRaytracingSamples(int samples);
void SetBackfaceType(const std::string& backfaceType);
void SetFinalShader(const std::string& finalShader);
///@}

///@{
Expand Down Expand Up @@ -328,6 +329,7 @@ class vtkF3DRenderer : public vtkOpenGLRenderer
std::string AnimationNameInfo;

std::string BackfaceType;
std::string FinalShader;
};

#endif
Loading

0 comments on commit 333c320

Please sign in to comment.