首页 > 其他分享 >从零开始游戏开发——3.5 纹理基础

从零开始游戏开发——3.5 纹理基础

时间:2022-10-15 15:46:29浏览次数:107  
标签:const 16 32 unsigned 纹理 char 从零开始 3.5

  之前的小节,我们显示了使用木箱子外观的三角形,纹理可以极大丰富物体的表现,在这节中,我们将介绍一张图像是如何做为纹理进行显示的,最终实现下图效果:

首先,我们拥有一张.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 = (unsigned char *)malloc(_ImageSize * sizeof(unsigned char));
32     if (_pData == 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 = 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 = (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。

  本节介绍了纹理的基础知识,后面介绍完光照后,将继续介绍凹凸映射、环境映射等高级技术,总之,游戏开发很多渲染表现都需要用到纹理相关技术。

标签:const,16,32,unsigned,纹理,char,从零开始,3.5
From: https://www.cnblogs.com/primarycode/p/16793765.html

相关文章

  • RE:从零开始的数论相关学习
    开坑。1-位运算我们知道,C++中的位运算有:&、|、^、~、>>、<<。应用:1-1快速幂:intqpow(inta,intb,intp){intvis=a;intsum=1;while(b){......
  • RE0:从零开始的高数生活
    分部积分法若函数$u(x)$和$v(x)$可导且$\intu(x)v'(x)dx$存在,可得以下公式:$$\intu'(x)v(x)dx=u(x)v(x)-\intu(x)v'(x)dx$$证:由$[u(x)v(x)]'=u'(x)v(x)+u(x)v......
  • 从零开始撸python
    Day4:多线程聊天室这个比较简单,用来练一下手,不过发现一个问题,多线程用while循环的时候,不论循环条件,CPU占用直接拉满,但用列表for循环却可以,应该是和操作系统原理有关,希望知......
  • 【微信小程序学习】从零开始学习微信小程序(一)
    生命周期App生命周期App(Objectobject)注册小程序,接受一个Object参数,用来指定小程序的生命周期回调等。App()必须在app.js中调用且只能调用一次,否则会出现无法预期的......
  • 从零开始配置vim(26)——LSP UI 美化
    之前我们通过几个实例演示如何配置其他语言的lsp服务,相信各位小伙伴碰到其他的编程语言也能熟练的配置它对应的lsp服务。本篇讲作为一个补充,我们来优化一下LSP相关的显示......
  • Photoshop 2022 for Mac(ps 2022最新版)v23.5.1永久激活版mac/win
    Photoshop2022新功能有哪些?Photoshop简称ps,它是一款专业图像处理软件,此次更新软件可选择项目云服务生成更准确和高质量的图像;软件界面也有了新的中性UI颜色模式,视觉效果更......
  • ESP32开发环境搭建 IDF3.3.5+VScode
    1、 软件准备:①ESP-IDF:包含ESP32API和用于操作工具链的脚本。②工具链msys32:用于编译ESP32应用程序。③编辑工具VisualStudioCode  注意:工具链和ESP-IDF需......
  • 从零开始撸python
    开始挑战30天从零开始--------->python全栈工程师Day1:图书管理系统没有选用pycharm,选择了更轻量化的SublimeText编辑器,撸了一个用json充当数据库的图书管理系统,简单的熟......
  • Mybatis——Plus 从零开始使用
    也就是想用学习环境的搭建,应该怎么做。这个可以参考官方文档:​​https://mp.baomidou.com/guide/quickstart.html#%E5%88%9D%E5%A7%8B%E5%8C%96%E5%B7%A5%E7%A8%8B​​ ......
  • 【问题】 Cocos3.5.2 左边和上方有黑边,任意点一下才能对齐
    版本:3.5.2 背景图是纯蓝色并且widget是适配全屏的,直接运行时,可以看到左边和上方有黑边。   任意点一下屏幕,背景图会上移,上方黑边消失,但是左边黑边还在。 ......