SLProject  4.2.000
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 
296  // Iterate over all meshes
297  SLMesh::meshIndex = 0;
298  for (auto mesh : meshes)
299  {
300  mesh->createMeshAccelerationStructure();
301  }
302 
303  _sbtClassic = createShaderBindingTable(meshes, false);
304  _sbtDistributed = createShaderBindingTable(meshes, true);
305 }
306 //-----------------------------------------------------------------------------
307 void SLOptixRaytracer::updateScene(SLSceneView* sv)
308 {
309  SLScene* scene = sv->s();
310  SLCamera* camera = sv->camera();
311  _sv = sv;
312 
314  // scene->root3D()->createInstanceAccelerationStructureTree();
315  scene->root3D()->createInstanceAccelerationStructureFlat();
316 
317  _params.handle = scene->root3D()->optixTraversableHandle();
318 
319  SLVec3f eye, u, v, w;
320  camera->UVWFrame(eye, u, v, w);
321  ortCamera cameraData{};
322  cameraData.eye = make_float3(eye);
323  cameraData.U = make_float3(u);
324  cameraData.V = make_float3(v);
325  cameraData.W = make_float3(w);
326 
327  if (doDistributed())
328  {
329  RayGenDistributedSbtRecord rayGenSbtRecord;
330  _rayGenDistributedBuffer.download(&rayGenSbtRecord);
331  OPTIX_CHECK(optixSbtRecordPackHeader(_lens_raygen_prog_group, &rayGenSbtRecord));
332  rayGenSbtRecord.data.lensDiameter = camera->lensDiameter();
333  rayGenSbtRecord.data.samples.samplesX = camera->lensSamples()->samplesX();
334  rayGenSbtRecord.data.samples.samplesY = camera->lensSamples()->samplesY();
335  rayGenSbtRecord.data.camera = cameraData;
336  _rayGenDistributedBuffer.upload(&rayGenSbtRecord);
337  }
338  else
339  {
340  RayGenClassicSbtRecord rayGenSbtRecord;
341  _rayGenClassicBuffer.download(&rayGenSbtRecord);
342  if (camera->projType() == P_monoPerspective)
343  {
344  OPTIX_CHECK(optixSbtRecordPackHeader(_pinhole_raygen_prog_group, &rayGenSbtRecord));
345  }
346  else
347  {
348  OPTIX_CHECK(optixSbtRecordPackHeader(_orthographic_raygen_prog_group, &rayGenSbtRecord));
349  }
350  rayGenSbtRecord.data = cameraData;
351  _rayGenClassicBuffer.upload(&rayGenSbtRecord);
352  }
353 
354  vector<ortLight> lights;
355  _lightBuffer.free();
356  unsigned int light_count = 0;
357  for (auto light : scene->lights())
358  {
359  if (light->isOn())
360  {
361  lights.push_back(light->optixLight(doDistributed()));
362  light_count++;
363  }
364  }
365  _lightBuffer.alloc_and_upload(lights);
366  _params.lights = reinterpret_cast<ortLight*>(_lightBuffer.devicePointer());
367  _params.numLights = light_count;
368  _params.globalAmbientColor = make_float4(SLLight::globalAmbient);
369 
370  _paramsBuffer.upload(&_params);
371 }
372 //-----------------------------------------------------------------------------
373 SLbool SLOptixRaytracer::renderClassic()
374 {
375  _state = rtBusy; // From here we state the RT as busy
376  _progressPC = 0; // % rendered
377  _renderSec = 0.0f;
378  double t1 = GlobalTimer::timeS();
379  double tStart = t1;
380 
381  OPTIX_CHECK(optixLaunch(
382  _pipeline,
383  SLOptix::stream,
384  _paramsBuffer.devicePointer(),
385  _paramsBuffer.size(),
386  &_sbtClassic,
387  _sv->scrW(),
388  _sv->scrH(),
389  /*depth=*/1));
390  CUDA_SYNC_CHECK(SLOptix::stream);
391 
392  _renderSec = (SLfloat)(GlobalTimer::timeS() - tStart);
393 
394  return true;
395 }
396 //-----------------------------------------------------------------------------
397 SLbool SLOptixRaytracer::renderDistrib()
398 {
399  _renderSec = 0.0f;
400  double t1 = GlobalTimer::timeS();
401  double tStart = t1;
402 
403  OPTIX_CHECK(optixLaunch(
404  _pipeline,
405  SLOptix::stream,
406  _paramsBuffer.devicePointer(),
407  _paramsBuffer.size(),
408  &_sbtDistributed,
409  _sv->scrW(),
410  _sv->scrH(),
411  /*depth=*/1));
412  CUDA_SYNC_CHECK(SLOptix::stream);
413 
414  _renderSec = (SLfloat)(GlobalTimer::timeS() - tStart);
415 
416  return true;
417 }
418 //-----------------------------------------------------------------------------
419 void SLOptixRaytracer::prepareImage()
420 {
421  // Create the image for the first time
422  if (_images.empty())
423  _images.push_back(new CVImage(_sv->scrW(), _sv->scrH(), PF_rgb, "Optix Raytracer"));
424 
425  // Allocate image of the inherited texture class
426  if (_sv->scrW() != (SLint)_images[0]->width() ||
427  _sv->scrH() != (SLint)_images[0]->height())
428  {
429  // Delete the OpenGL Texture if it already exists
430  if (_texID)
431  {
432  if (_cudaGraphicsResource)
433  {
434  CUDA_CHECK(cuGraphicsUnregisterResource(_cudaGraphicsResource));
435  _cudaGraphicsResource = nullptr;
436  }
437 
438  glDeleteTextures(1, &_texID);
439  _texID = 0;
440  }
441 
442  _vaoSprite.clearAttribs();
443  _images[0]->allocate(_sv->scrW(), _sv->scrH(), PF_rgb);
444  }
445 }
446 //-----------------------------------------------------------------------------
447 void SLOptixRaytracer::renderImage(bool updateTextureGL)
448 {
449  prepareImage(); // Setup image & precalculations
451 
452  CUarray texture_ptr;
453  CUDA_CHECK(cuGraphicsMapResources(1, &_cudaGraphicsResource, SLOptix::stream));
454  CUDA_CHECK(cuGraphicsSubResourceGetMappedArray(&texture_ptr, _cudaGraphicsResource, 0, 0));
455 
456  CUDA_ARRAY_DESCRIPTOR des;
457  cuArrayGetDescriptor(&des, texture_ptr);
458  CUDA_MEMCPY2D memcpy2D;
459  memcpy2D.srcDevice = _imageBuffer.devicePointer();
460  memcpy2D.srcMemoryType = CU_MEMORYTYPE_DEVICE;
461  memcpy2D.srcXInBytes = 0;
462  memcpy2D.srcY = 0;
463  memcpy2D.srcPitch = 0;
464  memcpy2D.dstArray = texture_ptr;
465  memcpy2D.dstMemoryType = CU_MEMORYTYPE_ARRAY;
466  memcpy2D.dstXInBytes = 0;
467  memcpy2D.dstY = 0;
468  memcpy2D.dstPitch = 0;
469  memcpy2D.WidthInBytes = des.Width * des.NumChannels * sizeof(float);
470  memcpy2D.Height = des.Height;
471  CUDA_CHECK(cuMemcpy2D(&memcpy2D));
472 
473  CUDA_CHECK(cuGraphicsUnmapResources(1, &_cudaGraphicsResource, SLOptix::stream));
474 
475  SLRaytracer::renderImage(updateTextureGL);
476 }
477 //-----------------------------------------------------------------------------
478 void SLOptixRaytracer::saveImage()
479 {
480  float4* image = static_cast<float4*>(malloc(_imageBuffer.size()));
481  _imageBuffer.download(image);
482 
483  for (int i = 0; i < _sv->scrH(); i++)
484  {
485  for (int j = 0; j < _sv->scrW(); j++)
486  {
487  float4 pixel = image[i * _sv->scrW() + j];
488  _images[0]->setPixeli(j, i, CVVec4f(pixel.x, pixel.y, pixel.z));
489  }
490  }
491 
493 }
494 //-----------------------------------------------------------------------------
495 #endif
@ PF_rgb
Definition: CVImage.h:36
cv::Vec4f CVVec4f
Definition: CVTypedefs.h:54
float SLfloat
Definition: SL.h:173
#define SL_LOG(...)
Definition: SL.h:233
unsigned int SLuint
Definition: SL.h:171
bool SLbool
Definition: SL.h:175
int SLint
Definition: SL.h:170
@ P_monoPerspective
standard mono pinhole perspective projection
Definition: SLEnums.h:135
SLSceneView * sv
Definition: SLGLImGui.h:28
vector< SLMesh * > SLVMesh
Definition: SLMesh.h:263
@ 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:321
SLRaytracer hold all the methods for Whitted style Ray Tracing.
Definition: SLRaytracer.h:57
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:145
void scrW(SLint scrW)
Definition: SLSceneView.h:147
SLScene * s()
Definition: SLSceneView.h:167
void log(const char *tag, const char *format,...)
logs a formatted string platform independently
Definition: Utils.cpp:1103