How do I affect Z height for shadow calculation using a normal map?

From my understanding, the closest concept Radiance has to a normal map is texdata. The general approach is described in this archived post. I have followed the approach as follows:

I have a defined:

{ - A normal texture mapping file
    A1 = intensity

And I am assigning a texture like so:

void texdata normal_map
9 pass_dx pass_dy pass_dz normal_x.dat normal_y.dat normal_z.dat frac(Lu) frac(Lv)
1 1

normal_map plastic some_material
5 .5 .5 .5 0 0

To generate the data files, I am using pvalue -o texture_normal.pic > texture_normal.dat, then splitting up the columns into separate data files using a Python script. Here is an example of an outcome with this technique, with all 3 X, Y and Z perturbed.

The normal map applied to the wall looks like this:


However, I am noticing that the perturbation only really impacts X, and Y, not Z. True enough, if I change the material definition to 9 nopert nopert pass_dz norm ... etc then the effect entirely disappears. In addition, as the effect is only done on X and Y, changing A1 has a limited effect. A value of “0” compared to “1” for A1 has a noticeable change, as 0 is no effect. However, changing A1 from 10 to 100000 has no visible change.

To better illustrate my point, here is the same render but this time with no normal map applied at all. This is the base case: a smooth wall with no fancy tricks.

And now here is the render again, but with nopert nopert pass_dz, so that only Z perturbation is applied. As you can see, it has an effect on the light (the glow on the upper portion of the wall disappears), but absolutely no effect on shadowing, creating a smooth and unnatural effect.

In contrast (no pun intended) in other rendering packages, the Z value affects shadow calculation. So here is the same normal map applied and rendered in Blender’s Cycles engine - the left half has an intensity of 1, and the right has an intensity of 5. The impact is very strong and visible.


Is this a limitation of Radiance, and it cannot use normal maps like other software? Is there any alternative?

You can pass a height map to gensurf and deal with the geometry. If you make the height map tileable, then you can put the output of gensurf into an octree for instantiation. Even better, use the “-o” and “-s” options to output a .OBJ file to mass to obj2mesh and tile that with the “mesh” primitive.

Thank you Greg. I ended up not using gensurf, as my 3D program also can generate the surface. I divided my wall into a smaller tileable surface, and exported that as an .OBJ. I then used obj2mesh and the mesh primitive in a file called wall.rad. I then called !xform -a 24 -t 0 .275 0 -a 12 -t 0 0 .225 lib/wall.rad to tile it to create my larger surface.

I noticed when trying to do this that I could not use the -a argument directly in the mesh primitive. I would get the error oconv: fatal - bad transform for mesh "wall", which is why I used xform as I described above.

I guess in this case it works because the wall is tileable, so I can have a very high resolution mesh and tile it. However it seems from your answer that for non-tileable surfaces, there is no other way: I have to model the entire mesh. I tried exporting a non-tiled version of the wall and it ended up being over a hundred megabytes, which seems a bit excessive. I guess I’ll have to live with it :slight_smile:

Here is the outcome, if you’re interested, I’m very happy with the results!

Glad you got it all to work. As you discovered, the “-a” option is supported by xform, but not any of the Radiance primitives directly.