OptiX8入门(一)optixHello
本人初学者,如有错误和更好的表述,请指出
环境:CLion+VS2022+CUDA Toolkit 12.0.1+OptiX8
下载好后打开SDK就可以看到OptiX官方提供的许多例子,CMake配置好后点开自己想看的内容直接开始看。推荐先把GAMES101看完之后再学API。可以看看文档,但是是英文的
OptiX8是一个基于硬件的光追,主要分为三块进行理解,管线(Pipeline),加速结构,shader binding table(sbt)(资源组织)。
GPU和CPU之间的区别只需记住,device表示GPU端,host表示CPU端。
管线就是配置整个光追流程,包括硬件部分的函数入口等。
加速结构,一般是BVH或KD-Tree,不懂的话当成黑盒使用即可,暂时不用去管,只要知道是提升光线的遍历速度的就好。
shader binding table表示里记录所有shader的绑定信息。
光追中主要存在这么几个函数:
Ray generation,可以理解为函数入口(main函数),对每个pixel都执行一遍,一般在这里进行写下TraceRay(发出光线)相关函数,具体是optixTrace()。Intersection,这个是光线和几何体的碰撞,但据说很少用,因为三角形和box的光线交是内置的,一般用于自己定义的可以解析的曲面,例如球。Any hit,射线在任意碰到的交点都会触发,但是不保证交点的触发顺序(应该是用加速结构的原因),也不保证一条线上所有的交点都会触发,比如碰到某些点,会更新光线的TMin和TMax,而在[TMin,TMax]之外的点就不会触发。Closest hit,一条射线上最早碰到的点,可以理解为直射,一般在这里进行计算信息,或者可以再发出射线。Miss,没碰到场景,可以在这里计算天空信息,或者再发出射线。

不懂没关系,看看代码,在这里介绍下基础的optixHello,这部分主要结果是生成一个带颜色的画面。
三个文件,optixHello.h、draw_solid_color.cu、optixHello.cpp
cpp和h文件就不说了,cu文件用于GPU,编译成ptx文件后绑定到程序中进行执行,.cu文件是可以printf进行调试的。
看下optixHello.h内容,对比一下draw_solid_color.cu,两个都是在GPU和CPU通信的参数。
struct Params
{
uchar4* image; //一维数组,其中rgb皆为char型,用于填充画面
unsigned int image_width; //只保存width就够了,对于(x,y)的数据用x*width+y就可以定位
};
struct RayGenData
{
float r,g,b; //在cu文件中作为填充色
};
看下draw_solid_color.cu文件,这里的所有函数都要类似__raygen__开头的命名
extern "C" {
__constant__ Params params; //记录结果
}
extern "C"
__global__ void __raygen__draw_solid_color()
{
uint3 launch_index = optixGetLaunchIndex(); //获取当前的pixel坐标
RayGenData* rtData = (RayGenData*)optixGetSbtDataPointer(); //获取sbt记录的数据,在这里是颜色,当然这个程序里直接记录在params也可以
params.image[launch_index.y * params.image_width + launch_index.x] =
make_color( make_float3( rtData->r, rtData->g, rtData->b ) ); //在image数据中记录颜色
}
看下optixHello.cpp
创建context
// Initialize CUDA and create OptiX context
OptixDeviceContext context = nullptr;
{
// Initialize CUDA
CUDA_CHECK( cudaFree( 0 ) );
CUcontext cuCtx = 0; // zero means take the current context
OPTIX_CHECK( optixInit() );
OptixDeviceContextOptions options = {};
options.logCallbackFunction = &context_log_cb;
options.logCallbackLevel = 4;
OPTIX_CHECK( optixDeviceContextCreate( cuCtx, &options, &context ) );
}
创建module,在这里绑定变量和cu文件
// Create module
OptixModule module = nullptr;
OptixPipelineCompileOptions pipeline_compile_options = {};
{
OptixModuleCompileOptions module_compile_options = {};
#if !defined(NDEBUG)
module_compile_options.optLevel = OPTIX_COMPILE_OPTIMIZATION_LEVEL_0;
module_compile_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_FULL;
#endif
pipeline_compile_options.usesMotionBlur = false;
pipeline_compile_options.traversableGraphFlags = OPTIX_TRAVERSABLE_GRAPH_FLAG_ALLOW_SINGLE_LEVEL_INSTANCING;
pipeline_compile_options.numPayloadValues = 2;
pipeline_compile_options.numAttributeValues = 2;
pipeline_compile_options.exceptionFlags = OPTIX_EXCEPTION_FLAG_NONE; // TODO: should be OPTIX_EXCEPTION_FLAG_STACK_OVERFLOW;
pipeline_compile_options.pipelineLaunchParamsVariableName = "params"; //这里绑定cu文件的params变量
size_t inputSize = 0;
const char* input = sutil::getInputData( OPTIX_SAMPLE_NAME, OPTIX_SAMPLE_DIR, "draw_solid_color.cu", inputSize ); //这里绑定cu文件
OPTIX_CHECK_LOG( optixModuleCreate(
context,
&module_compile_options,
&pipeline_compile_options,
input,
inputSize,
LOG, &LOG_SIZE,
&module
) );
}
创建program groups,在这里绑定函数
// Create program groups, including NULL miss and hitgroups
OptixProgramGroup raygen_prog_group = nullptr;
OptixProgramGroup miss_prog_group = nullptr;
{
OptixProgramGroupOptions program_group_options = {}; // Initialize to zeros
OptixProgramGroupDesc raygen_prog_group_desc = {}; //
raygen_prog_group_desc.kind = OPTIX_PROGRAM_GROUP_KIND_RAYGEN;
raygen_prog_group_desc.raygen.module = module;
raygen_prog_group_desc.raygen.entryFunctionName = "__raygen__draw_solid_color"; //看这里绑定入口函数
OPTIX_CHECK_LOG( optixProgramGroupCreate(
context,
&raygen_prog_group_desc,
1, // num program groups
&program_group_options,
LOG, &LOG_SIZE,
&raygen_prog_group
) );
// Leave miss group's module and entryfunc name null
OptixProgramGroupDesc miss_prog_group_desc = {}; //这个是miss相关的,在这个程序里暂时没用
miss_prog_group_desc.kind = OPTIX_PROGRAM_GROUP_KIND_MISS;
OPTIX_CHECK_LOG( optixProgramGroupCreate(
context,
&miss_prog_group_desc,
1, // num program groups
&program_group_options,
LOG, &LOG_SIZE,
&miss_prog_group
) );
}
创建pipeline,这里绑定program group
// Link pipeline
OptixPipeline pipeline = nullptr;
{
const uint32_t max_trace_depth = 0;
OptixProgramGroup program_groups[] = { raygen_prog_group };
OptixPipelineLinkOptions pipeline_link_options = {};
pipeline_link_options.maxTraceDepth = max_trace_depth;
OPTIX_CHECK_LOG( optixPipelineCreate( //创建pipeline,绑定program group
context,
&pipeline_compile_options,
&pipeline_link_options,
program_groups,
sizeof( program_groups ) / sizeof( program_groups[0] ),
LOG, &LOG_SIZE,
&pipeline
) );
OptixStackSizes stack_sizes = {};
for( auto& prog_group : program_groups )
{
OPTIX_CHECK( optixUtilAccumulateStackSizes( prog_group, &stack_sizes, pipeline ) );
}
uint32_t direct_callable_stack_size_from_traversal;
uint32_t direct_callable_stack_size_from_state;
uint32_t continuation_stack_size;
OPTIX_CHECK( optixUtilComputeStackSizes( &stack_sizes, max_trace_depth,
0, // maxCCDepth
0, // maxDCDEpth
&direct_callable_stack_size_from_traversal,
&direct_callable_stack_size_from_state, &continuation_stack_size ) );
OPTIX_CHECK( optixPipelineSetStackSize( pipeline, direct_callable_stack_size_from_traversal,
direct_callable_stack_size_from_state, continuation_stack_size,
2 // maxTraversableDepth
) );
}
创建sbt,在这里设置record和传进去的data,在这里就是生成的颜色
// Set up shader binding table
OptixShaderBindingTable sbt = {};
{
CUdeviceptr raygen_record;
const size_t raygen_record_size = sizeof( RayGenSbtRecord );
CUDA_CHECK( cudaMalloc( reinterpret_cast<void**>( &raygen_record ), raygen_record_size ) );
RayGenSbtRecord rg_sbt;
OPTIX_CHECK( optixSbtRecordPackHeader( raygen_prog_group, &rg_sbt ) );
rg_sbt.data = {0.462f, 0.725f, 0.f};
CUDA_CHECK( cudaMemcpy(
reinterpret_cast<void*>( raygen_record ),
&rg_sbt,
raygen_record_size,
cudaMemcpyHostToDevice
) );
CUdeviceptr miss_record;
size_t miss_record_size = sizeof( MissSbtRecord );
CUDA_CHECK( cudaMalloc( reinterpret_cast<void**>( &miss_record ), miss_record_size ) );
RayGenSbtRecord ms_sbt;
OPTIX_CHECK( optixSbtRecordPackHeader( miss_prog_group, &ms_sbt ) );
CUDA_CHECK( cudaMemcpy(
reinterpret_cast<void*>( miss_record ),
&ms_sbt,
miss_record_size,
cudaMemcpyHostToDevice
) );
sbt.raygenRecord = raygen_record;
sbt.missRecordBase = miss_record;
sbt.missRecordStrideInBytes = sizeof( MissSbtRecord );
sbt.missRecordCount = 1;
}
创建outputbuffer用于接收结果,然后launch,这个launch会在每个pixel中执行一次
sutil::CUDAOutputBuffer<uchar4> output_buffer( sutil::CUDAOutputBufferType::CUDA_DEVICE, width, height );
// launch
{
CUstream stream;
CUDA_CHECK( cudaStreamCreate( &stream ) );
Params params;
params.image = output_buffer.map(); //对应到outputbuffer
params.image_width = width;
CUdeviceptr d_param; //创建一个GPU指针
CUDA_CHECK( cudaMalloc( reinterpret_cast<void**>( &d_param ), sizeof( Params ) ) ); //malloc一个GPU空间存放Params
CUDA_CHECK( cudaMemcpy(
reinterpret_cast<void*>( d_param ),
¶ms, sizeof( params ),
cudaMemcpyHostToDevice
) );
OPTIX_CHECK( optixLaunch( pipeline, stream, d_param, sizeof( Params ), &sbt, width, height, /*depth=*/1 ) );
CUDA_SYNC_CHECK();
output_buffer.unmap();
CUDA_CHECK( cudaFree( reinterpret_cast<void*>( d_param ) ) );
}
显示图像
//// Display results
{
sutil::ImageBuffer buffer;
buffer.data = output_buffer.getHostPointer(); //这里要在CPU端展示,因此要转为CPU端的数据
buffer.width = width;
buffer.height = height;
buffer.pixel_format = sutil::BufferImageFormat::UNSIGNED_BYTE4; //对应uchar4
if( outfile.empty() )
sutil::displayBufferWindow( argv[0], buffer );
else
sutil::saveImage( outfile.c_str(), buffer, false );
}
清理资源,注意正序生成,倒序清理
// Cleanup
{
CUDA_CHECK( cudaFree( reinterpret_cast<void*>( sbt.raygenRecord ) ) );
CUDA_CHECK( cudaFree( reinterpret_cast<void*>( sbt.missRecordBase ) ) );
OPTIX_CHECK( optixPipelineDestroy( pipeline ) );
OPTIX_CHECK( optixProgramGroupDestroy( miss_prog_group ) );
OPTIX_CHECK( optixProgramGroupDestroy( raygen_prog_group ) );
OPTIX_CHECK( optixModuleDestroy( module ) );
OPTIX_CHECK( optixDeviceContextDestroy( context ) );
}
整个程序的大致结构如图(不保证正确)

执行结果:

码字不易,点个赞吧
总结
整个程序流程大致就是:
- 创建加速结构(在这里没有)
- 创建
module、program group - 创建
pipeline、sbt launch,显示图像
每个pixel中执行launch,在这里就是每个pixel执行__raygen__draw_solid_color函数设置颜色,传递形成一个outbuffer一维数组,形成图像。
OptiX8入门(一)optixHello的更多相关文章
- Angular2入门系列教程7-HTTP(一)-使用Angular2自带的http进行网络请求
上一篇:Angular2入门系列教程6-路由(二)-使用多层级路由并在在路由中传递复杂参数 感觉这篇不是很好写,因为涉及到网络请求,如果采用真实的网络请求,这个例子大家拿到手估计还要自己写一个web ...
- ABP入门系列(1)——学习Abp框架之实操演练
作为.Net工地搬砖长工一名,一直致力于挖坑(Bug)填坑(Debug),但技术却不见长进.也曾热情于新技术的学习,憧憬过成为技术大拿.从前端到后端,从bootstrap到javascript,从py ...
- Oracle分析函数入门
一.Oracle分析函数入门 分析函数是什么?分析函数是Oracle专门用于解决复杂报表统计需求的功能强大的函数,它可以在数据中进行分组然后计算基于组的某种统计值,并且每一组的每一行都可以返回一个统计 ...
- Angular2入门系列教程6-路由(二)-使用多层级路由并在在路由中传递复杂参数
上一篇:Angular2入门系列教程5-路由(一)-使用简单的路由并在在路由中传递参数 之前介绍了简单的路由以及传参,这篇文章我们将要学习复杂一些的路由以及传递其他附加参数.一个好的路由系统可以使我们 ...
- Angular2入门系列教程5-路由(一)-使用简单的路由并在在路由中传递参数
上一篇:Angular2入门系列教程-服务 上一篇文章我们将Angular2的数据服务分离出来,学习了Angular2的依赖注入,这篇文章我们将要学习Angualr2的路由 为了编写样式方便,我们这篇 ...
- Angular2入门系列教程4-服务
上一篇文章 Angular2入门系列教程-多个组件,主从关系 在编程中,我们通常会将数据提供单独分离出来,以免在编写程序的过程中反复复制粘贴数据请求的代码 Angular2中提供了依赖注入的概念,使得 ...
- wepack+sass+vue 入门教程(三)
十一.安装sass文件转换为css需要的相关依赖包 npm install --save-dev sass-loader style-loader css-loader loader的作用是辅助web ...
- wepack+sass+vue 入门教程(二)
六.新建webpack配置文件 webpack.config.js 文件整体框架内容如下,后续会详细说明每个配置项的配置 webpack.config.js直接放在项目demo目录下 module.e ...
- wepack+sass+vue 入门教程(一)
一.安装node.js node.js是基础,必须先安装.而且最新版的node.js,已经集成了npm. 下载地址 node安装,一路按默认即可. 二.全局安装webpack npm install ...
- js学习笔记:webpack基础入门(一)
之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...
随机推荐
- WPF入门教程系列二十四——DataGrid使用示例(2)
WPF入门教程系列目录 WPF入门教程系列二--Application介绍 WPF入门教程系列三--Application介绍(续) WPF入门教程系列四--Dispatcher介绍 WPF入门教程系 ...
- 2013年蓝桥杯C/C++大学B组省赛真题(马虎的算式)
题目描述: 小明是个急性子,上小学的时候经常把老师写在黑板上的题目抄错了. 有一次,老师出的题目是:36 x 495 = ? 他却给抄成了:396 x 45 = ? 但结果却很戏剧性,他的答案竟然是 ...
- Java设计模式中的几种常用设计模式总结
一.设计模式概念 1.定义 Java包含23种设计模式,是一套对代码设计经验的总结,被人们反复利用,多人熟知的代码设计方式. 2.目的 为了提高代码的可读性,可扩展性以及代码的复用性,为了解决 ...
- 利用jira及confluence的API进行批量操作(查找/更新/导出/备份/删除等)
前言: 近期因为某些原因需要批量替换掉 jira 和 confluence中的特定关键字,而且在替换前还希望进行备份(以便后续恢复)和导出(方便查看)atlassian官方的api介绍文档太简陋,很多 ...
- CVE-2021-41773 apache路径遍历
来自tryhackeme的漏洞复现 CVE-2021-41773/42013 利用:路径遍历利用将允许服务器公开任意文件 需要启用mod_cgi模块才能获得远程代码执行 2021 年 10 月 5 日 ...
- 手把手实践丨基于STM32+NBIOT+华为云IOT设计智能井盖
摘要:本文介绍基于STM32微控制器.BC26 NBIOT模组和华为云IOT平台,实现了一款智能井盖系统. 本文分享自华为云社区<基于STM32+NBIOT+华为云IOT设计的智能井盖>, ...
- CHS、LAB地址
CHS地址 CHS地址指的是柱面(Cylinder).磁头(Head).扇区(Sector)三个参数组成的地址,是用来表示磁盘上每个扇区位置的一种方式. 物理扇区号 = ((柱面号×磁头数) + 磁头 ...
- TheRoleofSpatialPyramidalPoolinginConvolutionalNeuralNe
目录 1. 引言 2. 技术原理及概念 2.1. 基本概念解释 2.2. 技术原理介绍 2.3. 相关技术比较 3. 实现步骤与流程 3.1. 准备工作:环境配置与依赖安装 3.2. 核心模块实现 3 ...
- Python编程和数据科学中的机器学习:如何处理和可视化具有噪声和干扰的数据
目录 随着数据科学和机器学习的快速发展,处理和分析具有噪声和干扰的数据成为了一个日益重要的挑战.在数据科学和机器学习中,噪声和干扰通常来自于各种因素,例如随机性和非随机性,数据缺失,数据集中的错误或错 ...
- macOS 系统 Kafka 快速入门
Kafka 的核心功能是高性能的消息发送与高性能的消息消费.以下是 Kafka 的快速入门教程. 下载并解压缩 Kafka 二进制代码压缩文件 打开 Kafka 官网的下载地址,可以看到不同版本的 K ...