MTLTexture
??一個MTLTexture對象代表了一個格式化后的圖像數(shù)據(jù)的內(nèi)存空間,它可以被用于頂點著色器、片段著色器和計算函數(shù)的資源,或者作為一個渲染目標(biāo)附件。
創(chuàng)建一個MTLTexture
-
通過MTLDevice創(chuàng)建,為紋理圖像數(shù)據(jù)開辟一個新的內(nèi)存空間并創(chuàng)建一個
MTLTexture對象,它將根據(jù)傳入的MTLTextureDescriptor對象設(shè)置此紋理的屬性- (nullable id <MTLTexture>)newTextureWithDescriptor:(MTLTextureDescriptor *)descriptor; -
通過MTLTexture創(chuàng)建,將會重新按照傳入的像素格式詮釋調(diào)用對象內(nèi)存儲的圖像數(shù)據(jù)。傳入的
MTLPixelFormat必須與原調(diào)用對象的MTLPixelFormat兼容.- (nullable id<MTLTexture>)newTextureViewWithPixelFormat:(MTLPixelFormat)pixelFormat; -
通過MTLBuffer創(chuàng)建一個與調(diào)用者共享內(nèi)存空間的
MTLTexture對象,由于它們共享相同的內(nèi)存空間,所有在新紋理對象上的改動都會反映到調(diào)用對象上,反之亦然。在紋理和緩沖之間共享存儲空間會防止使用某些紋理優(yōu)化,如像素調(diào)整或平鋪。- (nullable id <MTLTexture>)newTextureWithDescriptor:(MTLTextureDescriptor*)descriptor offset:(NSUInteger)offset bytesPerRow:(NSUInteger)bytesPerRow
利用紋理描述符創(chuàng)建紋理對象(MTLTextureDescriptor )
?MTLTextureDescriptor 類定義了創(chuàng)建一個紋理對象所需要的屬性,包括圖片尺寸(寬、高、深度)、像素格式、布局格式(數(shù)組或立方體)以及 mipmap 的個數(shù)。
?MTLTextureDescriptor 僅用于創(chuàng)建 MTLTexture 對象的過程中,一旦創(chuàng)建完成,對 MTLTextureDescriptor 的屬性修改將不再對 MTLTexture 生效。
紋理對象加載紋理圖片數(shù)據(jù)
從調(diào)用者指針指向的紋理存儲空間中拷貝一部分區(qū)域的數(shù)據(jù)到一個默認(rèn)的紋理切片
- (void)replaceRegion:(MTLRegion)region
mipmapLevel:(NSUInteger)level
withBytes:(const void *)pixelBytes
bytesPerRow:(NSUInteger)bytesPerRow;
傳遞紋理圖片數(shù)據(jù)到片元著色器
- (void)setFragmentTexture:(nullable id <MTLTexture>)texture
atIndex:(NSUInteger)index;
完整的紋理加載流程

Shader文件
-
頂點著色器
#include <metal_stdlib> //使用命名空間 Metal using namespace metal; // 導(dǎo)入Metal shader 代碼和執(zhí)行Metal API命令的C代碼之間共享的頭 #import "CCShaderTypes.h" // 頂點著色器輸出和片段著色器輸入 //結(jié)構(gòu)體 typedef struct { //處理空間的頂點信息 float4 clipSpacePosition [[position]]; //顏色 float4 color; } RasterizerData; //頂點著色函數(shù) vertex RasterizerData vertexShader(uint vertexID [[vertex_id]], constant CCVertex *vertices [[buffer(CCVertexInputIndexVertices)]], constant vector_uint2 *viewportSizePointer [[buffer(CCVertexInputIndexViewportSize)]]) { /* 處理頂點數(shù)據(jù): 1) 執(zhí)行坐標(biāo)系轉(zhuǎn)換,將生成的頂點剪輯空間寫入到返回值中. 2) 將頂點顏色值傳遞給返回值 */ //定義out RasterizerData out; //初始化輸出剪輯空間位置 out.clipSpacePosition = vector_float4(0.0, 0.0, 0.0, 1.0); // 索引到我們的數(shù)組位置以獲得當(dāng)前頂點 // 我們的位置是在像素維度中指定的. float2 pixelSpacePosition = vertices[vertexID].position.xy; //將vierportSizePointer 從verctor_uint2 轉(zhuǎn)換為vector_float2 類型 vector_float2 viewportSize = vector_float2(*viewportSizePointer); //每個頂點著色器的輸出位置在剪輯空間中(也稱為歸一化設(shè)備坐標(biāo)空間,NDC),剪輯空間中的(-1,-1)表示視口的左下角,而(1,1)表示視口的右上角. //計算和寫入 XY值到我們的剪輯空間的位置.為了從像素空間中的位置轉(zhuǎn)換到剪輯空間的位置,我們將像素坐標(biāo)除以視口的大小的一半. out.clipSpacePosition.xy = pixelSpacePosition / (viewportSize / 2.0); //把我們輸入的顏色直接賦值給輸出顏色. 這個值將于構(gòu)成三角形的頂點的其他顏色值插值,從而為我們片段著色器中的每個片段生成顏色值. out.color = vertices[vertexID].color; //完成! 將結(jié)構(gòu)體傳遞到管道中下一個階段: return out; } //當(dāng)頂點函數(shù)執(zhí)行3次,三角形的每個頂點執(zhí)行一次后,則執(zhí)行管道中的下一個階段.柵格化/光柵化. -
片元著色器
紋理采樣
fragment float4 fragmentShader(RasterizerData in [[stage_in]], texture2d<half> colorTexture [[texture(CCTextureIndexBaseColor)]]) { constexpr sampler textureSampler(mag_filter::linear, min_filter::linear); const half4 colorSampler = colorTexture.sample(textureSampler,in.textureCoordinate); return float4(colorSampler); //返回輸入的片元顏色 //return in.color; }
初始化
-
加載Shader文件
//1.設(shè)置繪制紋理的像素格式 mtkView.colorPixelFormat = MTLPixelFormatBGRA8Unorm_sRGB; //2.從項目中加載所以的.metal著色器文件 id<MTLLibrary> defaultLibrary = [_device newDefaultLibrary]; //從庫中加載頂點函數(shù) id<MTLFunction> vertexFunction = [defaultLibrary newFunctionWithName:@"vertexShader"]; //從庫中加載片元函數(shù) id<MTLFunction> fragmentFunction = [defaultLibrary newFunctionWithName:@"fragmentShader"]; -
加載渲染管道
//3.配置用于創(chuàng)建管道狀態(tài)的管道 MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init]; //管道名稱 pipelineStateDescriptor.label = @"Simple Pipeline"; //可編程函數(shù),用于處理渲染過程中的各個頂點 pipelineStateDescriptor.vertexFunction = vertexFunction; //可編程函數(shù),用于處理渲染過程總的各個片段/片元 pipelineStateDescriptor.fragmentFunction = fragmentFunction; //設(shè)置管道中存儲顏色數(shù)據(jù)的組件格式 pipelineStateDescriptor.colorAttachments[0].pixelFormat = mtkView.colorPixelFormat; //4.同步創(chuàng)建并返回渲染管線對象 NSError *error = NULL; _pipelineState = [_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&error]; //判斷是否創(chuàng)建成功 if (!_pipelineState) { NSLog(@"Failed to created pipeline state, error %@", error); } -
創(chuàng)建命令隊列
_commandQueue = [_device newCommandQueue]; -
處理頂點數(shù)據(jù)
//1.根據(jù)頂點/紋理坐標(biāo)建立一個MTLBuffer static const CCVertex quadVertices[] = { //像素坐標(biāo),紋理坐標(biāo) { { 250, -250 }, { 1.f, 0.f } }, { { -250, -250 }, { 0.f, 0.f } }, { { -250, 250 }, { 0.f, 1.f } }, { { 250, -250 }, { 1.f, 0.f } }, { { -250, 250 }, { 0.f, 1.f } }, { { 250, 250 }, { 1.f, 1.f } }, }; //2.創(chuàng)建我們的頂點緩沖區(qū),并用我們的Qualsits數(shù)組初始化它 _vertices = [_device newBufferWithBytes:quadVertices length:sizeof(quadVertices) options:MTLResourceStorageModeShared]; //3.通過將字節(jié)長度除以每個頂點的大小來計算頂點的數(shù)目 _numVertices = sizeof(quadVertices) / sizeof(CCVertex); -
處理紋理數(shù)據(jù)
獲取位圖數(shù)據(jù)
-
利用紋理描述符創(chuàng)建紋理對象(
MTLTextureDescriptor)//2.創(chuàng)建紋理描述對象 MTLTextureDescriptor *textureDescriptor = [[MTLTextureDescriptor alloc]init]; //表示每個像素有藍色,綠色,紅色和alpha通道.其中每個通道都是8位無符號歸一化的值.(即0映射成0,255映射成1); textureDescriptor.pixelFormat = MTLPixelFormatBGRA8Unorm; //設(shè)置紋理的像素尺寸 textureDescriptor.width = image.width; textureDescriptor.height = image.height; //使用描述符從設(shè)備中創(chuàng)建紋理 _texture = [_device newTextureWithDescriptor:textureDescriptor]; -
紋理對象加載紋理圖片數(shù)據(jù)
-(void)replaceRegion:mipmapLevel: withBytes:bytesPerRow://計算圖像每行的字節(jié)數(shù) NSUInteger bytesPerRow = 4 * image.width; /* typedef struct { MTLOrigin origin; //開始位置x,y,z MTLSize size; //尺寸width,height,depth } MTLRegion; */ //MLRegion結(jié)構(gòu)用于標(biāo)識紋理的特定區(qū)域。 demo使用圖像數(shù)據(jù)填充整個紋理;因此,覆蓋整個紋理的像素區(qū)域等于紋理的尺寸。 //3. 創(chuàng)建MTLRegion 結(jié)構(gòu)體 MTLRegion region = { {0,0,0}, {image.width,image.height,1} }; //4.復(fù)制圖片數(shù)據(jù)到texture [_texture replaceRegion:region mipmapLevel:0 withBytes:image.data.bytes bytesPerRow:bytesPerRow];
渲染(drawInMTKView:)
-
創(chuàng)建命令緩沖區(qū)
//1.為當(dāng)前渲染的每個渲染傳遞創(chuàng)建一個新的命令緩沖區(qū) id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer]; //指定緩存區(qū)名稱 commandBuffer.label = @"MyCommand"; -
獲取渲染描述符
//2. MTLRenderPassDescriptor:一組渲染目標(biāo),用作渲染通道生成的像素的輸出目標(biāo)。 //currentRenderPassDescriptor 從currentDrawable's texture,view's depth, stencil, and sample buffers and clear values. MTLRenderPassDescriptor *renderPassDescriptor = view.currentRenderPassDescriptor; //判斷渲染目標(biāo)是否為空 -
通過描述符創(chuàng)建渲染編碼器
//創(chuàng)建渲染命令編碼器,這樣我們才可以渲染到something id<MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; //渲染器名稱 renderEncoder.label = @"MyRenderEncoder"; //3.設(shè)置我們繪制的可繪制區(qū)域 /* 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 }]; //4. 設(shè)置渲染管道 [renderEncoder setRenderPipelineState:_pipelineState]; -
設(shè)置頂點數(shù)據(jù)及視口大小
//將_vertexBuffer 設(shè)置到頂點緩存區(qū)中 [renderEncoder setVertexBuffer:_vertexBuffer offset:0 atIndex:CCVertexInputIndexVertices]; //將 _viewportSize 設(shè)置到頂點緩存區(qū)綁定點設(shè)置數(shù)據(jù) [renderEncoder setVertexBytes:&_viewportSize length:sizeof(_viewportSize) atIndex:CCVertexInputIndexViewportSize]; -
設(shè)置紋理對象
- (void)setFragmentTexture:atIndex:[renderEncoder setFragmentTexture:_texture atIndex:CCTextureIndexBaseColor]; -
繪制
[renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:_numVertices]; //7/表示已該編碼器生成的命令都已完成,并且從NTLCommandBuffer中分離 [renderEncoder endEncoding]; //8.一旦框架緩沖區(qū)完成,使用當(dāng)前可繪制的進度表 [commandBuffer presentDrawable:view.currentDrawable]; //9.最后,在這里完成渲染并將命令緩沖區(qū)推送到GPU [commandBuffer commit];