2011-08-11 10 views
5

Esta es mi primera pregunta sobre StackOverflow, ¡hurra! Honestamente puedo decir que utilizo StackOverflow a diario para mis misterios de programación personal y de trabajo. El 99,9% de las veces encuentro la respuesta que necesito aquí también, ¡lo cual es genial!SlimDX/DirectX9/C# - Cómo acceder a los datos de píxeles en una Textura

Mi problema actual en realidad me dejó perplejo ya que parece que no puedo encontrar nada que realmente funcione. Ya leí varias publicaciones en GameDev.net y encontré otros recursos en la red, pero no puedo resolverlos.

Estoy en el proceso de portar un pequeño motor 2D que escribí para XNA a SlimDX (solo DirectX9 en este momento), lo que ha sido una buena movida ya que aprendí más sobre el funcionamiento interno de DirectX en solo unos días de lo que hice en seis meses de trabajo con XNA. Completé la mayoría de las funciones básicas de renderizado y logré recrear el XNA SpriteBatch con un montón de características adicionales (que realmente extrañé en XNA).

Una de las últimas cosas que trato de hacer es extraer un rectángulo fuente de una textura determinada y usarla para mosaico. Por qué: cuando no está embaldosado, puede meterse con el UV para obtener la fuente que desea visualizar (p. Ej .: 0.3, 0.3 a 0.5; 0.5), pero cuando está embaldosado, necesita los rayos UV para el mosaico (0; 0 a 2; 2 significa imágenes de mosaico dos veces) y, por lo tanto, necesitan una textura recortada.

Para hacer el cuento largo, trato de utilizar lo siguiente:

DataRectangle dataRectangle = sprite.Texture.LockRectangle(0, LockFlags.None); 
Format format = sprite.Texture.GetLevelDescription(0).Format; 

byte[] buffer = new byte[4]; 
dataRectangle.Data.Read(buffer, ([y] * dataRectangle.Pitch) + ([x] * 4), buffer.Length); 
texture.UnlockRectangle(0); 

me trataron diferentes píxeles, pero todos parecen dar datos falsos. Por ejemplo, en realidad traté de usar mi avatar actual para ver si el buffer que obtuve del DataRectangle coincide con el píxel real de la imagen, pero no tuve suerte (incluso comprobé si el formato era correcto, cuál es).

¿Qué estoy haciendo mal? Hay una mejor manera de hacerlo? ¿O es incorrecta mi historia de rayos ultravioleta y puede resolverse de forma mucho más simple que cortar un rectángulo fuente antes de ponerlo en mosaico?

Gracias por su tiempo,

Lennard Fonteijn

Actualización # 1

De hecho, me las arreglé para exportar los datos de píxeles a un mapa de bits utilizando la siguiente conversión de una matriz de bytes:

int pixel = (buffer[0] & 0xFF) | ((buffer[1] & 0xFF) << 8) | ((buffer[2] & 0xFF) << 16) | ((255 - buffer[3] & 0xFF) << 24); 

Así que los datos no parecen tan falsos como pensé que era. Mi próximo problema, sin embargo, es capturar los píxeles especificados en el rectángulo de origen y copiarlos a una nueva textura. La imagen que intento cortar es 150x150, pero por alguna razón se estira a una imagen de 256x256 (potencia de dos), pero cuando realmente intento acceder a píxeles más allá de 150x150, arroja una excepción OutOfBounds. Además, cuando intento crear una segunda textura en blanco de un tamaño de 256x256, sin importar lo que coloque en ella, resulta completamente negro.

Aquí está mi código actual:

//Texture texture = 150x150 
DataRectangle dataRectangle = texture.LockRectangle(0, LockFlags.None); 
SurfaceDescription surface = texture.GetLevelDescription(0); 

Texture texture2 = new Texture(_graphicsDevice, surface.Width, surface.Height, 0, surface.Usage, surface.Format, surface.Pool); 
DataRectangle dataRectangle2 = texture2.LockRectangle(0, LockFlags.None); 

for (int k = sourceX; k < sourceHeight; k++) 
{ 
    for (int l = sourceY; l < sourceWidth; l++) 
    { 
     byte[] buffer = new byte[4]; 
     dataRectangle.Data.Seek((k * dataRectangle.Pitch) + (l* 4), SeekOrigin.Begin); 
     dataRectangle.Data.Read(buffer, 0, 4); 

     dataRectangle2.Data.Seek(((k - sourceY) * dataRectangle2.Pitch) + ((l - sourceX) * 4), SeekOrigin.Begin); 
     dataRectangle2.Data.Write(buffer, 0, 4); 
    } 
} 

sprite.Texture.UnlockRectangle(0); 
texture2.UnlockRectangle(0); 

_graphicsDevice.SetTexture(0, texture2); 

Así que mis nuevas preguntas (adicionales) son: ¿Cómo puedo mover más de píxeles de un textura a otra textura más pequeños, incluyendo el canal alfa? ¿Por qué el informe SurfaceDescription 256x256 cuando mi textura original es 150x150?

+0

creo que esta realidad debe ser publicado bajo http://gamedev.stackexchange.com. Cualquier moderador debería sentirse en libertad de moverlo allí si esto es cierto. –

+0

Es una buena pregunta, y sobre el tema aquí ... pero sí, es posible que obtenga más ayuda allí. – jcoder

+0

Actualicé la pregunta con nueva información. –

Respuesta

5

Es un poco incómodo responder a mi propia pregunta, pero después de excavar más y por simple prueba y error, encontré la solución.

Al principio, tuve que cambiar la forma en que cargué mi textura. Para evitar que el cambio de tamaño internamente a un tamaño de alimentación de dos hijos, tuve que usar el siguiente método:

Texture texture = Texture.FromFile(_graphicsDevice, [filePath], D3DX.DefaultNonPowerOf2, D3DX.DefaultNonPowerOf2, 1, Usage.None, Format.Unknown, Pool.Managed, Filter.None, Filter.None, 0); 

Nota cómo he especificado específicamente que el tamaño es no-poder-de-dos.

A continuación, hubo un error en mi nueva definición de textura. En lugar de especificar 0 niveles (y por lo que es auto-generar un MipMap), tuve que especifique 1 nivel, como este:

Texture texture2 = new Texture(_graphicsDevice, [sourceWidth], [sourceHeight], 1, surface.Usage, surface.Format, surface.Pool); 

Después de haber hecho esto, el ciclo for que tengo en mi pregunta real, obras bien:

DataRectangle dataRectangle = texture.LockRectangle(0, LockFlags.None); 
SurfaceDescription surface = texture.GetLevelDescription(0); 

DataRectangle dataRectangle2 = texture2.LockRectangle(0, LockFlags.None); 

for (int y = [sourceX]; y < [sourceHeight]; k++) 
{ 
    for (int x = [sourceY]; x < [sourceWidth]; l++) 
    { 
     byte[] buffer = new byte[4]; 
     dataRectangle.Data.Seek((y * dataRectangle.Pitch) + (x * 4), SeekOrigin.Begin); 
     dataRectangle.Data.Read(buffer, 0, 4); 

     dataRectangle2.Data.Seek(((y - [sourceY]) * dataRectangle2.Pitch) + ((x - [sourceX]) * 4), SeekOrigin.Begin); 
     dataRectangle2.Data.Write(buffer, 0, 4); 
    } 
} 

texture.UnlockRectangle(0); 
texture2.UnlockRectangle(0); 

_graphicsDevice.SetTexture(0, texture2); 

Todo en paréntesis se considera una variable desde fuera de este fragmento. Supongo que _graphicsDevice es lo suficientemente claro. Soy consciente de que .Seek puede simplificarse, pero creo que funciona bien, por ejemplo. Tenga en cuenta que no recomiendo hacer este tipo de operación en cada fotograma, ya que agota su FPS bastante rápido cuando se usa incorrectamente.

Me tomó bastante tiempo averiguarlo, pero el resultado es satisfactorio. Me gustaría agradecer a todos los que vislumbraron la cuestión y han tratado de ayudarme.

Lennard Fonteijn

Cuestiones relacionadas