As a follow-on to saturday’s post about sampling textures in the vertex shader, I thought I’d write up some quick notes on how you can pass data to a vertex shader through textures. Again, this is another one of those things that articles talk about doing but rarely show full examples on how to actually do it. Some times I wonder if the authors of these articles didn’t actually get it working of if they kept it a secret because it gives them an advantage.
You can pass large arrays of data to a vertex shader by storing the data in a 1D or 2D texture. Sampling the texture I covered previously, you basically use the tex2Dlod() function in HLSL (texldl is the shader assembly call). The trick here is: how do you get the data into a texture and then to the vertex shader?
Currently only floating point textures are supported by today’s vertex shader hardware, so the data needs to be stored in one of these formats:
D3DFMT_R32F: 32-bit float format using 32 bits for the red channel.
D3DFMT_A32B32G32R32F: 128-bit float format using 32 bits for the each channel (alpha, blue, green, red).
Direct3D doesn’t allow you to modify textures stored in video memory, so you’ll to create two textures. One of these textures will be stored in video memory, the other in system memory. Whenever you need to change the data passed to the vertex shader you’ll need to change it in the system memory texture and then copy it over the video memory texture. Try not to do this every frame or you’ll kill performance. Most papers I’ve read suggest doing this update in a background thread so that you don’t affect rendering performance. It probably depends upon your application.
Here’s how you create your two textures:
hr = D3DXCreateTexture(
m_pD3DDevice,
width,
height,
D3DX_DEFAULT,
NULL,
D3DFMT_R32F,
D3DPOOL_SYSTEMMEM,
&_systemtex);
hr = D3DXCreateTexture(
m_pD3DDevice,
width,
height,
D3DX_DEFAULT,
D3DUSAGE_RENDERTARGET ,
D3DFMT_R32F,
D3DPOOL_DEFAULT,
&_videotex);
To update the texture, do the following:
D3DLOCKED_RECT rect;
hr = _systemtex->LockRect(0, &rect, NULL, NULL);
FLOAT *pbits = (FLOAT *) rect.pBits;
for(int x = 0; x < width; x++)
{
for(int y = 0; y < height; y++)
{
pbits[y * width + x] = sinf(x);
}
}
hr = _systemtex->UnlockRect(0);
hr = m_pD3DDevice->UpdateTexture(_systemtex, _videotex);
These examples assume the R32F format, obviously if you use the 4 float format things will be a little different.