Face normal vs vertex normal

I’ve come up with a new kind of problem, related to this discussion so far.

My model was working really nicely, rendering a torus with 20 horizontal and 12 vertical hexes. So I changed these parameters and it started to produce shards - triangles boldly going where no triangle should go.
Naturally I thought it was my MeshMaker and some magic number. I’ve spent days debugging to no avail. I examined every triangle going in to no avail.

Finally,I’ve pinned down a pattern. Provided the total number of triangles does not exceed 4080 on my iMac (with NVIDIA GeForce GTX 775M 2 GB) everything works. Exceed this and shards appear.

Then I thought, it doesn’t have time to produce more working at 60 fps so I added

metalView.preferredFramesPerSecond = 30

to Renderer.init(metalView:). This didn’t help. Indeed, setting fps to 90 doesn’t make matters worse either. On the other hand, adding further shader work (lighting) has pushed my limit down to 2688 triangles!

So my question: What kind of limit am I hitting here?

Have a nice Easter break

Update: Ran it this evening on my Mac Powerbook (which I always think of as my “toy” computer)… Intel Iris Pro 1536 MB

It ran models 30 times bigger. So looks like my desktop machine is the culprit.

Here’s a sunlit torus with 20,000 hexes (120,000 triangles).

Screenshot 2020-04-11 at 00.12.30

2 Likes

Without seeing the code, I can’t tell what sort of limit you’re hitting. That doesn’t sound like many triangles. Are your buffers big enough to handle them? If you’re creating the torus up front before rendering, as you should be, then changing the fps shouldn’t make any difference.
How complex are your shaders? Can you make them simpler?

Have you thought over your process? My instinct is not to assign a texture to every hex, but to procedurally create one texture to cover the whole torus, which would be constructed out of normal tris.
Or you could separate out your hexes and do instanced drawing? That might be worth trying if you are up for it.

Can’t think of any other way … I have a mesh. As you say, I hit the limit on not very many triangles.

Not sure I understand that …

My shaders are very very simple.

vertex VertexOut vertex_main(constant VertexIn *vertices [[buffer(0)]],
                             constant VertexUniforms &uniforms [[buffer(1)]],
                             constant float2 *localUVs [[buffer(2)]],      // contains 7 uv pairs
                             constant ushort *indexTable [[buffer(3)]],    // contains 18 values (0...6)
                             uint id [[vertex_id]]) {

   float4 position = float4(vertices[id].position, 1);
   ushort index = vertices[id].vertexType * 6 + uniforms.currentTriangle;   // produces integer in  (0...17)
   float2 uvLocal = localUVs[indexTable[index]];

   VertexOut out;
   out.worldPosition = (uniforms.modelMatrix * position).xyz;
   out.worldNormal = uniforms.normalMatrix * vertices[id].normal;
   out.position = uniforms.projectionMatrix * uniforms.viewMatrix * uniforms.modelMatrix * position;
   out.uvLocal = uvLocal;
   out.uvOffset = uniforms.uvOffset;
   out.uv = uvLocal + uniforms.uvOffset;
   return out;
}

fragment float4 fragment_main(VertexOut in [[stage_in]],
                              sampler textureSampler [[sampler(0)]],
                              texture2d<float> colorTexture [[texture(0)]],
                              constant Light *lights [[buffer(1)]],
                              constant FragmentUniforms &fragmentUniforms [[buffer(2)]]) {

   float2 uv = in.uv; //Local + in.uvOffset;
   float3 colour = colorTexture.sample(textureSampler, uv).rgb;
   float3 diffuseColor = 0;
   float3 normalDirection = normalize(in.worldNormal);
   for (uint i = 0; i < fragmentUniforms.lightCount; i++) {
      Light light = lights[i];
      if (light.type == Sunlight) {
         float3 lightDirection = normalize(-light.position);
         float diffuseIntensity =
         saturate(-dot(lightDirection, normalDirection));
         diffuseColor += light.color
         * colour * diffuseIntensity;
      }
   }
   return float4(diffuseColor, 1);
}

I think the process is good. Entire shader is a small picture and obtaining the uv coordinate is simple. It’s true I send 48 draw calls but each is only about 2% of the overall picture.

And bottom line is … it works efficiently on my little laptop. I thought my NVIDIA GeForce would gobble it up.

Apple/Nvidia haven’t provided a driver since High Sierra, but I’m still surprised it doesn’t do better than the integrated GPU.

Obviously the fewer the draw calls the better.

I think you’re right. I wouldn’t even have posted had I realised it worked on my laptop. Now I’m satisfied it’s the NVIDIA.

At least I have a new debugging strategy: MOVE TO ANOTHER COMPUTER
:slight_smile:

1 Like