首先从MIPMAP的原理说起,它是把一张贴图按照2的倍数进行缩小。直到1X1。
把缩小的图都存储起来。在渲染时,根据一个像素离眼睛为之的距离,来判断从一个合适的图层中取出texel颜色赋值给像素。
如果贴图的基本尺寸是256x256像素的话,它mipmap就会有8个层级。
每个层级是上一层级的四分之一的大小,依次层级大小就是:128x128;64x64;32x32;16x16;8x8;4x4;2x2;1x1(一个像素)。
例如在一个场景中,渲染贴图需要填满的空间大小是40x40像素的话,如果没有三线性过滤,那32x32 会被放大显示,或者有三线性过滤,会在64x64和32x32之间切换。最简单的生成贴图的方法就是依次做平均,当然也可以用更加高级的算法。
C++生成代码
void generate_mipmaps(float gamma) {
IUINT32** mipmaps = NULL;
int num_mip_levels = logbase2ofx(this->width) + 1;
this->datas_len = num_mip_levels;
mipmaps = new IUINT32*[num_mip_levels];
mipmaps[0] = datas[0];
int mip_width = width;
int mip_height = height;
for (int mip_level = 1; mip_level < num_mip_levels; mip_level++) {
mip_width = mip_width >> 1; // 除以2
mip_height = mip_height >> 1; // 除以2
// 开辟空间
mipmaps[mip_level] = new IUINT32[mip_width*mip_height];
// src
IUINT32 *src_buffer = mipmaps[mip_level - 1];
// dst
IUINT32 *dest_buffer = mipmaps[mip_level];
for (int x = 0; x < mip_width; x++) {
for (int y = 0; y < mip_height; y++) {
float r0,g0,b0,a0,
r1, g1, b1, a1,
r2, g2, b2, a2,
r3, g3, b3, a3;
// 平均值
int r_avg, g_avg, b_avg, a_avg;
IUINT32 c = src_buffer[(x * 2 + 0) + (y * 2 + 0) * 2 * mip_width];
b0 = c & 0xff;
g0 = (c >> 8) & 0xff;
r0 = (c >> 16) & 0xff;
a0 = (c >> 24) & 0xff;
c = src_buffer[(x * 2 + 1) + (y * 2 + 0) * 2 * mip_width];
b1 = c & 0xff;
g1 = (c >> 8) & 0xff;
r1 = (c >> 16) & 0xff;
a1 = (c >> 24) & 0xff;
c = src_buffer[(x * 2 + 0) + (y * 2 + 1) * 2 * mip_width];
b2 = c & 0xff;
g2 = (c >> 8) & 0xff;
r2 = (c >> 16) & 0xff;
a2 = (c >> 24) & 0xff;
c = src_buffer[(x * 2 + 1) + (y * 2 + 1) * 2 * mip_width];
b3 = c & 0xff;
g3 = (c >> 8) & 0xff;
r3 = (c >> 16) & 0xff;
a3 = (c >> 24) & 0xff;
r_avg = (IUINT32)(0.5f + gamma*(r0 + r1 + r2 + r3) / 4);
g_avg = (IUINT32)(0.5f + gamma*(g0 + g1 + g2 + g3) / 4);
b_avg = (IUINT32)(0.5f + gamma*(b0 + b1 + b2 + b3) / 4);
a_avg = (IUINT32)(0.5f + gamma*(b0 + b1 + b2 + b3) / 4);
int R = CMID(r_avg, 0, 255);
int G = CMID(g_avg, 0, 255);
int B = CMID(b_avg, 0, 255);
int A = CMID(a_avg, 0, 255);
// ARGB
dest_buffer[y*mip_width + x] = (A << 24) | (R << 16) | (G << 8) | B;
}
}
}
datas = mipmaps;
datas_len = num_mip_levels;
}