使用 Python對接快遞單號識別查詢api接口查詢物流信息
863
2025-04-04
本文是基于“Metal渲染繪制三角形”這樣頂點較少圖形基礎之上的延伸, 在渲染三角形的時候, 頂點數據的存儲使用的是數組,當頂點傳遞時通過setVertexBytes(_:length:index:)方法,主要是由于繪制三角形時,所需的頂點只有三個,頂點數據很少,所以可以通過數組存儲,此時的數據是存儲在CPU中的;
Metal三角形的渲染繪制請參考:Metal之渲染繪制三角形
對于小于4KB(即4096字節)的一次性數據,使用setVertexBytes(:length:index:),如果數據長度超過4KB 或者需要多次使用頂點數據時,需要創建一個MTLBuffer對象,創建的buffer的目的就是為了將頂點數據存儲到頂點緩存區,GPU可以直接訪問該緩存區獲取頂點數據,并且buffer緩存的數據需要通過setVertexBuffer(:offset:index:)方法傳遞到頂點著色器。
當圖形的頂點數據較多時, 頂點的傳遞與存儲過程如下:
① Metal -> MTLBuffer -> 緩存區(存儲非常多自定義數據,GPU直接訪問 -> 顯存) -> 存儲頂點數據;
② 創建的buffer的目的就是為了將頂點數據存儲到頂點緩存區,GPU可以直接訪問該緩存區獲取頂點數據,并且buffer緩存的數據需要通過 setVertexBuffer(_:offset:index:)方法傳遞到頂點著色器。
metal文件中,在頂點著色函數需要對頂點坐標進行歸一化處理,因為頂點數據初始化時使用的是物體坐標。頂點坐標的歸一化主要有以下步驟:
定義頂點著色器輸出
初始化輸出剪輯空間位置
獲取當前頂點坐標的xy:主要是因為繪制的圖形是2D的,其z都為0
將傳入的視圖大小轉換為vector_float2二維向量類型
頂點坐標歸一化:可以通過一行代碼同時分隔兩個通道x和y,并執行除法,然后將結果放入輸出的x和y通道中,即從像素空間位置轉換為裁剪空間位置
#include
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
主要需要加載metal文件來獲取頂點數據
獲取GPU設備device: 通過視圖控制器中初始化render對象時傳入的MTKView對象view,利用view來獲取GPU的使用權限
_device = mtkView.device;
1
設置繪制紋理的像素格式
mtkView.colorPixelFormat = MTLPixelFormatBGRA8Unorm_sRGB;
1
從項目中加載所以的.metal著色器文件
// 從項目中加載所以的.metal著色器文件 id
1
2
3
4
5
6
配置用于創建管道狀態的管道描述符
// 配置用于創建管道狀態的管道 MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init]; // 管道名稱 pipelineStateDescriptor.label = @"Simple Pipeline"; // 可編程函數,用于處理渲染過程中的各個頂點 pipelineStateDescriptor.vertexFunction = vertexFunction; // 可編程函數,用于處理渲染過程總的各個片段/片元 pipelineStateDescriptor.fragmentFunction = fragmentFunction; // 設置管道中存儲顏色數據的組件格式 pipelineStateDescriptor.colorAttachments[0].pixelFormat = mtkView.colorPixelFormat;
1
2
3
4
5
6
7
8
9
10
同步創建并返回渲染管線對象
// 同步創建并返回渲染管線對象 NSError *error = NULL; _pipelineState = [_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&error];
1
2
3
4
獲取頂點數據
// 獲取頂點數據 NSData *vertexData = [YDWRenderer generateVertexData]; // 創建一個vertex buffer,可以由GPU來讀取 _vertexBuffer = [_device newBufferWithLength:vertexData.length options:MTLResourceStorageModeShared]; /* 復制vertex data 到vertex buffer 通過緩存區的"content"內容屬性訪問指針 * * memcpy(void *dst, const void *src, size_t n); * dst:目的地 * src:源內容 * n: 長度 */ memcpy(_vertexBuffer.contents, vertexData.bytes, vertexData.length); // 計算頂點個數 = 頂點數據長度 / 單個頂點大小 _numVertices = vertexData.length / sizeof(CCVertex);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 頂點數據 + (nonnull NSData *)generateVertexData { // 正方形 = 三角形+三角形 const CCVertex quadVertices[] = { // Pixel 位置, RGBA 顏色 { { -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; // 計算X,Y 位置.注意坐標系基于2D笛卡爾坐標系,中心點(0,0),所以會出現負數位置 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; // 將quadVertices數據復制到currentQuad memcpy(currentQuad, &quadVertices, sizeof(quadVertices)); // 遍歷currentQuad中的數據 for (NSUInteger vertexInQuad = 0; vertexInQuad < NUM_VERTICES_PER_QUAD; vertexInQuad++) { //修改vertexInQuad中的position currentQuad[vertexInQuad].position += upperLeftPosition; } // 更新索引 currentQuad += 6; } } return vertexData; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
創建命令隊列
// 創建命令隊列 _commandQueue = [_device newCommandQueue];
1
2
主要加載頂點緩沖區數據
為當前渲染的每個渲染傳遞創建一個新的命令緩沖區
// 為當前渲染的每個渲染傳遞創建一個新的命令緩沖區 id
1
2
3
4
創建渲染描述符
MTLRenderPassDescriptor *renderPassDescriptor = view.currentRenderPassDescriptor; // 判斷渲染目標是否為空 if(renderPassDescriptor != nil) { // 創建渲染命令編碼器,這樣才可以渲染到something id
1
2
3
4
5
6
7
8
9
設置我們繪制的可繪制區域
/*設置繪制的可繪制區域 * *typedef struct { double originX, originY, width, height, znear, zfar; } MTLViewport; */ [renderEncoder setViewport:(MTLViewport){ 0.0, 0.0, _viewportSize.x, _viewportSize.y, -1.0, 1.0 }];
1
2
3
4
5
6
7
8
9
設置渲染管道
// 設置渲染管道 [renderEncoder setRenderPipelineState:_pipelineState];
1
2
為了從OC代碼找發送數據預加載的MTLBuffer 到Metal 頂點著色函數中
// 將_vertexBuffer 設置到頂點緩存區中 [renderEncoder setVertexBuffer:_vertexBuffer offset:0 atIndex:CCVertexInputIndexVertices]; // 將 _viewportSize 設置到頂點緩存區綁定點設置數據 [renderEncoder setVertexBytes:&_viewportSize length:sizeof(_viewportSize) atIndex:CCVertexInputIndexViewportSize];
1
2
3
4
5
6
7
8
9
開始繪圖
[renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:_numVertices];
1
2
3
結束編碼,表示已該編碼器生成的命令都已完成,并且從NTLCommandBuffer中分離
[renderEncoder endEncoding];
1
一旦框架緩沖區完成,使用當前可繪制的進度表
[commandBuffer presentDrawable:view.currentDrawable];
1
完成渲染并將命令緩沖區推送到GPU
[commandBuffer commit];
1
Metal之MTLBuffer批量加載頂點數量較多的圖形渲染
渲染
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。