Metal之加载TGA与PNG/JPEG纹理图片
生活随笔
收集整理的這篇文章主要介紹了
Metal之加载TGA与PNG/JPEG纹理图片
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
TGA紋理
① 效果展示
② 環境準備
- 視圖控制器類:在 viewDidLoad 函數中創建 MTKView 對象、自定義 render 對象,并設置 view 的代理為 render,其流程請參考:OpenGL之簡單渲染一個三角形;
- OC 與 C 的橋接文件:創建 metal 文件與 OC 通用的索引枚舉(包括頂點數據索引、紋理索引) 和頂點數據的結構體,如下:
- YDWImage 類:該類是將 TGA 轉換為位圖的工具類。由于平常的開發中很少去加載 TGA 文件,一般都是 PNG/JPG 文件,所以 TGA 文件轉換成位圖,其本質是將 TGA 文件的數據復制到 BGRA 圖像數據指針指向的內存中。
- 創建 metal 文件
-
- 主要是1個結構體+兩個函數,結構體中包含頂點坐標 和紋理坐標,作為定點著色器的輸出以及片元著色器的輸入;
-
- 頂點著色函數:主要是將頂點坐標歸一化處理,并將處理后的頂點坐標和紋理坐標輸出,經過 metal 的圖元裝配和光柵化處理,將頂點數據傳入片元著色器,其流程圖如下:
- 頂點著色函數:主要是將頂點坐標歸一化處理,并將處理后的頂點坐標和紋理坐標輸出,經過 metal 的圖元裝配和光柵化處理,將頂點數據傳入片元著色器,其流程圖如下:
-
- 片元著色函數:主要是通過采樣器獲取紋素,相當于 GLSL 中內建函數 texture2D,在 metal 中是通過紋理的 sample 函數獲取,主要流程圖如下:
- 片元著色函數:主要是通過采樣器獲取紋素,相當于 GLSL 中內建函數 texture2D,在 metal 中是通過紋理的 sample 函數獲取,主要流程圖如下:
- 創建自定義render渲染循環類:蘋果建議將渲染循環單獨寫成一個類,處理 metal 的渲染及委托事件,其中 TGA 文件的加載也是在 render 中處理的,它會將處理好的頂點數據及紋理圖片傳入著色器,著色器處理完成后會繪制并顯示到屏幕上。
③ 渲染循環類
- initWithMetalKitView 函數:
- 初始化GPU設備:通過傳入的view獲取獲取GPU的使用權限;
- setupVertex函數:設置頂點相關操作;
- setupPipeLine函數:設置渲染管道相關操作;
- setupTexture函數:加載TGA文件;
-
- setupVertex 函數:主要是出初始化頂點數據,包括頂點坐標和紋理坐標,當頂點坐標的范圍不是-1~1時,是位于物體坐標系,需要在 metal 文件的頂點著色器中作歸一化處理,并且將頂點數據存儲到 MTLBuffer 對象中,GPU 函數流程如下:
- setupVertex 函數:主要是出初始化頂點數據,包括頂點坐標和紋理坐標,當頂點坐標的范圍不是-1~1時,是位于物體坐標系,需要在 metal 文件的頂點著色器中作歸一化處理,并且將頂點數據存儲到 MTLBuffer 對象中,GPU 函數流程如下:
-
- setupPipeLine 函數:主要是渲染管道相關的初始化工作,對應的流程圖如下:
- setupPipeLine 函數:主要是渲染管道相關的初始化工作,對應的流程圖如下:
-
- 渲染管道的初始化分為:
- 加載metal文件;
- 配置渲染管道;
- 創建渲染管線對象;
- 設置 commandQueue 命令對象。
- 渲染管道的初始化分為:
-
- setupTexture 函數:加載TGA圖片,如下:
- setupTexture 函數:加載TGA圖片,如下:
-
- TGA 圖片的加載分為以下步驟:
-
-
- 獲取 TGA 文件:主要是 TGA 文件通過轉換為 YDWImage 對象,即紋理圖片轉換為位圖:
-
-
-
- 創建紋理描述對象 & 紋理對象:紋理對象通過創建的紋理描述對象以及解壓的位圖生成,其中需要設置紋理描述對象的相關屬性:
-
-
-
- 將紋理圖片復制到 texture 對象中:在復制紋理圖片之前,首先需要計算圖像每行的字節數,以及設置紋理對應的像素區域region,region的定義如下,其中包含兩個結構體對象origin和size,其中origin表示開始的頂點坐標(x,y,z),size表示尺寸(寬度、高度、深度):
-
-
-
- 然后通過replaceRegion: mipmapLevel: withBytes: bytesPerRow:函數將圖片復制到紋理對象中:
-
④ MTKViewDelegate 代理方法
- delegate的代理方法有兩種,這里主要是說明繪制drawInMTKView代理方法,其流程圖如下:
- 向著色器中傳遞的數據有三種:
- 頂點數據:包含頂點坐標和紋理坐標,由于頂點數據是存儲在緩存區中的,所以需要通過setVertexBuffer函數傳遞到頂點著色函數中;
- 視圖大小數據:需要通過setVertexBytes函數傳遞到頂點著色器中;
- 紋理圖片:將紋理對象加載到GPU中,需要通過setFragmentTexture函數傳遞到片元著色函數,通過采樣器讀物紋素,加載TGA圖片。
- 傳遞數據對應的代碼如下:
⑤ 整體流程
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纹理图片的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL之某个表中有近千万数据且CRU
- 下一篇: Swift之深入解析“指针”的使用和实现