3
\$\begingroup\$

I am trying to render an infinite world floor grid, similar to this question.

My project is using SceneKit with Metal Shading Language but the concepts are no doubt similar between GLSL/HLSL.

Here is an example project where I attempt to render a plane as a full screen quad with the grid lines drawn using a fragment shader.

Full shader code can be found here.


Grid Rendering

A simple vertex shader sets the position and grid coordinate for the plane:

f.position = scn_node.modelViewProjectionTransform * float4(v.position, 1.0);
f.ij = v.position.xy;

The fragment shader then determines which color to render the grid or floor:

fragment half4 floor_fragment(FragmentIn f [[stage_in]]) {

    float2 fractional  = abs(fract(f.ij; + 0.5));
    float2 partial = fwidth(f.ij;);

    float2 point = smoothstep(-partial, partial, fractional);

    float saturation = 1.0 - saturate(point.x * point.y);

    return half4(mix(backgroundColor.rgb, gridColor.rgb, saturation), 1.0);
}

Floor shader applied to plane (cube added to help visualise rotation).

I then remove any MVP transforms and simply set the vertex position as is in clip space to fill the screen. This does have the intended effect of filling the screen but mangles the grid lines.

//inside floor_vertex()
f.position = float4(v.position, 1.0);
f.coordinate = v.position.xy;

Floor shader with no view projection transformations


Ray Casting

As suggested in the comments, the fragment position should be projected into world space and intersected with the floor plane to find a new vector on the plane. I have an intersect(plane:ray) method returns a hit with a distance and a point on the plane or otherwise false if the ray is orthogonal to the plane.

It is my understanding that by multiplying the position in clip space with the inverseViewProjectionTransform the result will be in camera space. The plane is also converted to camera space with the modelViewTransform and I then test my ray against this which appears to work but with incorrect results.

Floor shader with incorrect perspective

fragment half4 floor_fragment(Fragment f [[stage_in]], constant SCNSceneBuffer& scn_frame [[buffer(0)]], constant NodeBuffer& scn_node [[buffer(1)]]) {

    //f.ij is in the vertex position in clip space (-1, -1) to (1, 1)

    //convert position into camera space
    float4 position = (scn_frame.inverseViewProjectionTransform * float4(f.ij.x, f.ij.y, 0.0, 1.0));

    //create ray from camera with direction
    Ray ray = Ray { .origin = float3(0.0), .direction = normalize(position.xyz) };

    //convert floor plane from world space to camera space
    float3 worldFloor = (scn_node.modelViewTransform * float4(float3(0.0, -5.0, 0.0), 1.0)).xyz;

    //hit test ray against floor plane
    Plane plane = Plane { .position = worldFloor, .normal = float3(0.0, 1.0, 0.0) };

    RayHitTest hitTest = intersect(plane, ray);

    if(!hitTest.hit) {

        return f.backgroundColor;
    }

    //grab xz values to determine floor fragment color
    float2 uv = hitTest.vector.xz;

    float2 fractional  = abs(fract(uv + 0.5));
    float2 partial = fwidth(uv);

    float2 point = smoothstep(-partial, partial, fractional);

    float saturation = 1.0 - saturate(point.x * point.y);

    return half4(mix(f.backgroundColor.rgb, f.gridColor.rgb, saturation), 1.0);
}

This video shows the final results where you can see that the grid is not correctly aligned with the world axis. The floor plane is positioned at 0.0, -5.0, 0.0 and the rectangle at 0.0, -4.5, 0.0 which should be aligned with each other but this is not the case. It looks to me that the perspective is off as the grid seems much larger than is should be.

Can anyone please point out the obvious mistake(s) I am making?

\$\endgroup\$
  • \$\begingroup\$ You'll need to convert the rendered vertex position to a world-space ray through the camera, then intersect that ray with your floor plane to find its position in space. Note that this can shoot off to infinity when looking at your floor edge-on, so you'll want a plan for that case. \$\endgroup\$ – DMGregory May 5 at 18:15
  • \$\begingroup\$ @DMGregory thanks for your response. Would you be able to elaborate on this a little further? I am using orthographic projection so the edge-on intersection is probably not applicable? \$\endgroup\$ – CaptainRedmuff May 6 at 13:39
  • \$\begingroup\$ Not applicable? Riddle me this: What is the orthographic projection of a plane, from the perspective of a camera on that plane, looking at a target also on that plane? \$\endgroup\$ – DMGregory May 6 at 13:44
  • \$\begingroup\$ Apologies if I have misunderstood your comment. Thank you for taking the time to reply. In calculating the intersecting ray with the floor plane, I presume that clipping this against the far plane of the camera would be sufficent for dealing with infinite values? \$\endgroup\$ – CaptainRedmuff May 6 at 15:01
  • \$\begingroup\$ I made an infinite grid by world space shader in unity can I send the answer? \$\endgroup\$ – Seyed Morteza Kamali May 18 at 9:45
1
\$\begingroup\$

I used world space shader to draw Infinite grid

sorry I haven't enough time to convert this to glsl but I hope it will help you

first you should draw a square in shader:

enter image description here

then you can use fraction to repeat the square pattern to make a grid

enter image description here

it seems good but it isn't infinite!

enter image description here

to make it infinite you can use world space shader

enter image description here

nice!

enter image description here

you can make your own grid

enter image description here

here's simple glsl shader: you can tweak it in shadertoy

float size = 5.;

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord/iResolution.xy;
    vec2 tiledUV = fract(uv*size);
    vec2 square = abs(tiledUV*2.-1.);
    vec2 sharpSquare = step(0.8,square);
    float result = sharpSquare.x+sharpSquare.y;

    fragColor = vec4(1.0)*result;
}
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Many thanks for your response. Would you be able to comment your DrawGrid method or describe what it is doing? It looks like you have a property in there that is unused. \$\endgroup\$ – CaptainRedmuff May 20 at 8:20
  • \$\begingroup\$ @CaptainRedmuff ok I will comment it as soon as possible \$\endgroup\$ – Seyed Morteza Kamali May 20 at 12:03
  • \$\begingroup\$ @CaptainRedmuff I edited the answer. is it understandable? \$\endgroup\$ – Seyed Morteza Kamali May 20 at 13:27
  • \$\begingroup\$ @CaptainRedmuff I made a glsl shader in shadertoy for you shadertoy.com/view/tsjfDh \$\endgroup\$ – Seyed Morteza Kamali May 20 at 13:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.