Metal开发入门(三) 绘制动画

9 篇文章 0 订阅
4 篇文章 0 订阅

上一节交代了如何使用gpu绘制一个三角形,这一届会利用gpu来绘制一个简单的动画#

首先绘制一个矩形

在上一节的基础上,如果需要绘制一个矩形,只需要在原来的verices里面多添加几个定点即可,如下图所示,第一个三角形的定点是,v0,v1,v2,第二个是v2,v3,v0。
在这里插入图片描述

 var vetices : [Float] = [
    -1,1,0,
     -1,-1,0,
     1,-1,0,
     1,-1,0,
     1,1,0,
     -1,1,0
]

使用index来减少vertex数量

vertex以后只负责记录有效顶点的位置,index则是负责,三角形的绘制顺序。

var vetices : [Float] = [
  -1,1,0,
     -1,-1,0,
     1,-1,0,
     1,1,0
 ]
 var indices:[UInt16] = [
     0,1,2,
     2,3,0
 ]

在初始化的过程中,需要初始化一个indices的buffer。

indexBuffer = device.makeBuffer(bytes: indices, length: indices.count * MemoryLayout<UInt16>.size, options: []) //定点存储的位置

最后,代替drawPrimitives,使用drawIndexedPrimitives,

commandEncoder?.drawIndexedPrimitives(type: .triangle, indexCount: indices.count, indexType: .uint16, indexBuffer: indexBuffer, indexBufferOffset: 0)

传入动画参数

  1. 创建一个数据结构,来存储动画信息,这里只有移动的偏移量。
struct Constants {
    var animateBy:Float = 0.0
}
var constants = Constants()
  1. 在commandCoder内设置一个vertextByte,其中index为1
commandEncoder?.setVertexBytes(&constants, length: MemoryLayout<Constants>.stride, index: 1)
  1. 在shader.metal里面,添加另外一个接受端的数据结构。添加Vertex的参数,constant Constants &constants [[buffer(1)]],这里buffer参数的1,对应的setVertexBytes 的最后一个参数 index。
struct Constants{
    float animate_by;
};
  1. 修改vertex_shader为以下代码,从传入的constants变量中拿到animate_by,计算后得到新的坐标点。然后下面的fragment的shader保持不变。
vertex float4 vertex_shader(const device packed_float3 *vertices [[buffer(0)]] ,
                            constant Constants &constants [[buffer(1)]],
                            uint vertexId [[vertex_id]] ){
    float4 pos = float4(vertices[vertexId],1);
    pos.x += constants.animate_by;
    return pos;
}
  1. 此时,每次传入的constants都是保持不变的,画面并不会根据帧数的改变而改变。因此我们在draw的时候,添加计算逻辑。其中,time作为一个成员变量来保存一个进度信息。
 time += 1 / Float(view.preferredFramesPerSecond) //60fps
 constants.animateBy = abs(sin(time)/2 + 0.5) // sin函数来模拟一个淡入淡出的效果

整体代码


import UIKit
import MetalKit

enum Colors {
    static let wenderlichGreen = MTLClearColor(red:0.0,green: 0.4,blue: 0.21,alpha: 1.0)
}
struct Constants {
    var animateBy:Float = 0.0
}

class MyViewController:UIViewController,MTKViewDelegate{
    var vetices : [Float] = [
        -1,1,0,
        -1,-1,0,
        1,-1,0,
        1,1,0
    ]
    var indices:[UInt16] = [
        0,1,2,
        2,3,0
    ]
    var constants = Constants()
    var metalView:MTKView{
        return view as! MTKView
    }
    var device:MTLDevice!
    var commandQueue:MTLCommandQueue!
    var vetextBuffer:MTLBuffer!
    var indexBuffer:MTLBuffer!
    var piplineState:MTLRenderPipelineState!
    var time:Float = 0
    override func viewDidLoad() {
        super.viewDidLoad()
        metalView.device = MTLCreateSystemDefaultDevice()// 创建设备
        device = metalView.device //设置到controller的成员变量中
        metalView.clearColor = Colors.wenderlichGreen //设置背景颜色
        commandQueue = device.makeCommandQueue() //为gpu准备指令队列
        vetextBuffer = device.makeBuffer(bytes: vetices, length: vetices.count * MemoryLayout<Float>.size, options: []) //定点存储的位置
        indexBuffer = device.makeBuffer(bytes: indices, length: indices.count * MemoryLayout<UInt16>.size, options: []) //定点存储的位置
        let library = device.makeDefaultLibrary()//框架会自动找到项目中的Metal文件
        let vertex_shader = library?.makeFunction(name: "vertex_shader")//编译vertex_shader函数
        let fragment_shader = library?.makeFunction(name: "fragment_shader")//编译fragment_shader函数
        let piplineDescriptor = MTLRenderPipelineDescriptor();//生成piplinDescriptor
        piplineDescriptor.vertexFunction = vertex_shader //设置vertex_shader函数
        piplineDescriptor.fragmentFunction = fragment_shader//设置fragment_shader函数
        piplineDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm //设置颜色格式,这个应该是固定写法
        do {
            piplineState = try device.makeRenderPipelineState(descriptor: piplineDescriptor)//通过piplineDescript生成piplineState
        } catch let error as NSError {
            print("error \(error.localizedDescription)")
        }
        metalView.delegate = self
    }
    func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
    }
    func draw(in view: MTKView) {
        guard let pState = piplineState else {
            return
        }
        time += 1 / Float(view.preferredFramesPerSecond) //60fps
        constants.animateBy = abs(sin(time)/2 + 0.5)
        let commandBuffer = commandQueue.makeCommandBuffer() //为指令队列设置缓冲区
        let commandEncoder = commandBuffer?.makeRenderCommandEncoder(descriptor: metalView.currentRenderPassDescriptor!) //为缓冲区创建一个编码器
        commandEncoder?.setRenderPipelineState(pState);
        commandEncoder?.setVertexBuffer(vetextBuffer, offset: 0, index: 0)
        commandEncoder?.setVertexBytes(&constants, length: MemoryLayout<Constants>.stride, index: 1)
        //commandEncoder?.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: vetices.count)
        commandEncoder?.drawIndexedPrimitives(type: .triangle, indexCount: indices.count, indexType: .uint16, indexBuffer: indexBuffer, indexBufferOffset: 0)
        commandEncoder?.endEncoding()//停止编码
        commandBuffer?.present(metalView.currentDrawable as! MTLDrawable) //绘制图像
        commandBuffer?.commit() //提交给gpu
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值