Jump to content

Insufficient mesh z-coordinate precision


photo

Recommended Posts

Problem

 

Mesh rendering of co-planar surfaces (e.g. fine-grained road mesh overlay/subface on coarse-grained terrain surface base mesh) shows uncontrollable z-fighting artifacts. Material assignment to overlay/subface mesh with max polygon offset cannot solve the problem.

 

Cause

 

UNIGINE internal mesh render vertex format uses float-precision encoding only for x and y coordinate, but encodes z-coordinate as half-precision-only normal vector w-component to save some memory.

 

Most probably this optimization is based on assumption, that half-precision is sufficient for smaller z-values. Unfortunately this assumption breaks for overlay/subface mesh rendering even for very small z-value ranges around 0. Insufficient z-coordinate precision causes no longer co-planar mesh triangles on rendering -> z-fighting between larger base mesh triangles and smaller overlay/subface triangles.

 

 

Solution

 

Usage of full float-precision for z-coordinate for static meshes. For skinned meshes used z-coordinate half compression can still be used, as overlay/subface usage on skinned meshes should be quite unusual.

 

framework/Mesh.h

       struct RenderVertex {
           float xyz[4];        // coordinate (use 4 element array for propper struct alignment !)
           hvec4 texcoord;        // texture coordinates
           hvec4 normal;        // normal (w-component no longer encodes half-precision z-coordinate)
           hvec4 tangent;        // tangent
       };

 

Remark Maybe position can be further optimized to 3-element-only float (xyz[3]/D3DDECLTYPE_FLOAT3) but we got some alignment issuse

 

 

e.g render/direct3d9/framework/D3D9Mesh.cpp

void D3D9Mesh::create_vertex_declaration() {

   // replaced UNIGINE half-precision z-coordinate encoding as 4th member
   // of normal vector with explicit float-precision position.z member to
   // avoid mesh precision artifacts (see framework/Mesh.h/.cpp for details) 

   static const D3DVERTEXELEMENT9 vertex_element[] = {
       { 0, 0,     D3DDECLTYPE_FLOAT4,       D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION,    0 },
       { 0, 16,    D3DDECLTYPE_FLOAT16_4,    D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD,    0 },
       { 0, 24,    D3DDECLTYPE_FLOAT16_4,    D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD,    1 },
       { 0, 32,    D3DDECLTYPE_FLOAT16_4,    D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD,    2 },
       D3DDECL_END()
   };

   static const D3DVERTEXELEMENT9 instanced_vertex_element[] = {
       { 0, 0,     D3DDECLTYPE_FLOAT4,     D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION,    0 },
       { 0, 16,    D3DDECLTYPE_FLOAT16_4,  D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD,    0 },
       { 0, 24,    D3DDECLTYPE_FLOAT16_4,  D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD,    1 },
       { 0, 32,    D3DDECLTYPE_FLOAT16_4,  D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD,    2 },
       { 1, 0,     D3DDECLTYPE_FLOAT1,     D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD,    3 },
       D3DDECL_END()
   };

   .....

 

 

e.g. core/shader/meshes/vertex_base.shader + vertex_leaf.shader

   .....

   float4 position = float4(IN.position.x,IN.position.y,IN.position.z,1.0f);

   .....

Link to comment

Regular meshes really use half-floats for Z coord. I suggest to use dynamic meshes for full precision elements cause they use full float values and work only slightly slower.

Link to comment

Regular meshes really use half-floats for Z coord. I suggest to use dynamic meshes for full precision elements cause they use full float values and work only slightly slower.

 

Hi Alexei,

 

thanks for the tip, UNIGINE also proposed this approach (at least as workaround). In practice dynamic meshes are unfortunately no solution, as external content creation tools and editor all create/use regular meshes, there is no dynamic mesh file loading/writing, etc.

 

The example use case of road overlays on terrain mesh or polygonal road markings overlays on road mesh is so common, that this should be handled correctly without any workarounds/problems.

 

The current rendering/workflow drawbacks by far outweight the minimal memory advantage of z-half-compression -> a little bit too aggressive optimization

Link to comment

You could load dynamic mesh in code simply by calling constructor new ObjectMeshDynamic("something.mesh"). Or you could patch the source code, as we do on every build :)

Link to comment

You could load dynamic mesh in code simply by calling constructor new ObjectMeshDynamic("something.mesh"). Or you could patch the source code, as we do on every build :)

 

didn't know that, so thanks :) Anyway the problem should be solved for mesh object itself, because building world levels by code instead of using the editor is an absolute no-go.

 

The above provided solution solves all the problems with with just a small increase in memory consumption and no measurable performance penalty.

Link to comment
  • 1 month later...

From some of the problems we are getting here our workflow would be greatly enhanced by at least having the option to load static and skinned meshes with a full z value.

 

I think supporting both variants would be quite complicated in code/shaders. Also more and more problems caused by this internal "over-optimization" show up in other use cases, e.g.

 

scene-accuracy-and-legoing

 

Unfortunately there still seems to be no UNIGINE activity for solving these issues...

Link to comment

e.g.scene-accuracy-and-legoing

 

Unfortunately there still seems to be no UNIGINE activity for solving these issues...

 

Yes we reported that issue :)

 

I personally would prefer more precision. But it will be possible to modify the Max export scripts to export nodes with ObjectMeshDynamic as opposed to ObjectMesh. What other limitations do dynamic meshes have so far we come accross at least one of our meshes that won't load as a dynamic mesh.

Link to comment
  • 7 months later...

The global console variable "render_use_vertex_float" will control type of mesh vertex format.

Floating point vertex format will be available only on OpenGL, Direct3D9, Direct3D10 and Direct3D11 renders.

OpenGLES and PlayStation3 will use half floating point vertex format always.

Link to comment

The global console variable "render_use_vertex_float" will control type of mesh vertex format.

Floating point vertex format will be available only on OpenGL, Direct3D9, Direct3D10 and Direct3D11 renders.

OpenGLES and PlayStation3 will use half floating point vertex format always.

 

Thankyou very kindly good sirs :)

Link to comment
×
×
  • Create New...