LPDIRECT3DTEXTURE9 get pixel

Started by
6 comments, last by Evil Steve 15 years, 6 months ago
Hi, Nice simple question How do I get the value of a pixel of a loaded texture at a given position in the texture. Its nice and easy using hlsl... tex2D(Texture, Pos ) But I can't use hlsl in this case. Thanks for the help! C.
Advertisement
You need to LockRect() the texture, then read from it.

Note that locking a texture is a very expensive thing to do, so you'd usually want to lock the entire texture, read a bunch of pixels, then unlock it.

Example code, assuming you just want to read one pixel:
D3DCOLOR readPixel(LPDIRECT3DTEXTURE9 pTexture, UINT x, UINT y){   // Lock the texture:   D3DLOCKED_RECT rect;   HRESULT hResult = pTexture->LockRect(0, &rect, NULL, D3DLOCK_READONLY);   if(FAILED(hResult))   {      // Failed to lock the texture, error      return 0;   }   // Get the bits as a BYTE*   const BYTE* pBits = rect.pBits;   // Seek to the correct pixel and read it   DWORD nBytesPerPixel = 4;   const DWORD* pPixel = (DWORD*)(pBits + y * rect.Pitch + x*nBytesPerPixel);   DWORD dwPixel = *pPixel;   // Unlock the texture   pTexture->UnlockRect(0);   // Return the pixel   return dwPixel;}
That assumes a 32-bit texture, if it's not then you'll have to adjust the code accordingly.
Cheers for the reply

I'm using a 8 bit rgb heightmap. But its a grayscale.

The function seems to be returning really big numbers.

I was expecting a value between 0 and 255.

Any ideas?

Thanks

C.
Quote:Original post by colinolivant
Cheers for the reply

I'm using a 8 bit rgb heightmap. But its a grayscale.

The function seems to be returning really big numbers.

I was expecting a value between 0 and 255.

Any ideas?

Thanks

C.
Yes, that function assumes a 32-bit texture. For 8-bit you'd want:
BYTE readPixel(LPDIRECT3DTEXTURE9 pTexture, UINT x, UINT y){   // Lock the texture:   D3DLOCKED_RECT rect;   HRESULT hResult = pTexture->LockRect(0, &rect, NULL, D3DLOCK_READONLY);   if(FAILED(hResult))   {      // Failed to lock the texture, error      return 0;   }   // Get the bits as a BYTE*   const BYTE* pBits = rect.pBits;   // Seek to the correct pixel and read it   BYTE byPixel = pBits[y * rect.Pitch + x];   // Unlock the texture   pTexture->UnlockRect(0);   // Return the pixel   return byPixel;}
EDIT: Again, that'll be very slow. You'd want to only LockRect() and UnlockRect() once to read multiple pixels normally. Also, if you're only using the heightmap to read in this way you'd be best loading it into D3DPOOL_SCRATCH if you're not already.
Hi I know this is problem has been fixed but i've got a another problem related to this...

I now want to write to a texture.
The whole reason i want to do this is because 0-255 isn't enough detail for a heightmap i need somelike like a short which i think is 16-bit i.e. 0 to 32767

The texture is 16-bit heightmap so i guess i should create the texture using D3DFMT_R16F?

if ( FAILED( D3DXCreateTexture(		DXUTGetD3DDevice(),		4096,		4096,		1,		0,		D3DFMT_R16F,		D3DPOOL_DEFAULT,		&HeigthTexture		) ) )	{		exit(-1);	}


Then writing to the texture something like...
IDirect3DSurface9 * surface = 0;	// get surface of first mip level	texture->GetSurfaceLevel(0, &surface);	// get description of the surface (width, height etc)	D3DSURFACE_DESC desc;	texture->GetLevelDesc(0, &desc);	D3DLOCKED_RECT locked;	// lock whole surface for read-only access	surface->LockRect(&locked, 0, D3DUSAGE_WRITEONLY); 	        DWORD* imageData = (DWORD*)locked.pBits;	short u = 0;	for(int h = 0; h < desc.Height; h++)	{		for(int j = 0; j < desc.Width; j++)		{			imageData[h * (locked.Pitch) + j] = u;		}			}        surface->UnlockRect();	surface->Release();}


But this ain't working....
I think i'm getting confused between 8,16,32 bit textures, any one explain?

Also if i use D3DFMT_R16F will i be able to do this in a pixel shader?
tex2D(texture,postion).r


Thanks for any help in advance, and big thanks for Evil Steve!!!

your imageData pointer is a pointer to a DWORD, which is an unsigned long. You probably want a pointer to a short, so that when you advance it or index it, it uses the correct size.

Also, the pitch is provided in bytes, not in shorts or DWORDs. Either use a pointer to byte and advance it by the pitch to get the start of each row, or divide the pitch by the size of a short (I wouldn't recommend this).

Also, R16F means a 16bit floating point value, you're using a 16bit signed integer format, so the data you're writing wouldn't fit the texture type you're creating.

You need to pick the data type you want to store in the texture and keep consistent about using it.
Sirob Yes.» - status: Work-O-Rama.
Right I think i'm getting there. The formats look better now but i just dont know how to write to the pixel...

        IDirect3DSurface9 * surface = 0;	// get surface of first mip level	texture->GetSurfaceLevel(0, &surface);	// get description of the surface (width, height etc)	D3DSURFACE_DESC desc;	texture->GetLevelDesc(0, &desc);	D3DLOCKED_RECT locked;	// lock whole surface for read-only access	surface->LockRect(&locked, 0, D3DUSAGE_WRITEONLY); 	short *pData;	BYTE* pBits = (BYTE*)locked.pBits;	for (int y=0; y<4096; y++)	{		for (int x=0; x<4096; x++)		{			//WTF			pBits[x] = (short)0;		}		pBits += locked.Pitch;	}	// important, clean up!	surface->UnlockRect();	surface->Release();


The format is now D3DFMT_D16, this right?

Thanks for the help
Quote:Original post by colinolivant
Right I think i'm getting there. The formats look better now but i just dont know how to write to the pixel...

*** Source Snippet Removed ***

The format is now D3DFMT_D16, this right?

Thanks for the help
D3DFMT_D16 is a depth buffer format, and it's not directly usable by the application - the driver is free to use those 16 bits however it wants, there's nothing to say what format it's in. It could be a 16-bit floating point number, it could be a short, or it could be some other scale.

What you want for a 16-bit format is something liek this:
short *pData;BYTE* pRawBits = (BYTE*)locked.pBits;for (int y=0; y<4096; y++){	// Get this scanline as a short*	short* pBits = (short*)pRawBits;	for (int x=0; x<4096; x++)	{		// pBits is a short* now, not a BYTE*		pBits[x] = (short)0;	}	// Advance the BYTE* to point to the next scanline	pRawBits += locked.Pitch;}

This topic is closed to new replies.

Advertisement