Статья актуальна для разработки в XNA Game Studio и MonoGame.
MSDN Library: Opaque and 1-Bit Alpha Textures (Direct3D 9)
В данной статье приведу пример распаковки DxtCompressed текстуры. DxtCompressed текстуры имеют несколько форматов сжатия. Ниже приведенный пример распаковки предназначен для работы с форматом DXT1. В моих проектах это наиболее распространенный применяемый формат. В DXT1 прозрачность кодируется только двумя значениями 0 и 1 т.е. ни какой полу прозрачности, что дает преимущество сжатия в 4 раза. Для сравнения в DXT5 поддерживает полу прозрачность, которая кодируется отдельным блоком, за счет чего текстура жмется всего в 2 раза.
Для чего может понадобиться распаковка текстуры? Например, что бы по признаку прозрачности построить области проходимости игрового уровня, а так же многое другое.
Код примера можно разбить на части:
- загрузка DxtCompressed текстуры;
- распаковка и преобразование в массив цветов Color[];
- преобразование массива цветов в обычную текстуру;
- вывод текстуры на экран для визуальной проверки.
Для работы примера необходимо:
- создать новый проект Windows Game (4.0);
- в Content добавить нашу DXT1 текстуру;
- в свойствах этой текстуры переключить параметр "Texture Format" с "Color" в "DxtCompressed"
- добавляем код из примера.
Код:
MSDN Library: Opaque and 1-Bit Alpha Textures (Direct3D 9)
В данной статье приведу пример распаковки DxtCompressed текстуры. DxtCompressed текстуры имеют несколько форматов сжатия. Ниже приведенный пример распаковки предназначен для работы с форматом DXT1. В моих проектах это наиболее распространенный применяемый формат. В DXT1 прозрачность кодируется только двумя значениями 0 и 1 т.е. ни какой полу прозрачности, что дает преимущество сжатия в 4 раза. Для сравнения в DXT5 поддерживает полу прозрачность, которая кодируется отдельным блоком, за счет чего текстура жмется всего в 2 раза.
Для чего может понадобиться распаковка текстуры? Например, что бы по признаку прозрачности построить области проходимости игрового уровня, а так же многое другое.
Код примера можно разбить на части:
- загрузка DxtCompressed текстуры;
- распаковка и преобразование в массив цветов Color[];
- преобразование массива цветов в обычную текстуру;
- вывод текстуры на экран для визуальной проверки.
Для работы примера необходимо:
- создать новый проект Windows Game (4.0);
- в Content добавить нашу DXT1 текстуру;
- в свойствах этой текстуры переключить параметр "Texture Format" с "Color" в "DxtCompressed"
- добавляем код из примера.
Код:
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace DxtCompressed
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager _graphics;
SpriteBatch _spriteBatch;
Texture2D _dxtCompressedTexture;
Texture2D _colorTexture;
public Game1()
{
_graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void LoadContent()
{
_spriteBatch = new SpriteBatch(GraphicsDevice);
_dxtCompressedTexture = Content.Load<Texture2D>("512x256");
_colorTexture = Unpacking(_dxtCompressedTexture);
}
protected override void Update(GameTime gameTime)
{
if(Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
_spriteBatch.Begin();
_spriteBatch.Draw(_colorTexture, Vector2.Zero, Color.White);
_spriteBatch.End();
}
public struct DXT1pixel
{
public ushort color0;
public ushort color1;
public byte[,] pixel;
public DXT1pixel(byte[] b)
{
color0 = 0;
color0 |= (ushort)(b[1] << 8);
color0 |= (ushort)b[0];
color1 = 0;
color1 |= (ushort)(b[3] << 8);
color1 |= (ushort)b[2];
pixel = new byte[4, 4];
byte[] bbbb;
for(int i = 0; i < 4; i++)
{
bbbb = DecompressByte(b[i + 4]);
pixel[0, i] = bbbb[0];
pixel[1, i] = bbbb[1];
pixel[2, i] = bbbb[2];
pixel[3, i] = bbbb[3];
}
}
private byte[] DecompressByte(byte b)
{
byte[] bb = new byte[4];
for(int i = 0; i < 4; i++)
{
bb[i] = (byte)(b & 0x03);
b = (byte)(b >> 2);
}
return bb;
}
}
private Color WordToColor(int word)
{
int r,g,b;
b = word & 0x1f;
word = word >> 5;
g = word & 0x3f;
word = word >> 6;
r = word;
return new Color(r * 8, g * 4, b * 8);
}
public Texture2D Unpacking(Texture2D dxtTexture)
{
if(dxtTexture.Format != SurfaceFormat.Dxt1)
return null;
int _width = dxtTexture.Width;
int _height = dxtTexture.Height;
int w = _width >> 2;
int h = _height >> 2;
byte[] color = new byte[w * h * 8];
dxtTexture.GetData<byte>(color);
//
DXT1pixel[,] dxt = new DXT1pixel[w, h];
int i;
byte[] b = new byte[8];
for(int y = 0; y < h; y++)
{
for(int x = 0; x < w; x++)
{
i = (y * w + x) * 8;
Array.Copy(color, i, b, 0, 8);
dxt[x, y] = new DXT1pixel(b);
}
}
i = 0;
Color[] newColor = new Color[_width * _height];
for(int y = 0; y < h; y++)
{
for(int x = 0; x < w; x++)
{
for(int yy = 0; yy < 4; yy++)
{
for(int xx = 0; xx < 4; xx++)
{
int ic = (y * 4 + yy) * _width + x * 4 + xx;
Color color0 = WordToColor(dxt[x, y].color0);
Color color1 = WordToColor(dxt[x, y].color1);
if(dxt[x, y].color0 > dxt[x, y].color1)
{
Color color2 = new Color(
(int)(color0.R * 0.66f + color1.R * 0.34f),
(int)(color0.G * 0.66f + color1.G * 0.34f),
(int)(color0.B * 0.66f + color1.B * 0.34f));
Color color3 = new Color(
(int)(color0.R * 0.34f + color1.R * 0.66f),
(int)(color0.G * 0.34f + color1.G * 0.66f),
(int)(color0.B * 0.34f + color1.B * 0.66f));
switch(dxt[x, y].pixel[xx, yy])
{
case 0x00:
newColor[ic] = color0;
break;
case 0x01:
newColor[ic] = color1;
break;
case 0x02:
newColor[ic] = color2;
break;
case 0x03:
newColor[ic] = color3;
break;
}
}
else
{
Color color2 = new Color(
(int)(color0.R / 2 + color1.R / 2),
(int)(color0.G / 2 + color1.G / 2),
(int)(color0.B / 2 + color1.B / 2));
Color color3 = Color.Transparent;
switch(dxt[x, y].pixel[xx, yy])
{
case 0x00:
newColor[ic] = color0;
break;
case 0x01:
newColor[ic] = color1;
break;
case 0x02:
newColor[ic] = color2;
break;
case 0x03:
newColor[ic] = color3;
break;
}
}
}
}
}
}
Texture2D t = new Texture2D(_graphics.GraphicsDevice, _width, _height);
t.SetData<Color>(newColor);
return t;
}
}
}