Rotating cylinders and lighting issues

Hello, I used Lighting fundamentals to create cylinders and spheres to represent atoms and connections in proteins. Everything works fine except colours and lighting.
To connect 2 atoms, I calculated vector between atomA and atomB and used float3(0, 1, 0) vector to find the correct rotation matrix with this function:

 private func rotateMatrix(v1: float3, v2: float3)->float4x4
    {
        let axis: float3 = cross(v1, v2);
        let  cosA: Float = dot( v1, v2 );
        let  k = 1.0 / (1.0 + cosA);
        
        let result = float4x4( [(axis.x * axis.x * k) + cosA,
                                (axis.y * axis.x * k) - axis.z,
                                (axis.z * axis.x * k) + axis.y, 0],
                               [(axis.x * axis.y * k) + axis.z,
                                (axis.y * axis.y * k) + cosA,
                                (axis.z * axis.y * k) - axis.x, 0],
                               [(axis.x * axis.z * k) - axis.y,
                                (axis.y * axis.z * k) + axis.x,
                                (axis.z * axis.z * k) + cosA, 0],
                               [0,0,0,1]
                               
        );
        return result;
    }

So in the end I changed modelMatrix property in Transform extension to return translation * rotateMatrix(v1,v2)* scale

What do you think where to look to fix this problem?

Hi @megagosha and welcome to the forum :slight_smile: !

Your problem is with the color and lighting?

The vertex position looks good with that change to the rotation matrix :clap:, so maybe the vertex function isn’t reading the normal and the color attributes correctly? Can you capture the GPU frame and inspect the vertex buffer to see whether the values in the normal and color are what you are expecting?

If they are not what you are expecting, then you will have to work out why. It could be the vertex descriptor if you are using [[stage_in]] in the vertex function, or it could that you define the vertex buffer differently between the Swift code and the GPU code.

Thank you for your reply. I’ve inspected the vertex buffer. Colours are fine but normals I am not sure what they have to be

This is what I get when I am rendering one of the cylinders in vertex buffer:

Maybe it has something to do with how I generate meshes?
I use
MDLMesh.newCylinder(withHeight: height, radii: radii, radialSegments: 10, verticalSegments: 10, geometryType: .triangles, inwardNormals: false, allocator: MTKMeshBufferAllocator(device: Renderer.device))

My shader function looks like this:

vertex VertexOut vertex_main(
                             const VertexIn in [[stage_in]],
                             constant float3 &color [[buffer(ColorBuffer)]],
                             constant float4x4 &rotation [[buffer(RotationBuffer)]],
                             constant Uniforms &uniforms [[buffer(UniformsBuffer)]])
{
    float4 position =
    uniforms.projectionMatrix * uniforms.viewMatrix
    * uniforms.modelMatrix * rotation *  in.position ;
    VertexOut out {
        .position = position,
        .normal = in.normal,
        .color = color,
        .worldPosition = (uniforms.modelMatrix * in.position).xyz,
        .worldNormal = uniforms.normalMatrix * (in.normal),
    };
    return out;
}

Rotation is what I get from rotateMatrix function from my first post.

I changed color generation from random to predefined and I’ve noticed that all colours are darker than expected. After playing with vertex function I found out that changing in.normal to -in.normal solves almost every problem.

.worldNormal = uniforms.normalMatrix * in.normal
To
.worldNormal = uniforms.normalMatrix * -in.normal

But I have no idea why this helps and how to fix everything completely.

Before:


After:
Screenshot 2022-09-12 at 20.37.05

Notice how the lowest cylinder color is black instead of white. But if you rotate this ligand around y axis you would see that it is white on the other side.

And this is what I get in vertex_main for this particular cylinder:

It’s really hard to say what’s going on without seeing the full code.

You could try substituting the result for the color. Eg instead of returning a color, return the normal / worldPosition / worldNormal for that fragment. Then you can visualise what’s happening more easily.

There was a slight error in fragment lighting that might be affecting you: View direction for specular calculation

Thank you for your advice. I used normals to as colours to see what is going on. Here you can see unmodified in.normal as color.
Screenshot 2022-09-14 at 17.45.05

So as expected as I rotate cylinders some normals end up facing wrong direction.

But when I multiply normal by modelMatrix.upperLeft I expect that the same rotation will be applied and they will face the required direction.
Screenshot 2022-09-14 at 18.02.53

But somehow this does not happen. I dont understand how is this possible if initial normals are ok and model matrix used to rotate cylinders is ok?

The initial normals in object space look wildly off. All faces pointing up should be green. Your normals, especially on some of the cylinders have half the normals facing one way, and half the other. The rotation matrix merely emphasises this.

This is what normals should look like. Up is green, and red is for the normals pointing in the x direction. The black ones are pointing at -z.

Oh yes, this is the problem. Thank you very much!

I tried to generate mesh instead of train in chapter 7 but I got the same result.
Screenshot 2022-09-15 at 21.15.36

I don’t understand why. All this normals are generated by mdlMesh and don’t touch any of them. Maybe it has something to do with how I generate Model from MDLMesh?
starter.zip (106.9 KB)

The layout of MTLVertexDescriptor.defaultLayout does not match the vertex descriptor that Model I/O creates for the box mesh.

Temporarily change MTLVertexDescriptor.defaultLayout to:

  static var defaultLayout: MDLVertexDescriptor {
    let mdlMesh = MDLMesh.newBox(
      withDimensions: float3(1, 1, 1),
      segments: vector_uint3(1, 1, 1),
      geometryType: .triangles,
      inwardNormals: false,
      allocator: MTKMeshBufferAllocator(device: Renderer.device))
    return mdlMesh.vertexDescriptor
}

That’s so you get the same vertex descriptor layout as in Cylinder.

You should get a better result.