D3D11顶点缓冲区及纹理数据的更新
2024-07-31
13
0
- gs_draw_sprite(0, 0, context->width, context->height);
- gs_vb_data* data = gs_vertexbuffer_get_data(graphics->sprite_buffer); //获取顶点数据缓冲区地址
- build_sprite_norm(data, fcx, fcy, flip); //填充顶点数据到CPU内存
- gs_vertexbuffer_flush(graphics->sprite_buffer); //将CPU顶点数据复制到ID3D11Buffer空间中
- gs_load_vertexbuffer(graphics->sprite_buffer);//设置为当前的curVertexBuffer device->curVertexBuffer = vertbuffer;
- gs_draw(GS_TRISTRIP, 0, 0)
- device_draw //d3d11渲染流程
- device->LoadVertexBufferData()//设置IASetVertexBuffers
- device_draw //d3d11渲染流程
顶点数据格式
struct gs_vb_data {
size_t num;
struct vec3 *points;
struct vec3 *normals;
struct vec3 *tangents;
uint32_t *colors;
size_t num_tex;
struct gs_tvertarray *tvarray;
};
- num:表示顶点个数。立顶点数量为512,sprite缓冲区为4个。
- num_tex表示纹理个数。一般为1个。
顶点参数设置
填充gs_vb_data中顶点points的内容使用函数build_sprite_norm,如参数如下:
build_sprite_norm(data, 1920, 1080, 0);
该函数的内容如下:
static inline void build_sprite_norm(struct gs_vb_data *data, float fcx, float fcy, uint32_t flip)
{
float start_u, end_u;
float start_v, end_v;
assign_sprite_uv(&start_u, &end_u, (flip & GS_FLIP_U) != 0);
assign_sprite_uv(&start_v, &end_v, (flip & GS_FLIP_V) != 0);
build_sprite(data, fcx, fcy, start_u, end_u, start_v, end_v);
}
static inline void assign_sprite_uv(float *start, float *end, bool flip)
{
if (!flip){
*start = 0.0f;
*end = 1.0f;
} else {
*start = 1.0f;
*end = 0.0f;
}
}
所以build_sprite参数如下:
build_sprite(data, 1920, 1080, 0, 1.0f, 0, 1.0f);
函数内容如下:
static void build_sprite(struct gs_vb_data *data, float fcx, float fcy,
float start_u, float end_u, float start_v, float end_v)
{
struct vec2 *tvarray = data->tvarray[0].array;
vec3_zero(data->points);
vec3_set(data->points + 1, fcx, 0.0f, 0.0f);
vec3_set(data->points + 2, 0.0f, fcy, 0.0f);
vec3_set(data->points + 3, fcx, fcy, 0.0f);
vec2_set(tvarray, start_u, start_v);
vec2_set(tvarray + 1, end_u, start_v);
vec2_set(tvarray + 2, start_u, end_v);
vec2_set(tvarray + 3, end_u, end_v);
}
通过分析,对于精录,其4个顶点坐标和纹理坐标如下:
(0,0,0)(1920,0,0)(0,1080,0)(1920,1080,0)
(0,0) (1.0,0) (0,1.0) (1.0,1.0)
所以4顶点,2个三角形。
将顶点数据更新到ID3D11Buffer
顶点更新通过函数gs_vertexbuffer_flush实现的
- gs_vertexbuffer_flush
- gs_vertexbuffer_flush
- gs_vertexbuffer_flush_internal
- vertbuffer->FlushBuffer(vertbuffer->vertexBuffer, data->points,sizeof(vec3));
- gs_vertexbuffer_flush_internal
- gs_vertexbuffer_flush
FlushBuffer就是对ID3D11Buffer进行映射,然后内存拷贝即可。因为是内存对齐的。
void gs_vertex_buffer::FlushBuffer(ID3D11Buffer *buffer, void *array,
size_t elementSize)
{
D3D11_MAPPED_SUBRESOURCE msr;
HRESULT hr;
if (FAILED(hr = device->context->Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &msr)))
throw HRError("Failed to map buffer", hr);
memcpy(msr.pData, array, elementSize * vbd.data->num);
device->context->Unmap(buffer, 0);
}
使用gs_vb_data的内存空间创建ID3D11Buffer,当数据更新时,需要用map映射出来再次更新。所以CPU和GPU之间的数据同步,是需要手动执行的。
纹理坐标的更新
顶点包含有纹理坐标,需要也更新。
在函数gs_vertexbuffer_flush_internal中
for (size_t i = 0; i < num_tex; i++)
{
gs_tvertarray &tv = data->tvarray[i];
vertbuffer->FlushBuffer(vertbuffer->uvBuffers[i], tv.array, tv.width * sizeof(float));
}
这里num_text=1表示只有一个纹理,而tv.width=2表示坐标为2个float型的xy.