渲染原理
本文是基于“Metal渲染繪制三角形”這樣頂點(diǎn)較少圖形基礎(chǔ)之上的延伸, 在渲染三角形的時(shí)候, 頂點(diǎn)數(shù)據(jù)的存儲使用的是數(shù)組,當(dāng)頂點(diǎn)傳遞時(shí)通過setVertexBytes(_:length:index:)方法,主要是由于繪制三角形時(shí),所需的頂點(diǎn)只有三個(gè),頂點(diǎn)數(shù)據(jù)很少,所以可以通過數(shù)組存儲,此時(shí)的數(shù)據(jù)是存儲在CPU中的; Metal三角形的渲染繪制請參考:Metal之渲染繪制三角形 對于小于4KB(即4096字節(jié))的一次性數(shù)據(jù),使用setVertexBytes(:length:index:),如果數(shù)據(jù)長度超過4KB 或者需要多次使用頂點(diǎn)數(shù)據(jù)時(shí),需要創(chuàng)建一個(gè)MTLBuffer對象,創(chuàng)建的buffer的目的就是為了將頂點(diǎn)數(shù)據(jù)存儲到頂點(diǎn)緩存區(qū),GPU可以直接訪問該緩存區(qū)獲取頂點(diǎn)數(shù)據(jù),并且buffer緩存的數(shù)據(jù)需要通過setVertexBuffer(:offset:index:)方法傳遞到頂點(diǎn)著色器。 當(dāng)圖形的頂點(diǎn)數(shù)據(jù)較多時(shí), 頂點(diǎn)的傳遞與存儲過程如下: ① Metal -> MTLBuffer -> 緩存區(qū)(存儲非常多自定義數(shù)據(jù),GPU直接訪問 -> 顯存) -> 存儲頂點(diǎn)數(shù)據(jù); ② 創(chuàng)建的buffer的目的就是為了將頂點(diǎn)數(shù)據(jù)存儲到頂點(diǎn)緩存區(qū),GPU可以直接訪問該緩存區(qū)獲取頂點(diǎn)數(shù)據(jù),并且buffer緩存的數(shù)據(jù)需要通過 setVertexBuffer(_:offset:index:)方法傳遞到頂點(diǎn)著色器。
渲染流程
一、Metal文件
metal文件中,在頂點(diǎn)著色函數(shù)需要對頂點(diǎn)坐標(biāo)進(jìn)行歸一化處理,因?yàn)轫旤c(diǎn)數(shù)據(jù)初始化時(shí)使用的是物體坐標(biāo)。頂點(diǎn)坐標(biāo)的歸一化主要有以下步驟:
定義頂點(diǎn)著色器輸出 初始化輸出剪輯空間位置 獲取當(dāng)前頂點(diǎn)坐標(biāo)的xy:主要是因?yàn)槔L制的圖形是2D的,其z都為0 將傳入的視圖大小轉(zhuǎn)換為vector_float2二維向量類型 頂點(diǎn)坐標(biāo)歸一化:可以通過一行代碼同時(shí)分隔兩個(gè)通道x和y,并執(zhí)行除法,然后將結(jié)果放入輸出的x和y通道中,即從像素空間位置轉(zhuǎn)換為裁剪空間位置
#include <metal_stdlib>
using namespace metal
;
#import "YDWShaderTypes.h"
typedef struct { float4 clipSpacePosition
[ [ position
] ] ; float4 color
; } RasterizerData
;
vertex RasterizerData
vertexShader ( uint vertexID
[ [ vertex_id
] ] , constant CCVertex
* vertices
[ [ buffer ( CCVertexInputIndexVertices
) ] ] , constant vector_uint2
* viewportSizePointer
[ [ buffer ( CCVertexInputIndexViewportSize
) ] ] ) { RasterizerData out
; out
. clipSpacePosition
= vector_float4 ( 0.0 , 0.0 , 0.0 , 1.0 ) ; float2 pixelSpacePosition
= vertices
[ vertexID
] . position
. xy
; vector_float2 viewportSize
= vector_float2 ( * viewportSizePointer
) ; out
. clipSpacePosition
. xy
= pixelSpacePosition
/ ( viewportSize
/ 2.0 ) ; out
. color
= vertices
[ vertexID
] . color
; return out
;
}
fragment float4
fragmentShader ( RasterizerData
in [ [ stage_in
] ] ) { return in . color
;
}
二、 initWithMetalKitView
主要需要加載metal文件來獲取頂點(diǎn)數(shù)據(jù)
獲取GPU設(shè)備device: 通過視圖控制器中初始化render對象時(shí)傳入的MTKView對象view,利用view來獲取GPU的使用權(quán)限
_device
= mtkView
. device
;
mtkView
. colorPixelFormat
= MTLPixelFormatBGRA8Unorm_sRGB
;
從項(xiàng)目中加載所以的.metal著色器文件
id
< MTLLibrary
> defaultLibrary
= [ _device newDefaultLibrary
] ; id
< MTLFunction
> vertexFunction
= [ defaultLibrary newFunctionWithName
: @"vertexShader" ] ; id
< MTLFunction
> fragmentFunction
= [ defaultLibrary newFunctionWithName
: @"fragmentShader" ] ;
配置用于創(chuàng)建管道狀態(tài)的管道描述符
MTLRenderPipelineDescriptor
* pipelineStateDescriptor
= [ [ MTLRenderPipelineDescriptor alloc
] init
] ; pipelineStateDescriptor
. label
= @"Simple Pipeline" ; pipelineStateDescriptor
. vertexFunction
= vertexFunction
; pipelineStateDescriptor
. fragmentFunction
= fragmentFunction
; pipelineStateDescriptor
. colorAttachments
[ 0 ] . pixelFormat
= mtkView
. colorPixelFormat
;
NSError
* error
= NULL ; _pipelineState
= [ _device newRenderPipelineStateWithDescriptor
: pipelineStateDescriptorerror
: & error
] ;
NSData
* vertexData
= [ YDWRenderer generateVertexData
] ; _vertexBuffer
= [ _device newBufferWithLength
: vertexData
. lengthoptions
: MTLResourceStorageModeShared
] ; memcpy ( _vertexBuffer
. contents
, vertexData
. bytes
, vertexData
. length
) ; _numVertices
= vertexData
. length
/ sizeof ( CCVertex
) ;
+ ( nonnull NSData
* ) generateVertexData
{ const CCVertex quadVertices
[ ] = { { { - 20 , 20 } , { 1 , 0 , 0 , 1 } } , { { 20 , 20 } , { 1 , 0 , 0 , 1 } } , { { - 20 , - 20 } , { 1 , 0 , 0 , 1 } } , { { 20 , - 20 } , { 0 , 0 , 1 , 1 } } , { { - 20 , - 20 } , { 0 , 0 , 1 , 1 } } , { { 20 , 20 } , { 0 , 0 , 1 , 1 } } , } ; const NSUInteger NUM_COLUMNS
= 25 ; const NSUInteger NUM_ROWS
= 15 ; const NSUInteger NUM_VERTICES_PER_QUAD
= sizeof ( quadVertices
) / sizeof ( CCVertex
) ; const float QUAD_SPACING
= 50.0 ; NSUInteger dataSize
= sizeof ( quadVertices
) * NUM_COLUMNS
* NUM_ROWS
; NSMutableData
* vertexData
= [ [ NSMutableData alloc
] initWithLength
: dataSize
] ; CCVertex
* currentQuad
= vertexData
. mutableBytes
; for ( NSUInteger row
= 0 ; row
< NUM_ROWS
; row
++ ) { for ( NSUInteger column
= 0 ; column
< NUM_COLUMNS
; column
++ ) { vector_float2 upperLeftPosition
; upperLeftPosition
. x
= ( ( - ( ( float ) NUM_COLUMNS
) / 2.0 ) + column
) * QUAD_SPACING
+ QUAD_SPACING
/ 2.0 ; upperLeftPosition
. y
= ( ( - ( ( float ) NUM_ROWS
) / 2.0 ) + row
) * QUAD_SPACING
+ QUAD_SPACING
/ 2.0 ; memcpy ( currentQuad
, & quadVertices
, sizeof ( quadVertices
) ) ; for ( NSUInteger vertexInQuad
= 0 ; vertexInQuad
< NUM_VERTICES_PER_QUAD
; vertexInQuad
++ ) { currentQuad
[ vertexInQuad
] . position
+ = upperLeftPosition
; } currentQuad
+ = 6 ; } } return vertexData
;
}
_commandQueue
= [ _device newCommandQueue
] ;
三、drawInMTKView
主要加載頂點(diǎn)緩沖區(qū)數(shù)據(jù)
為當(dāng)前渲染的每個(gè)渲染傳遞創(chuàng)建一個(gè)新的命令緩沖區(qū)
id
< MTLCommandBuffer
> commandBuffer
= [ _commandQueue commandBuffer
] ; commandBuffer
. label
= @"MyCommand" ;
MTLRenderPassDescriptor
* renderPassDescriptor
= view
. currentRenderPassDescriptor
; if ( renderPassDescriptor
!= nil
) { id
< MTLRenderCommandEncoder
> renderEncoder
= [ commandBuffer renderCommandEncoderWithDescriptor
: renderPassDescriptor
] ; renderEncoder
. label
= @"MyRenderEncoder" ; }
[ renderEncoder setViewport
: ( MTLViewport
) { 0.0 , 0.0 , _viewportSize
. x
, _viewportSize
. y
, - 1.0 , 1.0 } ] ;
[ renderEncoder setRenderPipelineState
: _pipelineState
] ;
為了從OC代碼找發(fā)送數(shù)據(jù)預(yù)加載的MTLBuffer 到Metal 頂點(diǎn)著色函數(shù)中
[ renderEncoder setVertexBuffer
: _vertexBufferoffset
: 0 atIndex
: CCVertexInputIndexVertices
] ; [ renderEncoder setVertexBytes
: & _viewportSizelength
: sizeof ( _viewportSize
) atIndex
: CCVertexInputIndexViewportSize
] ;
[ renderEncoder drawPrimitives
: MTLPrimitiveTypeTrianglevertexStart
: 0 vertexCount
: _numVertices
] ;
結(jié)束編碼,表示已該編碼器生成的命令都已完成,并且從NTLCommandBuffer中分離
[ renderEncoder endEncoding
] ;
一旦框架緩沖區(qū)完成,使用當(dāng)前可繪制的進(jìn)度表
[ commandBuffer presentDrawable
: view
. currentDrawable
] ;
[ commandBuffer commit
] ;
效果展示
完整示例
Metal之MTLBuffer批量加載頂點(diǎn)數(shù)量較多的圖形渲染
總結(jié)
以上是生活随笔 為你收集整理的Metal之MTLBuffer批量加载顶点数量较多的图形渲染 的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔 推薦給好友。