Rotation in 3D

I'm in the process of learning Metal for iOS for use in a visualizer app. It's been going good so far, but I'm stumped on this issue I've come across.

Just as a test, I wanted to create a rotating prism (rotating in place around it's y axis). I've used some of the code in Apple's "MetalShaderShowcase" sample code as a reference (it includes a spinning cube as one of the examples).


Here is the prism I created (with depth testing turned off so we can see all the sides) using the following vertices:

https://www.dropbox.com/s/neipoazlcm491vg/prism.PNG?dl=0

static const float vertexData[] = {

0.f, 1.f, 0.5f,

1.f, -1.f, 0.f,

-1.f, -1.f, 0.f,


0.f, 1.f, 0.5f,

1.f, -1.f, 1.f,

1.f, -1.f, 0.f,


0.f, 1.f, 0.5f,

-1.f, -1.f, 1.f,

1.f, -1.f, 1.f,


0.f, 1.f, 0.5f,

-1.f, -1.f, 0.f,

-1.f, -1.f, 1.f

};


The prism seems to be rendering just fine (minus the depth testing turned off of course). But when I try to rotate it, this happens:

https://www.dropbox.com/s/esg41j3ibncofox/prism_rotate.mov?dl=0


I'm not sure why it's clipping and does not seem to be rotating around it's own y axis. (Note that depth testing is ON in the video).

Here is how the MVP matrices are calculated:


Uniforms *uniforms = (Uniforms *)[uniformBuffer contents];
uniforms->model = AAPL::translate(0.f, 0.f, 12.f) * AAPL::rotate(tAngle, 0.f, 1.f, 0.f);
tAngle += 0.5f;

static simd::float3 viewEye = {0.f, 0.f, -2.f};
static simd::float3 viewCenter = {0.f, 0.f, 1.f};
static simd::float3 viewUp = {0.f, 1.f, 0.f};
static float fov = 45.f;

CGSize size = self.view.bounds.size;
uniforms->view = AAPL::lookAt(viewEye, viewCenter, viewUp);
uniforms->projection = AAPL::perspective_fov(fov, size.width, size.height, 0.1f, 100.f);


I'm just using convenience methods provided by Apple in their sample code for calculating the translate, rotate, lookAt, and perspective matrices.

And finally the shader:


typedef struct {
    float4 pos [[ position ]];
    half4 color;
    float mod;
} VertexOut;

vertex VertexOut basic_vertex(const device packed_float3* vertex_array [[ buffer(0) ]],
                              const device packed_float3* colors [[ buffer(1) ]],
                              constant Uniforms& uniform [[ buffer(2) ]],
                              uint vid [[ vertex_id ]],
                              uint iid [[ instance_id ]])
{
    float4 v = float4(vertex_array[vid], 1.f);
    float4x4 mvp_matrix = uniform.projection * uniform.view * uniform.model;

    VertexOut out;
    out.pos = v * mvp_matrix;
    uint colorIndex = vid / 3;
    out.color = half4(half3(colors[colorIndex]), 1.f);
    return out;
}

fragment half4 basic_fragment(VertexOut f [[ stage_in ]]) {
    return f.color;
}


Any helps/tips would be greatly appreciated!

Answered by Flyingsand in 103399022

I discovered the issue. It was in the shader, and I was multiplying in the wrong order (since matrix multiplication is not commutative).


So this:


out.pos = v * mvp_matrix;


should be this:


out.pos = mvp_matrix * v;


That fixed it. For whatever reason I'm so used to dealing with row vectors as opposed to column vectors. And often times it's not clear what graphics libraries use. OpenGL uses column vectors, but DirectX uses row vectors. I don't recall reading in the Metal guide what it uses, so..

Just from casually looking at it, it looks to me like it's intersecting the background plane. That would explain why it's being obscured.

It's definitely clipping through something, and I've tried to scale it down in size and translating it forward/backward.. Still the exact same result. And the distance between the near and far plane looks to be plenty big enough.

What about the camera draw distances?

Accepted Answer

I discovered the issue. It was in the shader, and I was multiplying in the wrong order (since matrix multiplication is not commutative).


So this:


out.pos = v * mvp_matrix;


should be this:


out.pos = mvp_matrix * v;


That fixed it. For whatever reason I'm so used to dealing with row vectors as opposed to column vectors. And often times it's not clear what graphics libraries use. OpenGL uses column vectors, but DirectX uses row vectors. I don't recall reading in the Metal guide what it uses, so..

Glad you were able to figure it out! 🙂

Rotation in 3D
 
 
Q