toji / gl-matrix

Javascript Matrix and Vector library for High Performance WebGL apps
glmatrix.net
MIT License
5.36k stars 720 forks source link

Handle projection matrices for WebGPU-style depth ranges #369

Open magcius opened 4 years ago

magcius commented 4 years ago

See: immersive-web/webxr#894, gpuweb/gpuweb#416

Currently, gl-matrix is modelled after GLM, which uses OpenGL's clip space defined with a Z range of -1...1. This is in contrast with other graphics APIs, including Direct3D 12, Vulkan, Metal, and WebGPU, which all use a depth range of 0...1.

This could cause an issue when users naively port their WebGL-using code to WebGPU. It will appear like the near plane is much further in than it is, as anything less than 0 will be clipped.

It might make sense for functions like mat4.perspective, mat4.frustum, mat4.ortho, etc. to all support a clip-space of 0...1, either as an argument, a global setting (e.g. mat4.useWebGPUClipSpace = true), separate functions (e.g. mat4.frustumWebGPU()), or as functions to convert between the two for a typical projection matrix. For the lattermost, see this, which I have implemented in my project: https://github.com/magcius/noclip.website/blob/master/src/gfx/helpers/ProjectionHelpers.ts

GLM actually supports 0...1 clip space with preprocessor defines, which is not the most adaptable choice for a JS library.

At the same time, it might be interesting to think about designing an API to handle reverse-depth projection matrices as well.

Thoughts?

stefnotch commented 4 years ago

I fully agree that gl-matrix should support this. Now, my thoughts as to how that support should look:

global setting

I think that'd be a footgun. The current global settings do not significantly alter the results of the calculations, a depth range global setting on the other hand would.

argument

How hard would it be to be able to specify the depth range as an argument? If this option is chosen, there is another choice that has to be made, which is whether gl-matrix should break backwards compatibility or have default values for the depth range.

separate functions

This is a pretty simple option that won't break backwards compatibility.

functions to convert between the two

This would be a quite neat option, but it's not as performant as the other ones.

magcius commented 4 years ago

I think that'd be a footgun. The current global settings do not significantly alter the results of the calculations, a depth range global setting on the other hand would.

I mention it because it's "easy" (set it once and forget it), and it's what GLM did.

How hard would it be to be able to specify the depth range as an argument? If this option is chosen, there is another choice that has to be made, which is whether gl-matrix should break backwards compatibility or have default values for the depth range.

I think it might make sense to have a flags argument, e.g.

mat4.frustum(dst, left, right, top, bottom, near, far, mat4.REVERSED_DEPTH | mat4.Z_RANGE_0_1);

Bikeshed over your favorite flag names. The defaults should be today's defaults ("standard" non-reversed depth with a -1...1 clip space)

This would be a quite neat option, but it's not as performant as the other ones.

Right. It might make sense as a utility function, but it's also easy enough to code up yourself (basically just a mat4.mul). I've used engines before that do all their math in one common space, and then have a hook inside the platform layer to convert to the platform's space, and it's what I'm investigating in my own project as well.

stefnotch commented 4 years ago

@toji What are your thoughts regarding this?

NoxWings commented 4 years ago

I'd love to have an option for reverse depth. I just came to the issues looking for info on that bit.

toji commented 4 years ago

I definitely think that the library should support these situations. @magcius' flag proposal seems very clean, and I think would be a reasonable path forward. In terms of global flags, it may be prudent to have the default set of flags for the frustum/projection functions defined as a const in common.js. At that point it's not a part of the public API to set the global state but a motivated developer who is making a WebGPU-only app could intentionally change the global behavior to suit their needs.

AshleyScirra commented 11 months ago

glMatrix is still missing frustumZO (#441). Is a workaround to use the standard frustum method, and then call the projectionMatrixD3DFromOpenGL method on the result as shown here?

AshleyScirra commented 10 months ago

Answering my own question: it turns out the answer is yes, which gives us a workaround for #441.