Kodeco Forums

iOS 8 Metal Tutorial with Swift Part 2: Moving to 3D

Learn how to use matrices and 3D transformations in this iOS 8 Metal Tutorial with Swift!


This is a companion discussion topic for the original entry at https://www.raywenderlich.com/2205-ios-8-metal-tutorial-with-swift-part-2-moving-to-3d

Hello,
I’m interested in depth testing faces for render. Backface culling is great but only for covex types of geometry. More complex types will show errors at render.
Tnx

Very nicely done tutorial. I would be very much interested in more.
And can all of this also be done without Shaders? Or is everything honed to work in only this way?

Problem with the tutorial at this point is that seemingly only one object can be visualized. If I create another cube, only one of them is drawn. I can draw both if I get myself a new drawable from the Metal Layer, but then the objects kind of flicker.

MrJingles

@mrjingles

I know this is late reply but I was asking myself the same question after first reading this tutorial. I think both cubes should be encoded into the same command buffer by the same render encoder. So the render pass descriptor, the command buffer and the render encoder should belong to the ViewController and each Node should receive the render encoder to render into by drawing its primitives. After all nodes did their drawPrimitives() then the ViewController should finalize rendering, i.e. endEncoding() on the render encoder, presentDrawable() on the command buffer and finally commit() the command buffer.

Thanks for writing this tutorial!

However, I don’t think you can safely use Matrix4 to make your projection matrix. Metal has a different device space definition than OpenGL - z goes -1…1 in Metal, and 0…1 in GL.

If you use a GL projection matrix it will work if you have TONS of extra room on your ‘near’ value, but doing that ends up making your rendering less precise because it squishes the values in the z buffer.

Thanks for this tutorial and a great introduction to Metal. Just one question, after I refactor from the previous tutorial a triangle renders on only half the view. What could be the reason ?

Thanks for reaching out! I will investigate this question.

I have gone through this tutorial again but with the latest Xcode beta and iOS 10 beta and there is an issue just after creating the worldModelMatrix.
When run, the app crashes and the following message is displayed (regarding the render method):

“fatal error: unexpectedly found nil while unwrapping an Optional value”

It is objectToDraw = (HelloMetal.cube!) nil which is the issue.

Well I was trying to get the depth Buffer and finally able to do it myself. Sharing it here for other’s help:

In ViewController add the following variable:

var depthStencilState: MTLDepthStencilState! = nil

Then add the following lines in the viewDidLoad() function:

let depthStencilDescriptor = MTLDepthStencilDescriptor()
depthStencilDescriptor.depthCompareFunction = .Less
depthStencilDescriptor.depthWriteEnabled = true
depthStencilState = try! device.newDepthStencilStateWithDescriptor(depthStencilDescriptor)

In Node.swift Update the signature of renderer function signature to

func render(commandQueue: MTLCommandQueue, pipelineState: MTLRenderPipelineState,depthStencilState: MTLDepthStencilState, drawable: CAMetalDrawable, parentModelViewMatrix: Matrix4, projectionMatrix: Matrix4, clearColor: MTLClearColor?)

Now fine the following line in that function:

    renderEncoder.setRenderPipelineState(pipelineState)

and add the following line right after the above line:

    renderEncoder.setDepthStencilState(depthStencilState)

Finally update the calling statement as follows:

objectToDraw.render(commandQueue, pipelineState: pipelineState,depthStencilState: depthStencilState, drawable: drawable, parentModelViewMatrix: worldModelMatrix, projectionMatrix: projectionMatrix, clearColor: nil)

You should check the following code in Node.swift
renderEncoderOpt.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 3, instanceCount: 1)

it must be replaced as:

renderEncoderOpt.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: vertexCount, instanceCount: 1)