为什么用粗糙的边缘而不是平滑的边缘渲染三角形?金属、Swift、Xcode [英] Why is the triangle being rendered with rough edges and not smooth edges? Metal, Swift, Xcode

查看:72
本文介绍了为什么用粗糙的边缘而不是平滑的边缘渲染三角形?金属、Swift、Xcode的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用此代码来呈现Hello Triangle"三角形.但是,在我的 iPhone 上,三角形的边缘非常粗糙,而不是像示例中那样平滑的边缘.

I am using this code to render the "Hello Triangle" triangle. On my iPhone, though, the triangle has very rough edges, not smooth edges, like in the example.

import UIKit
import Metal
import MetalKit
import simd

class MBEMetalView: UIView {

     // // // // // MAIN // // // // //
     var metalDevice: MTLDevice! = nil
     var metalLayer: CAMetalLayer! = nil
     var commandQueue: MTLCommandQueue! = nil
     var vertexBuffer: MTLBuffer! = nil
     var pipelineState: MTLRenderPipelineState! = nil
     var displayLink: CADisplayLink! = nil

     override class var layerClass : AnyClass {
          return CAMetalLayer.self
     }
//     override func didMoveToWindow() {
//          self.redraw()
//     }
     override func didMoveToSuperview() {
          super.didMoveToSuperview()
          if self.superview != nil {
               self.displayLink = CADisplayLink(target: self, selector: #selector(displayLinkFired))
               self.displayLink.add(to: RunLoop.main, forMode: .common)
          } else {
               self.displayLink.invalidate()
          }
     }
     @objc func displayLinkFired() {
          self.redraw()
     }





     // // // // // INIT // // // // //
     required init?(coder aDecoder: NSCoder) {
          super.init(coder: aDecoder)
          self.prepareDeviceLayerAndQueue()
          self.makeBuffers()
          self.makePipeline()
     }

     func prepareDeviceLayerAndQueue() {
          metalLayer = (self.layer as! CAMetalLayer)
          metalDevice = MTLCreateSystemDefaultDevice()
          metalLayer.device = metalDevice
          metalLayer.pixelFormat = .bgra8Unorm
          commandQueue = metalDevice.makeCommandQueue()
     }

     func makeBuffers() {
          var vertices: [MBEVertex] = [
               MBEVertex(position: vector_float4(0, 0.5, 0, 1) , color: vector_float4(1, 0, 0, 1)),
               MBEVertex(position: vector_float4(-0.5, -0.5, 0, 1)  , color: vector_float4(0, 1, 0, 1)),
               MBEVertex(position: vector_float4(0.5, -0.5, 0, 1)  , color: vector_float4(0, 0, 1, 1))
          ]
          self.vertexBuffer = metalDevice.makeBuffer(bytes: &vertices, length: 56, options: .storageModeShared)
     }

     func makePipeline() {
          guard let library = metalDevice.makeDefaultLibrary() else { print("COULD NOT CREATE LIBRARY") ; return }
          guard let vertexFunction = library.makeFunction(name: "vertex_main") else { print("COULD NOT CREATE A VERTEX FUNCTION") ; return }
          guard let fragmentFunction = library.makeFunction(name: "fragment_main") else { print("COULD NOT CREATE LIBRARY") ; return }

          let pipelineDescriptor = MTLRenderPipelineDescriptor()
          pipelineDescriptor.vertexFunction = vertexFunction
          pipelineDescriptor.fragmentFunction = fragmentFunction
          pipelineDescriptor.colorAttachments[0].pixelFormat = metalLayer.pixelFormat

          pipelineState = try? metalDevice.makeRenderPipelineState(descriptor: pipelineDescriptor)
          if pipelineState == nil { print("COULD NOT CREATE PIPELINE STATE") ; return }

     }





     // // // // // FUNCTIONS // // // // //
     func redraw() {
          guard let drawable = metalLayer.nextDrawable() else { print("COULD NOT CREATE A DRAWABLE") ; return }
          let texture = drawable.texture
          let renderPassDescriptor = MTLRenderPassDescriptor()
          renderPassDescriptor.colorAttachments[0].texture = texture
          renderPassDescriptor.colorAttachments[0].loadAction = .clear
          renderPassDescriptor.colorAttachments[0].storeAction = .store
          renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColor(red: 0.1, green: 0.1, blue: 0.1, alpha: 1)

          guard let commandBuffer = commandQueue.makeCommandBuffer() else { print("COULD NOT CREATE A COMMAND BUFFER") ; return }
          guard let commandEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor) else { print("COULD NOT CREATE AN ENCODER") ; return }

          commandEncoder.setRenderPipelineState(pipelineState)
          commandEncoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
          commandEncoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 3)

          commandEncoder.endEncoding()
          commandBuffer.present(drawable)
          commandBuffer.commit()
     }





     // // // // // TYPES // // // // //
     struct MBEVertex {
          var position: vector_float4
          var color: vector_float4
     }

}

我曾尝试用不同的方法多次渲染三角形(有时使用界面构建器中的 MetalKit 视图,有时手动创建视图)...但每次,三角形都带有粗糙的边缘.

I have tried to render the triangle a few different times with different methods (sometimes use a MetalKit view from interface builder, sometimes create the view manually)... each time, though, the triangle comes out with rough edges.

推荐答案

这里的主要问题是图层的可绘制尺寸远小于屏幕的分辨率.您可以通过以下步骤使它们匹配:

The main issue here is that the drawable size of your layer is much smaller than the resolution of your screen. You can get them to match by taking the following steps:

当您的 Metal 视图移动到新的超级视图时,更新其 contentsScale 属性以匹配托管显示的属性:

When your Metal view moves to a new superview, update its contentsScale property to match that of the hosting display:

layer.contentsScale = self.window?.screen.scale ?? 1.0

向您的视图子类添加一个属性,该属性根据视图的边界及其比例计算理想的可绘制大小:

Add a property to your view subclass that computes the ideal drawable size based on the bounds of the view and its scale:

var preferredDrawableSize: CGSize {
    return CGSize(width: bounds.size.width * layer.contentsScale,
                  height: bounds.size.height * layer.contentsScale)
}

当您检测到图层与计算出的首选大小不匹配时,更新它的 drawableSize:

Update the drawableSize of your layer when you detect that it doesn't match the computed preferred size:

 func redraw() {
      if metalLayer.drawableSize != preferredDrawableSize {
          metalLayer.drawableSize = preferredDrawableSize
      }
    ...
 }

顺便说一下,现在真的没有充分的理由不使用 MTKView 来达到这个目的.它为您抽象了所有这些细节,并且更易于使用.

By the way, these days there's really no good reason not to use MTKView for this purpose. It abstracts all of these details for you and is much nicer to work with.

这篇关于为什么用粗糙的边缘而不是平滑的边缘渲染三角形?金属、Swift、Xcode的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆