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!
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..