实时光追插件实现了光线与模型的加速结构构建与求交功能。
您只要初始化Vulkan运行环境,输入模型顶点数据,可以构建出一个加速结构,再输入光线数据,可以计算出所有光线和模型的交点。
cmake_minimum_required(VERSION 3.4.1)
set(PROJ_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../../..)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -O2 -DNDEBUG -DVK_USE_PLATFORM_ANDROID_KHR")
file(GLOB EXAMPLE_SRC "${SRC_DIR}/*.cpp") # 引入开发者主程序代码
# librtcore.so导入
include_directories(${PROJ_ROOT}/include)
ADD_LIBRARY(
rtcore
SHARED
IMPORTED)
SET_TARGET_PROPERTIES(rtcore
PROPERTIES IMPORTED_LOCATION
${PROJ_ROOT}/libs/${ANDROID_ABI}/librtcore.so)
# 编译Android NDK native lib
add_library(native-lib SHARED ${EXAMPLE_SRC})
add_library(native-app-glue STATIC ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate")
include_directories(${ANDROID_NDK}/sources/android/native_app_glue)
target_link_libraries(
native-lib
native-app-glue
libbase
android
log
z
) // 创建vulkan运行环境,包括VkPhysicalDevice, VkDevice, VkQueue。 RayShop::Vulkan::Traversal traversal; // 调用Setup接口初始化Traversal类。 // 将phycial devcie, logical device, compute queue, compute queue indice, jniEnv设置到Traversal。 traversal.Setup(physicalDevice, logicalDevice, computeQueue, computeIndice, jniEnv);
physicalDevice、logicalDevice、computeQueue、computeIndice需要开发者在创建Vulkan环境时创建并初始化。
jniEnv需要开发者实现jni接口创建。
// 加载模型顶点数据,构建GeometryTriangleDescription结构。
// 从gltf等文件中读取模型,仅参考,需自行实现
std::vector<vkglTF::Model> scene;
std::vector<vkvert::Vertex> worldVertices;
scene.loadFromFile("path/to/model/file");
worldVertices.resize(scene.vertexBuffer.size());
scene.convertLocalVertexToWorld(modelMatrix, worldVertices);
// 填充GeometryTriangleDescription结构
RayShop::GeometryTriangleDescription geometry;
geometry.stride = sizeof(vkvert::Vertex) / sizeof(float);
geometry.vertices.cpuBuffer = worldVertices.data();
geometry.vertices.type = RayShop::BufferType::CPU;
geometry.verticesCount = static_cast<uint32_t>(worldVertices.size());
geometry.indices.cpuBuffer = scene.indexBuffer.data();
geometry.indices.type = RayShop::BufferType::CPU;
geometry.indicesCount = static_cast<uint32_t>(scene.indexBuffer.size());
// 多个GeometryTriangleDescription结构组成vector
std::vector<RayShop::GeometryTriangleDescription> bvhGeometriesOnCPU;
std::vector<RayShop::BLAS> blases;
bvhGeometriesOnCPU.push_back(geometry);
blases.resize(bvhGeometriesOnCPU.size());
// 将每个geometry都构建成一个底层加速结构,第一个参数表示构建的方式,第二个参数表示geometry数量。
// 第三个参数表示geometry数据,第四个参数表示返回的加速结构handle。
RayShop::Result res = traversal.CreateBLAS(RayShop::ASBuildMethod::SAH_CPU,
static_cast<uint32_t>(bvhGeometriesOnCPU.size()), bvhGeometriesOnCPU.data(), blases.data());
if (res != RayShop::Result::SUCCESS) {
LOGE("Failed to CreateBLAS, err: %s.", traversal.GetErrorCodeString(res));
return false;
}
// 将若干个底层加速结构再构建成顶层加速结构。
// InstanceDescription表示一个需要参与构建的底层加速结构。
// 成员blas表示底层加速结构的handle,成员transform表示底层加速结构的变换矩阵。
std::vector<RayShop::InstanceDescription> intances;
for (unsigned int blase : blases) {
RayShop::InstanceDescription instance {};
instance.blas = blase;
instance.transform[0][0] = 1;
instance.transform[1][1] = 1;
instance.transform[2][2] = 1;
instance.transform[3][3] = 1;
intances.push_back(instance);
}
res = traversal.CreateTLAS(static_cast<uint32_t>(intances.size()), intances.data());
if (res != RayShop::Result::SUCCESS) {
LOGE("Failed to CreateTLAS, err: %s.", traversal.GetErrorCodeString(res));
return false;
} // 更新顶点数据,只能改变顶点坐标值,数量不能发生变化。
// 可以在CPU buffer或GPU buffer上使用,推荐GPU侧,效率更高
// 初始化,创建GPU buffer
struct BVHBuffers {
vks::Buffer vertex;
vks::Buffer index;
} bvhBuffers;
struct BVHStagingBuffers {
vks::Buffer vertex;
vks::Buffer index;
} bvhStagingBuffers;
std::vector<RayShop::GeometryTriangleDescription> bvhGeometriesOnGPU;
bvhGeometriesOnGPU.resize(bvhGeometriesOnCPU.size());
for (size_t i = 0; i < bvhGeometriesOnGPU.size(); i++) {
bvhGeometriesOnGPU[i].vertices.type = RayShop::BufferType::GPU;
bvhGeometriesOnGPU[i].vertices.gpuVkBuffer = bvhBuffers.vertex.buffer;
bvhGeometriesOnGPU[i].verticesCount = bvhGeometriesOnCPU[i].verticesCount;
bvhGeometriesOnGPU[i].stride = bvhGeometriesOnCPU[i].stride;
bvhGeometriesOnGPU[i].indices.type = RayShop::BufferType::GPU;
bvhGeometriesOnGPU[i].indices.gpuVkBuffer = bvhBuffers.index.buffer;
bvhGeometriesOnGPU[i].indicesCount = bvhGeometriesOnCPU[i].indicesCount;
}
// 更新顶点信息
scene.convertLocalVertexToWorld(modelMatrix, m_worldVertices); // 自定义函数
// 拷贝到共享内存
size_t size = m_worldVertices.size() * sizeof(vkvert::Vertex);
memcpy(m_bvhStagingBuffers.vertex.mapped, m_worldVertices.data(), size);
size = scene.indexBuffer.size() * sizeof(uint32_t);
memcpy(m_bvhStagingBuffers.index.mapped, scene.indexBuffer.data(), size);
// 拷贝到GPU侧
VkBufferCopy copyRegion = {};
copyRegion.srcOffset = 0;
copyRegion.dstOffset = 0;
copyRegion.size = m_bvhStagingBuffers.vertex.size;
vkCmdCopyBuffer(cmdBuffer, m_bvhStagingBuffers.vertex.buffer, m_bvhBuffers.vertex.buffer, 1, ©Region);
copyRegion.size = m_bvhStagingBuffers.index.size;
vkCmdCopyBuffer(cmdBuffer, m_bvhStagingBuffers.index.buffer, m_bvhBuffers.index.buffer, 1, ©Region);
// 对坐标改变的geometry更新构建底层加速结构。
traversal.RefitBLAS(static_cast<uint32_t>(bvhGeometriesOnGPU.size()),
bvhGeometriesOnGPU.data(), blases.data()); // xxx.frag, fragment shader文件
// (可选)使用include扩展
#extension GL_GOOGLE_include_directive : enable
#include "sth.glsl"
void main()
{
// 创建光线并设置光线信息
Ray ray;
ray.origin = ...;
ray.direction = ...;
ray.mint = 0.01f;
ray.maxt = 10.0f;
HitInfo hitInfo;
hitInfo.t = -1.0f;
// 调用接口获取交点信息
traceRay(ray, RAY_FLAG_CLOSEST_HIT, hitInfo);
// ...
}
// cpp侧,配置管线时将创建着色器模块
// 创建ShaderModule
RayShop::Vulkan::ShaderModule rtShaderModule;
rtShaderModule.vkHandle = VK_NULL_HANDLE;
RayShop::Vulkan::RayTracingShaderModuleCreateInfo rtShaderCreateInfo{};
// 从shader文本中读取内容(仅示意)
std::string rtFragContent = Open("path/to/xxx.frag")->Read();
// (可选)添加shader的包含文件,多个
std::vector<const char *> shaderIncludes;
std::vector<const char *> shaderIncludeSources;
shaderIncludes.push_back("sth.glsl");
std::string raytraceInclude = Open("path/to/sth.frag")->Read();
shaderIncludeSources.push_back(raytraceInclude.c_str());
// 设置shader信息
rtShaderCreateInfo.pName = "rtFragmentShader";
rtShaderCreateInfo.pSource = rtFragContent.c_str();
rtShaderCreateInfo.stage = RayShop::Vulkan::ShaderStage::SHADER_STAGE_FRAGMENT_BIT;
rtShaderCreateInfo.hitFormat = RayShop::TraceRayHitFormat::T_PRIMID_INSTID_U_V;
rtShaderCreateInfo.ppShaderIncludes = shaderIncludes.data();
rtShaderCreateInfo.shaderIncludesCnt = static_cast<uint32_t>(shaderIncludes.size());
rtShaderCreateInfo.ppShaderIncludeSources = shaderIncludeSources.data();
// 调用接口创建光追ShaderModule
RayShop::Result res = traversal.CreateRayTracingShaderModule(&rtShaderCreateInfo, &rtShaderModule);
// 将光追ShaderModule配置到管线中
// 配置vertex shader ... 略
// 指定fragment shader的资源绑定,仅示意
VkDescriptorSetLayout m_descSetLayout = VK_NULL_HANDLE;
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
VK_SHADER_STAGE_FRAGMENT_BIT, 0),
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
VK_SHADER_STAGE_FRAGMENT_BIT, 1),
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
VK_SHADER_STAGE_FRAGMENT_BIT, 2),
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
VK_SHADER_STAGE_FRAGMENT_BIT, 3),
// 更多开发者自定义buffer
};
// 调用接口获取光追所需相关buffer并填入
VkDescriptorBufferInfo bvhTree {};
VkDescriptorBufferInfo bvhTriangles {};
VkDescriptorBufferInfo tlasInfo {};
VkDescriptorBufferInfo rtCoreUniforms {};
res = traversal.GetTraversalDescBufferInfos(&bvhTree, &bvhTriangles, &tlasInfo, &rtCoreUniforms);
if (res != RayShop::Result::SUCCESS) {
LOGE("%s: Failed to GetTraversalDescBufferInfos, err: %s.", __func__,
RayShop::Vulkan::Traversal::GetErrorCodeString(res));
return false;
}
// 更新descriptorsets, 仅示意。
std::vector<VkWriteDescriptorSet> writeDescriptorSets = { Utils::WriteDescriptorSet(
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 0, bvhTree),
Utils::WriteDescriptorSet(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, bvhTriangles),
Utils::WriteDescriptorSet(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 2, tlasInfo),
Utils::WriteDescriptorSet(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 3, rtCoreUniforms),
// 更多开发者自定义
};
vkUpdateDescriptorSets(
m_vulkanDevice->logicalDevice,
static_cast<uint32_t>(writeDescriptorSets.size()),
writeDescriptorSets.data(),
0,
nullptr);
// 更多管线配置...
流程代码多为示意,仅标出核心API使用,管线配置需要开发者定义。
RayShop::Ray *rays;
uint32_t rayCount;
// 创建光线,每条光线用RayShop::Ray表示,需要初始化其中的光线原点origin和方向dir,记录光线数量。
GenerateRays(rays, rayCount); // 自定义函数,初始化光线
// 填充表示ray的RayShop::Buffer,同时支持cpu类型的buffer地址和gpu类型的VkBuffer。
// 如果ray buffer是cpu地址,type填BufferType::CPU,如果是vkBuffer,type填BufferType::GPU。
RayShop::Buffer rays;
rays.type = BufferType::CPU;
rays.cpuBuffer = rays;
// 创建保存交点信息的buffer。
// 使用Vulkan创建一个Storage Buffer,将其handle保存到hits.gpuVkBuffer。
// 交点信息支持不同的格式,通过GetHitFormatBytes()接口获取每种格式的大小。
// 交点信息buffer的总大小需要是rayCount乘以一个交点信息大小。
vks::Buffer hitBuffer;
vks::VulkanDevice *vulkanDevice;
// vulkan 流程代码,开发者实现,仅示意
vulkanDevice = new vks::VulkanDevice(physicalDevice);
vulkanDevice->createBuffer(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &hitBuffer,
rayCount * RayShop::Vulkan::Traversal::GetHitFormatBytes(RayShop::TraceRayHitFormat::T_PRIMID_U_V));
RayShop::Buffer hits;
hits.gpuVkBuffer = hitBuffer.vkBuffer;
hits.type = BufferType::GPU;
// TraceRays计算光线和加速结构的交点,求交结果保存在传入的hits参数对应的buffer。
traversal.TraceRays(rayCount, TRACERAY_FLAG_INTERSECT_DEFAULT, rays, hits,
TraceRayHitFormat::T_PRIMID_U_V); traversal.DestroyBLAS(static_cast<uint32_t>(blases.size()), blases.data()); traversal.Destroy();