7
$\begingroup$

Consider this simple 3D rendering with some geometric primitives:

Graphics3D[{Cylinder[{{-1, -1, 0}, {-1, -1, 1}}, .42], Cuboid[], 
  Polygon[{{-2, -2, 0}, {2, -2, 0}, {2, 2, 0}, {-2, 2, 0}}], 
  Sphere[{.3, -.6, .5}, .5]}, Boxed -> False]

How can I get the depth of each pixel of the rendering, i.e. the distance of the corresponding point to the camera?

Edit: The required depth image would be something like the OpenGL z-buffer. Vertices alone are not enough - I need the depth of every pixel in the rendered image.

$\endgroup$
4
  • $\begingroup$ I have two applications: One where there are half a million (full image) and one with around 1000 (test points). $\endgroup$
    – Danvil
    Jul 25 '13 at 10:42
  • $\begingroup$ Pixels or vertices? $\endgroup$
    – cormullion
    Jul 25 '13 at 11:09
  • $\begingroup$ You might interested in this question. See, mathematica.stackexchange.com/questions/24211/… $\endgroup$
    – s.s.o
    Jul 25 '13 at 11:22
  • $\begingroup$ @cormullion: Pixel! $\endgroup$
    – Danvil
    Jul 25 '13 at 12:21
6
$\begingroup$

We can set the Method option for Graphics3D:

dmap = Graphics3D[{Cylinder[{{-1, -1, 0}, {-1, -1, 1}}, .42], Cuboid[], 
  Polygon[{{-2, -2, 0}, {2, -2, 0}, {2, 2, 0}, {-2, 2, 0}}], 
  Sphere[{.3, -.6, .5}, .5]}, Boxed -> False, 
  Method -> {"OneLayer" -> {"Depth", 1}}]

enter image description here

To get the distance values rescaled from 0 to 1, we can rasterize:

raster = ColorConvert[Rasterize[dmap], "Grayscale"];

We can view the depth map from different view points:

ListPlot3D[ImageData[raster], Axes -> False, Boxed -> False, 
 ViewPoint -> {0, 0, -10}, ViewVertical -> {0, -1, 0}, Mesh -> None, 
 SphericalRegion -> True]

enter image description here

$\endgroup$
3
  • $\begingroup$ +1 I guess this is the winning answer. Where did you find out about Method -> {"OneLayer" -> {"Depth", 1}} ? Also what other options are available? $\endgroup$
    – flinty
    Aug 14 at 17:22
  • 1
    $\begingroup$ ^ nevermind - found it here - a direct example: wolfram.com/xid/0wgrojc-tlmeyj $\endgroup$
    – flinty
    Aug 14 at 17:31
  • $\begingroup$ Right, the Method option has been documented in the Graphics3D ref page since 11.3 I believe. $\endgroup$
    – Chip Hurst
    Aug 14 at 17:32
2
$\begingroup$

It would certainly be helpful if you could specify your input data. As an example I'll use the scene you proposed above.

Let's first extract the vertices of the 3D scene. One way to achieve this is to export the scene as an .obj and then reimport the vertex data only. Let's neglect the fact that the vertices aren't uniformly distributed over the surfaces.

scene = Graphics3D[{Cylinder[{{-1, -1, 0}, {-1, -1, 1}}, .42], Cuboid[], 
  Polygon[{{-2, -2, 0}, {2, -2, 0}, {2, 2, 0}, {-2, 2, 0}}], Sphere[{.3, -.6, .5}, .5]}, 
  Boxed -> False, ViewPoint -> {5, 5, 5}]

Export["scene.obj", scene];
impScene = Import["scene.obj", "VertexData"];

The camera location is at position $(5,5,5)$ as defined by ViewPoint above:

cameraLoc = {5, 5, 5};

These are all the lines connecting the camera to the vertices:

Graphics3D[Line[{cameraLoc, #}] & /@ impScene]

enter image description here

The distance from the camera to each of the vertices is easily obtained by:

dist = EuclideanDistance[cameraLoc, #] & /@ impScene;
$\endgroup$
1
  • $\begingroup$ Thanks for the answer, but I need the depth of every pixel not only of vertices. Much like the OpenGL z-Buffer. $\endgroup$
    – Danvil
    Jul 25 '13 at 12:00
2
$\begingroup$

One (very slow) method I've used is to sweep through the depth by increasing the ViewRange gradually, and rasterizing those scenes in black & white. You can then sum the resulting images to produce something that looks like a depth map.

drange = Range[12.0, 18, 0.05];
dimensions = {512, 512};

objects = {Cylinder[{{-1, -1, 0}, {-1, -1, 1}}, .42], Cuboid[], 
   Polygon[{{-2, -2, 0}, {2, -2, 0}, {2, 2, 0}, {-2, 2, 0}}], 
   Sphere[{.3, -.6, .5}, .5]};

dmasks = ParallelTable[ColorNegate@ColorConvert[Rasterize[
      Graphics3D[{FaceForm[Black], EdgeForm[Black], objects}, 
       Boxed -> False, ViewRange -> {0, r}], 
      RasterSize -> dimensions], "Grayscale"], {r, drange}];

ImageAdjust@(Image[Total[ImageData /@ dmasks]])

depth map

This is of course inadequate for your purposes due to poor performance and low depth resolution.

Another option is to set the VertexColors of the mesh to a GrayLevel corresponding to the distance from the camera. This is faster, but it's not accurate as it interpolates the depth. Also there are some artifacts near polygon edges.

camera = {1.3, -2.4, 2.};
scene = Graphics3D[{Cylinder[{{-1, -1, 0}, {-1, -1, 1}}, .42], 
    Cuboid[], 
    Polygon[{{-2, -2, 0}, {2, -2, 0}, {2, 2, 0}, {-2, 2, 0}}], 
    Sphere[{.3, -.6, .5}, .5]}, Boxed -> False, ViewPoint -> camera];
(* get the whole scene as one big mesh *)
polys = TriangulateMesh[ImportString[ExportString[scene, "OBJ"]]];

coords = MeshCoordinates[polys];
cells = MeshCells[polys, 2];

(* get all polygon vertices distances to camera, normalize, and set a GrayLevel *)
distances = GrayLevel /@ Rescale[EuclideanDistance[camera, #] & /@ coords];

(* render the scene with the VertexColors *)
Rasterize[
  Graphics3D[{EdgeForm[None], Lighting -> {{"Ambient", White}}, 
    GraphicsComplex[coords, cells, VertexColors -> distances]}, 
   Boxed -> False]] // ColorNegate

poly depth

$\endgroup$

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.