之前的小節,我們顯示了使用木箱子外觀的三角形,紋理可以極大豐富物體的表現,在這節中,我們將介紹一張影像是如何做為紋理進行顯示的,最終實作下圖效果:

首先,我們擁有一張.tga格式的圖片,tga檔案頭結構如下:
1 #pragma pack(1) 2 typedef struct 3 { 4 char identsize; // Size of ID field that follows header (0) 5 char colorMapType; // 0 = None, 1 = paletted 6 char imageType; // 0 = none, 1 = indexed, 2 = rgb, 3 = grey, +8=rle 7 unsigned short colorMapStart; // First colour map entry 8 unsigned short colorMapLength; // Number of colors 9 unsigned char colorMapBits; // bits per palette entry 10 unsigned short xstart; // image x origin 11 unsigned short ystart; // image y origin 12 unsigned short width; // width in pixels 13 unsigned short height; // height in pixels 14 char bits; // bits per pixel (8 16, 24, 32) 15 char descriptor; // image descriptor 16 } TGAHEADER; 17 #pragma pack(pop)
第1行和第17行的作用將頭結構按1位元組記憶體對齊,然后進行檔案讀取,獲取到影像的顏色資料:
1 bool CTGAImage::Load(const char *filename) 2 { 3 FILE *pFile; // File pointer 4 short sDepth; // Pixel depth; 5 6 // Attempt to open the file 7 pFile = fopen(filename, "rb"); 8 if (pFile == NULL) 9 { 10 printf("Openfile %s failed\n", filename); 11 return false; 12 } 13 // Read in header (binary) 14 fread(&_TgaHeader, 18 /* sizeof(TGAHEADER)*/, 1, pFile); 15 16 // Get width, height, and depth of texture 17 _Width = _TgaHeader.width; 18 _Height = _TgaHeader.height; 19 sDepth = _TgaHeader.bits / 8; 20 21 // Put some validity checks here. Very simply, I only understand 22 // or care about 8, 24, or 32 bit targa's. 23 if (_TgaHeader.bits != 8 && _TgaHeader.bits != 24 && _TgaHeader.bits != 32) 24 { 25 return false; 26 } 27 // Calculate size of image buffer 28 _ImageSize = _TgaHeader.width * _TgaHeader.height * sDepth; 29 30 // Allocate memory and check for success 31 _pData = https://www.cnblogs.com/primarycode/archive/2022/10/15/(unsigned char *)malloc(_ImageSize * sizeof(unsigned char)); 32 if (_pData =https://www.cnblogs.com/primarycode/archive/2022/10/15/= NULL) 33 { 34 return false; 35 } 36 // Read in the bits 37 // Check for read error. This should catch RLE or other 38 // weird formats that I don't want to recognize 39 if (fread(_pData, _ImageSize, 1, pFile) != 1) 40 { 41 free(_pData); 42 _pData =https://www.cnblogs.com/primarycode/archive/2022/10/15/ NULL; 43 return false; 44 } 45 46 // Set OpenGL format expected 47 switch (sDepth) 48 { 49 #ifndef OPENGL_ES 50 case 3: // Most likely case 51 _Format = BGR; 52 break; 53 #endif 54 case 4: 55 _Format = BGRA; 56 break; 57 case 1: 58 _Format = LUMINANCE; 59 break; 60 default: // RGB 61 // If on the iPhone, TGA's are BGR, and the iPhone does not 62 // support BGR without alpha, but it does support RGB, 63 // so a simple swizzle of the red and blue bytes will suffice. 64 // For faster iPhone loads however, save your TGA's with an Alpha! 65 #ifdef OPENGL_ES 66 for (int i = 0; i < lImageSize; i += 3) 67 { 68 GLbyte temp = pBits[i]; 69 pBits[i] = pBits[i + 2]; 70 pBits[i + 2] = temp; 71 } 72 #endif 73 break; 74 } 75 76 // Done with File 77 fclose(pFile); 78 79 return false; 80 }
上面的代碼讀取8位、16位和32位影像的頭資訊并進行賦值,然后獲取實作的顏色資料地址,根據頭資訊中的位數,獲得影像的模式,由于iPhone不支持BRG格式,這里對gles下的使用作了RGB的轉換,現在已經有用于顯示的影像的顏色資料,現在定義一個用于軟體渲染的紋理類如下,類中Image類包含了一張影像的頭資訊和顏色資料,在模型初始化的階段,將這張紋理做為渲染器的輸入資料進行傳遞,同時頂點資料需要傳入紋理坐標,
1 class CSoftTexture2D : public ITexture 2 { 3 public: 4 CSoftTexture2D(const char *fullName); 5 Color GetPixel(const Vector2f &uv) const; 6 Color GetPixel(int x, int y) const; 7 private: 8 IImage *_Image; 9 };
在渲染程序中的片斷著色器處理階段,通過使用插值計算得到當前片斷的uv值,就可以對紋理資料進行采樣了,我們的Shader代碼如下:
1 typedef map<string, vector<float>> UniformMap; 2 3 static void VertexShader(const void *globalUniforms, const void *uniforms, const void *datas, unsigned char *out) 4 { 5 UniformMap globalU = *((UniformMap *)globalUniforms); 6 UniformMap u = *((UniformMap *)uniforms); 7 Matrix4x4f mvpMat4(u["mvpMat"].data()); 8 9 const Vector3f &inPosition = *(Vector3f *)(datas); 10 const Vector2f &inUV = *(Vector2f *)(&inPosition + 1); 11 12 Vector4f &outPosition = *(Vector4f *)out; 13 Vector2f &outUV = *(Vector2f *)(&outPosition + 1); 14 outPosition = mvpMat4 * Vector4f(inPosition.x, inPosition.y, inPosition.z, 1.0f); 15 outUV = inUV; 16 } 17 18 REGISTER_VPROGRAM(VertexShader); 19 20 static Color FragmentShader(const void *globalUniforms, const void *uniforms, ISampler **samplers, const void *datas) 21 { 22 Vector2f &inUV = V2F_UV(datas); 23 24 ISampler *sampler1 = samplers[0]; 25 Color albedo = sampler1->Sample(inUV); 26 return albedo; 27 } 28 29 REGISTER_FPROGRAM(FragmentShader);
在FragmentShader代碼中,第25行使用采樣器進行采樣,在不考慮多重采樣等情況下 ,對于BGR格式的影像最簡單的采樣代碼如下,這樣,模型上就可以顯示出帶紋理的外觀了,
int width = _Image->GetWidth(); int height = _Image->GetHeight(); if (x >= 0 && x < width && y >=0 && y < height) { unsigned char *data = https://www.cnblogs.com/primarycode/archive/2022/10/15/(unsigned char*)_Image->GetData(); unsigned char *addr = data + width * y * 3 + x * 3; float inv = 1.f / 255.f; return Color(1.f, *(addr + 2) * inv, *(addr + 1) * inv, (*addr) *inv ); }
上面程序介紹了一張影像從加載到顯示的程序,展示了紋理的簡單應用,然后紋理的作用遠不止如此,上面介紹的的是一張2D紋理的使用,在游戲中,紋理還有1D紋理、3D紋理、Cube紋理,1D紋理在卡通渲染中明暗過濾處理比較常見,3D紋理則相當于2D紋理疊在了一起,在體積渲染和一些drawcall合批優化中可以看到相關應用,Cube紋理則可以在天空盒、環境映射中看到它的身影,
通常對一張紋理進行采樣,其Wrap Mode通常有Repeat、Clamp、Mirror幾種,Wrap Mode是指當紋理坐標是Normalized即0~1時,uv超出這個范圍的處理,下圖是幾種模式的效果圖:

上面的例子中,我們沒有處理紋理過濾的情況,由于紋理坐標與螢屏解析度無關,紋理尺寸與模型尺寸的對應需要紋理過濾來處理這種適應關系,通常用到的有Nearest——取最鄰近的像素值和Linear——獲取附近像素加權平均值,同時當一個模型距離攝像機很遠時,可以對紋理使用Mipmap來避免遠處的閃爍現象,一張32*32的紋理其Mipmap級別從0到5分別為32*32、16*16、8*8、4*4、2*2、1*1,如果一張紋理啟用了Mipmap其占用記憶體大小約增加1/3,推導如下:
設S = 1/4 + 1/16 + ... + 1/(4^n) 為增加的記憶體大小,
1 + 1/4 + 1/16 + ... + 1/(4^(n-1)) == 4S 為總占用記憶體大小,
4S - S = 3S = 1 - 1/(4^n)
n趨向于無窮大時,3S = 1,因此S = 1/3,
本節介紹了紋理的基礎知識,后面介紹完光照后,將繼續介紹凹凸映射、環境映射等高級技術,總之,游戲開發很多渲染表現都需要用到紋理相關技術,
本文來自博客園,作者:毅安,轉載請注明原文鏈接:https://www.cnblogs.com/primarycode/p/16793765.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/514295.html
標籤:其他
上一篇:哈夫曼編碼解碼(資料結構實驗)
下一篇:資料結構:二叉樹
