Creating this topic to catch any typos and bugs in the 3rd Edition of Metal by Tutorials.
I found a typo on the PDF page 27,
Who This Book Is For?
We recommend the Swift Apprentice book, available in our catlagoue:
Should be catalogue.
Kind regards
Fernando
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
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”.
@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
.
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 ![]()
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!
Yes, you’re absolutely right!
colors.count is correct, not indices.count ![]()
In chapter 7 right before the checker board graphic please revisit step 3. I believe you misinterpreted the result of the step function.
Hi, for chapter 7, I think the MemoryLayout should take Params as input instead of Uniforms
renderEncoder.setFragmentBytes(
¶ms,
length: MemoryLayout<Uniforms>.stride, // HERE, should it be <Params>?
index: 12)
Good catch - thanks! It’s allocating too much memory using Uniforms as the stride.
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.
It certainly looks like it!
- that’s made it through three editions!
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!!
[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
}

