2

I'm maintaining a vertex shader encapsulated in a custom material class (inherited from ShaderMaterial but now from MeshStandardMaterial) which convert 3D coordinate to NDC as usual:

vec4 ndcPos = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
ndcPos /= ndcPos.w;
//...other transformations to ndcPos

then a set of transformations are applied to ndcPos. As you can see they are applied in NDC space. I need to take the result coordinate back to camera (eye) space so I guess we need to inverse the steps, something like this:

vec4 mvPos = ndcPos * ndcPos.w;
mvPos *= inverse of projectionMatrix;

Expected result: mvPos has only the modelView transformation applied.

Questions:

  1. Is that correct?
  2. How to compute inverse of projectionMatrix? It would be easy and low-cost passing the camera.projectionMatrixInverse as uniform to the vertex shader but I didn't find a way to do so in three.js: neither ancestor material classes, nor onBeforeCompile can access the camera.
Delmo
  • 2,188
  • 2
  • 20
  • 29

1 Answers1

3

The inverse operation to compute the view space position is

vec4 p = inverse(projectionMatrix) * ndc;
vec4 mvPos = p / p.w;

and the inverse operation to compute the object position is

vec4 p = inverse(projectionMatrix * modelViewMatrix) * ndc;
vec4 pos = p / p.w;

(Note that in the above code pos corresponds to the attribute position and mvPos corresponds to position * modelViewMatrix.)

Note: If you transform a Cartesian coordinate with a perspective projection matrix (or inverse perspective projection matrix), the result is a Homogeneous coordinate. To convert a Homogeneous coordinate into a Cartesian coordinate, you must perform a Perspective Divide (after the Perspective Divide, the component w is 1).
It should be mentioned that the inverse function exists only since GLSL ES 3.00 (WebGL 2.0) and is not available in GLSL ES 1.00 (WebGL 1.0). So it may be necessary to calculate the inverse matrix in Javascript and pass it as a uniform variable to the shader.

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • @Delmo Do you use [ShaderMaterial](https://threejs.org/docs/#api/en/materials/ShaderMaterial)? The documentation explains in detail how to create and set a uniform. Anyway, a GLSL shader is something highly optimized. All operations that can be precalculated are precalculated. The `inverse` of a matrix stored in an uniform variable is calculated only once. – Rabbid76 Aug 16 '22 at 14:21
  • @Delmo In one of your previous comments you said "any idea about how to pass `camera.projectionMatrixInverse` as uniform" so I'm surprised that you have no access to the camera. I can't see your javascript code, so I don't know how I can help. Do have a projection matrix? Anyway, voting an accepting is independent. It is completely up to you. – Rabbid76 Aug 16 '22 at 16:26
  • In my question #2 I wrote: **neither ancestor material classes, nor onBeforeCompile can access the camera** anyway my apologize if it was no clear enough but indeed the issue is not how to pass uniforms to shader but how to access the camera from my custom class to grab the `projectionMatrixInverse` property. I going to try just passing the camera as a parameter when create the custom material. – Delmo Aug 16 '22 at 16:30
  • @Delmo Please understand that no one can help you without seeing the code. However, you should only ask 1 question at once. This is a completely different question that relates to your javascript code. – Rabbid76 Aug 16 '22 at 16:32
  • I get it @Rabbid76. I initially thought that questions were related enough to ask them together and the code example seemed so trivial just `class MyCustomMaterial extends MeshStandardMaterial` but you are probably right, I will consider your advices for future questions. Thanks a lot for your patience. – Delmo Aug 16 '22 at 16:47