Custom Shader Implementation for Floor Plane Grid

I’m currently working on a project where I’m trying to implement a floor plane grid using a custom vertex and fragment shader. I came across a helpful example and decided to adapt it for my use in VTK.

A couple questions I have:

  • Are the points defined in vtkPoints correctly passed as the vertexAttribute “a_position” ?
  • I attempted to set the uniforms using the VTKShaderProperty of the actor object, but I’m not entirely sure if this is the correct approach.
  • Likewise I attempted to set the shader code using “SetVertexShaderCode” and “SetFragmentShaderCode” on the ShaderPropery object. is this correct?

The code i tried:

#include <iostream>
#include <array>

#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL2) // VTK was built with vtkRenderingOpenGL2
VTK_MODULE_INIT(vtkInteractionStyle)
VTK_MODULE_INIT(vtkRenderingFreeType)

// VTK includes
#include <vtkActor.h>
#include <vtkCamera.h>
#include <vtkPolyData.h>
#include <vtkPoints.h>
#include <vtkNew.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkInteractorStyleTrackballCamera.h>
#include "vtkMatrix4x4.h"
#include <vtkShaderProperty.h>
#include <vtkUniforms.h>

using namespace std;

const char * vertexShaderCode = "#version 150 \
\
    uniform mat4 u_view_matrix; \
    uniform mat4 u_projection_matrix; \
\
    uniform mat4 u_view_matrix_inv;\
    uniform mat4 u_projection_matrix_inv;\
\
    uniform float u_grid_spacing;\
\
    in vec3 a_position;\
\
    out vec3 nearPoint;\
    out vec3 farPoint;\
\
    vec3 UnprojectPoint(float x, float y, float z) {\
        vec4 unprojectedPoint =  u_view_matrix_inv * u_projection_matrix_inv * vec4(x, y, z, 1.0);\
        return unprojectedPoint.xyz / unprojectedPoint.w;\
}\
\
    void main() {\
        vec3 p = a_position;\
        nearPoint = UnprojectPoint(p.x, p.y, 0.0).xyz; // unprojecting on the near plane\
        farPoint = UnprojectPoint(p.x, p.y, 1.0).xyz; // unprojecting on the far plane\
        gl_Position = vec4(p, 1.0); // using directly the clipped coordinates\
    }";

const char * fragmentShaderCode = "#version 150 \
 \
    in vec3 nearPoint; \
    in vec3 farPoint; \
 \
    uniform mat4 u_view_matrix; \
    uniform mat4 u_projection_matrix; \
 \
    uniform mat4 u_view_matrix_inv; \
    uniform mat4 u_projection_matrix_inv; \
 \
    uniform float u_grid_spacing; \
 \
    out vec4 f_color; \
 \
    vec4 grid(vec3 fragPos3D, float scale) { \
        vec2 coord = fragPos3D.xz * scale * 1/u_grid_spacing ; \
 \
        // anti aliasing using screen-space partial deribatives \
        vec2 derivative = fwidth(coord); \
        vec2 grid = abs(fract(coord - 0.5) - 0.5) / derivative; \
        float line = min(grid.x, grid.y); \
        float minimumz = min(derivative.y, 1); \
        float minimumx = min(derivative.x, 1); \
        vec4 color = vec4(0.2, 0.2, 0.2, 1.0 - min(line, 1.0)); \
 \
        float axis_line_width = u_grid_spacing * 10; \
 \
 \
 \
        return color; \
} \
 \
    float computeDepth(vec3 pos) { \
        float far=gl_DepthRange.far; float near=gl_DepthRange.near; \
        vec4 clip_space_pos = u_projection_matrix * u_view_matrix * vec4(pos.xyz, 1.0); \
        float ndc_depth = clip_space_pos.z / clip_space_pos.w; \
        float depth = (((far-near) * ndc_depth) + near + far) / 2.0; \
        return depth; \
} \
 \
    float computeLinearDepth(vec3 pos) { \
        float near = 0.01; \
        float far = 100; \
        vec4 clip_space_pos = u_projection_matrix * u_view_matrix * vec4(pos.xyz, 1.0); \
        float clip_space_depth = (clip_space_pos.z / clip_space_pos.w) * 2.0 - 1.0; // put back between -1 and 1 \
        float linearDepth = (2.0 * near * far) / (far + near - clip_space_depth * (far - near)); // get linear value between 0.01 and 100  \
        return linearDepth / far; // normalize  \
} \
 \
    void main(void) { \
        float t = -nearPoint.y / (farPoint.y - nearPoint.y); \
        vec3 fragPos3D = nearPoint + t * (farPoint - nearPoint); \
 \
        gl_FragDepth = computeDepth(fragPos3D); \
 \
        float gridFadeSpeed = 16; \
        float linearDepth = computeLinearDepth(fragPos3D); \
        float fading = max(0, (0.5 - linearDepth * gridFadeSpeed)); \
 \
        f_color =  grid(fragPos3D, 1, true); \
        f_color += grid(fragPos3D, 0.1, true); \
 \
        f_color.a *= fading; \
}";

int main()
{

    // A renderer and render window.
    vtkNew<vtkRenderer> renderer;
    vtkNew<vtkRenderWindow> renderWindow;
    renderWindow->AddRenderer(renderer);
    renderWindow->SetWindowName("Grid Example");

    // An interactor
    vtkNew<vtkRenderWindowInteractor> renderWindowInteractor;
    renderWindowInteractor->SetRenderWindow(renderWindow);

    renderWindow->Render();

    vtkNew<vtkInteractorStyleTrackballCamera> style;
    renderWindowInteractor->SetInteractorStyle(style);

    vtkNew<vtkPoints> points;
    points->InsertNextPoint( 1,  1, 0);
    points->InsertNextPoint(-1, -1, 0);
    points->InsertNextPoint(-1,  1, 0);
    points->InsertNextPoint(-1, -1, 0);
    points->InsertNextPoint( 1,  1, 0);
    points->InsertNextPoint( 1, -1, 0);

    vtkNew<vtkPolyData> grid;
    grid->SetPoints(points);

    vtkNew<vtkPolyDataMapper> mapper;
    mapper->SetInputData(grid);

    vtkSmartPointer<vtkActor> gridActor = vtkSmartPointer<vtkActor>::New();
    gridActor->SetMapper(mapper);

    vtkCamera* camera = renderer->GetActiveCamera();
    vtkMatrix4x4* viewMatrix = camera->GetViewTransformMatrix();
    vtkMatrix4x4* viewMatrixInverted = vtkMatrix4x4::New();
    vtkMatrix4x4::Invert(viewMatrix, viewMatrixInverted);

    float* viewMatrixArr = new float[16];
    float* viewMatrixArrInv = new float[16];
    for (size_t i=0;i<16;++i) viewMatrixArr[i] = static_cast<float>(viewMatrix->GetData()[i]);
    for (size_t i=0;i<16;++i) viewMatrixArrInv[i] = static_cast<float>(viewMatrixInverted->GetData()[i]);


    vtkMatrix4x4* projMatrix = camera->GetProjectionTransformMatrix(renderer);
    vtkMatrix4x4* projMatrixInverted = vtkMatrix4x4::New();
    vtkMatrix4x4::Invert(projMatrix, projMatrixInverted);

    float* projMatrixArr = new float[16];
    float* projMatrixInvArr = new float[16];
    for (size_t i=0;i<16;++i) projMatrixArr[i] = static_cast<float>(projMatrix->GetData()[i]);
    for (size_t i=0;i<16;++i) projMatrixInvArr[i] = static_cast<float>(projMatrixInverted->GetData()[i]);

    vtkShaderProperty* sp = gridActor->GetShaderProperty();

    sp->GetVertexCustomUniforms()->SetUniformMatrix4x4("u_view_matrix", viewMatrixArr);
    sp->GetVertexCustomUniforms()->SetUniformMatrix4x4("u_projection_matrix", viewMatrixArrInv);
    sp->GetVertexCustomUniforms()->SetUniformMatrix4x4("u_view_matrix_inv", viewMatrixArr);
    sp->GetVertexCustomUniforms()->SetUniformMatrix4x4("u_projection_matrix_inv", projMatrixInvArr);
    sp->GetVertexCustomUniforms()->SetUniformf("u_grid_spacing", 10.0f);

    sp->SetVertexShaderCode(vertexShaderCode);
    sp->SetFragmentShaderCode(fragmentShaderCode);

    gridActor->GetProperty()->ShadingOn();

    // Add the actors to the scene.
    renderer->AddActor(gridActor);

    // Begin mouse interaction.
    renderWindowInteractor->Start();

    return EXIT_SUCCESS;
}

This is how the final result should look.

Could you please provide insights into the recommended method for achieving this? Your guidance would be greatly appreciated.

Hi @Ruben_Moermans

It looks nice already!

In any case, please find here a VTK example of an infinite grid:

And result :

image

1 Like

Unfortunately the code of F3D is not the same approach… its a finite grid with round fading to fake the effect.

1 Like

Hi everyone,

I wanted to provide an update regarding the custom shader implementation for the floor plane grid that I discussed earlier in this thread. Based on the interest and questions I’ve received about the grid, I decided to push the code to a Git repository for easier access and collaboration.

You can find the repository here: https://github.com/moermansr/VTKOpenGLGrid. It contains the custom mapper for the grid along with an example implementation. It is based on the F3D example but with different shader code. At first glance, the grid seems to work as intended. However, I’ve noticed an issue where the grid sometimes disappears when moving the camera.

If anyone has insights into why this might be happening or would like to take a closer look at the implementation, feel free to check out the repository. I’d greatly appreciate any feedback or suggestions on how to address this issue.

Thanks in advance to anyone who takes the time to check this out!

Have you looked at the vtkSkybox::SetProjectiontoFloor API? TestFloor shows an example of using it to render an infinite floor grid.

Thank you for bringing up the vtkSkybox example; I wasn’t aware of this solution before. However, when I tried it, I found it quite challenging to control aspects like grid line width, grid spacing (as using values other than powers of 2 creates the appearance of overlapping grids), and fade distance. I rendered both the vtkSkybox (left), my current shader solution (right) examples using the same camera settings with a grid spacing of 10,10, and here is the result.