Interpolation of vertex normals inside each triangle with barycentric coordinates

Hi!

I am currently working on a project about the optimization of mesh. The data provided is simply a heightmap with a uniform grid, which will be converted to triangle mesh before transferred to the device. I would like to ask if there is any convenient way for OptiX7 to get the normals of vertices on the device side. Or must I pre-process the whole mesh and assign the normal attribute to each vertex before the ray tracing? I think there might be not but I still would like to ask, just in case.

Best regards,
Linkous

OptiX is just a ray casting SDK with a flexible programming model which allows implementing various developer defined algorithms.
Calculating vertex attributes is fully under the developer’s control, means it’s your responsibility.

If your height-map is just a regular grid and you want to calculate the smooth vertex normal at the grid coordinates which are also the 2D vertex coordinates, that should be pretty straightforward.

You could calculate that by smoothing the face normals of the adjacent triangles at each vertex.
With a regular grid you have the necessary topology information about the adjacent triangle/vertex data and could do that either as a pre-process or at runtime.
If at all, I would do that inside a native CUDA kernel as preprocess. Then the vertex attribute data is already on the device for your OptiX 7 ray tracer to use and doesn’t need to be recalculated on closest hit program invocation.
(OptiX doesn’t care about normals. Acceleration structures are only build over vertex positions.)

The simpler approach would be to directly calculate the derivatives from the height map by doing central differencing over the height map values resp. forward or backward differencing at the height map edges.
That would be faster to do on the device by uploading the height map as a texture.
With the proper bilinear filtering that would automatically interpolate the necessary values for that differencing to work per fragment (surface hit point).

It depends on for what purpose you require the normals in the end. Only for visualization or actually per vertex data inside some final mesh result.

Thanks! Your suggestions are really helpful.

Best regards,
Zhoufan Jia

I have a related question. My team is implementing raytracing against a triangular mesh model of a humanoid. We would like to define the vertex normals by the area weighted average of surrounding face normals, then interpolate the normal at a hit point using a standard barycentric approach. Does OptiX provide built-in methods to achieve this, or does the developer need to code this? I am guessing the latter is correct, but would like to confirm.

Thanks,

Hey @user99334,

The answer Detlef gave still applies, OptiX has no API for dealing with normals, but there are some methods OptiX provides that give you room for some choices.

One option is to write a CUDA kernel to compute vertex normals however you like, and store them in a buffer. Then in your OptiX shader programs, you can lookup your vertex normal data using the triangle id of the triangle you hit. All you need for this is optixGetPrimitiveIndex() – use that perhaps to lookup your vertex ids, and then lookup the vertex normals. Or use the primitive index to find your vertex normals directly (more on this below.) Once you have the vertex normals, you can blend them using the result of a call to optixGetTriangleBarycentrics().

If you wanted to compute the vertex normals on the fly during shading, it’s possible but might be complicated. OptiX doesn’t store face normals or topology for you, so you would need to reconstruct them on your own. Here are the steps you’d need if you don’t pre-compute any auxiliary data, and you only have a vertex buffer:

  • Use the primitive id to find the ids of neighbor triangles optixGetPrimitiveIndex()
    – You might need a topology index buffer of some sort
  • For each triangle involved (hit + neighbors):
    – Query the vertices of the triangle optixGetTriangleVertexData()
    – Compute the face normal
  • For each vertex of the hit triangle:
    – Average the connected face normals to arrive at a vertex normal
  • Query barycentrics optixGetTriangleBarycentrics()
  • Blend the vertex normals using barys

There are reasons you might want to compute normals on the fly, but my recommendation would be to start with the first method & bake vertex normals into a buffer. Use a CUDA kernel to compute face normals, and then another one to compute vertex normals. Then in shading, lookup the vertex normals and blend them using the hit barycentrics. Since buffer lookups are costly, if you have spare memory, then it might be worth storing the 3 vertex normals for each triangle ID, rather than storing one vertex normal for each vertex ID. Storing the vertex normals per triangle is 3x more storage than storing one vertex normal per vertex. The reason you might want to consider this is it allows you to avoid needing to use your triangle index buffer to figure out the vertex IDs followed by 3 separate vertex normal lookups. This lets you avoid a dependent memory lookup, and also leaves the data in a more cache friendly layout. The cost is memory usage bloat, so if you need to keep usage to a minimum then a per-vertex normal buffer is the better choice.


David.