5
$\begingroup$

I'm trying to create a shader that displays the normalized (0 to 1) camera Z-dpeth on top of the viewed object.

Yeah, I know I can easily do this in post with the normalize node if I render out the z depth pass, but I prefer to have this in a shader.

I tried to do this with an OSL script using the clamp function but it didn't really do what I wanted. See image below for clarification: Normalized Z Depth

You can see the desired result on the right (achieved with compositor nodes), and the existing result on the left (with shader nodes).

This is the OSL script that didn't quite get me where I wanted, but might be a good start (I intended to use it between the camera data and emission nodes, the same way I use the normalize node in the compositor):

shader normalize_value(
    float inputValue = 0.0,
    output float normalizedValue = 0.0
){
    normalizedValue = clamp(inputValue, 0, 1);
}

I'm of course open to any other method, for instance involving other shader nodes (I couldn't find a way to get the same result but hey who knows!).

$\endgroup$
5
  • $\begingroup$ Normalize and Clamp are different meanings to me. Have you tried to scale/multiple by a float number such as (.5)? When you examine the failed image (RGB Values) do you see subtle color variation or a single color? If you see variation then you can perform math ops to transform those values to be more detectable by humans. If you see no variation then may the clamped values come into the function at one extreme of the clamped range. Are you sure your clamp result is a floating point value? $\endgroup$ Commented Sep 9, 2015 at 16:11
  • $\begingroup$ OSL has normalize function, that either gets and returns vector, but it won't provide you with the same result as to what I tried (it'll be the same, without changes). So maybe there lacks something besides Normalize in this case $\endgroup$
    – Mr Zak
    Commented Sep 10, 2015 at 13:00
  • $\begingroup$ I couldn't seem to get anywhere with OSL, even when I used a normalization algorithm of my own that takes the min and max values into account. Not sure why, but since the script above is my first OSL script, I probably just don't know what I'm doing... If only the normalize node from the compositor was ported into the shader nodes, that would have been really nice... $\endgroup$
    – TLousky
    Commented Sep 10, 2015 at 13:02
  • 1
    $\begingroup$ Are you open to a GN-based solution? We can instance all geo onto camera space and measure the min/max Z, which we can use to make marker points to measure the Z bounds in the shader. $\endgroup$
    – Nathan
    Commented Feb 4, 2023 at 19:02
  • $\begingroup$ You could use two empties to define the normalization limits. Could this suit you? $\endgroup$
    – lemon
    Commented Feb 5, 2023 at 9:36

4 Answers 4

3
+200
$\begingroup$

(Limitiations: Normalize is done per-object. I believe there is no way in native shader nodes to fix this.)


Example screen shot

By using the generated coordinates, we can access the bounding box for the object.

Using this, we could estimate the depth the object occupies, and set min and max for the clamping operation.


However a much more elegant solution arises:

Shader nodes By dot-producting with the camera vector, we can sample the texel's position within its own space, along the camera's (local Z) vector. Using a bit of additionnal math, we ajust the generated to operate on [1:-1], then bring the result back to [0:1]


Note: because the "incoming" vector describes not the camera's Z vector, but the vector from the texel to thec camera origin. It's not ''flat'' raster distance that is calculated but the arguably more correct stereographic distance, of camera X + camera Y + camera Z local distances. This can be if needed, by deriving the camera vector from a different source or correcting it before the dot-product operation

$\endgroup$
2
  • $\begingroup$ Oho, this doesn't exactly answer the question but it's still interesting. It works pretty well for viewing an object from outside its bounding box, but if you have a room, say, and enter it and walk straight ahead to the far wall, the depth of the far wall won't change at all even when you're right in front of it. $\endgroup$
    – scurest
    Commented Feb 4, 2023 at 5:58
  • 1
    $\begingroup$ For camera within the bounding box, it may be possible to perform a minimum between the camera position and the bounding box. I'll look into it and update this answer if I find something $\endgroup$
    – sybog64
    Commented Feb 4, 2023 at 11:31
3
$\begingroup$

If it's ok to put in some values manually, you can use a Cycles shader like this. You have to know the distance of your scene from the camera and the scene size along camera z. In this example the object starts 3 units away from the camera and has a size along camera z of 2 units.

Input the values into the two red value nodes.

enter image description here

$\endgroup$
9
  • $\begingroup$ That's an interesting solution, I'll check it out and update here, thanks! I don't mind using "manual" values because I generate this shader dynamically in a script for every object, so it can use the correct values for the scene and object ever time. $\endgroup$
    – TLousky
    Commented Sep 9, 2015 at 18:07
  • $\begingroup$ By the way there's an invert node under Color --> Invert :) $\endgroup$
    – TLousky
    Commented Sep 9, 2015 at 18:10
  • 1
    $\begingroup$ But that's for a color triple. I prefer to use this math node for single values, as it directly maps [0...1] to [1...0]. Feels somehow cleaner to me :) $\endgroup$
    – Sanne
    Commented Sep 9, 2015 at 18:12
  • $\begingroup$ OK that's close, but not quite it... I still need to tweak the values arbitrarily to get a result that better resembles the normalized image... $\endgroup$
    – TLousky
    Commented Sep 9, 2015 at 18:55
  • 1
    $\begingroup$ I see the difference. Your setup is not entirely correct, though. The distance from the camera to the start of the model is about 6.72 instead of 7.27. The driver calculates the distance to Suzanne's object center, which is a bit behind the start of the geometry. But even with 6.72 as distance there's still a little difference to the compositor output. Why that is I don't know. Maybe somehow related to color space (linear vs sRGB). Just an idea, though. $\endgroup$
    – Sanne
    Commented Sep 9, 2015 at 20:36
1
$\begingroup$

Try to scale (multiply) the values first followed by other math operations.

(original - min)/(max - min)

I am investigating clamping numbers to the same type ... float values clamp(inputValue, 0.0, 1.0);


Guessing example. For example ... (original * (.5)) + offset ..... Then clamp.

$\endgroup$
4
  • $\begingroup$ Thanks. I did try various math operations, but I actually do want to normalize the output. I.e. map each value in the original z depth image (which is a low contrast image but not a single value) into a float between 0-1, depending on its relative value to the min and max values of the original image. $\endgroup$
    – TLousky
    Commented Sep 9, 2015 at 17:04
  • $\begingroup$ Do you know the min and max values? Does some mechanism provide them? $\endgroup$ Commented Sep 9, 2015 at 17:06
  • $\begingroup$ do you think you could add more detail, maybe a few screenshots to this post? thanks- $\endgroup$
    – ruckus
    Commented Sep 9, 2015 at 17:07
  • $\begingroup$ Might be math operations that get them from the image, will check when I get home. $\endgroup$
    – TLousky
    Commented Sep 9, 2015 at 17:07
1
$\begingroup$

If you don't want to hardcode the near/far planes distances, you can use two empties for that.

Both are aligned to the camera axis and rotated as the camera is (looking at minus Z). They are also parented to the camera.

enter image description here

From that, you can take the object coordinates of the three objects (the camera, the near empty called 'min' below and the far empty called 'max').

Now, we keep only the Z part of their coordinates and map range the view Z depth from a min value that is the Z distance between the camera and "min" empty and a max value that is the Z distance between the camera and "max" empty.

Once done, invert the resulting color.

(unlinked below, Z of the camera that can be used in place of the "min" empty if you want to get ride of it).

enter image description here

Note: inverting the color is useless, as you can also map range to min = 1 and max = 0.

enter image description here

To move the empties, use GZZ to grab them along their own Z axis.

$\endgroup$
3
  • $\begingroup$ This just hardcodes it in the position of the empties though; the extra indirection doesn't really change anything. I want it to always just work for whatever the current camera is. $\endgroup$
    – scurest
    Commented Feb 6, 2023 at 17:29
  • $\begingroup$ @scurest, in this case, I think its not possible. $\endgroup$
    – lemon
    Commented Feb 6, 2023 at 17:42
  • $\begingroup$ Yeah, seems like it. $\endgroup$
    – scurest
    Commented Feb 6, 2023 at 17:43

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .