Errata for Metal by Tutorials 3rd Edition

Thank you for the update, I was just doing some reading over the selection I’d buffer, and noticed that there is a multiple of two which is assuming the screen draw scale is 2 this does change depending on the device and screen. Pg 252 “* 2”

I would suggest you use contentScaleFactor.native or UIScreen.main.scale. It depends on how you are accessing the screen position data.

Hope that helps, as I got caught out by this when writing my idBuffer and testing on different devices.

Cheers Si

1 Like

The ToC bookmarks in both the PDF and ePub say “Chapter 21: Imaged-based lighting”. Should be “Image-based lighting”.

In the ePub the chapter header itself is spelled correctly, while in the PDF it still says “Imaged-based”.

1 Like

@rumor - Yes! That’s a lesson in not using magic numbers.

To correct this, I think an extra parameter on params would be useful.

➤ In Common.h, add this property to params:

uint scaleFactor;

➤ In Renderer.swift, add this to the end of init(metalView:options:):

#if os(macOS)
  params.scaleFactor = uint(NSScreen.main?.backingScaleFactor ?? 1)
#elseif os(iOS)
  params.scaleFactor = uint(UIScreen.main.scale)
#else
  params.scaleFactor = 1
#endif

➤ In PBR.metal, replace:

uint2 coord = uint2(params.touchX * 2, params.touchY * 2);

with:

uint2 coord = uint2(
  params.touchX * params.scaleFactor,
  params.touchY * params.scaleFactor); 

Thank you :slight_smile: .

Chapter 22

Final code:

I was wondering about this line:

float2 rippleY = float2(-uv.x, uv.y) + timer

Is it the intention here to add timer to both xy components? (I’m assuming that is what adding a scalar float to a vector float does.)

Or should this line be:

float2 rippleY = float2(-uv.x, uv.y + timer);

Since timer is only added to the x component for rippleX:

float2 rippleX = float2(uv.x + timer, uv.y);

I can’t say what @mhorga’s original intention was, but the nice thing about fragment shaders is that when you’re not creating a physically exact reproduction, you can create the effect you want for your render.

I tried both options, and I prefer the original code as written. When timer is applied the same to x and y, I find that the ripples are too straight. But you can go with whatever you think looks best.

I believe I’ve found an error at the beginning of chapter 4, when creating the vertices to pass along as a raw bytes buffer to the vertex shader.

See my post here: Chapter 4 (Third Edition) shader validation error :slight_smile:

1 Like

Perhaps another possible bug, but still in chapter 4, under the Adding Another Vertex Attribute section, the snippet for creating the color buffer is the following:

guard let colorBuffer = device.makeBuffer(
  bytes: &colors,
  length: MemoryLayout<simd_float3>.stride * indices.count,
  options: []) else {
    fatalError("Unable to create quad color buffer")
  }
self.colorBuffer = colorBuffer

I believe the length is incorrect. It should be:

MemoryLayout<simd_float3>.stride * colors.count

Otherwise we’re multiplying by 6 instead of 4, which is the amount of SIMD Float3 elements in the array!

3 Likes

Yes, you’re absolutely right!

colors.count is correct, not indices.count :woman_facepalming:

In chapter 7 right before the checker board graphic please revisit step 3. I believe you misinterpreted the result of the step function.

1 Like

Hi, for chapter 7, I think the MemoryLayout should take Params as input instead of Uniforms

renderEncoder.setFragmentBytes(
  &params,
  length: MemoryLayout<Uniforms>.stride,  // HERE, should it be <Params>?
  index: 12)
3 Likes

Good catch - thanks! It’s allocating too much memory using Uniforms as the stride.

1 Like

Hi, for chapter 8, for the picture that denoted as “UV coordinates”, I think the coordinates whose y value are 0.15 should swap with the ones whose y values are 0.34.

2 Likes

It certainly looks like it! :clap:- that’s made it through three editions!

1 Like

Chapter 6: Coordinate Spaces

The Starter Project

I think it should be MTLVertexDescriptor right ?

1 Like

Absolutely yes! Thank you very much for pointing this out.

Enjoying the book. I noticed in chapter 11 on normal maps, page 296 the first section ends: “and that’s the power of normal apps” and I think you intended that final word to be maps.

Thanks!!

1 Like

[Chapter 10 Lighting Fundamentals]

In Ambient Reflection part, I noticed the intensity attribute is not there as it says in the book description

Common.h:

typedef struct {
  LightType type;
  vector_float3 position;
  vector_float3 color;
  vector_float3 specularColor;
  float radius;
  vector_float3 attenuation;
  float coneAngle;
  vector_float3 coneDirection;
  float coneAttenuation;
  float intensity;                                 // <-- insert here
} Light;

SceneLighting.swift:

struct Lighting {
  static func buildDefaultLight() -> Light {
    var light = Light()
    light.position = [0, 0, 0]
    light.color = [1, 1, 1]
    light.specularColor = [0.6, 0.6, 0.6]
    light.attenuation = [1, 0, 0]
    light.type = Sun
    light.intensity = 0.1                          // <-- insert here
    return light
  }
  let ambientLight: Light = {
    var light = Self.buildDefaultLight()
    light.color = [0.05, 0.1, 0]
    light.intensity = 0.5                         // <-- change property here
    light.type = Ambient
    return light
  }()

Lighting.metal:

float3 phongLighting(...) {
  // code
  case Ambient: {
     ambientColor += light.color * light.intensity; // <-- that right ?
     break;
  }
  // code
}
1 Like

Thank you @nghiaphamsg! Yes, you are correct.

In chapter 11: Maps & Materials
I think .float type roughness will be more reasonable with the code below

1 Like

Thank you. I think you are correct.

Thinking back to olden times, I believe float didn’t work for this, but I can’t remember what file format I was using. So I would advise that whatever file format you’re using, you check that the output matches your expectation. If this isn’t a recent Apple change, I obviously didn’t.

When using texture maps, the roughness value is ignored in the current code. That’s another point. I was looking at some code that uses the diffuse material colour as a scalar on the diffuse texture, whereas the book’s code just ignores the material if there is a texture. Maybe the code should use the roughness value as a scalar. Up to you.