Ch 4 - DebugLights

PDF Page 133 suggests using DebugLights.swift : “debugLights(renderEncoder: renderEncoder, lightType: Sunlight)”

This didn’t work for me, and not knowing why, I spent time making sure I had followed the instructions. When that didn’t work, I started adding more vertices for lines I knew I would see. When that didn’t work, I started changing the DebugLights.metal code. I finally got the drawDirectionalLight function (in DebugLights.swift) to work by commenting out some lines in fragment_light():

// float d = distance(point, float2(0.5, 0.5));
// if (d > 0.5) {
//     discard_fragment();
// }

When I got to PDF page 140, I figured out why. The code I commented out was evidently added to turn the point light square into a circle. This feature doesn’t work for the sunlight case. The DebugLights idea is clever but needs additional explanation and/or a redesign if it’s going to be useful in the general case.


@12o1v - that’s interesting. What device are you using?

This is the result I get from adding this code, in the final project sample, toward the end of draw(in:):

    debugLights(renderEncoder: renderEncoder, lightType: Spotlight)
    debugLights(renderEncoder: renderEncoder, lightType: Pointlight)
    debugLights(renderEncoder: renderEncoder, lightType: Sunlight)

The sunlight lines are thin, but they still appear. If I comment out the code as you suggest, this is what I get:

The lines are thicker, as you’d expect.

Yes, interesting. Thanks for knowing that I had the wrong chapter, it was chapter 5.

Here’s what I see. I took a fresh copy of the final project sample and made the code the same as yours. I don’t see any sunlight lines and it looks to me that the dots are bigger than yours.


My device info:

Hardware Overview:

Model Name: iMac
Model Identifier: iMac14,2
Processor Name: Intel Core i7
Processor Speed: 3.5 GHz
Number of Processors: 1
Total Number of Cores: 4
L2 Cache (per Core): 256 KB
L3 Cache: 8 MB
Memory: 32 GB
Boot ROM Version: IM142.0130.B00
SMC Version (system): 2.15f7
Serial Number (system): D25LX06EF8JC
Hardware UUID: F1DC10C3-9BA9-55B5-8E64-C7808A1DFA3E

System Software Overview:

System Version: macOS 10.14 (18A391)
Kernel Version: Darwin 18.0.0
Boot Volume: OSX
Boot Mode: Normal
Secure Virtual Memory: Enabled
System Integrity Protection: Enabled
Time since boot: 19 days 1:09

NVIDIA GeForce GTX 780M:

Chipset Model: NVIDIA GeForce GTX 780M
Type: GPU
Bus: PCIe
PCIe Lane Width: x16
VRAM (Dynamic, Max): 4096 MB
Vendor: NVIDIA (0x10de)
Device ID: 0x119e
Revision ID: 0x00a2
ROM Revision: 3782
Metal: Supported, feature set macOS GPUFamily1 v4
Display Type: LCD
Resolution: 2560 x 1440 (QHD/WQHD - Wide Quad High Definition)
UI Looks like: 2560 x 1440
Framebuffer Depth: 24-Bit Color (ARGB8888)
Main Display: Yes
Mirror: Off
Online: Yes
Rotation: Supported
Automatically Adjust Brightness: No
Connection Type: DisplayPort

My guess is that it’s the NVIDIA chip. There are a few oddities with NVIDIA and Metal, but I don’t remember that being one of them.

Do you have a mobile device to check your code on?

Interestingly, one of your tree triangles is missing too. So I might be blaming the chip too quickly :smiley:

OK, ran it on an iPad Air. Same results - except that the tree triangle is not missing. With the final sample version I don’t see the sunlight lines unless I comment out the fragment code.

Are you running on 10.14 Mojave? And xCode 10.0? Maybe Apple changed something?

My apologies. I feel sure that it used to work on my iPhone, but doesn’t now. I have a retina iMac, so it might be that the retina has enough pixels to pick up the line when it’s halved where the non-retina doesn’t.

Although as I rotate the train on my iPhone and earlier devices, I can see the spotlight line showing up occasionally, so it can’t be pixel resolution.

Thank you so much for your patience and for bringing this to our attention. I guess I don’t have any solution at the moment except, as you suggested, to comment out those lines.

You can change the size of a point, but unfortunately you can’t change the width of a line. To render thicker lines, you need to create triangle strips.

As warrenm suggests in the comment here, it’s very likely anti-aliasing quality, although why it would have changed since iOS 11 and early betas of 12, I don’t know

I’m running iOS 11 on the iPad, so I don’t think it’s the OS version.

I did this to make it work:

  1. Add another parameter to the fragment function
var isLine : simd_bool = false
renderEncoder.setFragmentBytes(&isLine, length: MemoryLayout<simd_bool>.stride, index: 2)

Set isLine to false for drawPointLight, true for the others.

  1. Change fragment_light to:
fragment float4 fragment_light(float2 point [[ point_coord]],
                               constant float3 &color [[ buffer(1) ]],
                               constant bool &is_line [[ buffer(2) ]]) {
    float d = distance(point, float2(0.5, 0.5));
    if (is_line == false && d > 0.5) {
    return float4(color ,1);

Since this is debug code, using the conditional shouldn’t be a problem.

Now my results look correct:


1 Like

Great solution :slight_smile: - thanks for taking the time to post it.

I have noticed the artifact with fragmented lines too (latest model MacBook Pro). What I’m confused about is the expected behavior for [[ point_coord ]]. The Metal Shading Language spec 2.1 implies that it is only meaningful for points. What is it actually doing to the lines? The effect changes as I rotate the scene around.

Hi @cocheret

[[ point_coord ]] is only meaningful for points.

@12o1v pointed out that this code:

  float d = distance(point, float2(0.5, 0.5));
  if (d > 0.5) {

currently takes place even if the rendered fragment is not from a point. The rasterizer is placing some value in point, even if it’s not rendering a point.

To test this, I duplicated the above point code in the main fragment shader (passing on a point size and taking in the fragment point coordinate value) and got a meaningless result like this:

where the fragments for each rasterized triangle are discarded based on the distance of the fragment in each triangle from the rasterizer’s interpretation of a central point in the triangle. At this time I don’t know what that central point in the triangle is. (It changes depending on the viewing angle too.)

In the original book code, the rendered line is affected in the same “arbitrary” way. (In quotes because I don’t currently know how the point coordinate in the fragment is calculated if it’s not rendering a point.) @12o1v passed a boolean so that the code would only work on points.

I’m going the second time through this book and I have got the same problem again: I don’t see those red lines.
Your suggestion worked for me, too … Thank You :blush:

Until I get to the Pointlight:

I got a square.

Then I commented in and I got the red sphere:

But I got this much more bigger than in book. But zooming in or out, makes this red sphere smaller or bigger:

@ilmaros - The debug “point light” has no mesh associated with it. It’s a single point drawn on the screen. It has no depth. By default it’s a square shape - you determine the size of the point with the attribute point_size in VertexOut.

As you zoom in and out, the size of the point won’t change. It’s just a point.

The round point isn’t a 3d sphere - it’s a square with the corners taken out. In fragment_light, you found out the distance of the fragment from the centre using the distance function. At this stage, the size of the point is always 1. So 0.5 is the radius, and all fragments to be shown must be within this distance from the centre. The square’s corners are outside of this radius distance, so you discard_fragment.

1 Like

Thank You that You pointed out that this is not sphere :+1: … Basically this was shown in Chapter 4 where we made transformations with points :slight_smile:

Actually this DebugLights stuff are very useful and give some examples to explore and learn basic of basics!

1 Like