SLProject  4.3.020
A platform independent 3D computer graphics framework for desktop OS, Android, iOS and online in web browsers
SLOptixRaytracer.cpp
Go to the documentation of this file.
1 /**
2  * \file SLOptixRaytracer.cpp
3  * \authors Nic Dorner
4  * \date October 2019
5  * \authors Nic Dorner
6  * \copyright http://opensource.org/licenses/GPL-3.0
7  * \remarks Please use clangformat to format the code. See more code style on
8  * https://github.com/cpvrlab/SLProject4/wiki/SLProject-Coding-Style
9 */
10 
11 #ifdef SL_HAS_OPTIX
12 # include <SLAssetManager.h>
13 # include <SLLightRect.h>
14 # include <SLSceneView.h>
15 # include <SLOptix.h>
16 # include <SLOptixRaytracer.h>
17 # include <SLOptixDefinitions.h>
18 # include <optix.h>
19 # include <utility>
20 # include <SLOptixHelper.h>
21 # include <GlobalTimer.h>
22 
23 //-----------------------------------------------------------------------------
24 SLOptixRaytracer::SLOptixRaytracer()
25  : SLRaytracer()
26 {
27  name("OptiX ray tracer");
28  _params = {};
29  _paramsBuffer.alloc(sizeof(ortParams));
30  initCompileOptions();
31 }
32 //-----------------------------------------------------------------------------
33 SLOptixRaytracer::~SLOptixRaytracer()
34 {
35  SL_LOG("Destructor : ~SLOptixRaytracer");
36  destroy();
37 }
38 //-----------------------------------------------------------------------------
39 void SLOptixRaytracer::destroy()
40 {
41  try
42  {
43  OPTIX_CHECK(optixPipelineDestroy(_pipeline));
44  OPTIX_CHECK(optixProgramGroupDestroy(_radiance_hit_group));
45  OPTIX_CHECK(optixProgramGroupDestroy(_occlusion_hit_group));
46  OPTIX_CHECK(optixProgramGroupDestroy(_radiance_miss_group));
47  OPTIX_CHECK(optixProgramGroupDestroy(_occlusion_miss_group));
48  OPTIX_CHECK(optixProgramGroupDestroy(_pinhole_raygen_prog_group));
49  OPTIX_CHECK(optixModuleDestroy(_cameraModule));
50  OPTIX_CHECK(optixModuleDestroy(_shadingModule));
51  }
52  catch (exception e)
53  {
54  Utils::log("SLProject",
55  "Exception in SLOptixRaytracer::destroy: %s",
56  e.what());
57  }
58 }
59 //-----------------------------------------------------------------------------
60 void SLOptixRaytracer::initCompileOptions()
61 {
62  // Set compile options for modules and pipelines
63  _module_compile_options = {};
64  _module_compile_options.maxRegisterCount = OPTIX_COMPILE_DEFAULT_MAX_REGISTER_COUNT;
65 
66 # ifdef NDEBUG
67  _module_compile_options.optLevel = OPTIX_COMPILE_OPTIMIZATION_DEFAULT;
68  _module_compile_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_NONE;
69  _pipeline_compile_options.exceptionFlags = OPTIX_EXCEPTION_FLAG_NONE;
70 # else
71  _module_compile_options.optLevel = OPTIX_COMPILE_OPTIMIZATION_LEVEL_0;
72  //_module_compile_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_FULL;
73  _module_compile_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_LINEINFO;
74  _pipeline_compile_options.exceptionFlags = OPTIX_EXCEPTION_FLAG_DEBUG | OPTIX_EXCEPTION_FLAG_USER;
75 # endif
76 
77  _pipeline_compile_options.usesMotionBlur = false;
78  _pipeline_compile_options.traversableGraphFlags = OPTIX_TRAVERSABLE_GRAPH_FLAG_ALLOW_SINGLE_LEVEL_INSTANCING;
79  _pipeline_compile_options.numPayloadValues = 7;
80  _pipeline_compile_options.numAttributeValues = 2;
81  _pipeline_compile_options.pipelineLaunchParamsVariableName = "params";
82 }
83 //-----------------------------------------------------------------------------
84 void SLOptixRaytracer::setupOptix()
85 {
86  _cameraModule = createModule("SLOptixRaytracerCamera.cu");
87  _shadingModule = createModule("SLOptixRaytracerShading.cu");
88 
89  OptixProgramGroupDesc pinhole_raygen_desc = {};
90  pinhole_raygen_desc.kind = OPTIX_PROGRAM_GROUP_KIND_RAYGEN;
91  pinhole_raygen_desc.raygen.module = _cameraModule;
92  pinhole_raygen_desc.raygen.entryFunctionName = "__raygen__pinhole_camera";
93  _pinhole_raygen_prog_group = createProgram(pinhole_raygen_desc);
94 
95  OptixProgramGroupDesc lens_raygen_desc = {};
96  lens_raygen_desc.kind = OPTIX_PROGRAM_GROUP_KIND_RAYGEN;
97  lens_raygen_desc.raygen.module = _cameraModule;
98  lens_raygen_desc.raygen.entryFunctionName = "__raygen__lens_camera";
99  _lens_raygen_prog_group = createProgram(lens_raygen_desc);
100 
101  OptixProgramGroupDesc orthographic_raygen_desc = {};
102  orthographic_raygen_desc.kind = OPTIX_PROGRAM_GROUP_KIND_RAYGEN;
103  orthographic_raygen_desc.raygen.module = _cameraModule;
104  orthographic_raygen_desc.raygen.entryFunctionName = "__raygen__orthographic_camera";
105  _orthographic_raygen_prog_group = createProgram(orthographic_raygen_desc);
106 
107  OptixProgramGroupDesc radiance_miss_desc = {};
108  radiance_miss_desc.kind = OPTIX_PROGRAM_GROUP_KIND_MISS;
109  radiance_miss_desc.miss.module = _shadingModule;
110  radiance_miss_desc.miss.entryFunctionName = "__miss__radiance";
111  _radiance_miss_group = createProgram(radiance_miss_desc);
112 
113  OptixProgramGroupDesc occlusion_miss_desc = {};
114  occlusion_miss_desc.kind = OPTIX_PROGRAM_GROUP_KIND_MISS;
115  occlusion_miss_desc.miss.module = _shadingModule;
116  occlusion_miss_desc.miss.entryFunctionName = "__miss__occlusion";
117  _occlusion_miss_group = createProgram(occlusion_miss_desc);
118 
119  OptixProgramGroupDesc radiance_hitgroup_desc = {};
120  radiance_hitgroup_desc.kind = OPTIX_PROGRAM_GROUP_KIND_HITGROUP;
121  radiance_hitgroup_desc.hitgroup.moduleAH = _shadingModule;
122  radiance_hitgroup_desc.hitgroup.entryFunctionNameAH = "__anyhit__radiance";
123  radiance_hitgroup_desc.hitgroup.moduleCH = _shadingModule;
124  radiance_hitgroup_desc.hitgroup.entryFunctionNameCH = "__closesthit__radiance";
125  _radiance_hit_group = createProgram(radiance_hitgroup_desc);
126 
127  OptixProgramGroupDesc occlusion_hitgroup_desc = {};
128  occlusion_hitgroup_desc.kind = OPTIX_PROGRAM_GROUP_KIND_HITGROUP;
129  occlusion_hitgroup_desc.hitgroup.moduleAH = _shadingModule;
130  occlusion_hitgroup_desc.hitgroup.entryFunctionNameAH = "__anyhit__occlusion";
131  occlusion_hitgroup_desc.hitgroup.moduleCH = nullptr;
132  occlusion_hitgroup_desc.hitgroup.entryFunctionNameCH = nullptr;
133  _occlusion_hit_group = createProgram(occlusion_hitgroup_desc);
134 
135  OptixProgramGroup program_groups[] = {
136  _pinhole_raygen_prog_group,
137  _radiance_miss_group,
138  _occlusion_miss_group,
139  _radiance_hit_group,
140  _occlusion_hit_group,
141  };
142  _pipeline = createPipeline(program_groups, 5);
143 }
144 //-----------------------------------------------------------------------------
145 OptixModule SLOptixRaytracer::createModule(string filename)
146 {
147  OptixModule module = nullptr;
148  {
149  const string ptx = getPtxStringFromFile(std::move(filename));
150  char log[2048];
151  size_t sizeof_log = sizeof(log);
152 
153  OPTIX_CHECK_LOG(optixModuleCreateFromPTX(
154  SLOptix::context,
155  &_module_compile_options,
156  &_pipeline_compile_options,
157  ptx.c_str(),
158  ptx.size(),
159  log,
160  &sizeof_log,
161  &module));
162  }
163  return module;
164 }
165 //-----------------------------------------------------------------------------
166 OptixProgramGroup SLOptixRaytracer::createProgram(OptixProgramGroupDesc desc)
167 {
168  OptixProgramGroup program_group = {};
169  OptixProgramGroupOptions program_group_options = {};
170 
171  char log[2048];
172  size_t sizeof_log = sizeof(log);
173 
174  OPTIX_CHECK_LOG(optixProgramGroupCreate(
175  SLOptix::context,
176  &desc,
177  1, // num program groups
178  &program_group_options,
179  log,
180  &sizeof_log,
181  &program_group));
182 
183  return program_group;
184 }
185 //-----------------------------------------------------------------------------
186 OptixPipeline SLOptixRaytracer::createPipeline(OptixProgramGroup* program_groups,
187  unsigned int numProgramGroups)
188 {
189  OptixPipeline pipeline;
190  OptixPipelineLinkOptions pipeline_link_options = {};
191  pipeline_link_options.maxTraceDepth = _maxDepth;
192  pipeline_link_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_FULL;
193  pipeline_link_options.overrideUsesMotionBlur = false;
194 
195  char log[2048];
196  size_t sizeof_log = sizeof(log);
197 
198  // Todo: Bugfix needed for Optix needs some work for newer shader models
199  //OPTIX_CHECK_LOG(
200  optixPipelineCreate(
201  SLOptix::context,
202  &_pipeline_compile_options,
203  &pipeline_link_options,
204  program_groups,
205  numProgramGroups,
206  log,
207  &sizeof_log,
208  &pipeline);
209  //);
210 
211  return pipeline;
212 }
213 //-----------------------------------------------------------------------------
214 OptixShaderBindingTable SLOptixRaytracer::createShaderBindingTable(const SLVMesh& meshes,
215  const bool doDistributed)
216 {
217  SLCamera* camera = _sv->camera();
218 
219  OptixShaderBindingTable sbt = {};
220  {
221  // Setup ray generation records
222  if (doDistributed)
223  {
224  RayGenDistributedSbtRecord rg_sbt;
225  _rayGenDistributedBuffer.alloc_and_upload(&rg_sbt, 1);
226  }
227  else
228  {
229  RayGenClassicSbtRecord rg_sbt;
230  _rayGenClassicBuffer.alloc_and_upload(&rg_sbt, 1);
231  }
232 
233  // Setup miss records
234  vector<MissSbtRecord> missRecords;
235 
236  MissSbtRecord radiance_ms_sbt;
237  OPTIX_CHECK(optixSbtRecordPackHeader(_radiance_miss_group, &radiance_ms_sbt));
238  radiance_ms_sbt.data.bg_color = make_float4(camera->background().colors()[0]);
239  missRecords.push_back(radiance_ms_sbt);
240 
241  MissSbtRecord occlusion_ms_sbt;
242  OPTIX_CHECK(optixSbtRecordPackHeader(_occlusion_miss_group, &occlusion_ms_sbt));
243  missRecords.push_back(occlusion_ms_sbt);
244 
245  _missBuffer.alloc_and_upload(missRecords);
246 
247  // Setup hit records
248  vector<HitSbtRecord> hitRecords;
249 
250  for (auto mesh : meshes)
251  {
252  OptixProgramGroup hitgroup_radicance = _radiance_hit_group;
253  OptixProgramGroup hitgroup_occlusion = _occlusion_hit_group;
254 
255  HitSbtRecord radiance_hg_sbt;
256  OPTIX_CHECK(optixSbtRecordPackHeader(hitgroup_radicance, &radiance_hg_sbt));
257  radiance_hg_sbt.data = mesh->createHitData();
258  hitRecords.push_back(radiance_hg_sbt);
259 
260  HitSbtRecord occlusion_hg_sbt;
261  OPTIX_CHECK(optixSbtRecordPackHeader(hitgroup_occlusion, &occlusion_hg_sbt));
262  occlusion_hg_sbt.data.material.kt = mesh->mat()->kt();
263  occlusion_hg_sbt.data.material.emissive_color = make_float4(mesh->mat()->emissive());
264  hitRecords.push_back(occlusion_hg_sbt);
265  }
266  _hitBuffer.alloc_and_upload(hitRecords);
267 
268  if (doDistributed)
269  sbt.raygenRecord = _rayGenDistributedBuffer.devicePointer();
270  else
271  sbt.raygenRecord = _rayGenClassicBuffer.devicePointer();
272 
273  sbt.missRecordBase = _missBuffer.devicePointer();
274  sbt.missRecordStrideInBytes = sizeof(MissSbtRecord);
275  sbt.missRecordCount = RAY_TYPE_COUNT;
276  sbt.hitgroupRecordBase = _hitBuffer.devicePointer();
277  sbt.hitgroupRecordStrideInBytes = sizeof(HitSbtRecord);
278  sbt.hitgroupRecordCount = RAY_TYPE_COUNT * (SLuint)meshes.size();
279  }
280 
281  return sbt;
282 }
283 //-----------------------------------------------------------------------------
284 void SLOptixRaytracer::setupScene(SLSceneView* sv, SLAssetManager* am)
285 {
286  SLVMesh meshes = am->meshes();
287  _sv = sv;
288 
289  _imageBuffer.resize(_sv->scrW() * _sv->scrH() * sizeof(float4));
290 
291  _params.image = reinterpret_cast<float4*>(_imageBuffer.devicePointer());
292  _params.width = _sv->scrW();
293  _params.height = _sv->scrH();
294  _params.max_depth = _maxDepth;
295  _params.oneOverGamma = _oneOverGamma;
296 
297  // Iterate over all meshes
298  SLMesh::meshIndex = 0;
299  for (auto mesh : meshes)
300  {
301  mesh->createMeshAccelerationStructure();
302  }
303 
304  _sbtClassic = createShaderBindingTable(meshes, false);
305  _sbtDistributed = createShaderBindingTable(meshes, true);
306 }
307 //-----------------------------------------------------------------------------
308 void SLOptixRaytracer::updateScene(SLSceneView* sv)
309 {
310  SLScene* scene = sv->s();
311  SLCamera* camera = sv->camera();
312  _sv = sv;
313 
315  // scene->root3D()->createInstanceAccelerationStructureTree();
316  scene->root3D()->createInstanceAccelerationStructureFlat();
317 
318  _params.handle = scene->root3D()->optixTraversableHandle();
319 
320  SLVec3f eye, u, v, w;
321  camera->UVWFrame(eye, u, v, w);
322  ortCamera cameraData{};
323  cameraData.eye = make_float3(eye);
324  cameraData.U = make_float3(u);
325  cameraData.V = make_float3(v);
326  cameraData.W = make_float3(w);
327 
328  if (doDistributed())
329  {
330  RayGenDistributedSbtRecord rayGenSbtRecord;
331  _rayGenDistributedBuffer.download(&rayGenSbtRecord);
332  OPTIX_CHECK(optixSbtRecordPackHeader(_lens_raygen_prog_group, &rayGenSbtRecord));
333  rayGenSbtRecord.data.lensDiameter = camera->lensDiameter();
334  rayGenSbtRecord.data.samples.samplesX = camera->lensSamples()->samplesX();
335  rayGenSbtRecord.data.samples.samplesY = camera->lensSamples()->samplesY();
336  rayGenSbtRecord.data.camera = cameraData;
337  _rayGenDistributedBuffer.upload(&rayGenSbtRecord);
338  }
339  else
340  {
341  RayGenClassicSbtRecord rayGenSbtRecord;
342  _rayGenClassicBuffer.download(&rayGenSbtRecord);
343  if (camera->projType() == P_monoPerspective)
344  {
345  OPTIX_CHECK(optixSbtRecordPackHeader(_pinhole_raygen_prog_group, &rayGenSbtRecord));
346  }
347  else
348  {
349  OPTIX_CHECK(optixSbtRecordPackHeader(_orthographic_raygen_prog_group, &rayGenSbtRecord));
350  }
351  rayGenSbtRecord.data = cameraData;
352  _rayGenClassicBuffer.upload(&rayGenSbtRecord);
353  }
354 
355  vector<ortLight> lights;
356  _lightBuffer.free();
357  unsigned int light_count = 0;
358  for (auto light : scene->lights())
359  {
360  if (light->isOn())
361  {
362  lights.push_back(light->optixLight(doDistributed()));
363  light_count++;
364  }
365  }
366  _lightBuffer.alloc_and_upload(lights);
367  _params.lights = reinterpret_cast<ortLight*>(_lightBuffer.devicePointer());
368  _params.numLights = light_count;
369  _params.globalAmbientColor = make_float4(SLLight::globalAmbient);
370 
371  _paramsBuffer.upload(&_params);
372 }
373 //-----------------------------------------------------------------------------
374 SLbool SLOptixRaytracer::renderClassic()
375 {
376  _state = rtBusy; // From here we state the RT as busy
377  _progressPC = 0; // % rendered
378  _renderSec = 0.0f;
379  double t1 = GlobalTimer::timeS();
380  double tStart = t1;
381 
382  OPTIX_CHECK(optixLaunch(
383  _pipeline,
384  SLOptix::stream,
385  _paramsBuffer.devicePointer(),
386  _paramsBuffer.size(),
387  &_sbtClassic,
388  _sv->scrW(),
389  _sv->scrH(),
390  /*depth=*/1));
391  CUDA_SYNC_CHECK(SLOptix::stream);
392 
393  _renderSec = (SLfloat)(GlobalTimer::timeS() - tStart);
394 
395  return true;
396 }
397 //-----------------------------------------------------------------------------
398 SLbool SLOptixRaytracer::renderDistrib()
399 {
400  _renderSec = 0.0f;
401  double t1 = GlobalTimer::timeS();
402  double tStart = t1;
403 
404  OPTIX_CHECK(optixLaunch(
405  _pipeline,
406  SLOptix::stream,
407  _paramsBuffer.devicePointer(),
408  _paramsBuffer.size(),
409  &_sbtDistributed,
410  _sv->scrW(),
411  _sv->scrH(),
412  /*depth=*/1));
413  CUDA_SYNC_CHECK(SLOptix::stream);
414 
415  _renderSec = (SLfloat)(GlobalTimer::timeS() - tStart);
416 
417  return true;
418 }
419 //-----------------------------------------------------------------------------
420 void SLOptixRaytracer::prepareImage()
421 {
422  // Create the image for the first time
423  if (_images.empty())
424  _images.push_back(new CVImage(_sv->scrW(), _sv->scrH(), PF_rgb, "Optix Raytracer"));
425 
426  // Allocate image of the inherited texture class
427  if (_sv->scrW() != (SLint)_images[0]->width() ||
428  _sv->scrH() != (SLint)_images[0]->height())
429  {
430  // Delete the OpenGL Texture if it already exists
431  if (_texID)
432  {
433  if (_cudaGraphicsResource)
434  {
435  CUDA_CHECK(cuGraphicsUnregisterResource(_cudaGraphicsResource));
436  _cudaGraphicsResource = nullptr;
437  }
438 
439  glDeleteTextures(1, &_texID);
440  _texID = 0;
441  }
442 
443  _vaoSprite.clearAttribs();
444  _images[0]->allocate(_sv->scrW(), _sv->scrH(), PF_rgb);
445  }
446 }
447 //-----------------------------------------------------------------------------
448 void SLOptixRaytracer::renderImage(bool updateTextureGL)
449 {
450  prepareImage(); // Setup image & precalculations
452 
453  CUarray texture_ptr;
454  CUDA_CHECK(cuGraphicsMapResources(1, &_cudaGraphicsResource, SLOptix::stream));
455  CUDA_CHECK(cuGraphicsSubResourceGetMappedArray(&texture_ptr, _cudaGraphicsResource, 0, 0));
456 
457  CUDA_ARRAY_DESCRIPTOR des;
458  cuArrayGetDescriptor(&des, texture_ptr);
459  CUDA_MEMCPY2D memcpy2D;
460  memcpy2D.srcDevice = _imageBuffer.devicePointer();
461  memcpy2D.srcMemoryType = CU_MEMORYTYPE_DEVICE;
462  memcpy2D.srcXInBytes = 0;
463  memcpy2D.srcY = 0;
464  memcpy2D.srcPitch = 0;
465  memcpy2D.dstArray = texture_ptr;
466  memcpy2D.dstMemoryType = CU_MEMORYTYPE_ARRAY;
467  memcpy2D.dstXInBytes = 0;
468  memcpy2D.dstY = 0;
469  memcpy2D.dstPitch = 0;
470  memcpy2D.WidthInBytes = des.Width * des.NumChannels * sizeof(float);
471  memcpy2D.Height = des.Height;
472  CUDA_CHECK(cuMemcpy2D(&memcpy2D));
473 
474  CUDA_CHECK(cuGraphicsUnmapResources(1, &_cudaGraphicsResource, SLOptix::stream));
475 
476  SLRaytracer::renderImage(updateTextureGL);
477 }
478 //-----------------------------------------------------------------------------
479 void SLOptixRaytracer::saveImage()
480 {
481  float4* image = static_cast<float4*>(malloc(_imageBuffer.size()));
482  _imageBuffer.download(image);
483 
484  for (int i = 0; i < _sv->scrH(); i++)
485  {
486  for (int j = 0; j < _sv->scrW(); j++)
487  {
488  float4 pixel = image[i * _sv->scrW() + j];
489  _images[0]->setPixeli(j, i, CVVec4f(pixel.x, pixel.y, pixel.z));
490  }
491  }
492 
494 }
495 //-----------------------------------------------------------------------------
496 #endif
@ PF_rgb
Definition: CVImage.h:36
cv::Vec4f CVVec4f
Definition: CVTypedefs.h:54
float SLfloat
analog to GLfloat
Definition: SL.h:200
#define SL_LOG(...)
Some debugging and error handling macros.
Definition: SL.h:279
unsigned int SLuint
analog to GLuint
Definition: SL.h:198
bool SLbool
analog to GLbool
Definition: SL.h:202
int SLint
analog to GLint
Definition: SL.h:197
@ P_monoPerspective
standard mono pinhole perspective projection
Definition: SLEnums.h:135
SLSceneView * sv
Definition: SLGLImGui.h:28
vector< SLMesh * > SLVMesh
Definition: SLMesh.h:263
Error checking macros and helpers for the OptiX ray tracer.
@ rtBusy
Definition: SLRaytracer.h:30
OpenCV image class with the same interface as the former SLImage class.
Definition: CVImage.h:64
static float timeS()
Definition: GlobalTimer.cpp:20
Toplevel holder of the assets meshes, materials, textures and shaders.
SLVMesh & meshes()
void colors(const SLCol4f &uniformColor)
Sets a uniform background color.
Active or visible camera node class.
Definition: SLCamera.h:54
void lensSamples(SLuint x, SLuint y)
Definition: SLCamera.h:118
void projType(SLProjType p)
Definition: SLCamera.h:92
void lensDiameter(const SLfloat d)
Definition: SLCamera.h:117
SLBackground & background()
Definition: SLCamera.h:165
void UVWFrame(SLVec3f &EYE, SLVec3f &U, SLVec3f &V, SLVec3f &W)
Definition: SLCamera.cpp:1578
void bindActive(SLuint texUnit=0)
static SLCol4f globalAmbient
static global ambient light intensity
Definition: SLLight.h:202
static unsigned int instanceIndex
???
Definition: SLNode.h:322
SLRaytracer hold all the methods for Whitted style Ray Tracing.
Definition: SLRaytracer.h:58
virtual void renderImage(bool updateTextureGL)
virtual void saveImage()
Saves the current RT image as PNG image.
The SLScene class represents the top level instance holding the scene structure.
Definition: SLScene.h:47
SLVLight & lights()
Definition: SLScene.h:107
void root3D(SLNode *root3D)
Definition: SLScene.h:78
SceneView class represents a dynamic real time 3D view onto the scene.
Definition: SLSceneView.h:69
void camera(SLCamera *camera)
Definition: SLSceneView.h:149
void scrW(SLint scrW)
Definition: SLSceneView.h:151
SLScene * s()
Definition: SLSceneView.h:171
void log(const char *tag, const char *format,...)
logs a formatted string platform independently
Definition: Utils.cpp:1100