FidelityFX Brixelizer 1.0
FidelityFX Brixelizer 是一种基于计算、高度优化的稀疏距离场技术,使用 HLSL 实现。
目录
要求
HLSLCS_6_6
集成指南
FidelityFX Brixelizer 提供两个级别的 API 以适应不同的集成需求。主 API 用于通过默认行为快速集成。提供了一个更灵活的低级 API,称为“原生”API,用于需要更精细控制的集成。Brixelizer 的集成分为两个部分。首先,必须创建 SDF 的加速结构并用几何数据填充它,然后必须将加速结构绑定到用户编写的着色器中,以便与 Brixelizer 光线步进函数一起使用。
为了计算加速结构,主 API 提供了一个默认方法的实现,该方法应适用于大多数集成用例。在主 API 中,加速结构的参数在上下文创建时指定,然后实例被提交到上下文,上下文在调用帧更新以生成时在内部处理这些数据。相比之下,原生 API 允许在更具体的用例中对 Brixelizer 进行更精细化的控制,以用于具有更专业化需求的集成。在这种情况下,用户可以精细控制如何以及何时计算 Brixelizer 加速结构,但后续在光线步进查询中的使用保持不变。
为 Brixelizer 创建资源
为了便于集成到现有引擎中,Brixelizer 将用于存储加速结构的 GPU 资源的创建留给用户,并在写入命令列表时将这些资源作为参数请求。用户创建的资源如下:
| 名称 | 每级联 | 类型 | 格式/步幅 | 尺寸/字节大小 |
|---|---|---|---|---|
| SDF 图集 | 否 | Texture3D | R8_UNORM | FFX_BRIXELIZER_STATIC_CONFIG_SDF_ATLAS_SIZE x FFX_BRIXELIZER_STATIC_CONFIG_SDF_ATLAS_SIZE x FFX_BRIXELIZER_STATIC_CONFIG_SDF_ATLAS_SIZE |
| 砖块 AABB | 否 | StructuredBuffer | UINT32 (FFX_BRIXELIZER_BRICK_AABBS_STRIDE) | FFX_BRIXELIZER_BRICK_AABBS_SIZE |
| 级联 AABB 树 | 是 | StructuredBuffer | UINT32 (FFX_BRIXELIZER_CASCADE_AABB_TREE_STRIDE) | FFX_BRIXELIZER_CASCADE_AABB_TREE_SIZE |
| 级联砖块图 | 是 | StructuredBuffer | UINT32 (FFX_BRIXELIZER_CASCADE_BRICK_MAP_STRIDE) | FFX_BRIXELIZER_CASCADE_BRICK_MAP_SIZE |
主 API
主 API 可通过包含 FidelityFX/host/ffx_brixelizer.h 头文件来访问。主 API 是使用原生 API 实现的,主 API 的实现可在文件 src/components/ffx_brixelizer.cpp 中找到。
// =============================================================================// Initialize/destroy the FFX Brixelizer backend// =============================================================================
FfxBrixelizerContextDescription desc = {};
desc.sdfCenter = ...; // The point in world space around which to center the cascades.desc.numCascades = ...; // The number of cascades managed by the Brixelizer simple context.desc.flags = ...; // Flags for context creation. See <c><i>FfxBrixelizerContextFlags</i></c>.
uint32_t numCascadeResources = 0;for (uint32_t i = 0; i < desc.numCascades; ++i) { FfxBrixelizerCascadeDescription *cascadeDesc = &cascadeDescs[i]; cascadeDesc->flags = ...; // Flags for cascade creation. See <c><i>FfxBrixelizerCascadeFlag</i></c>. cascadeDesc->voxelSize = ...; // The edge size of voxels in world space for the cascade. switch (cascadeDesc->flags & (FFX_BRIXELIZER_CASCADE_STATIC | FFX_BRIXELIZER_CASCADE_DYNAMIC)) { case FFX_BRIXELIZER_CASCADE_STATIC: case FFX_BRIXELIZER_CASCADE_DYNAMIC: ++numCascadeResources; break; case (FFX_BRIXELIZER_CASCADE_STATIC | FFX_BRIXELIZER_CASCADE_DYNAMIC): numCascadeResources += 3; break; default: assert(false); }}
desc.backendInterface = ffxGetInterface(...); // An implementation of the FidelityFX backend for use with Brixelizer.
FfxBrixelizerContext context = {};FfxErrorCode error = ffxBrixelizerContextCreate(&desc, &context);assert(error == FFX_OK);
// ...
FfxErrorCode error = ffxBrixelizerContextDestroy(context);assert(error == FFX_OK);
// =============================================================================// Submit an instance to the brixelizer context// =============================================================================
FfxBrixelizerInstanceDescription instanceDesc = {};FfxBrixelizerInstanceID instanceID = FFX_BRIXELIZER_INVALID_ID;
instanceDesc.maxCascade = ...; // The index of the highest cascade this instance will be submitted to. This helps avoid submitting many small objects to least detailed cascades.instanceDesc.aabb = ...; // An axis-aligned bounding box in world space surrounding the instanceinstanceDesc.transform = ...; // A 3x4 matrix in row major order representing a transform to be applied to the vertices
instanceDesc.indexFormat = ...; // Either FFX_INDEX_TYPE_UINT16 or FFX_INDEX_TYPE_UINT32instanceDesc.indexBuffer = ...; // The index of the index buffer set in ffxBrixelizerSetBufferinstanceDesc.indexBufferOffset = ...; // An offset into the index buffer.instanceDesc.triangleCount = ...; // The count of triangles in the index buffer.
instanceDesc.vertexBuffer = ...; // The index of the vertex buffer set in ffxBrixelizerSetBufferinstanceDesc.vertexStride = ...; // The stride of the vertex buffer in bytes.instanceDesc.vertexBufferOffset = ...; // An offset into the vertex buffer.instanceDesc.vertexCount = ...; // The count of vertices in the vertex buffer.instanceDesc.vertexFormat = ...; // Either FFX_SURFACE_FORMAT_R16G16B16A16_FLOAT or FFX_SURFACE_FORMAT_R32G32B32A32_FLOAT
instanceDesc.flags = ...; // A combination of one or more FfxBrixelizerInstanceFlags. Currently used to specify whether an instance is static or dynamic.instanceDesc.outInstanceID = &instanceID; // An optional pointer to an FfxBrixelizerInstanceID for writing the ID of the created instance. This is required for static instances and unused for dynamic instances.
// Create instances for a given context with the ffxBrixelizerCreateInstance function.// Static instances are persistent across frames. Dynamic instances are discarded after a single frame.FfxErrorCode error = ffxBrixelizerCreateInstance(&context, &instanceDesc);assert(error == FFX_OK);
// ...
// Later if the static geometry changes instances may be deleted as followserror = ffxBrixelizerDeleteInstance(&context, instanceID);assert(error == FFX_OK);
// =============================================================================// Brixelizer buffer registration// =============================================================================
// Register vertex/index buffers to Brixelizer.FfxBrixelizerBufferDescription *bufferDescs = (FfxBrixelizerBufferDescription*)malloc(numVertexAndIndexBuffers * sizeof(*bufferDescs));uint32_t *bufferIndices = (uint32_t*)malloc(numVertexAndIndexBuffers * sizeof(*bufferIndices));
for (uint32_t i = 0; i < numVertexAndIndexBuffers; ++i) { bufferDescs[i].buffer = ffxGetResource(...); bufferDescs[i].outIndex = &bufferIndices[i];}FfxErrorCode error = ffxBrixelizerRegisterBuffers(&context, bufferDescs, numVertexAndIndexBuffers);assert(error == FFX_OK);
free(bufferDescs);
// ...
// Unregister buffers from Brixelizer.error = ffxBrixelizerUnregisterBuffers(&context, bufferIndices, numVertexAndIndexBuffers);assert(error == FFX_OK);
free(bufferIndices);
// =============================================================================// Brixelizer frame update// =============================================================================
FfxBrixelizerStats stats = {};size_t scratchSize = 0;
FfxBrixelizerUpdateDescription desc = {};desc.frameIndex = ...; // The index of the current frame.desc.sdfCenter[3] = ...; // The center of the cascades.desc.populateDebugAABBsFlags = ...; // Flags determining which AABBs to draw in a debug visualization. See FfxBrixelizerPopulateDebugAABBsFlag.desc.debugVisualizationDesc = ...; // An optional debug visualization description. If this parameter is set to NULL no debug visualization is drawn.desc.maxReferences = ...; // The maximum number of triangle voxel references to be stored in the update.desc.triangleSwapSize = ...; // The size of the swap space available to be used for storing triangles in the update.desc.maxBricksPerBake = ...; // The maximum number of bricks to be updated.
desc.resources.sdfAtlas = ffxGetResource(...); // A resource for storing the SDF atlas.desc.resources.brickAABBs = ffxGetResource(...); // A resource for storing the AABBs for the bricks.for (uint32_t i = 0; i < numCascadeResources; ++i) { desc.resources.cascadeResources[i].aabbTree = ffxGetResource(...); // A resource for storing the AABB tree for the cascade. desc.resources.cascadeResources[i].brickMap = ffxGetResource(...); // A resource for storing the brick map for the cascade.}
desc.outScratchBufferSize = &scratchSize; // An optional pointer to a size_t to receive the amount of GPU scratch space required for the update.desc.outStats = &stats; // An optional pointer to an FfxBrixelizerStats struct receiving stats from the pass.
FfxBrixelizerBakedUpdateDescription bakedUpdateDesc = {};FfxErrorCode error = ffxBrixelizerBakeUpdate(&context, &desc, &bakedUpdateDesc);assert(error == FFX_OK);
// Create a GPU buffer with at least as much storage as scratchSize for use as scratch spaceFfxResource scratchBuffer = ffxGetResource(...);
// call frame updateFfxErrorCode error = ffxBrixelizerUpdate(&context, &bakedUpdateDesc, scratchBuffer, commandList);assert(error == FFX_OK);
// =============================================================================// Using Brixelizer in a shader// =============================================================================
FfxBrixelizerContextInfo contextInfo = {};FfxErrorCode error = ffxBrixelizerGetContextInfo(&context, &contextInfo);assert(error == FFX_OK);
// copy the contents of contextInfo to a constant buffer for use by the shader// and bind the resources passed in to context creation for the:// * SDF atlas// * Brick AABB map// * Cascade brick map (for each cascade)// * Cascade AABB tree (for each cascade)// to the shader.原生 API
原生 API 可通过包含 FidelityFX/host/ffx_brixelizer.h 头文件来访问。原生 API 的实现可在文件 src/components/brixelizer/ffx_brixelizer_raw_private.h 和 src/components/brixelizer/ffx_brixelizer_raw.cpp 中找到。
// =============================================================================// Create/destroy a Brixelizer context// =============================================================================
FfxBrixelizerRawContextDescription desc = {};desc.maxDebugAABBs = ...; // Maximum number of debug AABBs to be stored for use in debug visualizationdesc.flags = ...; // Flags for context creation. See <c><i>FfxBrixelizerContextFlags</i></c>.desc.backendInterface = ...; // FFX backend interface
FfxBrixelizerContext context = {};FfxErrorCode error = ffxBrixelizerRawContextCreate(&context, &desc);assert(error == FFX_OK);
// ...
error = ffxBrixelizerRawContextDestroy(&context);assert(error == FFX_OK);
// =============================================================================// Create/destroy a cascade// =============================================================================
uint32_t cascadeIndex = ...; // Index of the cascade to create, between 0 and FFX_BRIXELIZER_MAX_CASCADES - 1
FfxBrixelizerRawCascadeDescription cascadeDesc = {};cascadeDesc.brickSize = ...; // Edge length in world space units of a single brickcascadeDesc.cascadeMin[3] = ...; // Corner in world space of the first brickcascadeDesc.index = cascadeIndex; // Cascade index
error = ffxBrixelizerRawContextCreateCascade(&context, &cascadeDesc);assert(error == FFX_OK);
// ...
// Destroy the cascade. Unregisters the cascade resources from the Brixelizer context.error = ffxBrixelizerRawContextDestroyCascade(&context, cascadeIndex);assert(error == FFX_OK);
// =============================================================================// Create/destroy instances// =============================================================================
FfxBrixelizerRawInstanceDescription *instanceDescs = (FfxBrixelizerRawInstanceDescription*)malloc(numInstances * sizeof(*instanceDescs));FfxBrixelizerInstanceID *instanceIDs = (FfxBrixelizerInstanceID*)malloc(numInstances * sizeof(*instanceIDs));
for (uint32_t i = 0; i < numInstances; ++i) { FfxBrixelizerRawInstanceDescription *instanceDesc = &instanceDescs[i];
instanceDesc->aabbMin = ...; // The minimum coordinates of an AABB surrounding the instance. instanceDesc->aabbMax = ...; // The maximum coordinates of an AABB surrounding the instance. instanceDesc->transform = ...; // A tranform of the instance into world space. The transform is in row major order.
instanceDesc->indexFormat = ...; // The format of the index buffer. Accepted formats are FFX_INDEX_UINT16 or FFX_INDEX_UINT32. instanceDesc->indexBuffer = ...; // The index of the index buffer set with ffxBrixelizerContextSetBuffer. instanceDesc->indexBufferOffset = ...; // An offset into the index buffer. instanceDesc->triangleCount = ...; // The count of triangles in the index buffer.
instanceDesc->vertexBuffer = ...; // The index of the vertex buffer set with ffxBrixelizerContextSetBuffer. instanceDesc->vertexStride = ...; // The stride of the vertex buffer in bytes. instanceDesc->vertexBufferOffset = ...; // An offset into the vertex buffer. instanceDesc->vertexCount = ...; // The count of vertices in the vertex buffer. instanceDesc->vertexFormat = ...; // The format of vertices in the vertex buffer. Accepted values are FFX_SURFACE_FORMAT_R16G16B16A16_FLOAT and FFX_SURFACE_FORMAT_R32G32B32A32_FLOAT.
instanceDesc->flags = ...; // Flags for the instance. See FfxBrixelizerInstanceFlags. instanceDesc->outInstanceID = &instanceIDs[i]; // Pointer to an FfxBrixelizerInstanceID to receive the ID of the created instance.}
FfxBrixelizerInstanceID instanceID = FFX_BRIXELIZER_INVALID_ID;error = ffxBrixelizerContextCreateInstances(&context, instanceDescs, numInstances);assert(error == FFX_OK);
free(instanceDescs)
// ...
error = ffxBrixelizerContextDestroyInstances(&context, instanceIDs, numInstances);assert(error == FFX_OK);
free(instanceIDs);
// =============================================================================// Register/unregister vertex and index buffers// =============================================================================
// Register vertex/index buffers to Brixelizer.FfxBrixelizerBufferDescription *bufferDescs = (FfxBrixelizerBufferDescription*)malloc(numVertexAndIndexBuffers * sizeof(*bufferDescs));uint32_t *bufferIndices = (uint32_t*)malloc(numVertexAndIndexBuffers * sizeof(*bufferIndices));
for (uint32_t i = 0; i < numVertexAndIndexBuffers; ++i) { bufferDescs[i].buffer = ffxGetResource(...); bufferDescs[i].outIndex = &bufferIndices[i];}FfxErrorCode error = ffxBrixelizerRawContextRegisterBuffers(&context, bufferDescs, numVertexAndIndexBuffers);assert(error == FFX_OK);
free(bufferDescs);
// ...
// Unregister buffers from Brixelizer.error = ffxBrixelizerRawContextUnregisterBuffers(&context, bufferIndices, numVertexAndIndexBuffers);assert(error == FFX_OK);
free(bufferIndices);
// =============================================================================// Calculate a frame of brixelizer// =============================================================================
FfxCommandList commandList = ffxGetCommandList(...);error = ffxBrixelizerRawContextFlushInstances(&context, commandList);assert(error == FFX_OK);
FfxBrixelizerResources resources = {};resources.sdfAtlas = ffxGetResource(...); // SDF atlas resourceresources.brickAABBs = ffxGetResource(...); // Brick AABBs resourcefor (uint32_t i = 0; i < numCascadeResources; ++i) { resources.cascadeResources[i].aabbTree = ffxGetResource(...); // A resource for storing the AABB tree for the cascade. resources.cascadeResources[i].brickMap = ffxGetResource(...); // A resource for storing the brick map for the cascade.}
error = ffxBrixelizerRawContextBegin(&context, &resources);assert(error == FFX_OK);
// Reset/clear a cascade. Frees all bricks from the cascade allowing geometry to be built to the cascade from scratch.
error = ffxBrixelizerRawContextResetCascade(&context, cascadeIndex);assert(error == FFX_OK);
// Create jobs
FfxBrixelizerRawJobDescription jobs[NUM_JOBS] = {};
for (uint32_t i = 0; i < NUM_JOBS; ++i) { FfxBrixelizerRawJobDescription *job = &jobs[i]; job->aabbMin = ...; // The mimimum corner of the AABB of the job. job->aabbMax = ...; // The maximum corner of the AABB of the job. job->flags = ...; // Flags for the job (to be set from FfxBrixelizerJobFlags). job->instanceIdx = ...; // The ID for an instance for the job.}
// Update cascades
FfxBrixelizerRawCascadeUpdateDescription updateDescs[NUM_CASCADES_TO_UPDATE] = {};size_t scratchSpaceSize = 0;
for (uint32_t i = 0; i < NUM_CASCADES_TO_UPDATE; ++i) { FfxBrixelizerRawCascadeUpdateDescription *desc = &updateDescs[i]; desc->maxReferences = ...; // Storage for triangle->voxel references desc->triangleSwapSize = ...; // Scratch storage for triangles desc->maxBricksPerBake = ...; // Max SDF brick baked per update desc->cascadeIndex = ...; // Target Cascade desc->jobs = jobs; // A pointer to an array of jobs. desc->numJobs = NUM_JOBS; // The number of jobs in the array pointed to by jobs. desc->cascadeMin = ...; // Lower corner of the first brick in world space. desc->clipmapOffset = ...; // Changing that invalidates portion of the cascade. it's an offset in the voxel->brick table. desc->flags = ...; // See FfxBrixelizerCascadeUpdateFlags.
size_t thisScratchSpaceSize = 0; error = ffxBrixelizerRawContextGetScratchMemorySize(&context, desc, &thisScratchSpaceSize); assert(error == FFX_OK); scratchSpaceSize = max(scratchSpaceSize, thisScratchSpaceSize);}
FfxResource scratchBuffer = ffxGetResource(...); // A resource of size at least scratchSpaceSize to be used as the scratch space for brixelizer
for (uint32_t i = 0; i < NUM_CASCADES_TO_UPDATE; ++i) { FfxBrixelizerCascadeUpdateDescription *desc = &updateDescs[i]; error = ffxBrixelizerRawContextUpdateCascade(&context, desc, scratchBuffer); assert(error == FFX_OK);}
// Merge cascades
for (uint32_t i = 0; i < NUM_CASCADES_TO_MERGE; ++i) { uint32_t sourceCascadeIndex1 = ...; // index of first source cascade uint32_t sourceCascadeIndex2 = ...; // index of second source cascade uint32_t destCascadeIndex = ...; // index of destination cascade error = ffxBrixelizerRawContextMergeCascades(&context, sourceCascadeIndex1, sourceCascadeIndex2, destCascadeIndex); assert(error == FFX_OK);}
// Build cascade AABB trees
for (uint32_t i = 0; i < NUM_AABB_TREES_TO_BUILD; ++i) { uint32_t cascadeIndex = ...; // index of cascade to build an AABB tree for ffxBrixelizerRawContextBuildAABBTree(&context, cascadeIndex); assert(error == FFX_OK);}
// Optionally do debug visualization
if (doDebugVisualization) { FfxBrixelizerDebugVisualizationDescription desc = {}; desc.inverseViewMatrix = ...; // Inverse view matrix for the scene in row major order. desc.inverseProjectionMatrix = ...; // Inverse projection matrix for the scene in row major order. desc.debugState = ...; // An FfxBrixelizerTraceDebugModes determining what kind of debug output to draw. desc.startCascadeIndex = ...; // The index of the most detailed cascade in the cascade chain. desc.endCascadeIndex = ...; // The index of the least detailed cascade in the cascade chain. desc.sdfSolveEps = ...; // The epsilon value used in SDF ray marching. desc.tMin = ...; // The tMin value for minimum ray intersection. desc.tMax = ...; // The tMax value for maximum ray intersection. desc.renderWidth = ...; // The width of the output resource. desc.renderHeight = ...; // The height of the output resource. desc.output = ...; // An FfxResource to draw the debug visualization to.
desc.commandList = ...; // An FfxCommandList to write the draw commands to. desc.numDebugAABBInstanceIDs = ...; // The number of FfxBrixelizerInstanceIDs in the debugAABBInstanceIDs array. desc.debugAABBInstanceIDs = ...; // An array of FfxBrixelizerInstanceIDs for instances to draw the bounding boxes of.
for (uint32_t i = 0; i < numCascades; ++i) { desc.cascadeDebugAABB[i] = ...; // An array of flags showing what AABB debug output to draw for each cascade. }
error = ffxBrixelizerRawContextDebugVisualization(&context, &desc); assert(error == FFX_OK);}
error = ffxBrixelizerRawContextEnd(&context);assert(error == FFX_OK);
error = ffxBrixelizerRawContextSubmit(&context, commandList);assert(error == FFX_OK);
// =============================================================================// Using Brixelizer in a shader// =============================================================================
FfxBrixelizerContextInfo contextInfo = {};FfxErrorCode error = ffxBrixelizerRawContextGetInfo(&context, &contextInfo);assert(error == FFX_OK);
// copy the contents of contextInfo to a constant buffer for use by the shader// and bind the resources passed in to context creation for the:// * SDF atlas// * Brick AABB map// * Cascade brick map (for each cascade)// * Cascade AABB tree (for each cascade)// to the shader.
// =============================================================================// Misc// =============================================================================
// Read back counters from brixelizer for understanding performance
FfxBrixelizerDebugCounters debugCounters = {};error = ffxBrixelizerRawContextGetDebugCounters(&context, &debugCounters);assert(error == FFX_OK);
FfxBrixelizerScratchCounters scratchCounters = {};uint32_t cascadeIndex = ...; // Index of cascade to queryerror = ffxBrixelizerRawContextGetCascadeCounters(&context, cascadeIndex, &scratchCounters);assert(error == FFX_OK);HLSL API
HLSL API 可通过在 HLSL 源文件中包含 FidelityFX/gpu/brixelizer/ffx_brixelizer_trace_ops.h 头文件来访问。在包含 FidelityFX/gpu/brixelizer/ffx_brixelizer_trace_ops.h 头文件之前,必须定义 FFX_BRIXELIZER_TRAVERSAL_EPS 宏,指定 SDF 步进中使用的 epsilon 值。为了使用光线追踪器,必须定义以下资源访问器函数:
FfxBrixelizerCascadeInfo GetCascadeInfo(FfxUInt32 cascadeID);FfxFloat32x3 LoadCascadeAABBTreesFloat3(FfxUInt32 cascadeID, FfxUInt32 elementIndex);FfxUInt32 LoadCascadeAABBTreesUInt(FfxUInt32 cascadeID, FfxUInt32 elementIndex);FfxUInt32 LoadBricksAABB(FfxUInt32 elementIndex);FfxFloat32 SampleSDFAtlas(FfxFloat32x3 uvw);FfxUInt32 LoadCascadeBrickMapArrayUniform(FfxUInt32 cascadeID, FfxUInt32 elementIndex);这些访问器函数必须访问 Brixelizer 的以下资源:
FfxBrixelizerCascadeInfo GetCascadeInfo(FfxUInt32 cascadeID)读取FfxBrixelizerContextInfo结构体中的成员cascades[cascadeID]。这应该是一个常量缓冲区,更新为ffxBrixelizerContextGetInfo或ffxBrixelizerGetContextInfo返回的值。FfxFloat32x3 LoadCascadeAABBTreesFloat3(FfxUInt32 cascadeID, FfxUInt32 elementIndex)应读取简单 API 中传递给 Brixelizer 的资源cascadeAABBTree[cascadeID]或原生 API 中索引为cascadeID的级联的aabbTree的elementIndex + 0到elementIndex + 2的元素。FfxUInt32 LoadCascadeAABBTreesUInt(FfxUInt32 cascadeID, FfxUInt32 elementIndex)应读取简单 API 中传递给 Brixelizer 的资源cascadeAABBTree[cascadeID]或原生 API 中索引为cascadeID的级联的aabbTree的elementIndex处的元素。FfxUInt32 LoadBricksAABB(FfxUInt32 elementIndex)应读取在上下文创建时传递给 Brixelizer 的资源brickAABBs的elementIndex处的元素。FfxFloat32 SampleSDFAtlas(FfxFloat32x3 uvw)应在上下文创建时作为sdfAtlas传入的纹理的uvw坐标处采样。FfxUInt32 LoadCascadeBrickMapArrayUniform(FfxUInt32 cascadeID, FfxUInt32 elementIndex)应读取简单 API 中传递给 Brixelizer 的资源cascadeBrickMaps[cascadeID]或原生 API 中索引为cascadeID的级联的brickMap的elementIndex处的元素。
绑定资源后,可以通过以下结构体和函数在 HLSL 代码中使用 Brixelizer。
struct FfxBrixelizerRayDesc { FfxUInt32 start_cascade_id; // The index of the most detailed cascade for ray traversal. FfxUInt32 end_cascade_id; // The index of the least detailed cascade for ray traversal. FfxFloat32 t_min; // The minimum distance at which to accept a hit. FfxFloat32 t_max; // The maximum distance at which to accept a hit. FfxFloat32x3 origin; // The origin of the ray. FfxFloat32x3 direction; // The direction of the ray. This input should be normalized.};
struct FfxBrixelizerHitRaw { FfxFloat32 t; // The distance from the ray origin to the hit. FfxUInt32 brick_id; // The ID of a hit brick. FfxUInt32 uvwc; // Packed UVW coordinates of the hit location. UVW coordinates are in brick space. FfxUInt32 iter_count; // The count of iterations to find the intersection.};
// This function is used for running a ray query against the Brixelizer SDF acceleration structure.// The "raw" version returns the data immediately accessible from the SDF structure generated by a hit.FfxBoolean FfxBrixelizerTraverseRaw(in FfxBrixelizerRayDesc ray_desc, out FfxBrixelizerHitRaw hit);
struct FfxBrixelizerHit { FfxFloat32 t; // The distance from the ray origin to the hit.};
// This function is used for running a ray query against the Brixelizer SDF acceleration structure.// This version simply returns the distance to a hit if a hit is encountered.FfxBoolean FfxBrixelizerTraverse(FfxBrixelizerRayDesc ray_desc, out FfxBrixelizerHit hit);
struct FfxBrixelizerHitWithNormal { FfxFloat32 t; // The distance from the ray origin to the hit. FfxFloat32x3 normal; // The normal of the SDF surface at the hit location.};
// This function is used for running a ray query against the Brixelizer SDF acceleration structure.// This version returns the distance to a hit and a normal to the SDF geometry at a hit location when a hit// is encountered.FfxBoolean FfxBrixelizerTraverseWithNormal(FfxBrixelizerRayDesc ray_desc, out FfxBrixelizerHitWithNormal hit);调整参数
在集成 Brixelizer 时,需要进行的一项任务是调整参数以获得合适的值,从而确保充分利用可用内存。特别是,Brixelizer 有三个参数用于确定 Brixelizer 所需的 GPU 临时缓冲区的大小。这些参数是每个级联更新的最大砖块数;每个级联更新的最大引用数;以及每个级联更新的最大三角形数。在提供的 Brixelizer 示例中,这些值简单地设置为较大的值,并提供了一个足够大的 GPU 临时缓冲区供 Brixelizer 使用。在临时缓冲区空间有限的更复杂场景中,应根据场景调整这些参数。为此,Brixelizer 提供了以下函数调用:
// =============================================================================// Main API// =============================================================================
/// A structure containing the statistics for a Brixelizer context readable after an update of the Brixelizer API.////// @ingroup Brixelizertypedef struct FfxBrixelizerContextStats { uint32_t brickAllocationsAttempted; ///< Total number of brick allocations attempted this frame. uint32_t brickAllocationsSucceeded; ///< Total number of brick allocations succeeded this frame. uint32_t bricksCleared; ///< Total number of bricks cleared in SDF atlas at the beginning of this frame. uint32_t bricksMerged; ///< Total number of bricks merged this frame. uint32_t freeBricks; ///< The number of free bricks in the Brixelizer context.} FfxBrixelizerContextStats;
/// A structure containing the statistics for a Brixelizer cascade readable after an update of the Brixelizer API.////// @ingroup Brixelizertypedef struct FfxBrixelizerCascadeStats { uint32_t trianglesAllocated; ///< The number of triangle allocations that were attempted to the cascade in a given frame. uint32_t referencesAllocated; ///< The number of reference allocations that were attempted to the cascade in a given frame. uint32_t bricksAllocated; ///< The number of brick allocations that were attempted to the cascade in a given frame.} FfxBrixelizerCascadeStats;
/// A structure containing the statistics readable after an update of the Brixelizer API.////// @ingroup Brixelizertypedef struct FfxBrixelizerStats { uint32_t cascadeIndex; ///< The index of the cascade that the statisticss have been collected for. FfxBrixelizerCascadeStats staticCascadeStats; ///< The statistics for the static cascade. FfxBrixelizerCascadeStats dynamicCascadeStats; ///< The statistics for the dynamic cascade. FfxBrixelizerContextStats contextStats; ///< The statistics for the Brixelizer context.} FfxBrixelizerStats;
typedef struct FfxBrixelizerUpdateDescription { // ... FfxBrixelizerStats *outStats;} FfxBrixelizerUpdateDescription;
FfxErrorCode ffxBrixelizerBakeUpdate(FfxBrixelizerContext* context, const FfxBrixelizerUpdateDescription* desc, FfxBrixelizerBakedUpdateDescription* outDesc);
// =============================================================================// Raw API// =============================================================================
typedef struct FfxBrixelizerDebugCounters { uint32_t brickCount; uint32_t dirtyBricks; uint32_t freeBricks; uint32_t clearBricks; uint32_t mergeBricks; uint32_t numDebugAABBs;} FfxBrixelizerDebugCounters;
typedef struct FfxBrixelizerScratchCounters { uint32_t triangles; uint32_t maxTriangles; uint32_t references; uint32_t maxReferences; uint32_t groupIndex; uint32_t compressionBricks; uint32_t storageOffset; uint32_t storageSize; uint32_t numBricksAllocated; uint32_t clearBricks;} FfxBrixelizerScratchCounters;
FfxErrorCode ffxBrixelizerContextGetDebugCounters(FfxBrixelizerContext* context, FfxBrixelizerDebugCounters* debugCounters);FfxErrorCode ffxBrixelizerContextGetCascadeCounters(FfxBrixelizerContext* context, uint32_t cascadeIndex, FfxBrixelizerScratchCounters* counters);在主 API 中,FfxBrixelizerCascadeStats 结构体中的 trianglesAllocated、referencesAllocated 和 bricksAllocated 的值可用于调整 FfxBrixelizerUpdateDescription 结构体中的 triangleSwapSize、maxReferences 和 maxBricksPerUpdate 的值。
在原生 API 中,用于调整 FfxBrixelizerRawCascadeUpdateDescription 的 maxBricksPerBake、triangleSwapSize 和 maxReferences 参数,应在更新级联后使用 ffxBrixelizerContextGetCascadeCounters 查询 triangles、references 和 numBricksAllocated 的值。
请注意,计数器是从 GPU 读取回的,因此会有延迟。因此,必须重复调用 ffxBrixelizerContextGetCascadeCounters 并存储值以供后续分析。
该技术
距离场
距离场是一种机制,通过它可以查询空间中任意点到最近几何体的距离。距离可以是带符号的,在这种情况下,负值表示空间中位于几何体内部的点,或者无符号的,在这种情况下,距离是到最近边缘的距离。在 Brixelizer 中,我们使用的距离场是无符号距离场(在 Brixelizer 的上下文中,SDF 代表 *sparse* distance field,而不是 signed distance field)。

当有一个有效的函数来计算空间中任意点到最近几何体的距离时,我们可以将其作为有效光线遍历算法的基础。要计算给定光线的交点,我们首先将 ray_cursor 设置为光线原点,将 ray_direction 设置为光线方向。然后,我们重复计算 ray_cursor 处的 SDF 函数。如果到最近几何体的距离小于目标 ray_traversal_epsilon 值,我们则终止并输出命中。否则,我们将光线光标的值更新为 ray_cursor = ray_cursor + ray_direction * sdf(ray_cursor)。

对于某些简单场景,我们可以通过使用易于计算的原始函数或原始函数的组合来创建 SDF 函数。然而,随着场景变得越来越复杂,这种方法变得不可行,而是将到最近几何体的距离预先计算到 3D 纹理中进行纹理查找是表示 SDF 最明显的方法。然而,光线遍历 SDF 的基本原因是几何体往往是稀疏的,并且大多数光线步进可以跳过很长的距离。因此,我们希望找到一种有效的 SDF 表示,只存储靠近几何体的 SDF 值,并跳过空白空间。

在 Brixelizer 中,稀疏 SDF 数据的表示方法是创建一系列小的局部 SDF,称为“砖块”(bricks),它们在 3D 纹理中存储 SDF 值以进行纹理查找,然后将砖块组织到“AABB 树”中,其中不包含几何体的节点可以标记为空,从而可以快速遍历空白空间。

除了使用砖块和 AABB 树表示稀疏表示的几何体外,Brixelizer 还通过排列“级联”(cascades)来表示不同细节层次的几何体。每个级联包含一个包含砖块的 AABB 树。级联中的每个砖块对应一个固定的真实世界尺寸。

有了这个加速结构,Brixelizer 然后通过依次考虑每个级联(从最详细到最不详细),遍历级联的 AABB 树,并在光线穿过砖块时执行光线步进来执行光线遍历。
算法概述
Brixelizer 分为两部分。第一部分从输入几何体构建加速结构。第二部分在光线步进通道中使用加速结构来确定光线与几何体的交点。
加速结构构建
高级
Brixelizer 构建的加速结构是一系列不同细节级别的“级联”。每个级联是一个立方体,每条边有固定数量的“砖块”。通过改变级联的几何空间砖块大小来改变每个级联的细节级别。在加速结构中,更详细的级联嵌套在不太详细的级联中。

每个级联包含一个稀疏的“砖块”数组和一个用于光线遍历的 AABB 树。每个砖块是一个 8x8x8 的立方体,其值在 0 到 1 之间,表示到最近几何体的距离,如距离场部分所述。每个级联的 AABB 树分为三层,第一层有一个根节点,每个节点包含一个 4x4x4 的子节点集合,位于其 AABB 内。

级联通过处理由三角形网格组成的几何体来构建。处理三角形网格并用于创建到最近几何体距离的砖块表示。首次创建级联时,有必要处理所有几何体以创建砖块。在后续通道中,只有更改的几何体才需要处理以计算加速结构。为了实现这一点,提交给级联构建的几何体可被标记为已更改,从而需要重建,或标记为未更改,从而不会引起重建(如果它对应于级联已构建的部分)。级联中心的移动不会引起级联重建,而是改变级联中表示中心的一个偏移量,并仅引起对应于之前中心在其边界之外的几何体的区域的重建。除了这些避免重建静态几何体的方法之外,Brixelizer 还支持将几何体分离到独立的级联中,分别处理静态网格和动态网格,然后合并这两个级联以最大化预构建静态几何体的重用。
Brixelizer 的典型用法将涉及每帧重建用于光线遍历的级联之一。如果此级联是通过合并一个包含静态几何体的级联和另一个包含动态几何体的级联来创建的,这将意味着构建这两个级联的更新,将它们合并为单个合并的级联,最后计算该级联的 AABB 树。主 API ffx_brixelizer_simple.h 和 ffx_brixelizer_simple.cpp 中提供了典型预期用法的实现,适用于大多数用例的直接集成。
级联更新的输入用“作业”(jobs)来描述。作业可用于使级联的一部分失效,或用于将实例提交到级联。级联中的所有实例都必须在每帧作为作业重新提交到级联。Brixelizer 不维护实例在哪些级联中的内部状态,也不假定所有实例都在所有级联中。在静态级联中,如果级联的某个部分发生几何变化,则必须向 Brixelizer 提交一个无效化作业,以排队重建该区域。例如,如果一个实例从静态级联中移除,仅不再将该实例作为级联几何体的一部分提交是不够的,必须将该实例曾经所在区域无效化,以强制重建。相比之下,在动态级联中,级联每帧从头开始重建,并且不需要提交任何无效化作业。所需的一切是提交构成该级联的所有实例。
总结一下,Brixelizer 提供了三个用于构建加速结构的功能:
- 更新级联(
ffxBrixelizerContextUpdateCascade)- 可选地重置动态级联;更新级联中心的位置;根据作业列表重建已无效化的区域。 - 合并级联(
ffxBrixelizerContextMergeCascades)- 将两个级联合并以创建一个包含两个级联几何体的单个级联。 - 构建 AABB 树(
ffxBrixelizerContextBuildAABBTree)- 对于已通过级联更新或级联合并构建的级联,扫描砖块并创建 AABB 树供光线遍历代码使用。
这些操作的典型用法可以在 ffx_brixelizer_simple.h API 的 ffx_brixelizer_simple.cpp 实现中找到。
低级 - 从网格几何体更新级联
要通过作业列表更新级联,首先我们识别并标记级联中需要处理的所有体素。这些是更新级联中心引入的任何新体素,以及由无效化作业标记的任何体素。之后,作业列表将被精简为仅包含几何体并且与需要处理的体素相交的作业。这在 FFX_BRIXELIZER_PASS_CASCADE_SCROLL_CASCADE、FFX_BRIXELIZER_PASS_CASCADE_INVALIDATE_JOB_AREAS 和 FFX_BRIXELIZER_PASS_CASCADE_COARSE_CULLING 通道中实现。
接下来,我们创建一个扫描缓冲区,用于在工作分布中使用,当我们希望为每个作业的每个三角形启动一个线程时。这在 FFX_BRIXELIZER_PASS_CASCADE_SCAN_JOBS 通道中计算。
完成后,我们进行体素化。在体素化中,我们遍历每个作业的每个三角形,从外部网格数据加载三角形,将其分类为小或大,并将其存储到临时缓冲区中。然后我们计算三角形的 AABB,并为与该 AABB 相交的每个体素存储一个指向该三角形的引用。三角形引用是为不同的体素 ID 三角形 ID 对创建的,每个引用都被分配一个引用 ID,因此如果存在 n 个对同一体素 ID 的引用,则每个引用都被分配一个从 0 到 n - 1 的不同引用 ID。这在 FFX_BRIXELIZER_PASS_CASCADE_VOXELIZE 通道中实现。
接下来,我们扫描所有体素,并为包含至少一个三角形引用的每个体素分配一个砖块。在此处将被识别为需要处理但未对应于三角形引用的体素被标记为空。在此阶段创建两个扫描缓冲区,一个用于允许每个引用批次每个体素执行一个线程,另一个用于允许我们为每个引用启动一个线程。这在 FFX_BRIXELIZER_CASCADE_PASS_SCAN_REFERENCES 通道中实现。
接下来,读取所有三角形引用,并将它们按体素 ID 和引用 ID 排序后存储在一个扁平的三角形 ID 数组中。这在 FFX_BRIXELIZER_CASCADE_PASS_COMPACT_REFERENCES 通道中实现。
接下来,我们为每个体素的每个引用批次启动一个波次。每个线程加载一个三角形引用。对于小三角形,我们计算最近的 brixel 并存储到该 brixel 的距离。对于大三角形,我们循环遍历 AABB,并为 AABB 内的每个 brixel 计算到三角形最近点的距离。这在 FFX_BRIXELIZER_CACADE_PASS_EMIT_SDF 通道中实现。
接下来,我们计算我们处理过的每个砖块的 AABB,并将计算出的 brixel 值存储到 SDF 图集中。这在 FFX_BRIXELIZER_CASCADE_PASS_COMPRESS_BRICK 通道中实现。
最后,我们使用 eikonal 通道填充砖块的未初始化值。这会在帧结束时发生一次,并计算本帧所有级联中创建的砖块的未初始化值。这在 FFX_BRIXELIZER_PASS_CONTEXT_EIKONAL 通道中实现。
在如上所述更新级联后,它可以被用作合并级联的源,或者可以直接计算其 AABB 树以用于光线步进。
杂项
扫描缓冲区 - 为了在线程之间均匀分配工作负载,Brixelizer 频繁使用*扫描缓冲区*。例如,如果输入是作业列表和每个作业的子体素数量,并且我们希望为每个作业的每个子体素启动一个线程,我们可以按如下方式实现:首先,我们创建一个包含每个作业一个整数的数组 a[n]。在该数组中,我们在第 n 个元素中存储直到第 n 个作业的所有作业的子体素总数。然后,我们为每个作业的每个子体素启动一个线程,其中第 i 个线程找到 n 使得 a[n] <= i 且 i < a[n + 1]。然后,第 i 个线程对应于第 n 个作业的子体素 j = i - a[n]。这为我们提供了从线程 ID 到作业 ID 子体素 ID 对的映射。这可以用伪代码表示如下:
// pseudocode of launching one thread per subvoxel of job
uint32_t numSubvoxelsForJob[NUM_JOBS] = ...; // Input array showing number of subvoxels for each jobuint32_t scanBuffer[NUM_JOBS + 1] = {}; // Scan buffer we create, initialised to zero
uint32_t total = 0;for (uint32_t i = 0; i < NUM_JOBS + 1; ++i) { scanBuffer[i] = total; total += numSubvoxelsForJob[i];}
void launchThread(uint32_t threadIndex){ // Use scan buffer to calculate jobID subvoxelID for thread with index threadIndex uint32_t jobID = 0; uint32_t subvoxelID = 0; while (scanBuffer[jobID + 1] < threadIndex) { ++jobID; } subvoxelID = threadIndex - scanBuffer[jobID];
// run code for job jobID and subvoxel subvoxelID // ...}
launchThreads(launchThread, total); // launch one thread per job subvoxel光线步进
在 Brixelizer 中,通过依次考虑每个级联(从最详细到最不详细)来步进光线。然后,对于光线相交的每个级联,遍历 AABB 树。对于光线击中的任何砖块,对该砖块执行光线步进。特别值得注意的是,光线仅在它生成的级联或更不详细的级联中进行几何命中测试。此外,光线位置在与每个级联进行交点测试时会向前推进,这意味着假设几何体在级联之间是一致的。当在砖块光线步进步骤中遇到小于 FFX_BRIXELIZER_TRAVERSAL_EPS 的 SDF 值时,光线被认为已命中几何体。
实现
数据类型
| 名称 | 描述 |
|---|---|
FfxBrixelizerBrickID | 一个 uint32_t,范围从 0 到 2^18 - 1。它直接索引到许多提供砖块信息的数组结构。这也可以解释为,从最高有效位到最低有效位,是 Brixelizer_SDFAtlas 的 6 位 z 坐标、6 位 y 坐标和 6 位 x 坐标索引。此外,值 0xFFFFFF 可用于表示无效的砖块 ID。 |
FfxBrixelizerVoxelID | 一个 uint32_t,其最高 8 位是级联 ID,最低 24 位是体素索引。 |
FfxBrixelizerVoxelIndex | 一个 uint32_t,范围从 0 到 2^24 - 1。 |
FfxBrixelizerStampID | 一个 uint32_t,范围从 0 到 7,它是砖块的 2D 切片的索引(即,一个砖块是一个 8x8x8 的距离值集合,范围从 0 到 1,一个 Stamp 是砖块中的一个 8x8 切片)。 |
FfxBrixelizerJobID | 作业缓冲区的 uint32_t 索引。 |
FfxBrixelizerBrixelizationJob | 一个包含 AABB、instanceIndex 和一个表示作业是无效化区域还是提交几何体的作业标志的结构体。 |
FfxBrixelizerInstanceInfo | |
FfxBrixelizerInstanceID | Brixelizer_InstanceInfo 缓冲区的 uint32_t 索引。 |
FfxBrixelizerTriangleID | 一个三角形的三角形临时空间中的 uint32_t 偏移量。此外,LSB 设置为 1 表示小三角形(12 字节存储),设置为 0 表示大三角形(24 位存储)。 |
资源
用户创建的资源
| 名称 | 每级联 | 类型 | 格式/步幅 | 尺寸/字节大小 |
|---|---|---|---|---|
| SDF 图集 | 否 | Texture3D | R8_UNORM | FFX_BRIXELIZER_STATIC_CONFIG_SDF_ATLAS_SIZE x FFX_BRIXELIZER_STATIC_CONFIG_SDF_ATLAS_SIZE x FFX_BRIXELIZER_STATIC_CONFIG_SDF_ATLAS_SIZE |
| 砖块 AABB | 否 | StructuredBuffer | UINT32 (FFX_BRIXELIZER_BRICK_AABBS_STRIDE) | FFX_BRIXELIZER_BRICK_AABBS_SIZE |
| 级联 AABB 树 | 是 | StructuredBuffer | UINT32 (FFX_BRIXELIZER_CASCADE_AABB_TREE_STRIDE) | FFX_BRIXELIZER_CASCADE_AABB_TREE_SIZE |
| 级联砖块图 | 是 | StructuredBuffer | UINT32 (FFX_BRIXELIZER_CASCADE_BRICK_MAP_STRIDE) | FFX_BRIXELIZER_CASCADE_BRICK_MAP_SIZE |
| 调试输出 | 否 | Texture2D | 用户定义的 RGB 格式 | 用户定义的 |
内部帧持久性资源
以下是 Brixelizer 上下文内部管理的 S所有结构化缓冲区的表。
| 名称 | 格式/步幅 | 长度 | 描述 |
|---|---|---|---|
Brixelizer_Counters | UINT32 | sizeof(FfxBrixelizerContextCounters) | 结构体包含已分配的砖块、脏砖块、空闲砖块、待清除砖块、待合并砖块的数量以及调试 AABB 的数量。 |
Brixelizer_BrickVoxelMap | UINT32 | FFX_BRIXELIZER_MAX_BRICKS | 一个扁平数组,由 brick_id 索引 voxel_id 值。Voxel ID 是一个 32 位无符号整数,最高 8 位是体素的级联 ID,最低 24 位是体素索引。体素索引为 0xFFFFFF 的模式被视为无效。 |
Brixelizer_BrickEikonalList | UINT32 | FFX_BRIXELIZER_MAX_BRICKS | |
Brixelizer_BrickEikonalCounters | UINT32 | FFX_BRIXELIZER_MAX_BRICKS | 一个扁平数组,由 brick_id 索引 eikonal_counter 值。 eikonal_counter 是…… |
Brixelizer_BrickFreeList | UINT32 | FFX_BRIXELIZER_MAX_BRICKS | 一个 brick_id 值数组,已标记为要释放。数组长度存储在 freeBricks 计数器中。砖块释放后 |
Brixelizer_BricksClearList | UINT32 | FFX_BRIXELIZER_MAX_BRICKS | 一个 brick_id 值数组,已标记为要清除。数组长度存储在 clearBricks 计数器中。 |
Brixelizer_IndirectArgs1 | UINT32 | FFX_BRIXELIZER_NUM_INDIRECT_OFFSETS * 16 | 一个包含间接分派参数值的缓冲区。 |
Brixelizer_InstanceBuffer | FfxBrixelizerInstanceInfo | 一个扁平数组,由 FfxBrixelizerInstanceID 索引。 | |
Brixelizer_TransformBuffer | FfxFloat32x3x4 | 一个扁平数组,由 FfxBrixelizerInstanceID 索引,其中第 i 个元素给出了实例 i 的世界空间变换。 | |
Brixelizer_UploadJobBuffer | FfxBrixelizerBrixelizationJob | 一个扁平数组,由 FfxBrixelizerJobID 索引,包含描述要执行的作业的 FfxBrixelizerBrixelizationJob 结构体。 | |
Brixelizer_UploadJobIndexBuffer | UINT32 | 一个扁平数组,由 FfxBrixelizerJobID 索引。一个扫描缓冲区,用于分散工作负载,其中为每个作业的每个体素启动一个线程。 | |
Brixelizer_BrickMergeList | UINT32 | 2 * FFX_BRIXELIZER_MAX_BRICKS | 一个砖块 ID 对数组,用于合并。数组包含 2 * mergeBricks 个砖块 ID,表示位置 2*n 中的砖块 ID 应合并到位置 2*n + 1 中的砖块 ID。 |
Brixelizer_UploadDebugAABBInstanceIDBuffer | FfxBrixelizerInstanceID | ||
Brixelizer_UploadInstanceBuffer | FfxBrixelizerInstanceInfo | 用于将实例数据上传到 Brixelizer_InstanceBuffer 的上传缓冲区。 | |
Brixelizer_UploadTransformBuffer | FfxFloat32x3x4 | 用于将变换数据上传到 Brixelizer_TransformBuffer 的上传缓冲区。 |
以下是 Brixelizer 上下文管理的各种常量缓冲区的列表。
| 名称 | 格式 | 描述 |
|---|---|---|
Brixelizer_ContextInfoBuffer | FfxBrixelizerContextInfo | 描述 Brixelizer 上下文的参数。 |
Brixelizer_CascadeInfoBuffer | FfxBrixelizerCascadeInfo | 描述单个级联的参数。 |
Brixelizer_BuildInfoBuffer | FfxBrixelizerBuildInfo | 描述构建选项的参数。 |
Brixelizer_DebugInfoBuffer | FfxBrixelizerDebugInfo | 描述调试可视化选项的参数。 |
临时资源
| 名称 | 描述 |
|---|---|
g_scratch_counters | 包含用于临时数据结构的计数器。 |
g_scratch_index_swap | 用于存储三角形数据的交换空间。 |
g_scratch_voxel_allocation_fail_counter | 一个扁平数组,由 FfxBrixelizerVoxelIndex 索引,跟踪体素的失败砖块分配。 |
g_scratch_bricks_storage | 用于计算砖块的存储空间,在复制到 SDF 图集之前使用 32 位原子操作存储。 |
g_scratch_bricks_storage_offsets | 一个扁平数组,由 FfxBrixelizerBrickID 索引,存储该砖块在 g_scratch_bricks_storage 缓冲区中的偏移量。 |
g_scratch_bricks_compression_list | 一个待在 FfxBrixelizerCompressBrick 通道中压缩的砖块列表。 |
g_scratch_bricks_clear_list | 一个待在 FfxBrixelizerClearBrickStorage 通道中清除的砖块列表。 |
g_scratch_job_counters | 一个扁平数组,由 FfxBrixelizerJobID 索引,给出粗略剔除后每个作业需要考虑的体素总数。 |
g_scratch_job_counters_scan | 一个扁平数组,由 FfxBrixelizerJobID 索引,包含 uint32_t,用于构建扫描缓冲区。 |
g_scratch_job_global_counters_scan | 与 g_scratch_job_counters_scan 一起用作扫描缓冲区。 |
g_scratch_cr1_references | 一个 FfxBrixelizerTriangleReference 值数组,存储 FfxBrixelizerVoxelIndex、FfxBrixelizerTriangleIndex 和局部引用索引的三个值。 |
g_scratch_cr1_compacted_references | 一个扁平的 FfxBrixelizerTriangleIndex 数组,按 FfxBrixelizerVoxelIndex 和局部引用排序。 |
g_scratch_cr1_ref_counters | 一个扁平数组,由 FfxBrixelizerVoxelIndex 索引,用于存储每个体素的引用数量。 |
g_scratch_cr1_ref_counters_scan | 一个扁平数组,由 FfxBrixelizerVoxelIndex 索引,用于构建扫描缓冲区。 |
g_scratch_cr1_ref_global_scan | 与 g_scratch_cr1_ref_counters_scan 一起用作扫描缓冲区。 |
g_scratch_cr1_stamp_scan | 一个扁平数组,由 FfxBrixelizerVoxelIndex 索引,用于构建扫描缓冲区。 |
g_scratch_cr1_stamp_global_scan | 与 g_scratch_cr1_stamp_scan 一起用作扫描缓冲区。 |
rw_debug_aabbs | 一个 AABB 列表,用于在调试可视化中绘制。 |
着色器通道
以下是 Brixelizer 使用的所有着色器通道列表。
| 功能 | 文件 | 组维度 | 描述 |
|---|---|---|---|
FfxBrixelizerClearCounters | ffx_brixelizer_context_ops.h | 1 x 1 x 1 | 将 Brixelizer_Counters 中的所有计数器重置为零。 |
FfxBrixelizerCollectClearBricks | ffx_brixelizer_context_ops.h | 64 x 1 x 1 | 输入参数 brick_offset,每个线程扫描一个砖块,brick_offset 与 brick_id 相同。如果砖块在 Brixelizer_BrickVoxelMap 中的体素 ID 无效,则该砖块被标记为“free”(空闲)。此过程还会扫描以查看砖块的 eikonal_counter 是否非零,如果是,则将该砖块标记为“cleared”(待清除)。砖块通过添加到 Brixelizer_BrickFreeList 来标记为空闲,并通过添加到 Brixelizer_BrickClearList 来标记为待清除。 |
FfxBrixelizerPrepareClearBricks | ffx_brixelizer_context_ops.h | 1 x 1 x 1 | 为 FfxBrixelizerClearBrickEntry 传递准备间接参数。 |
FfxBrixelizerClearBrickEntry | ffx_brixelizer_context_ops.h | 64 x 1 x 1 | 为每个待清除的砖块启动 8 个组。每个线程在 Brixelizer_SDFAtlas 中设置一个单独的值为 1。XXX - 此功能用作 SDF 图集的初始化,为 Eikonal 算法的下一次调用做准备。 |
FfxBrixelizerClearBuildCounters | ffx_brixelizer_cascade_ops.h | 1 x 1 x 1 | 将所有临时计数器设置为零,将 storageSize 初始化为 g_scratch_bricks_storage 缓冲区的大小,也将 maxTriangles 设置为 storageSize。将所有间接参数值设置为零。 |
FfxBrixelizerScrollCascade | ffx_brixelizer_cascade_ops.h | 64 x 1 x 1 | 输入打包的体素索引。每个级联中的每个体素运行一个线程。随着级联中心的移动,所有不再位于级联内的体素都会被无效化。如果一个被无效化的体素包含一个砖块,则该砖块被标记为空闲。 |
FfxBrixelizerClearRefCounters | ffx_brixelizer_cascade_ops.h | 64 x 1 x 1 | 输入打包的体素索引。每个级联中的每个体素运行一个线程。将 g_scratch_cr1_ref_counters、g_scratch_voxel_allocation_counter 和 g_scratch_voxel_allocation_fail_counter 的每个值设置为零。 |
FfxBrixelizerClearJobCounter | ffx_brixelizer_cascade_ops.h | 64 x 1 x 1 | 输入 job_id。每个分派给 Brixelizer 的作业运行一个线程。对于每个作业,我们将 g_scratch_job_counters 的值初始化为零。 |
FfxBrixelizerInvalidateJobAreas | ffx_brixelizer_cascade_ops.h | 256 x 1 x 1 | 每个作业的每个子体素启动一个线程。将所有与提交的无效化作业相交的体素标记为空闲/未初始化。 |
FfxBrixelizerCoarseCulling | ffx_brixelizer_cascade_ops.h | 256 x 1 x 1 | 每个作业的每个子体素启动一个线程。剔除对应于级联中已初始化体素的任何子体素。 |
FfxBrixelizerScanJobs | ffx_brixelizer_cascade_ops.h | 256 x 1 x 1 | 每个作业运行一个线程。计算每个作业的三角形数量,并创建一个扫描表,以便在后续传递中为每个三角形启动一个线程。 |
FfxBrixelizerVoxelize | ffx_brixelizer_cascade_ops.h | 256 x 1 x 1 | 每个三角形运行一个线程。扫描三角形,对三角形进行分类(大或小),将三角形存储在交换空间中,并存储对三角形的引用。 |
FfxBrixelizerScanReferences | ffx_brixelizer_cascade_ops.h | 256 x 1 x 1 | 每个体素运行一个线程。为具有引用的未初始化体素分配砖块。为引用和标记计数创建扫描缓冲区。 |
FfxBrixelizerCompactReferences | ffx_brixelizer_cascade_ops.h | 64 x 1 x 1 | 启动的线程数量是每个级联的体素数量和引用数量中的最大值。对于每个引用,我们将 g_scratch_cr1_references 扁平化到 g_scratch_cr1_compacted_references 结构中。对于每个体素,我们进行一次 |
FfxBrixelizerClearBrickStorage | ffx_brixelizer_cascade_ops.h | 64 x 1 x 1 | 每个 brixel 启动一个线程(即每个砖块 8 个组)。将临时砖块存储中的所有值设置为 1。 |
FfxBrixelizerEmitSDF | ffx_brixelizer_cascade_ops.h | 32 x 1 x 1 | 每个子体素启动一个组 |
FfxBrixelizerCompressBrick | ffx_brixelizer_cascade_ops.h | 512 x 1 x 1 | 每个砖块一个组,每个 brixel 一个线程。计算砖块的 AABB,释放任何空的砖块,并将砖块写入 SDF 图集。 |
FfxBrixelizerCollectDirtyBricks | ffx_brixelizer_context_ops.h | 64 x 1 x 1 | 每个砖块一个线程。遍历所有砖块,检查砖块的 eikonal 计数器是否低于 16,如果是,则将其添加到脏砖块列表中,并将其 eikonal 计数器增加 16。 |
FfxBrixelizerPrepareEikonalArgs | ffx_brixelizer_context_ops.h | 1 x 1 x 1 | 准备 FfxBrixelizerEikonal 的间接分派参数。 |
FfxBrixelizerEikonal | ffx_brixelizer_context_ops.h | 512 x 1 x 1 | 每个脏砖块一个组,每个 brixel 一个线程。根据 EmitSDF 传递写入的部分数据,计算 8x8x8 砖块中剩余的距离。 |
FfxBrixelizerBuildTreeAABB | ffx_brixelizer_context_ops.h | 4 x 4 x 4 | 用于为级联构建 AABB 树。如 FfxBuildInfo::tree_iteration 所指定,运行三次迭代。第一次迭代构建底层,第二次迭代构建中间层,第三次迭代构建根节点。 |
FfxBrixelizerMergeBricks | ffx_brixelizer_context_ops.h | 64 x 1 x 1 | 合并 Brixelizer_BrickMergeList 中指定的砖块。砖块的合并方式是将源砖块和目标砖块中每对 brixel 的最小值存储到目标砖块中。 |
FfxBrixelizerMergeCascades | ffx_brixelizer_context_ops.h | 64 x 1 x 1 | 将两个级联合并到一个目标级联中,并创建一个列表,用于在后续传递中合并砖块。 |
FfxBrixelizerFreeCascade | ffx_brixelizer_cascade_ops.h | 64 x 1 x 1 | 释放已初始化的级联的所有砖块,并将级联标记为未初始化。 |
FfxBrixelizerInitializeCascade | ffx_brixelizer_cascade_ops.h | 64 x 1 x 1 | 将已标记为未初始化的级联中的所有砖块标记为空。 |
FfxBrixelizerResetCascade | ffx_brixelizer_cascade_ops.h | 64 x 1 x 1 | 与 FfxBrixelizerFreeCascade 相同。 |
FfxBrixelizerMarkCascadeUninitialized | ffx_brixelizer_cascade_ops.h | 64 x 1 x 1 | 将级联中的所有砖块标记为未初始化(不释放砖块)。用于在首次注册到 Brixelizer 时初始化级联资源。 |
FfxBrixelizerDrawAABBTreeAABB | ffx_brixelizer_debug_aabbs.h | 64 x 1 x 1 | 将来自级联 AABB 树的调试 AABB 添加到用于调试可视化的 AABB 列表中。 |
FfxBrixelizerDrawInstanceAABB | ffx_brixelizer_debug_aabbs.h | 64 x 1 x 1 | 将来自实例列表的调试 AABB 添加到用于调试可视化的 AABB 列表中。 |
FfxBrixelizerDebugVisualization | ffx_brixelizer_debug_visualization.h | 8 x 8 x 1 | 绘制 Brixelizer 加速结构的视觉效果,用于调试目的。 |