MEGA-challenged with per-vertex worldspace "ray dir", any gurus wanna co-investigate?

One of these things that should be so straightforward in theory but is killing me for 2 days without getting anywhere :smiley: cries

OK here’s the deal…

  • I have a simple “grid” triangle mesh that my script (based rougly on CreatePlane wiki script) is generating once at startup at some given resolution.
  • Generating this model as a “vertical” plane with center at 0,0,0 — so all z are 0, all x and y are between -1 and 1
  • This is identical to the view-space near-plane so by having my vertex shader do no transforms whatsoever (and ensuring the grid gameobject is always in front of the camera so it never gets culled) I have now this grid mesh being rendered exactly at the full width and height of the screenspace viewport. This is what I need to proceed, and this works fine. The grid mesh -1…1 fills exactly the full screen no matter how the camera moves or rotates, because there’s no transforms and the vertex shader outputs just the streaming-in vertex positions x,y=[-1…1].

For debug purposes, I’m generating the grid with somewhat random vertex colors so I can see it filling the screen and very blurrily (interpol.) can see its tesselation too.

Now here comes the bad tough challenge. I want to:

  • figure out exactly which world-space point the grid vertex is pointing to, whether the cam is looking forward or downwards or upwards
  • in an even bigger twist, I want that world-space position to reset its world-space Y coordinate to zero. Think the infinite virtual “ground plane” at worldPos.Y=0 with X and Z stretching from -infinity…+infinity (or nearPlane…farPlane?)
  • then transform this back into object-space (but need to keep the worldpos coord for later so I can’t skip the above) such that I can mul(MVP,objSpacePos) it
  • but not really the full MVP because that would take the grid gameobjects pos/rot/scale transform data into account which it shouldnt. Perhaps just the P of the MVP then?
  • Because even if the vert now has a worldspace ground-plane Y=0 position it’s just been relocated to, for this new virtual ground-plane to render correctly and interact with other geometry/zbuffer etc, we need to apply the current perspective-projection. But no model-view, just world-to-object so that none of the grid vertices ever gets clipped, even if half of them end up at the same horizon position or wherever…

Sounds kinda easy at first but tricky as hell and have tried many things that all didn’t work out!

To wrap up: imagine the infinite virtual ground-plane lying flat at world.Y=0. You have a screenspace-filling vertical-plane that’s effectively partitioning, at the near-plane, the view/cam/eye/clip-space box [-1…1] that the vertex-shader is operating in. I need to map each vertex onto only that part of the “virtual infinite ground-plane at world.Y=0” that is potentially visible (as per farplane projection) in front of the camera. As a result, after mul(mvp) this rendered grid blurry vertex-colored grid would visualize that ground-plane with each final output vertex position properly in place in terms of resulting clip-space coords (so that other geometry can occlude or be occluded by it).

Here’s the vertex-shader setup that works to show the grid fully covering the screen, our “ray-shooting matrix” if you will:

// bog standard
struct appdata {
	float4 vertex : POSITION;
	fixed4 color : COLOR;
};

// bog standard
struct v2f_surf {
	float4 pos : SV_POSITION;
	fixed4 color : COLOR;
};

// no transformations!
inline v2f_surf vert_surf(const in appdata v) {
	v2f_surf o;
	o.pos = v.vertex;
	o.color = v.color;
}

(Yes my vert/frag shaders still use the #pragma-debug’d Surface Shaders function/struct naming but make no mistake, this is a clean vert/frag with no SS interactions at this point.)

So the above works to render this fully-screenspace-filling “virtual projection plane” grid matrix thing. Now for the remapping to worldspace-virtual-ground-plane…

Oh tried so many things for this remapping, the latest was some logic that “totally worked” per-pixel in another screen-space post-process shader of mine (same logic, xy=-1…1, z=1, there’s our clipspace viewDir, transform to worldSpace) but doesn’t succeed here somehow. Well the grid always showing as a flat plane on the ground at world.Y=0 but it’s incredibly small instead of still filling the screenspace X from -1…1. Ouch, I give up!

// _MatInv is set via script as per next snippet just below this one
float4 worldPos = mul(_MatInv, float4(o.pos.xy, 1.0, 1.0));
worldPos.xyz /= worldPos.w;
worldPos.y = 0.0;
o.pos = mul(_World2Object, worldPos);
o.pos = mul(UNITY_MATRIX_MVP, o.pos);

Each frame btw my cam script is setting the following uniforms:

Shader.SetGlobalMatrix ("_MatCam", camMain.worldToCameraMatrix);
Shader.SetGlobalMatrix ("_MatWorld", camMain.cameraToWorldMatrix);
Shader.SetGlobalMatrix ("_MatProj", camMain.projectionMatrix);
Shader.SetGlobalMatrix ("_MatInv", (camMain.projectionMatrix * camMain.worldToCameraMatrix).inverse);

My brain is now officially fried! Wondering how long recovery will take this time… hopefully just a shower and a nap away, instead of a full-blown multi-week shading-holiday :smile: sheds-another-refractive-reflective-tear

I didnt understand what you want. Try to post a an image of what you require.
If you want to make a grid in screen space without geometry;

  • get worldPosition of each vertex (as you are already doing it)
  • get the eyevector: eyeVec = worldPosition - _WorldSpaceCameraPos;
  • get the difference between your plane level: diff = myPlaneLevel - worldPosition.y;
    now if diff is positive:
  • get the cameras depth from the planeLevel: cameraDepth = _WorldSpaceCameraPos.y - worldPosition.y;
  • the scalar: scale = (myPlaneLevel - _WorldSpaceCameraPos.y) / eyevector.y(you should normalize);
    and there you get the point of a surface: surfacePoint = _WorldSpaceCameraPos + eyevector * scale;
    aaand there you get projected texture coordinates on xz plane: texCoord = (surfacePoint.xz + eyevector .xz) * TextureScale;

for xy, yz planes, you know what to do.

Hope it helps.

I think I got somewhere now with ray-plane intersection so thanks for your attention :wink:

I still dont know what you are trying to do.
Here is another helper though, check the UnityShaderVariables.cginc file, there is the float4 unity_CameraWorldClipPlanes[6] variable there.

It’s all cool, I got it now :wink: would have to work out many various kinks only now apparent if I want to continue down this path, but the major pain point is solved!

What I tried and finally managed to do: reproject a vertical screen-filling grid/plane onto the (non-existing) “ground” right in front of the camera, with all verts filling out the entire frustum from near to far plane and “left” to “right” plane so to speak.

But with the shimmering I’m seeing I think I’ll go back from my per-vertex experiment to per-pixel screen-space… oh well :smile: