Skip to content

Instantly share code, notes, and snippets.

@cmbruns
Last active Jan 13, 2022
Embed
What would you like to do?
Infinite plane shader version 2. See description at https://biospud.blogspot.com/2020/05/infinite-plane-rendering-2-texturing.html
#version 450 core
// Simplified vertex shader for rendering an infinite plane.
layout(location = 1) uniform mat4 projection = mat4(1);
layout(location = 2) uniform mat4 view = mat4(1);
layout(location = 3) uniform mat4 model = mat4(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0.3, 0, 1);
// simple but expensive 1: vertices cover the entire screen
// projected screen quad - this is our hard-coded "vertex buffer"
const vec4 screen_quad[4] = vec4[4](
vec4(-1, -1, 1, 1),
vec4( 1, -1, 1, 1),
vec4( 1, 1, 1, 1),
vec4(-1, 1, 1, 1));
const int triangle_strip[4] = int[4](
0, 1, 3, 2);
// Output coordinates of view/plane intersection in view space
out vec4 point;
out vec4 uhat;
out vec4 vhat;
out vec4 ndc;
vec3 dehomog(vec4 v)
{
return v.xyz/v.w;
}
void main()
{
vec4 corner_ndc = screen_quad[triangle_strip[gl_VertexID]];
// Screen-quad corner coordinates arrive dirctly in clip-space. Pass them through unchanged.
gl_Position = corner_ndc;
vec3 plane_translation_eye = dehomog(view * model * vec4(0, 0, 0, 1));
// Basis vectors for texture coordinates
uhat = view * model * vec4(1, 0, 0, 0);
vhat = view * model * vec4(0, 0, 1, 0);
// Pack uv translation into w-component of texture axes
uhat.w = -dot(plane_translation_eye, uhat.xyz);
vhat.w = -dot(plane_translation_eye, vhat.xyz);
// Notation from OpenGL super bible ray tracing section
vec4 D = inverse(projection) * corner_ndc;
vec4 N = view * model * vec4(0, 1, 0, 0);
float d = -dot(N.xyz, plane_translation_eye);
point = vec4(
-d * D.xyz, // xyz, numerator
dot(D.xyz, N.xyz) // w, denominator
);
ndc = projection * point;
}
#version 450 core
in vec4 point; // in view space
in vec4 uhat;
in vec4 vhat;
in vec4 ndc;
out vec4 frag_color;
void main()
{
// simple but expensive 2: discard pixels not on the plane
if (point.w > 0)
discard;
float u = dot(uhat.xyz, point.xyz/point.w) + uhat.w;
float v = dot(vhat.xyz, point.xyz/point.w) + vhat.w;
// Simple procedural texture test
frag_color = vec4(fract(u), fract(v), 0.5, 1);
gl_FragDepth = (ndc.z / ndc.w + 1.0) / 2.0;
}
@Peaj
Copy link

Peaj commented Jan 13, 2022

Thanks for your informative blog post. There is hardly any good information on rendering infinite planes out there.

I am trying to port the shader to Unity's Universal Render Pipeline as a full screen post effect (Render Feature).
The plane horizon and uv all work fine, but I cannot get to work the depthbbuffer.

I have the suspicion that it is somehow connected to NDC differences between OpenGL and DirectX but I am not sure.
This is my transformation from NDC to depth as DirectX should not require the offset as OpenGL does.

float planeDepth = input.ndc.z / input.ndc.w;

The resulting color is pretty much white (with a slight gradient) however.

It may also be caused by me using the cameras projection matrix which has a far plane.
But if I would use an infinite projection matrix it would not be possible to compare the plane and the scene depth would it?

Btw. I do not understand why D is in view space while every other vector used is translated to object/model space?
I would really appreciate any help with this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment