Sign in to follow this  
colinolivant

LPDIRECT3DTEXTURE9 get pixel

Recommended Posts

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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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!!!

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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;
}

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this