2011-08-11 65 views
5

這是我在StackOverflow上的第一個問題,歡呼!我可以誠實地說,我每天都使用StackOverflow來處理我的工作和個人編程的謎題。 99.9%的時間我真的在這裏找到了我需要的答案,這太棒了!SlimDX/DirectX9/C# - 如何訪問紋理中的像素數據

我目前的問題實際上讓我有點難過,因爲我似乎無法找到任何實際可行的東西。我已經閱讀了GameDev.net上的幾篇文章,並發現了網絡上的其他資源,但無法整理。

我正在將我爲XNA寫的一個小型2D引擎移植到SlimDX(目前只是DirectX9),這是一個很好的舉措,因爲我在短短几天內就瞭解了更多關於DirectX內部工作的信息比我在與XNA合作的六個月中所做的都要多。我完成了大部分基本渲染功能,並實際上設法重新創建了帶有大量附加功能的XNA SpriteBatch(我在XNA中真的很遺憾)。

我試圖去工作的最後一件事情是從給定的紋理中提取源矩形並將其用於平鋪。爲什麼:當不平鋪時,你可以用UV來搞定你想要顯示的源(例如:0.3; 0.3到0.5; 0.5),但是當平鋪時需要UV來平鋪(0; 0到2; 2意味着平鋪圖像兩次),因此需要剪裁紋理。

爲了使長話短說,我嘗試使用以下命令:

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

我嘗試了不同的像素,但一切似乎給虛假數據。例如,我實際上嘗試使用我當前的頭像來查看從DataRectangle獲得的緩衝區是否與圖像中的實際像素相匹配,但沒有運氣(甚至檢查格式是否正確,是哪個)。

我在做什麼錯?有沒有更好的方法來做到這一點?或者,我的UV故事是錯誤的,它可以在平鋪之前比切割源矩形更簡單得多嗎?

謝謝您的時間,

倫納德Fonteijn

更新#1

我實際上設法使用從字節數組下轉換輸出像素數據爲位圖:

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

因此,數據似乎並不像我認爲的那麼虛假。然而,我的下一個問題是抓取源矩形中指定的像素並將其複製到新紋理。我想要剪切的圖像是150x150,但由於某種原因,它被拉伸爲256x256圖像(兩個冪),但實際嘗試訪問超過150x150的像素時,會引發OutOfBounds異常。另外,當我真的嘗試創建尺寸爲256x256的第二張空白紋理時,無論我放入什麼,它都變成完全黑色。

這裏是我當前的代碼:

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

所以我的新的(附加)的問題是:我怎麼能在像素移動從一個紋理到另一個較小的質感,包括Alpha通道?任何爲什麼SurfaceDescription在我的原始紋理爲150x150時報告256x256?

+0

我認爲這實際上應該發佈在http://gamedev.stackexchange.com下。如果這是真的,任何主持人應該隨時將其移動到那裏。 –

+0

這是一個很好的問題 - 關於這裏的主題......但是,是的,你可能會在那裏得到更多的幫助。 – jcoder

+0

我用新信息更新了問題。 –

回答

5

回答我自己的問題有些尷尬,但經過一些更多的挖掘,並通過簡單的試驗和錯誤,我找到了解決方案。

起初,我不得不改變我加載紋理的方式。爲了防止它在內部調整到電源的一個兩倍大小,我不得不使用下面的方法:

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

注意如何我特地指定了大小非功率的兩種。

接下來,我的新紋理定義出現錯誤。除了指定0水平(並使其自動生成紋理貼圖),我必須指定1平,就像這樣:

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

已經這樣做了之後,for循環我有我的實際問題,作品罰款:

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

括號內的所有內容均被視爲此片段外部的變量。我想_graphicsDevice是足夠清晰的。我知道.Seek可以簡化,但我認爲它可以很好地用於示例目的。請注意,我不建議在每一幀都進行這種操作,因爲它在使用錯誤時會很快耗盡您的FPS。

我花了相當一段時間才弄明白,但結果令人滿意。我想感謝所有瞥見問題並試圖幫助我的人。

Lennard Fonteijn

相關問題