2011-08-11 12 views
5

To jest moje pierwsze pytanie na temat StackOverflow, hurra! Mogę szczerze powiedzieć, że używam StackOverflow na co dzień zarówno dla mojej pracy, jak i osobistych tajemnic programistycznych. 99,9% czasu, w którym rzeczywiście znajduję odpowiedź, której potrzebuję, również tutaj, co jest świetne!SlimDX/DirectX9/C# - Jak uzyskać dostęp do danych pikseli w tekstury

Mój obecny problem faktycznie mnie trochę zaskoczył, ponieważ nie mogę znaleźć niczego, co faktycznie działa. Przeczytałem już kilka postów na GameDev.net i znalazłem inne zasoby w sieci, ale nie mogę tego rozwiązać.

Jestem w trakcie przenoszenia małego silnika 2D, który napisałem dla XNA do SlimDX (tylko DirectX9 w tej chwili), co było dobrym posunięciem, ponieważ dowiedziałem się więcej o wewnętrznych działaniach DirectX w ciągu zaledwie kilku dni niż w ciągu sześciu miesięcy pracy z XNA. Większość moich podstawowych funkcji renderowania została wykonana i faktycznie udało mi się odtworzyć XNA SpriteBatch z mnóstwem dodatkowych funkcji (których tak naprawdę brakowało mi w XNA).

Jedną z ostatnich rzeczy, do których próbuję dostać się do pracy, jest wyodrębnienie prostokąta źródłowego z zadanej tekstury i użycie go do kafelkowania. Dlaczego: Kiedy nie układasz kafelków, możesz po prostu zadzierać promieniowaniem UV, aby uzyskać źródło, które chcesz wyświetlić (np. 0,3; 0,3 do 0,5; 0,5), ale kiedy układasz kafelek, potrzebujesz UV do płytek (0; 0 do 2; 2) oznacza dwa razy obrazy płytek) i dlatego potrzebna jest tekstura wycięcia.

Aby długie opowiadanie, staram się korzystać z następujących powodów:

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); 

Próbowałem różnych pikseli, ale wszystkie wydają się dawać fałszywe dane. Na przykład, próbowałem użyć mojego obecnego awatara, aby sprawdzić, czy bufor, który dostałem z DataRectangle, pasuje do rzeczywistego piksela na obrazku, ale bez powodzenia (nawet sprawdzony, czy Format jest poprawny, jaki to jest).

Co robię źle? Czy istnieje lepszy sposób na zrobienie tego? A może moja historia UV jest błędna i czy można ją rozwiązać znacznie łatwiej niż wycinanie prostokąta źródłowego przed jej układaniem?

Dziękuję za poświęcony czas,

Lennard Fonteijn

Aktualizacja # 1

I rzeczywiście udało się wyeksportować dane pikseli na bitmapę stosując następującą konwersję z tablicy bajtów:

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

Dane nie wydają się tak nieprawdziwe, jak myślałem. Moim następnym problemem jest jednak chwytanie pikseli określonych w prostokącie źródłowym i kopiowanie ich do nowej tekstury. Obraz, który próbuję wyciąć, ma wymiary 150 x 150, ale z jakiegoś powodu jest rozciągnięty do obrazu 256 x 256 (moc dwóch), ale kiedy faktycznie próbuje uzyskać dostęp do pikseli powyżej 150x150, generuje wyjątek OutOfBounds. Ponadto, gdy próbuję stworzyć drugą pustą teksturę o rozmiarze 256x256, bez względu na to, co w nią włożę, okazuje się ona całkowicie czarna.

Oto mój bieżący kod:

//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); 

Więc moje nowe (dodatkowe) pytania: Jak mogę przenieść nad pikseli z jednego do innego mniejszego tekstury tekstury, w tym kanałem Alpha? Dowolne dlaczego raport SurfaceDescription daje 256x256, gdy moja oryginalna tekstura ma wymiary 150 x 150?

+0

Myślę, że powinien on zostać opublikowany pod adresem http://gamedev.stackexchange.com. Każdy moderator powinien mieć możliwość przeniesienia go tam, jeśli to prawda. –

+0

To dobre pytanie - i na temat tutaj ... ale tak, możesz uzyskać dodatkową pomoc. – jcoder

+0

Zaktualizowałem pytanie, podając nowe informacje. –

Odpowiedz

5

To trochę niefortunne, aby odpowiedzieć na moje własne pytanie, ale po kilku innych kopaniu i prostym procesie i błędzie, znalazłem rozwiązanie.

Najpierw musiałem zmienić sposób wczytywania tekstury. Aby zapobiec jej wewnętrznie rozmiaru do potęgi dwójki rozmiaru, musiałem użyć następującej metody:

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

Uwaga jak ja konkretnie określone, że rozmiar jest Non-Power-of-Two.

Następnie w mojej nowej definicji tekstury wystąpił błąd. Zamiast określać 0 poziomy (i czyni go automatycznie wygenerować mipmapa), musiałem podać 1 poziom, tak:

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

Po zrobisz, pętli for mam w rzeczywistej pytanie, prace dobrze:

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); 

Wszystko w nawiasach jest uważane za zmienną spoza tego fragmentu. Przypuszczam, że _graphicsDevice jest wystarczająco jasne. Wiem, że .Seek można uprościć, ale myślę, że działa dobrze na przykład dla celów. Zauważ, że nie zalecam wykonywania tego rodzaju operacji w każdej klatce, ponieważ dość szybko wyczerpuje twój FPS, gdy zostanie użyty źle.

Zajęło mi to trochę czasu, aby to rozgryźć, ale wynik jest satysfakcjonujący. Chciałbym podziękować wszystkim, którzy przejrzeli to pytanie i próbowali mi pomóc.

Lennard Fonteijn

Powiązane problemy