Metal Tutorial with Swift 3 Part 4: Lighting

In this fourth part of our Metal tutorial series, learn how to light 3D objects using the Phong lighting model. By Andrew Kharchyshyn.

Leave a rating/review
Save for later
Share
You are currently viewing page 4 of 4 of this article. Click here to view the first page.

Byte Alignment

The problem you faced is a bit complicated. In your Light structure, size returns MemoryLayout.size * 10 = 40 bytes.

In Shaders.metal, your Light structure should also be 40 bytes, because that’s exactly the same structure. Right?

Yes — but that’s not how the GPU works. The GPU operates with memory chunks 16 bytes in size..

Replace the Light structure in Shaders.metal with this:

struct Light{
  packed_float3 color;      // 0 - 2
  float ambientIntensity;          // 3
  packed_float3 direction;  // 4 - 6
  float diffuseIntensity;   // 7
  float shininess;          // 8
  float specularIntensity;  // 9
  
  /*
  _______________________
 |0 1 2 3|4 5 6 7|8 9    |
  -----------------------
 |       |       |       |
 | chunk0| chunk1| chunk2|
  */
};

Even though you have 10 floats, the GPU is still allocating memory for 12 floats — which gives you a mismatch error.

To fix this crash, you need to increase the Light structure size to match those 3 chunks (12 floats).

Open Light.swift and change size() to return 12 instead of 10:

static func size() -> Int {
  return MemoryLayout<Float>.size * 12
}

Build and run. Everything should work as expected:

IMG_4281

Adding the Specular Lighting Calculation

Now that you’re passing the data through, it’s time for the calculation itself.

Open Shaders.metal, and add the following value to the VertexOut struct, right below position:

float3 fragmentPosition;

In the vertex shader, find this line:

VertexOut.position = proj_Matrix * mv_Matrix * float4(VertexIn.position,1);

…and add this line right below it:

VertexOut.fragmentPosition = (mv_Matrix * float4(VertexIn.position,1)).xyz;

This new “fragment position” value does just what it says: it’s a fragment position related to a camera. You’ll use this value to get the eye vector.

Now add the following under the diffuse calculations in the fragment shader:

//Specular
float3 eye = normalize(interpolated.fragmentPosition); //1
float3 reflection = reflect(light.direction, interpolated.normal); // 2
float specularFactor = pow(max(0.0, dot(reflection, eye)), light.shininess); //3
float4 specularColor = float4(light.color * light.specularIntensity * specularFactor ,1.0);//4

This is the same algorithm you learned about earlier:

  1. Get the eye vector.
  2. Calculate the reflection vector of the light across the current fragment.
  3. Calculate the specular factor.
  4. Combine all the values above to get the specular color.

Now with modify the return line in the fragment shader to match the following:

return color * (ambientColor + diffuseColor + specularColor);

Build and run.

IMG_4282

Enjoy your new shiny object!

Where to Go From Here?

Here is the final example project from this iOS Metal Tutorial.

Nicely done! Take a moment to review what you’ve done in this tutorial:

  1. You created a Light structure to send with matrices in a uniform buffer to the GPU.
  2. You modified the BufferProvider class to handle Light data.
  3. You implemented ambient lighting, diffuse lighting, and specular lighting.
  4. You learned how the GPU handles memory, and fixed your crash.

Go for a walk, take a nap or play around with your app a little — you totally deserve some rest! :]

Don’t feel tired? Then feel free to check out some of these great resources:

You also might enjoy the Beginning Metal course on our site, where we explain these same concepts in video form, but with even more detail.

Thank you for joining me for this tour through Metal. As you can see, it’s a powerful technology that’s relatively easy to implement — once you understand how it works!

If you have questions, comments or Metal discoveries to share, please leave them in the comments below!

Andrew Kharchyshyn

Contributors

Andrew Kharchyshyn

Author

Over 300 content creators. Join our team.