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