ospray / hdospray

Rendering plugin for Pixar's USD Hydra
Apache License 2.0
115 stars 14 forks source link

Depth AOV Is Euclidean Camera Distance Rather Than "Traditional" Image Plane Distance #22

Open SimonCSmith opened 1 month ago

SimonCSmith commented 1 month ago

The depth buffer AOV records the depth to the camera rather than what most depth aov's record, which is the depth from the image plane. I think it might be reasonable/expected that hdospray followed this convention when exposing its depth aov through hydra.

This can cause issues if you want to use the depth plane and the camera projection matrix in Hydra to get a world position from an image plane position and the depth value.

To reproduce, put a plane in front of the camera at a known distance, then check out the depth values in the AOV. They will vary across its surface rather than be constant.

carsonbrownlee commented 1 month ago

After looking more into it, it turns out that there already is a mode in hdospray that is supposed to return the depth buffer projected along camera z in renderPass.cpp:826. It appears to be a bit off. Working on fixing this.

carsonbrownlee commented 1 month ago

I believe I have fixed the issue, a deceptively simple one line fix which I'll add into the next release. Let me know if this resolves it:

diff --git a/hdOSPRay/renderPass.cpp b/hdOSPRay/renderPass.cpp
index b8e6e2f..5cef0a0 100644
--- a/hdOSPRay/renderPass.cpp
+++ b/hdOSPRay/renderPass.cpp
@@ -817,7 +817,7 @@ HdOSPRayRenderPass::_CopyFrameBuffer(
                             GfVec3f dir = _inverseProjMatrix.Transform(pos);
                             GfVec3f origin = GfVec3f(0, 0, 0);
                             origin = _inverseViewMatrix.Transform(origin);
-                            dir = -_inverseViewMatrix.Transform(dir)
+                            dir = _inverseViewMatrix.TransformDir(dir)
                                           .GetNormalized();
                             float& d = depth[static_cast<int>(y * w + x)];
                             GfVec3f hit = origin + dir * d;
carsonbrownlee commented 1 month ago

screenshot of hdOSPRay (left) vs hdEmbree (right) depth buffer for visual verification. A quantitative test would be better, we can run that if you are getting odd results.

Screenshot from 2024-05-17 10-22-50

SimonCSmith commented 1 month ago

This fix does result in the depth values now reflecting the distance to the imaging plane, and match what other delegates return on the same scene.

I want to note though that the world depth values across a flat surface do vary and lead to world camera/hit distances fluctuating by around 1% in my test scene (49.81 to 50.30) which I do not see under other delegates using the same aov/calculations such as RenderMan, which gives a solid reading of 50.03 across the face.

johguenther commented 1 month ago

I suspect the variance is because the conversion happens in hdOSPRay, assuming the rays go through the pixel center (which they don't for anti-aliasing). This should be solve once the projected depth to the image-plane is directly calculated by OSPRay.