日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Metal之加载TGA与PNG/JPEG纹理图片

發布時間:2024/5/21 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Metal之加载TGA与PNG/JPEG纹理图片 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

TGA紋理

① 效果展示

② 環境準備
  • 視圖控制器類:在 viewDidLoad 函數中創建 MTKView 對象、自定義 render 對象,并設置 view 的代理為 render,其流程請參考:OpenGL之簡單渲染一個三角形;
  • OC 與 C 的橋接文件:創建 metal 文件與 OC 通用的索引枚舉(包括頂點數據索引、紋理索引) 和頂點數據的結構體,如下:
typedef enum YDWVertexInputIndex {// 頂點YDWVertexInputIndexVertices = 0,// 視圖大小YDWVertexInputIndexViewportSize = 1,}YDWVertexInputIndex;// 紋理索引:用于往片元著色器傳遞紋理數據的索引typedef enum YDWTextureIndex {YDWTextureIndexBaseColor = 0}YDWTextureIndex;// 頂點數據結構體:頂點/顏色值typedef struct{// 像素空間的位置// 像素中心點(100,100)// 頂點坐標vector_float2 position;// 2D 紋理// 紋理坐標vector_float2 textureCoordinate;}YDWVertex;
  • YDWImage 類:該類是將 TGA 轉換為位圖的工具類。由于平常的開發中很少去加載 TGA 文件,一般都是 PNG/JPG 文件,所以 TGA 文件轉換成位圖,其本質是將 TGA 文件的數據復制到 BGRA 圖像數據指針指向的內存中。
  • 創建 metal 文件
    • 主要是1個結構體+兩個函數,結構體中包含頂點坐標 和紋理坐標,作為定點著色器的輸出以及片元著色器的輸入;
// 返回值結構體:頂點著色器輸出和片元著色器輸入(相當于OpenGL ES中的varying修飾的變量,即橋接)typedef struct {// 頂點坐標float4 clipSpacePosition [[position]];// 紋理坐標float2 textureCoordinate;}RasterizerData;
    • 頂點著色函數:主要是將頂點坐標歸一化處理,并將處理后的頂點坐標和紋理坐標輸出,經過 metal 的圖元裝配和光柵化處理,將頂點數據傳入片元著色器,其流程圖如下:
vertex RasterizerDatavertexShader(uint vertexID [[vertex_id]],constant YDWVertex *vertexArray [[buffer(YDWVertexInputIndexVertices)]],constant vector_uint2 *viewportSizePointer [[buffer(YDWVertexInputIndexViewportSize)]]){/*處理頂點數據:1) 執行坐標系轉換,將生成的頂點剪輯空間寫入到返回值中.2) 將頂點顏色值傳遞給返回值*/// 1、定義outRasterizerData out;// 2、初始化輸出剪輯空間位置,將w改為2.0,實際運行結果比1.0小一倍out.clipSpacePosition = vector_float4(0.0, 0.0, 0.0, 1.0);// 3、獲取當前頂點坐標的xy,因為是2D圖形// 索引到我們的數組位置以獲得當前頂點// 我們的位置是在像素維度中指定的.float2 pixelSpacePosition = vertexArray[vertexID].position.xy;// 將vierportSizePointer 從verctor_uint2 轉換為vector_float2 類型vector_float2 viewportSize = vector_float2(*viewportSizePointer);// 4、頂點坐標歸一化處理// 每個頂點著色器的輸出位置在剪輯空間中(也稱為歸一化設備坐標空間,NDC),剪輯空間中的(-1,-1)表示視口的左下角,而(1,1)表示視口的右上角.// 計算和寫入 XY值到我們的剪輯空間的位置.為了從像素空間中的位置轉換到剪輯空間的位置,我們將像素坐標除以視口的大小的一半.// 可以使用一行代碼同時分割兩個通道。執行除法,然后將結果放入輸出位置的x和y通道中out.clipSpacePosition.xy = pixelSpacePosition / (viewportSize / 2.0);out.clipSpacePosition.z = 0.0f;out.clipSpacePosition.w = 1.0f;// 把我們輸入的顏色直接賦值給輸出顏色. 這個值將于構成三角形的頂點的其他顏色值插值,從而為我們片段著色器中的每個片段生成顏色值.// 紋理坐標橋接out.textureCoordinate = vertexArray[vertexID].textureCoordinate;// 完成! 將結構體傳遞到管道中下一個階段return out;}
    • 片元著色函數:主要是通過采樣器獲取紋素,相當于 GLSL 中內建函數 texture2D,在 metal 中是通過紋理的 sample 函數獲取,主要流程圖如下:
fragment float4 fragmentShader(RasterizerData in [[stage_in]],texture2d<half> colorTexture [[texture(YDWTextureIndexBaseColor)]]){// 當texture2d沒有寫access時,默認是sampler,// 設置采樣器:過濾方式constexpr sampler textureSampler(mag_filter::linear, min_filter::linear);// 讀取紋素(即紋理對應像素點的顏色值),相當于GLSL中的內建函數texture2D// half4取決于 texture2d<half>中的half ,由于顏色是RGBA,所以是half4// GLSL中屬性的設置都是通過狀態機,// 而metal中屬性的設置是一個對象的思維,都是紋理采樣的屬性設置const half4 colorSampler = colorTexture.sample(textureSampler, in.textureCoordinate);// 進行灰度/...// 將half4 類型轉換為 float4類型,如果不想這么麻煩,也可以將texture2d<half>中的half改為float,這樣顏色的類型就是float4了return float4(colorSampler);}
  • 創建自定義render渲染循環類:蘋果建議將渲染循環單獨寫成一個類,處理 metal 的渲染及委托事件,其中 TGA 文件的加載也是在 render 中處理的,它會將處理好的頂點數據及紋理圖片傳入著色器,著色器處理完成后會繪制并顯示到屏幕上。
③ 渲染循環類
  • initWithMetalKitView 函數:
    • 初始化GPU設備:通過傳入的view獲取獲取GPU的使用權限;
    • setupVertex函數:設置頂點相關操作;
    • setupPipeLine函數:設置渲染管道相關操作;
    • setupTexture函數:加載TGA文件;
    • setupVertex 函數:主要是出初始化頂點數據,包括頂點坐標和紋理坐標,當頂點坐標的范圍不是-1~1時,是位于物體坐標系,需要在 metal 文件的頂點著色器中作歸一化處理,并且將頂點數據存儲到 MTLBuffer 對象中,GPU 函數流程如下:
// 設置頂點相關操作- (void) setupVertex{// 1、根據頂點/紋理坐標建立一個MTLBufferstatic const YDWVertex quadVertices[] = {// 不是-1~1的都是物體坐標系// 像素坐標,紋理坐標{ { 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、創建我們的頂點緩沖區,并用我們的Qualsits數組初始化它_vertexBuffer = [_device newBufferWithBytes:quadVertices length:sizeof(quadVertices) options:MTLResourceStorageModeShared];// 3、通過將字節長度除以每個頂點的大小來計算頂點的數目_numVertices = sizeof(quadVertices) / sizeof(YDWVertex); }
    • setupPipeLine 函數:主要是渲染管道相關的初始化工作,對應的流程圖如下:
    • 渲染管道的初始化分為:
      • 加載metal文件;
      • 配置渲染管道;
      • 創建渲染管線對象;
      • 設置 commandQueue 命令對象。
// 設置渲染管道相關操作- (void)setupPipeLine{// 1、創建渲染管道// 從項目中加載.metal文件,創建一個libraryid<MTLLibrary> defaultLibrary = [_device newDefaultLibrary];// 從庫中加載頂點函數id<MTLFunction> vertexFunction = [defaultLibrary newFunctionWithName:@"vertexShader"];// 從庫中加載片元函數id<MTLFunction> fragmentFunction = [defaultLibrary newFunctionWithName:@"fragmentShader"];// 2、配置用于創建渲染管道狀態的管道MTLRenderPipelineDescriptor *renderPipelineDescriptor = [[MTLRenderPipelineDescriptor alloc] init];// 管道名稱renderPipelineDescriptor.label = @"Texturing Pipeline";// 可編程函數,用于處理渲染過程中的各個頂點renderPipelineDescriptor.vertexFunction = vertexFunction;// 可編程函數,用于處理渲染過程總的各個片段/片元renderPipelineDescriptor.fragmentFunction = fragmentFunction;// 設置管道中存儲顏色數據的組件格式renderPipelineDescriptor.colorAttachments[0].pixelFormat = ydwMTKView.colorPixelFormat;// 3、創建并返回渲染管線對象 & 判斷是否創建成功NSError *error;_pipelineState = [_device newRenderPipelineStateWithDescriptor:renderPipelineDescriptor error:&error];if (!_pipelineState) {NSLog(@"Failed to created pipeline state, error %@", error);}// 4、使用device創建commandQueue_commandQueue = [_device newCommandQueue];}
    • setupTexture 函數:加載TGA圖片,如下:
    • TGA 圖片的加載分為以下步驟:
      • 獲取 TGA 文件:主要是 TGA 文件通過轉換為 YDWImage 對象,即紋理圖片轉換為位圖:
// 1、獲取TGA文件路徑 --- TGA文件解壓NSURL *imageFileLocation = [[NSBundle mainBundle] URLForResource:@"circle" withExtension:@"tga"];// 將tag文件->YDWImage對象YDWImage *image = [[YDWImage alloc] initWithTGAFileAtLocation:imageFileLocation];// 判斷圖片是否轉換成功if (!image) {NSLog(@"Failed to create the image from:%@",imageFileLocation.absoluteString);return;}
      • 創建紋理描述對象 & 紋理對象:紋理對象通過創建的紋理描述對象以及解壓的位圖生成,其中需要設置紋理描述對象的相關屬性:
// 2、創建紋理描述對象 & 設置屬性 --- YDWImage --> 紋理(即位圖變成紋理對象)MTLTextureDescriptor *textureDescriptor = [[MTLTextureDescriptor alloc] init];// 表示每個像素有藍色,綠色,紅色和alpha通道.其中每個通道都是8位無符號歸一化的值.(即0映射成0,255映射成1);// 位圖信息textureDescriptor.pixelFormat = MTLPixelFormatBGRA8Unorm;// 設置紋理的像素尺寸,即紋理的分辨率textureDescriptor.width = image.width;textureDescriptor.height = image.height;// 3、創建紋理對象:使用描述符從設備中創建紋理_texture = [_device newTextureWithDescriptor:textureDescriptor];
      • 將紋理圖片復制到 texture 對象中:在復制紋理圖片之前,首先需要計算圖像每行的字節數,以及設置紋理對應的像素區域region,region的定義如下,其中包含兩個結構體對象origin和size,其中origin表示開始的頂點坐標(x,y,z),size表示尺寸(寬度、高度、深度):
typedef struct {MTLOrigin origin; // 開始位置x,y,zMTLSize size; // 尺寸width,height,depth} MTLRegion;
      • 然后通過replaceRegion: mipmapLevel: withBytes: bytesPerRow:函數將圖片復制到紋理對象中:
// 計算圖像每行的字節數NSUInteger bytesPerRow = 4 * image.width;// 4、創建MTLRegion結構體/*typedef struct{MTLOrigin origin; //開始位置x,y,zMTLSize size; //尺寸width,height,depth} MTLRegion;*/// MLRegion結構用于標識紋理的特定區域。 demo使用圖像數據填充整個紋理;因此,覆蓋整個紋理的像素區域等于紋理的尺寸。MTLRegion region = {{0,0,0},{image.width, image.height, 1},};// 5、復制圖片數據到texture/*將圖片復制到紋理0中(即用紋理替換region表示的區域)- (void)replaceRegion:(MTLRegion)region mipmapLevel:(NSUInteger)level withBytes:(const void *)pixelBytes bytesPerRow:(NSUInteger)bytesPerRow;參數1-region:像素區域在紋理中的位置參數2-level:從零開始的值,指定哪個mipmap級別是目標。如果紋理沒有mipmap,請使用0。參數3-pixelBytes:指向要復制圖片的字節數參數4-bytesPerRow:對于普通或壓縮像素格式,源數據行之間的跨度(以字節為單位)。對于壓縮像素格式,跨度是從一排塊的開頭到下一行的開始的字節數。*/[_texture replaceRegion:region mipmapLevel:0 withBytes:image.data.bytes bytesPerRow:bytesPerRow];****
④ MTKViewDelegate 代理方法
  • delegate的代理方法有兩種,這里主要是說明繪制drawInMTKView代理方法,其流程圖如下:

  • 向著色器中傳遞的數據有三種:
    • 頂點數據:包含頂點坐標和紋理坐標,由于頂點數據是存儲在緩存區中的,所以需要通過setVertexBuffer函數傳遞到頂點著色函數中;
    • 視圖大小數據:需要通過setVertexBytes函數傳遞到頂點著色器中;
    • 紋理圖片:將紋理對象加載到GPU中,需要通過setFragmentTexture函數傳遞到片元著色函數,通過采樣器讀物紋素,加載TGA圖片。
  • 傳遞數據對應的代碼如下:
/*需要傳遞的數據有以下三種:1)頂點數據、紋理坐標,2)viewportSize視圖大小3)紋理圖片*/// 將數據加載到MTLBuffer (即metal文件中的頂點著色函數)--> 頂點函數[commandEncoder setVertexBuffer:_vertexBuffer offset:0 atIndex:YDWVertexInputIndexVertices];//將數據加載到GPU(即metal文件中的頂點著色函數) --> 視圖大小[commandEncoder setVertexBytes:&_viewportSize length:sizeof(_viewportSize) atIndex:YDWVertexInputIndexViewportSize];//將紋理對象傳遞到片元著色器(即metal中的片元著色函數) -- 紋理圖片[commandEncoder setFragmentTexture:_texture atIndex:YDWTextureIndexBaseColor];
⑤ 整體流程

PNG/JPEG紋理

  • 與加載 TGA 圖片相比,在原代碼基礎上,需要將 setupTexture 函數修改為 setupTexturePNG 函數;
① setupTexturePNG函數
  • 該函數的功能是加載PNG/JPG圖片,其加載的流程如圖所示:

② loadImage 函數
  • 除了獲取圖片以及loadImage函數,其他步驟與加載TGA圖片時,步驟是一致的,將PNG/JPG圖片加載成位圖的,通過CGContextRef對象將圖片進行重繪,解壓成位圖數據的,圖片解壓為位圖的流程如圖所示:

③ 實現如下
// 加載紋理TGA文件 - (void)setupTexturePNG{// 1、獲取圖片UIImage *image = [UIImage imageNamed:@"mouse.jpg"];// 2、創建紋理描述符MTLTextureDescriptor *textureDescriptor = [[MTLTextureDescriptor alloc] init];// 表示每個像素有藍色,綠色,紅色和alpha通道.其中每個通道都是8位無符號歸一化的值.(即0映射成0,255映射成1);textureDescriptor.pixelFormat = MTLPixelFormatRGBA8Unorm;// 設置紋理的像素尺寸textureDescriptor.width = image.size.width;textureDescriptor.height = image.size.height;// 3、使用紋理描述符創建紋理_texture = [_device newTextureWithDescriptor:textureDescriptor];// 4、創建MTLRegion對象// MLRegion結構用于標識紋理的特定區域。 demo使用圖像數據填充整個紋理;因此,覆蓋整個紋理的像素區域等于紋理的尺寸。/*typedef struct{MTLOrigin origin; //開始位置x,y,zMTLSize size; //尺寸width,height,depth (即寬、高、深度)} MTLRegion;*/MTLRegion region = {{0, 0, 0},{image.size.width, image.size.height, 1},};// 5、獲取紋理圖片:通過context重繪獲取紋理圖片Byte *imageBytes = [self loadImage:image];// 6、UIImage的數據需要轉成二進制才能上傳,且不用jpg、png的NSDataif (imageBytes) {// 將紋理圖片復制到texture[_texture replaceRegion:region mipmapLevel:0 withBytes:imageBytes bytesPerRow:4 * image.size.width];// 釋放free(imageBytes);imageBytes = NULL;} }// 從UIImage 中讀取Byte 數據返回 -- png/jpg 都是通過context重繪 解壓成位圖- (Byte *)loadImage:(UIImage *)image{// 1、將UIImage轉換為CGImageRefCGImageRef spriteImage = image.CGImage;// 2、讀取圖片的大小size_t width = CGImageGetWidth(spriteImage);size_t height = CGImageGetHeight(spriteImage);// 3、計算圖片字節數Byte *spriteData = (Byte *)calloc(width * height * 4, sizeof(Byte));// 4、創建contextCGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width*4, CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);// 5、在context上繪圖CGContextDrawImage(spriteContext, CGRectMake(0, 0, width, height), spriteImage);// 6、圖片翻轉過來CGRect rect = CGRectMake(0, 0, width, height);CGContextTranslateCTM(spriteContext, 0, rect.size.height);CGContextScaleCTM(spriteContext, 1.0, -1.0);CGContextDrawImage(spriteContext, rect, spriteImage);// 7、釋放contextCGContextRelease(spriteContext);return spriteData; }
④ 總結

根據TGA圖片加載以及PNG/JPG圖片加載過程的分析,如下:

  • 將紋理圖片解壓成位圖;
  • 創建 MTLTextureDescriptor 對象,即紋理描述對象,并設置 pixelFormat 像素信息、width以及height尺寸信息;
  • 使用紋理描述對象創建MTLTexture對象,即紋理對象;
  • 創建MTLRegion對象,用于標識紋理的像素區域;
  • 通過texture的replaceRegion:mipmapLevel:withBytes:bytesPerRow:函數將紋理圖片解壓的位圖數據復制到紋理對象中;
  • 繪制回調函數drawInMTKView中,通過MTLrenderCommandEncoder對象的setFragmentTexture:atIndex:函數,將紋理傳遞到片元著色函數。

總結

以上是生活随笔為你收集整理的Metal之加载TGA与PNG/JPEG纹理图片的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。