1.3.64
 
Loading...
Searching...
No Matches
OptiX6Backend.cpp
Go to the documentation of this file.
1
16#include "OptiX6Backend.h"
17#include "Context.h"
18#include <chrono>
19
20using namespace helios;
21
22// Forward declaration of error handling functions
23static void sutilReportError(const char *message);
24static void sutilHandleError(RTcontext context, RTresult code, const char *file, int line);
25
26OptiX6Backend::OptiX6Backend() {
27 // Constructor - initialization happens in initialize()
28}
29
30OptiX6Backend::~OptiX6Backend() {
31 if (is_initialized) {
32 shutdown();
33 }
34}
35
37
38 if (is_initialized) {
39 helios_runtime_error("ERROR (OptiX6Backend::initialize): Backend already initialized.");
40 }
41
42 /* Create OptiX Context */
43 RT_CHECK_ERROR(rtContextCreate(&OptiX_Context));
44 RT_CHECK_ERROR(rtContextSetPrintEnabled(OptiX_Context, 1));
45
46 /* Set ray type and entry point counts */
47 RT_CHECK_ERROR(rtContextSetRayTypeCount(OptiX_Context, 4));
48 // ray types: 0=direct, 1=diffuse, 2=camera, 3=pixel_label
49
50 RT_CHECK_ERROR(rtContextSetEntryPointCount(OptiX_Context, 4));
51 // entry points: 0=direct_raygen, 1=diffuse_raygen, 2=camera_raygen, 3=pixel_label_raygen
52
53 /* Declare ray type variables */
54 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "direct_ray_type", &direct_ray_type_RTvariable));
55 RT_CHECK_ERROR(rtVariableSet1ui(direct_ray_type_RTvariable, RAYTYPE_DIRECT));
56 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "diffuse_ray_type", &diffuse_ray_type_RTvariable));
57 RT_CHECK_ERROR(rtVariableSet1ui(diffuse_ray_type_RTvariable, RAYTYPE_DIFFUSE));
58 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "camera_ray_type", &camera_ray_type_RTvariable));
59 RT_CHECK_ERROR(rtVariableSet1ui(camera_ray_type_RTvariable, RAYTYPE_CAMERA));
60 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "pixel_label_ray_type", &pixel_label_ray_type_RTvariable));
61 RT_CHECK_ERROR(rtVariableSet1ui(pixel_label_ray_type_RTvariable, RAYTYPE_PIXEL_LABEL));
62
63 /* Load ray generation programs from PTX */
64 std::string ptx_path = helios::resolvePluginAsset("radiation", "cuda_compile_ptx_generated_rayGeneration.cu.ptx").string();
65 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, ptx_path.c_str(), "direct_raygen", &direct_raygen));
66 RT_CHECK_ERROR(rtContextSetRayGenerationProgram(OptiX_Context, RAYTYPE_DIRECT, direct_raygen));
67 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, ptx_path.c_str(), "diffuse_raygen", &diffuse_raygen));
68 RT_CHECK_ERROR(rtContextSetRayGenerationProgram(OptiX_Context, RAYTYPE_DIFFUSE, diffuse_raygen));
69 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, ptx_path.c_str(), "camera_raygen", &camera_raygen));
70 RT_CHECK_ERROR(rtContextSetRayGenerationProgram(OptiX_Context, RAYTYPE_CAMERA, camera_raygen));
71 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, ptx_path.c_str(), "pixel_label_raygen", &pixel_label_raygen));
72 RT_CHECK_ERROR(rtContextSetRayGenerationProgram(OptiX_Context, RAYTYPE_PIXEL_LABEL, pixel_label_raygen));
73
74 /* Load hit programs from PTX */
75 std::string hit_ptx_path = helios::resolvePluginAsset("radiation", "cuda_compile_ptx_generated_rayHit.cu.ptx").string();
76 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, hit_ptx_path.c_str(), "closest_hit_direct", &closest_hit_direct));
77 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, hit_ptx_path.c_str(), "closest_hit_diffuse", &closest_hit_diffuse));
78 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, hit_ptx_path.c_str(), "closest_hit_camera", &closest_hit_camera));
79 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, hit_ptx_path.c_str(), "closest_hit_pixel_label", &closest_hit_pixel_label));
80 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, hit_ptx_path.c_str(), "miss_direct", &miss_direct));
81 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, hit_ptx_path.c_str(), "miss_diffuse", &miss_diffuse));
82 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, hit_ptx_path.c_str(), "miss_camera", &miss_camera));
83
84 /* Set miss programs */
85 RT_CHECK_ERROR(rtContextSetMissProgram(OptiX_Context, RAYTYPE_DIRECT, miss_direct));
86 RT_CHECK_ERROR(rtContextSetMissProgram(OptiX_Context, RAYTYPE_DIFFUSE, miss_diffuse));
87 RT_CHECK_ERROR(rtContextSetMissProgram(OptiX_Context, RAYTYPE_CAMERA, miss_camera));
88
89 /* Load intersection programs from PTX */
90 std::string intersect_ptx_path = helios::resolvePluginAsset("radiation", "cuda_compile_ptx_generated_primitiveIntersection.cu.ptx").string();
91
92 // Patch (rectangle) programs
93 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, intersect_ptx_path.c_str(), "rectangle_intersect", &rectangle_intersect));
94 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, intersect_ptx_path.c_str(), "rectangle_bounds", &rectangle_bounds));
95
96 // Triangle programs
97 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, intersect_ptx_path.c_str(), "triangle_intersect", &triangle_intersect));
98 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, intersect_ptx_path.c_str(), "triangle_bounds", &triangle_bounds));
99
100 // Disk programs
101 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, intersect_ptx_path.c_str(), "disk_intersect", &disk_intersect));
102 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, intersect_ptx_path.c_str(), "disk_bounds", &disk_bounds));
103
104 // Tile programs
105 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, intersect_ptx_path.c_str(), "tile_intersect", &tile_intersect));
106 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, intersect_ptx_path.c_str(), "tile_bounds", &tile_bounds));
107
108 // Voxel programs
109 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, intersect_ptx_path.c_str(), "voxel_intersect", &voxel_intersect));
110 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, intersect_ptx_path.c_str(), "voxel_bounds", &voxel_bounds));
111
112 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, intersect_ptx_path.c_str(), "bbox_intersect", &bbox_intersect));
113 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, intersect_ptx_path.c_str(), "bbox_bounds", &bbox_bounds));
114
115 /* Create geometry objects for each primitive type */
116
117 // Patch geometry
118 RT_CHECK_ERROR(rtGeometryCreate(OptiX_Context, &patch_geometry));
119 RT_CHECK_ERROR(rtGeometrySetBoundingBoxProgram(patch_geometry, rectangle_bounds));
120 RT_CHECK_ERROR(rtGeometrySetIntersectionProgram(patch_geometry, rectangle_intersect));
121
122 // Triangle geometry
123 RT_CHECK_ERROR(rtGeometryCreate(OptiX_Context, &triangle_geometry));
124 RT_CHECK_ERROR(rtGeometrySetBoundingBoxProgram(triangle_geometry, triangle_bounds));
125 RT_CHECK_ERROR(rtGeometrySetIntersectionProgram(triangle_geometry, triangle_intersect));
126
127 // Disk geometry
128 RT_CHECK_ERROR(rtGeometryCreate(OptiX_Context, &disk_geometry));
129 RT_CHECK_ERROR(rtGeometrySetBoundingBoxProgram(disk_geometry, disk_bounds));
130 RT_CHECK_ERROR(rtGeometrySetIntersectionProgram(disk_geometry, disk_intersect));
131
132 // Tile geometry
133 RT_CHECK_ERROR(rtGeometryCreate(OptiX_Context, &tile_geometry));
134 RT_CHECK_ERROR(rtGeometrySetBoundingBoxProgram(tile_geometry, tile_bounds));
135 RT_CHECK_ERROR(rtGeometrySetIntersectionProgram(tile_geometry, tile_intersect));
136
137 // Voxel geometry
138 RT_CHECK_ERROR(rtGeometryCreate(OptiX_Context, &voxel_geometry));
139 RT_CHECK_ERROR(rtGeometrySetBoundingBoxProgram(voxel_geometry, voxel_bounds));
140 RT_CHECK_ERROR(rtGeometrySetIntersectionProgram(voxel_geometry, voxel_intersect));
141
142 // Bbox geometry
143 RT_CHECK_ERROR(rtGeometryCreate(OptiX_Context, &bbox_geometry));
144 RT_CHECK_ERROR(rtGeometrySetBoundingBoxProgram(bbox_geometry, bbox_bounds));
145 RT_CHECK_ERROR(rtGeometrySetIntersectionProgram(bbox_geometry, bbox_intersect));
146
147 /* Create materials for each primitive type */
148
149 // Patch material
150 RT_CHECK_ERROR(rtMaterialCreate(OptiX_Context, &patch_material));
151 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(patch_material, RAYTYPE_DIRECT, closest_hit_direct));
152 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(patch_material, RAYTYPE_DIFFUSE, closest_hit_diffuse));
153 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(patch_material, RAYTYPE_CAMERA, closest_hit_camera));
154 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(patch_material, RAYTYPE_PIXEL_LABEL, closest_hit_pixel_label));
155
156 // Triangle material
157 RT_CHECK_ERROR(rtMaterialCreate(OptiX_Context, &triangle_material));
158 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(triangle_material, RAYTYPE_DIRECT, closest_hit_direct));
159 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(triangle_material, RAYTYPE_DIFFUSE, closest_hit_diffuse));
160 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(triangle_material, RAYTYPE_CAMERA, closest_hit_camera));
161 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(triangle_material, RAYTYPE_PIXEL_LABEL, closest_hit_pixel_label));
162
163 // Disk material
164 RT_CHECK_ERROR(rtMaterialCreate(OptiX_Context, &disk_material));
165 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(disk_material, RAYTYPE_DIRECT, closest_hit_direct));
166 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(disk_material, RAYTYPE_DIFFUSE, closest_hit_diffuse));
167 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(disk_material, RAYTYPE_CAMERA, closest_hit_camera));
168 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(disk_material, RAYTYPE_PIXEL_LABEL, closest_hit_pixel_label));
169
170 // Tile material
171 RT_CHECK_ERROR(rtMaterialCreate(OptiX_Context, &tile_material));
172 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(tile_material, RAYTYPE_DIRECT, closest_hit_direct));
173 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(tile_material, RAYTYPE_DIFFUSE, closest_hit_diffuse));
174 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(tile_material, RAYTYPE_CAMERA, closest_hit_camera));
175 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(tile_material, RAYTYPE_PIXEL_LABEL, closest_hit_pixel_label));
176
177 // Voxel material
178 RT_CHECK_ERROR(rtMaterialCreate(OptiX_Context, &voxel_material));
179 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(voxel_material, RAYTYPE_DIRECT, closest_hit_direct));
180 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(voxel_material, RAYTYPE_DIFFUSE, closest_hit_diffuse));
181 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(voxel_material, RAYTYPE_CAMERA, closest_hit_camera));
182 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(voxel_material, RAYTYPE_PIXEL_LABEL, closest_hit_pixel_label));
183
184 // Bbox material
185 RT_CHECK_ERROR(rtMaterialCreate(OptiX_Context, &bbox_material));
186 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(bbox_material, RAYTYPE_DIRECT, closest_hit_direct));
187 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(bbox_material, RAYTYPE_DIFFUSE, closest_hit_diffuse));
188 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(bbox_material, RAYTYPE_CAMERA, closest_hit_camera));
189 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(bbox_material, RAYTYPE_PIXEL_LABEL, closest_hit_pixel_label));
190
191 /* Create OptiX scene graph structure */
192
193 // Create top level group
194 RT_CHECK_ERROR(rtGroupCreate(OptiX_Context, &top_level_group));
195 RT_CHECK_ERROR(rtGroupSetChildCount(top_level_group, 1));
196
197 // Create top level acceleration (NoAccel for minimal overhead)
198 RT_CHECK_ERROR(rtAccelerationCreate(OptiX_Context, &top_level_acceleration));
199 RT_CHECK_ERROR(rtAccelerationSetBuilder(top_level_acceleration, "NoAccel"));
200 RT_CHECK_ERROR(rtAccelerationSetTraverser(top_level_acceleration, "NoAccel"));
201 RT_CHECK_ERROR(rtGroupSetAcceleration(top_level_group, top_level_acceleration));
202 RT_CHECK_ERROR(rtAccelerationMarkDirty(top_level_acceleration));
203
204 // Create transform node (identity matrix)
205 RT_CHECK_ERROR(rtTransformCreate(OptiX_Context, &transform));
206 float identity[16] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1};
207 RT_CHECK_ERROR(rtTransformSetMatrix(transform, 0, identity, nullptr));
208 RT_CHECK_ERROR(rtGroupSetChild(top_level_group, 0, transform));
209
210 // Create geometry group
211 RT_CHECK_ERROR(rtGeometryGroupCreate(OptiX_Context, &base_geometry_group));
212 RT_CHECK_ERROR(rtGeometryGroupSetChildCount(base_geometry_group, 6)); // 6 primitive types
213 RT_CHECK_ERROR(rtTransformSetChild(transform, base_geometry_group));
214
215 // Create geometry acceleration (Trbvh for fast BVH building)
216 RT_CHECK_ERROR(rtAccelerationCreate(OptiX_Context, &base_acceleration));
217 RT_CHECK_ERROR(rtAccelerationSetBuilder(base_acceleration, "Trbvh"));
218 RT_CHECK_ERROR(rtAccelerationSetTraverser(base_acceleration, "Bvh"));
219 RT_CHECK_ERROR(rtGeometryGroupSetAcceleration(base_geometry_group, base_acceleration));
220 RT_CHECK_ERROR(rtAccelerationMarkDirty(base_acceleration));
221
222 // Create geometry instances
223 RT_CHECK_ERROR(rtGeometryInstanceCreate(OptiX_Context, &patch_geometryinstance));
224 RT_CHECK_ERROR(rtGeometryInstanceSetGeometry(patch_geometryinstance, patch_geometry));
225 RT_CHECK_ERROR(rtGeometryInstanceSetMaterialCount(patch_geometryinstance, 1));
226 RT_CHECK_ERROR(rtGeometryInstanceSetMaterial(patch_geometryinstance, 0, patch_material));
227 RT_CHECK_ERROR(rtGeometryGroupSetChild(base_geometry_group, 0, patch_geometryinstance));
228
229 RT_CHECK_ERROR(rtGeometryInstanceCreate(OptiX_Context, &triangle_geometryinstance));
230 RT_CHECK_ERROR(rtGeometryInstanceSetGeometry(triangle_geometryinstance, triangle_geometry));
231 RT_CHECK_ERROR(rtGeometryInstanceSetMaterialCount(triangle_geometryinstance, 1));
232 RT_CHECK_ERROR(rtGeometryInstanceSetMaterial(triangle_geometryinstance, 0, triangle_material));
233 RT_CHECK_ERROR(rtGeometryGroupSetChild(base_geometry_group, 1, triangle_geometryinstance));
234
235 RT_CHECK_ERROR(rtGeometryInstanceCreate(OptiX_Context, &disk_geometryinstance));
236 RT_CHECK_ERROR(rtGeometryInstanceSetGeometry(disk_geometryinstance, disk_geometry));
237 RT_CHECK_ERROR(rtGeometryInstanceSetMaterialCount(disk_geometryinstance, 1));
238 RT_CHECK_ERROR(rtGeometryInstanceSetMaterial(disk_geometryinstance, 0, disk_material));
239 RT_CHECK_ERROR(rtGeometryGroupSetChild(base_geometry_group, 2, disk_geometryinstance));
240
241 RT_CHECK_ERROR(rtGeometryInstanceCreate(OptiX_Context, &tile_geometryinstance));
242 RT_CHECK_ERROR(rtGeometryInstanceSetGeometry(tile_geometryinstance, tile_geometry));
243 RT_CHECK_ERROR(rtGeometryInstanceSetMaterialCount(tile_geometryinstance, 1));
244 RT_CHECK_ERROR(rtGeometryInstanceSetMaterial(tile_geometryinstance, 0, tile_material));
245 RT_CHECK_ERROR(rtGeometryGroupSetChild(base_geometry_group, 3, tile_geometryinstance));
246
247 RT_CHECK_ERROR(rtGeometryInstanceCreate(OptiX_Context, &voxel_geometryinstance));
248 RT_CHECK_ERROR(rtGeometryInstanceSetGeometry(voxel_geometryinstance, voxel_geometry));
249 RT_CHECK_ERROR(rtGeometryInstanceSetMaterialCount(voxel_geometryinstance, 1));
250 RT_CHECK_ERROR(rtGeometryInstanceSetMaterial(voxel_geometryinstance, 0, voxel_material));
251 RT_CHECK_ERROR(rtGeometryGroupSetChild(base_geometry_group, 4, voxel_geometryinstance));
252
253 RT_CHECK_ERROR(rtGeometryInstanceCreate(OptiX_Context, &bbox_geometryinstance));
254 RT_CHECK_ERROR(rtGeometryInstanceSetGeometry(bbox_geometryinstance, bbox_geometry));
255 RT_CHECK_ERROR(rtGeometryInstanceSetMaterialCount(bbox_geometryinstance, 1));
256 RT_CHECK_ERROR(rtGeometryInstanceSetMaterial(bbox_geometryinstance, 0, bbox_material));
257 RT_CHECK_ERROR(rtGeometryGroupSetChild(base_geometry_group, 5, bbox_geometryinstance));
258
259 // Set top_object variable
260 RTvariable top_object;
261 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "top_object", &top_object));
262 RT_CHECK_ERROR(rtVariableSetObject(top_object, top_level_group));
263
264 /* Create all required buffers and variables */
265
266 // Geometry buffers
267 addBuffer("patch_vertices", patch_vertices_RTbuffer, patch_vertices_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, 2);
268 addBuffer("triangle_vertices", triangle_vertices_RTbuffer, triangle_vertices_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, 2);
269 addBuffer("disk_centers", disk_centers_RTbuffer, disk_centers_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, 1);
270 addBuffer("disk_radii", disk_radii_RTbuffer, disk_radii_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
271 addBuffer("disk_normals", disk_normals_RTbuffer, disk_normals_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, 1);
272 addBuffer("tile_vertices", tile_vertices_RTbuffer, tile_vertices_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, 2);
273 addBuffer("voxel_vertices", voxel_vertices_RTbuffer, voxel_vertices_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, 2);
274 addBuffer("bbox_vertices", bbox_vertices_RTbuffer, bbox_vertices_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, 2);
275
276 // Primitive data buffers
277 addBuffer("transform_matrix", transform_matrix_RTbuffer, transform_matrix_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 2);
278 addBuffer("primitive_type", primitive_type_RTbuffer, primitive_type_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
279 addBuffer("primitive_solid_fraction", primitive_solid_fraction_RTbuffer, primitive_solid_fraction_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
280 addBuffer("twosided_flag", twosided_flag_RTbuffer, twosided_flag_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_BYTE, 1);
281
282 // UUID buffers
283 addBuffer("patch_UUID", patch_UUID_RTbuffer, patch_UUID_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
284 addBuffer("triangle_UUID", triangle_UUID_RTbuffer, triangle_UUID_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
285 addBuffer("disk_UUID", disk_UUID_RTbuffer, disk_UUID_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
286 addBuffer("tile_UUID", tile_UUID_RTbuffer, tile_UUID_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
287 addBuffer("voxel_UUID", voxel_UUID_RTbuffer, voxel_UUID_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
288 addBuffer("bbox_UUID", bbox_UUID_RTbuffer, bbox_UUID_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
289
290 // Mapping buffers
291 addBuffer("objectID", objectID_RTbuffer, objectID_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
292 addBuffer("primitiveID", primitiveID_RTbuffer, primitiveID_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
293 addBuffer("primitive_positions", primitive_positions_RTbuffer, primitive_positions_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
294 addBuffer("object_subdivisions", object_subdivisions_RTbuffer, object_subdivisions_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_INT2, 1);
295
296 // Material property buffers
297 addBuffer("rho", rho_RTbuffer, rho_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
298 addBuffer("tau", tau_RTbuffer, tau_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
299 addBuffer("rho_cam", rho_cam_RTbuffer, rho_cam_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
300 addBuffer("tau_cam", tau_cam_RTbuffer, tau_cam_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
301 addBuffer("specular_exponent", specular_exponent_RTbuffer, specular_exponent_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
302 addBuffer("specular_scale", specular_scale_RTbuffer, specular_scale_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
303
304 // Radiation energy buffers
305 addBuffer("radiation_in", radiation_in_RTbuffer, radiation_in_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT, 1);
306 addBuffer("radiation_out_top", radiation_out_top_RTbuffer, radiation_out_top_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT, 1);
307 addBuffer("radiation_out_bottom", radiation_out_bottom_RTbuffer, radiation_out_bottom_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT, 1);
308 addBuffer("radiation_in_camera", radiation_in_camera_RTbuffer, radiation_in_camera_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT, 1);
309 addBuffer("camera_pixel_label", camera_pixel_label_RTbuffer, camera_pixel_label_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_UNSIGNED_INT, 1);
310 addBuffer("camera_pixel_depth", camera_pixel_depth_RTbuffer, camera_pixel_depth_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT, 1);
311 addBuffer("scatter_buff_top", scatter_buff_top_RTbuffer, scatter_buff_top_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT, 1);
312 addBuffer("scatter_buff_bottom", scatter_buff_bottom_RTbuffer, scatter_buff_bottom_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT, 1);
313 addBuffer("radiation_specular", radiation_specular_RTbuffer, radiation_specular_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT, 1);
314 addBuffer("Rsky", Rsky_RTbuffer, Rsky_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT, 1);
315 addBuffer("scatter_buff_top_cam", scatter_buff_top_cam_RTbuffer, scatter_buff_top_cam_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT, 1);
316 addBuffer("scatter_buff_bottom_cam", scatter_buff_bottom_cam_RTbuffer, scatter_buff_bottom_cam_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT, 1);
317
318 // Source buffers
319 addBuffer("source_positions", source_positions_RTbuffer, source_positions_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, 1);
320 addBuffer("source_widths", source_widths_RTbuffer, source_widths_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT2, 1);
321 addBuffer("source_rotations", source_rotations_RTbuffer, source_rotations_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, 1);
322 addBuffer("source_types", source_types_RTbuffer, source_types_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
323 addBuffer("source_fluxes", source_fluxes_RTbuffer, source_fluxes_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
324 addBuffer("source_fluxes_cam", source_fluxes_cam_RTbuffer, source_fluxes_cam_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
325
326 // Diffuse radiation buffers
327 addBuffer("diffuse_flux", diffuse_flux_RTbuffer, diffuse_flux_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
328 addBuffer("diffuse_extinction", diffuse_extinction_RTbuffer, diffuse_extinction_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
329 addBuffer("diffuse_peak_dir", diffuse_peak_dir_RTbuffer, diffuse_peak_dir_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, 1);
330 addBuffer("diffuse_dist_norm", diffuse_dist_norm_RTbuffer, diffuse_dist_norm_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
331
332 // Sky model buffers
333 addBuffer("sky_radiance_params", sky_radiance_params_RTbuffer, sky_radiance_params_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT4, 1);
334 addBuffer("camera_sky_radiance", camera_sky_radiance_RTbuffer, camera_sky_radiance_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
335 addBuffer("solar_disk_radiance", solar_disk_radiance_RTbuffer, solar_disk_radiance_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
336
337 // Band control buffers
338 addBuffer("band_launch_flag", band_launch_flag_RTbuffer, band_launch_flag_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_BYTE, 1);
339 addBuffer("max_scatters", max_scatters_RTbuffer, max_scatters_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
340
341 // Texture/masking buffers
342 addBuffer("masksize", masksize_RTbuffer, masksize_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_INT2, 1);
343 addBuffer("maskID", maskID_RTbuffer, maskID_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_INT, 1);
344 addBuffer("uvdata", uvdata_RTbuffer, uvdata_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT2, 2);
345 addBuffer("uvID", uvID_RTbuffer, uvID_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_INT, 1);
346
347 // Special handling for 3D mask buffer
348 RT_CHECK_ERROR(rtBufferCreate(OptiX_Context, RT_BUFFER_INPUT, &maskdata_RTbuffer));
349 RT_CHECK_ERROR(rtBufferSetFormat(maskdata_RTbuffer, RT_FORMAT_BYTE));
350 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "maskdata", &maskdata_RTvariable));
351 RT_CHECK_ERROR(rtVariableSetObject(maskdata_RTvariable, maskdata_RTbuffer));
352 std::vector<std::vector<std::vector<bool>>> dummydata;
353 initializeBuffer3Dbool(maskdata_RTbuffer, dummydata);
354
355 // Context variables
356 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "Nprimitives", &Nprimitives_RTvariable));
357 RT_CHECK_ERROR(rtVariableSet1ui(Nprimitives_RTvariable, 0));
358
359 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "bbox_UUID_base", &bbox_UUID_base_RTvariable));
360 RT_CHECK_ERROR(rtVariableSet1ui(bbox_UUID_base_RTvariable, UINT_MAX)); // Initialize to sentinel (no bboxes)
361
362 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "Nsources", &Nsources_RTvariable));
363 RT_CHECK_ERROR(rtVariableSet1ui(Nsources_RTvariable, 0));
364
365 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "Nbands_global", &Nbands_global_RTvariable));
366 RT_CHECK_ERROR(rtVariableSet1ui(Nbands_global_RTvariable, 0));
367
368 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "Nbands_launch", &Nbands_launch_RTvariable));
369 RT_CHECK_ERROR(rtVariableSet1ui(Nbands_launch_RTvariable, 0));
370
371 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "Ncameras", &Ncameras_RTvariable));
372 RT_CHECK_ERROR(rtVariableSet1ui(Ncameras_RTvariable, 0));
373
374 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "periodic_flag", &periodic_flag_RTvariable));
375 RT_CHECK_ERROR(rtVariableSet2f(periodic_flag_RTvariable, 0.f, 0.f));
376
377 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "specular_reflection_enabled", &specular_reflection_enabled_RTvariable));
378 RT_CHECK_ERROR(rtVariableSet1ui(specular_reflection_enabled_RTvariable, 0));
379
380 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "scattering_iteration", &scattering_iteration_RTvariable));
381 RT_CHECK_ERROR(rtVariableSet1ui(scattering_iteration_RTvariable, 0));
382
383 // Launch control variables
384 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "random_seed", &random_seed_RTvariable));
385 RT_CHECK_ERROR(rtVariableSet1ui(random_seed_RTvariable, std::chrono::system_clock::now().time_since_epoch().count()));
386
387 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "launch_offset", &launch_offset_RTvariable));
388 RT_CHECK_ERROR(rtVariableSet1ui(launch_offset_RTvariable, 0));
389
390 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "launch_face", &launch_face_RTvariable));
391 RT_CHECK_ERROR(rtVariableSet1ui(launch_face_RTvariable, 0));
392
393 // Camera variables
394 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "camera_position", &camera_position_RTvariable));
395 RT_CHECK_ERROR(rtVariableSet3f(camera_position_RTvariable, 0.f, 0.f, 0.f));
396
397 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "camera_direction", &camera_direction_RTvariable));
398 RT_CHECK_ERROR(rtVariableSet2f(camera_direction_RTvariable, 0.f, 0.f));
399
400 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "camera_lens_diameter", &camera_lens_diameter_RTvariable));
401 RT_CHECK_ERROR(rtVariableSet1f(camera_lens_diameter_RTvariable, 0.f));
402
403 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "FOV_aspect_ratio", &FOV_aspect_ratio_RTvariable));
404 RT_CHECK_ERROR(rtVariableSet1f(FOV_aspect_ratio_RTvariable, 1.f));
405
406 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "camera_HFOV", &camera_HFOV_RTvariable));
407 RT_CHECK_ERROR(rtVariableSet1f(camera_HFOV_RTvariable, 0.f));
408
409 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "camera_focal_length", &camera_focal_length_RTvariable));
410 RT_CHECK_ERROR(rtVariableSet1f(camera_focal_length_RTvariable, 0.f));
411
412 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "camera_viewplane_length", &camera_viewplane_length_RTvariable));
413 RT_CHECK_ERROR(rtVariableSet1f(camera_viewplane_length_RTvariable, 0.f));
414
415 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "camera_pixel_solid_angle", &camera_pixel_solid_angle_RTvariable));
416 RT_CHECK_ERROR(rtVariableSet1f(camera_pixel_solid_angle_RTvariable, 0.f));
417
418 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "camera_pixel_offset_x", &camera_pixel_offset_x_RTvariable));
419 RT_CHECK_ERROR(rtVariableSet1ui(camera_pixel_offset_x_RTvariable, 0));
420
421 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "camera_pixel_offset_y", &camera_pixel_offset_y_RTvariable));
422 RT_CHECK_ERROR(rtVariableSet1ui(camera_pixel_offset_y_RTvariable, 0));
423
424 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "camera_ID", &camera_ID_RTvariable));
425 RT_CHECK_ERROR(rtVariableSet1ui(camera_ID_RTvariable, 0));
426
427 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "camera_resolution_full", &camera_resolution_full_RTvariable));
428 RT_CHECK_ERROR(rtVariableSet2i(camera_resolution_full_RTvariable, 0, 0));
429
430 // Sun direction for sky model
431 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "sun_direction", &sun_direction_RTvariable));
432 RT_CHECK_ERROR(rtVariableSet3f(sun_direction_RTvariable, 0.f, 0.f, 1.f));
433
434 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "solar_disk_cos_angle", &solar_disk_cos_angle_RTvariable));
435 RT_CHECK_ERROR(rtVariableSet1f(solar_disk_cos_angle_RTvariable, 0.f));
436
437 is_initialized = true;
438}
439
441
442 if (!is_initialized) {
443 return; // Already shut down or never initialized
444 }
445
446 // Destroy OptiX context (this destroys all child objects automatically)
447 if (OptiX_Context) {
448 RT_CHECK_ERROR_NOEXIT(rtContextDestroy(OptiX_Context));
449 OptiX_Context = nullptr;
450 }
451
452 is_initialized = false;
453}
454
456 if (!is_initialized) {
457 helios_runtime_error("ERROR (OptiX6Backend::updateGeometry): Backend not initialized.");
458 }
459
460 // Validate geometry before upload (debug builds only)
461 // Catches buffer sizing errors that cause 90% of backend debugging issues
462 validateGeometryBeforeUpload(geometry);
463
464 // Convert geometry data to OptiX buffers
465 geometryToBuffers(geometry);
466
467 // Update primitive counts
468 current_primitive_count = geometry.primitive_count;
469 current_patch_count = geometry.patch_count;
470 current_triangle_count = geometry.triangle_count;
471 current_disk_count = geometry.disk_count;
472 current_tile_count = geometry.tile_count;
473 current_voxel_count = geometry.voxel_count;
474 current_bbox_count = geometry.bbox_count;
475
476 RT_CHECK_ERROR(rtVariableSet1ui(Nprimitives_RTvariable, geometry.primitive_count));
477 RT_CHECK_ERROR(rtVariableSet1ui(bbox_UUID_base_RTvariable, geometry.bbox_UUID_base));
478
479 // Update periodic boundary flags
480 RT_CHECK_ERROR(rtVariableSet2f(periodic_flag_RTvariable, geometry.periodic_flag.x, geometry.periodic_flag.y));
481
482 // Mark acceleration structure as dirty (needs rebuild)
483 RT_CHECK_ERROR(rtAccelerationMarkDirty(base_acceleration));
484}
485
487 if (!is_initialized) {
488 helios_runtime_error("ERROR (OptiX6Backend::buildAccelerationStructure): Backend not initialized.");
489 }
490
491 // Set primitive counts for each geometry type (use type-specific counts, not total)
492 RT_CHECK_ERROR(rtGeometrySetPrimitiveCount(patch_geometry, current_patch_count));
493 RT_CHECK_ERROR(rtGeometrySetPrimitiveCount(triangle_geometry, current_triangle_count));
494 RT_CHECK_ERROR(rtGeometrySetPrimitiveCount(disk_geometry, current_disk_count));
495 RT_CHECK_ERROR(rtGeometrySetPrimitiveCount(tile_geometry, current_tile_count));
496 RT_CHECK_ERROR(rtGeometrySetPrimitiveCount(voxel_geometry, current_voxel_count));
497 RT_CHECK_ERROR(rtGeometrySetPrimitiveCount(bbox_geometry, current_bbox_count));
498
499 // OptiX will automatically rebuild the acceleration structure on next launch
500}
501
503 if (!is_initialized) {
504 helios_runtime_error("ERROR (OptiX6Backend::updateMaterials): Backend not initialized.");
505 }
506
507 materialsToBuffers(materials);
508
509 // Update material counts
510 current_band_count = materials.num_bands;
511 current_source_count = materials.num_sources;
512 current_camera_count = materials.num_cameras;
513
514 // Update Ncameras variable in backend's OptiX context
515 RT_CHECK_ERROR(rtVariableSet1ui(Ncameras_RTvariable, materials.num_cameras));
516}
517
518void OptiX6Backend::updateSources(const std::vector<RayTracingSource> &sources) {
519 if (!is_initialized) {
520 helios_runtime_error("ERROR (OptiX6Backend::updateSources): Backend not initialized.");
521 }
522
523 sourcesToBuffers(sources);
524
525 // Update source count variable
526 current_source_count = sources.size();
527 RT_CHECK_ERROR(rtVariableSet1ui(Nsources_RTvariable, sources.size()));
528}
529
530void OptiX6Backend::updateDiffuseRadiation(const std::vector<float> &flux, const std::vector<float> &extinction, const std::vector<helios::vec3> &peak_dir, const std::vector<float> &dist_norm, const std::vector<float> &sky_energy) {
531
532 if (!is_initialized) {
533 helios_runtime_error("ERROR (OptiX6Backend::updateDiffuseRadiation): Backend not initialized.");
534 }
535
536 diffuseToBuffers(flux, extinction, peak_dir, dist_norm, sky_energy);
537}
538
539void OptiX6Backend::updateSkyModel(const std::vector<helios::vec4> &sky_radiance_params, const std::vector<float> &camera_sky_radiance, const helios::vec3 &sun_direction, const std::vector<float> &solar_disk_radiance, float solar_disk_cos_angle) {
540
541 if (!is_initialized) {
542 helios_runtime_error("ERROR (OptiX6Backend::updateSkyModel): Backend not initialized.");
543 }
544
545 skyModelToBuffers(sky_radiance_params, camera_sky_radiance, sun_direction, solar_disk_radiance, solar_disk_cos_angle);
546}
547
549 if (!is_initialized) {
550 helios_runtime_error("ERROR (OptiX6Backend::launchDirectRays): Backend not initialized.");
551 }
552
553 // Set launch parameters
554 launchParamsToVariables(params);
555
556 // Validate context to ensure acceleration structure is built and buffers are synchronized
557 RT_CHECK_ERROR(rtContextValidate(OptiX_Context));
558
559 // Launch direct rays: dimension = (n, n, primitives) where n = sqrt(rays_per_primitive)
560 // This matches CUDA code: launch_index.x/y for ray sampling, launch_index.z for primitive
561 uint n = std::ceil(std::sqrt(static_cast<double>(params.rays_per_primitive)));
562 RT_CHECK_ERROR(rtContextLaunch3D(OptiX_Context, RAYTYPE_DIRECT, n, n, params.launch_count));
563}
564
566 if (!is_initialized) {
567 helios_runtime_error("ERROR (OptiX6Backend::launchDiffuseRays): Backend not initialized.");
568 }
569
570 // Set launch parameters
571 launchParamsToVariables(params);
572
573 // Upload emission/outgoing radiation if provided
574 if (!params.radiation_out_top.empty()) {
575 initializeBuffer1Df(radiation_out_top_RTbuffer, params.radiation_out_top);
576 }
577 if (!params.radiation_out_bottom.empty()) {
578 initializeBuffer1Df(radiation_out_bottom_RTbuffer, params.radiation_out_bottom);
579 }
580
581 // Upload diffuse parameters if provided
582 if (!params.diffuse_flux.empty()) {
583 initializeBuffer1Df(diffuse_flux_RTbuffer, params.diffuse_flux);
584 }
585 if (!params.diffuse_extinction.empty()) {
586 initializeBuffer1Df(diffuse_extinction_RTbuffer, params.diffuse_extinction);
587 }
588 if (!params.diffuse_peak_dir.empty()) {
589 initializeBuffer1Dfloat3(diffuse_peak_dir_RTbuffer, params.diffuse_peak_dir);
590 }
591 if (!params.diffuse_dist_norm.empty()) {
592 initializeBuffer1Df(diffuse_dist_norm_RTbuffer, params.diffuse_dist_norm);
593 }
594
595 // Validate context to ensure acceleration structure is built
596 RT_CHECK_ERROR(rtContextValidate(OptiX_Context));
597
598 // Launch diffuse rays: dimension = (n, n, primitives) where n = sqrt(rays_per_primitive)
599 // This matches the diffuse ray generation pattern expecting 2D hemisphere sampling
600 uint n = std::ceil(std::sqrt(static_cast<double>(params.rays_per_primitive)));
601 RT_CHECK_ERROR(rtContextLaunch3D(OptiX_Context, RAYTYPE_DIFFUSE, n, n, params.launch_count));
602}
603
605 if (!is_initialized) {
606 helios_runtime_error("ERROR (OptiX6Backend::launchCameraRays): Backend not initialized.");
607 }
608
609
610 // Set common launch parameters
611 launchParamsToVariables(params);
612
613 // Set camera-specific parameters
614 RT_CHECK_ERROR(rtVariableSet3f(camera_position_RTvariable, params.camera_position.x, params.camera_position.y, params.camera_position.z));
615
616 RT_CHECK_ERROR(rtVariableSet2f(camera_direction_RTvariable, params.camera_direction.x, params.camera_direction.y));
617
618 RT_CHECK_ERROR(rtVariableSet1f(camera_focal_length_RTvariable, params.camera_focal_length));
619 RT_CHECK_ERROR(rtVariableSet1f(camera_lens_diameter_RTvariable, params.camera_lens_diameter));
620 RT_CHECK_ERROR(rtVariableSet1f(FOV_aspect_ratio_RTvariable, params.camera_fov_aspect));
621
622 // Debug: check camera_HFOV value
623 if (std::isnan(params.camera_HFOV) || std::isinf(params.camera_HFOV)) {
624 }
625
626 RT_CHECK_ERROR(rtVariableSet1f(camera_HFOV_RTvariable, params.camera_HFOV));
627
628 RT_CHECK_ERROR(rtVariableSet1ui(camera_pixel_offset_x_RTvariable, params.camera_pixel_offset.x));
629 RT_CHECK_ERROR(rtVariableSet1ui(camera_pixel_offset_y_RTvariable, params.camera_pixel_offset.y));
630 RT_CHECK_ERROR(rtVariableSet1ui(camera_ID_RTvariable, params.camera_id));
631
632 // Set the 3 new camera parameters
633 RT_CHECK_ERROR(rtVariableSet1f(camera_viewplane_length_RTvariable, params.camera_viewplane_length));
634 RT_CHECK_ERROR(rtVariableSet1f(camera_pixel_solid_angle_RTvariable, params.camera_pixel_solid_angle));
635 RT_CHECK_ERROR(rtVariableSet2i(camera_resolution_full_RTvariable, params.camera_resolution_full.x, params.camera_resolution_full.y));
636
637 // Resize camera buffer for full resolution (not tile resolution!)
638 size_t total_pixels = params.camera_resolution_full.x * params.camera_resolution_full.y;
639 size_t buffer_size = total_pixels * params.num_bands_launch;
640 if (buffer_size > 0) {
641 zeroBuffer1D(radiation_in_camera_RTbuffer, buffer_size);
642 }
643
644 // Validate context to ensure acceleration structure is built and buffers are synchronized
645 RT_CHECK_ERROR(rtContextValidate(OptiX_Context));
646
647 // Launch camera rays: dimension = (antialiasing_samples, resolution.x, resolution.y) for pixel sampling
648 RT_CHECK_ERROR(rtContextLaunch3D(OptiX_Context, RAYTYPE_CAMERA, params.antialiasing_samples, params.camera_resolution.x, params.camera_resolution.y));
649}
650
652 if (!is_initialized) {
653 helios_runtime_error("ERROR (OptiX6Backend::launchPixelLabelRays): Backend not initialized.");
654 }
655
656 // Set launch parameters
657 launchParamsToVariables(params);
658
659 // Set only essential parameters for pixel coordinate calculations
660 // Camera orientation (position/direction) is inherited from camera rendering
661 // (This matches master's behavior where pixel labeling reuses camera settings)
662 RT_CHECK_ERROR(rtVariableSet1f(camera_viewplane_length_RTvariable, params.camera_viewplane_length));
663 RT_CHECK_ERROR(rtVariableSet1f(camera_pixel_solid_angle_RTvariable, params.camera_pixel_solid_angle));
664 RT_CHECK_ERROR(rtVariableSet2i(camera_resolution_full_RTvariable, params.camera_resolution_full.x, params.camera_resolution_full.y));
665
666 // Set camera pixel offset for tiling
667 RT_CHECK_ERROR(rtVariableSet1ui(camera_pixel_offset_x_RTvariable, params.camera_pixel_offset.x));
668 RT_CHECK_ERROR(rtVariableSet1ui(camera_pixel_offset_y_RTvariable, params.camera_pixel_offset.y));
669
670 // NOTE: Camera pixel buffers must be zeroed BEFORE the tile loop, not here!
671 // Zeroing happens in zeroCameraPixelBuffers() called from RadiationModel.cpp
672
673 // Launch pixel label rays: dimension = (1, resolution.x, resolution.y) - no antialiasing
674 RT_CHECK_ERROR(rtContextLaunch3D(OptiX_Context, RAYTYPE_PIXEL_LABEL, 1, params.camera_resolution.x, params.camera_resolution.y));
675}
676
678 if (!is_initialized) {
679 helios_runtime_error("ERROR (OptiX6Backend::getRadiationResults): Backend not initialized.");
680 }
681
682 // Extract results from OptiX buffers
683 buffersToResults(results);
684
685 // Set dimension information
686 results.num_primitives = current_primitive_count;
687 results.num_bands = current_band_count;
688 results.num_sources = current_source_count;
689 results.num_cameras = current_camera_count;
690}
691
692void OptiX6Backend::getCameraResults(std::vector<float> &pixel_data, std::vector<uint> &pixel_labels, std::vector<float> &pixel_depths, uint camera_id, const helios::int2 &resolution) {
693
694 if (!is_initialized) {
695 helios_runtime_error("ERROR (OptiX6Backend::getCameraResults): Backend not initialized.");
696 }
697
698 // Extract camera pixel data from buffers
699 pixel_data = getOptiXbufferData(radiation_in_camera_RTbuffer);
700 pixel_labels = getOptiXbufferData_ui(camera_pixel_label_RTbuffer);
701 pixel_depths = getOptiXbufferData(camera_pixel_depth_RTbuffer);
702}
703
704void OptiX6Backend::zeroRadiationBuffers(size_t launch_band_count) {
705 if (!is_initialized) {
706 helios_runtime_error("ERROR (OptiX6Backend::zeroRadiationBuffers): Backend not initialized.");
707 }
708
709 // Validation: launch bands cannot exceed global bands
710 if (launch_band_count > current_band_count) {
711 helios_runtime_error("ERROR (OptiX6Backend::zeroRadiationBuffers): launch_band_count (" + std::to_string(launch_band_count) + ") exceeds current_band_count (" + std::to_string(current_band_count) + ").");
712 }
713
714 // Zero all radiation result buffers (use current_band_count for global accumulation)
715 // Note: Bbox primitives don't accumulate radiation (they only wrap rays),
716 // so buffers are sized for real primitives only
717 size_t buffer_size = current_primitive_count * current_band_count;
718 if (buffer_size > 0) {
719 zeroBuffer1D(radiation_in_RTbuffer, buffer_size);
720 zeroBuffer1D(radiation_out_top_RTbuffer, buffer_size);
721 zeroBuffer1D(radiation_out_bottom_RTbuffer, buffer_size);
722 zeroBuffer1D(scatter_buff_top_RTbuffer, buffer_size);
723 zeroBuffer1D(scatter_buff_bottom_RTbuffer, buffer_size);
724 }
725
726 // Zero camera scatter buffers (use launch_band_count for per-launch sizing)
727 // Camera scatter uses same indexing as regular scatter: [primitive][band]
728 if (current_camera_count > 0) {
729 size_t cam_scatter_size = current_primitive_count * launch_band_count;
730 if (cam_scatter_size > 0) {
731 zeroBuffer1D(scatter_buff_top_cam_RTbuffer, cam_scatter_size);
732 zeroBuffer1D(scatter_buff_bottom_cam_RTbuffer, cam_scatter_size);
733 }
734 }
735
736 // Zero specular buffer (use current_band_count for global accumulation)
737 size_t specular_size = current_source_count * current_camera_count * current_primitive_count * current_band_count;
738 if (specular_size > 0) {
739 zeroBuffer1D(radiation_specular_RTbuffer, specular_size);
740 }
741
742 // Zero sky energy buffer
743 if (current_band_count > 0) {
744 zeroBuffer1D(Rsky_RTbuffer, current_band_count);
745 }
746}
747
749 if (!is_initialized) {
750 helios_runtime_error("ERROR (OptiX6Backend::zeroScatterBuffers): Backend not initialized.");
751 }
752
753 // Zero primitive scatter buffers (between iterations)
754 size_t buffer_size = current_primitive_count * current_band_count;
755 if (buffer_size > 0) {
756 zeroBuffer1D(scatter_buff_top_RTbuffer, buffer_size);
757 zeroBuffer1D(scatter_buff_bottom_RTbuffer, buffer_size);
758 }
759
760 // NOTE: Camera scatter buffers are NOT zeroed here
761 // They accumulate across all scatter iterations and are only zeroed once in zeroRadiationBuffers()
762}
763
764void OptiX6Backend::zeroCameraScatterBuffers(size_t launch_band_count) {
765 if (!is_initialized) {
766 helios_runtime_error("ERROR (OptiX6Backend::zeroCameraScatterBuffers): Backend not initialized.");
767 }
768
769 // Validation: launch bands cannot exceed global bands
770 if (launch_band_count > current_band_count) {
771 helios_runtime_error("ERROR (OptiX6Backend::zeroCameraScatterBuffers): launch_band_count (" + std::to_string(launch_band_count) + ") exceeds current_band_count (" + std::to_string(current_band_count) + ").");
772 }
773
774 // Zero camera scatter buffers (use launch_band_count for per-launch sizing)
775 if (current_camera_count > 0) {
776 size_t buffer_size = current_primitive_count * launch_band_count;
777 if (buffer_size > 0) {
778 zeroBuffer1D(scatter_buff_top_cam_RTbuffer, buffer_size);
779 zeroBuffer1D(scatter_buff_bottom_cam_RTbuffer, buffer_size);
780 }
781 }
782}
783
785 if (!is_initialized) {
786 helios_runtime_error("ERROR (OptiX6Backend::zeroCameraPixelBuffers): Backend not initialized.");
787 }
788
789 // Zero pixel label and depth buffers for full resolution
790 size_t total_pixels = resolution.x * resolution.y;
791 if (total_pixels > 0) {
792 zeroBuffer1D(camera_pixel_label_RTbuffer, total_pixels);
793 zeroBuffer1D(camera_pixel_depth_RTbuffer, total_pixels);
794 }
795}
796
798 if (!is_initialized) {
799 helios_runtime_error("ERROR (OptiX6Backend::copyScatterToRadiation): Backend not initialized.");
800 }
801
802 // Copy scatter buffer contents to radiation_out buffers
803 copyBuffer1D(scatter_buff_top_RTbuffer, radiation_out_top_RTbuffer);
804 copyBuffer1D(scatter_buff_bottom_RTbuffer, radiation_out_bottom_RTbuffer);
805}
806
807void OptiX6Backend::uploadRadiationOut(const std::vector<float> &radiation_out_top, const std::vector<float> &radiation_out_bottom) {
808 if (!is_initialized) {
809 helios_runtime_error("ERROR (OptiX6Backend::uploadRadiationOut): Backend not initialized.");
810 }
811
812 if (!radiation_out_top.empty()) {
813 initializeBuffer1Df(radiation_out_top_RTbuffer, radiation_out_top);
814 }
815 if (!radiation_out_bottom.empty()) {
816 initializeBuffer1Df(radiation_out_bottom_RTbuffer, radiation_out_bottom);
817 }
818}
819
820void OptiX6Backend::uploadCameraScatterBuffers(const std::vector<float> &scatter_top_cam, const std::vector<float> &scatter_bottom_cam) {
821 if (!is_initialized) {
822 helios_runtime_error("ERROR (OptiX6Backend::uploadCameraScatterBuffers): Backend not initialized.");
823 }
824
825 if (!scatter_top_cam.empty()) {
826 initializeBuffer1Df(scatter_buff_top_cam_RTbuffer, scatter_top_cam);
827 }
828 if (!scatter_bottom_cam.empty()) {
829 initializeBuffer1Df(scatter_buff_bottom_cam_RTbuffer, scatter_bottom_cam);
830 }
831}
832
833void OptiX6Backend::uploadSourceFluxes(const std::vector<float> &fluxes) {
834 if (!is_initialized) {
835 helios_runtime_error("ERROR (OptiX6Backend::uploadSourceFluxes): Backend not initialized.");
836 }
837
838 if (!fluxes.empty()) {
839 initializeBuffer1Df(source_fluxes_RTbuffer, fluxes);
840 }
841}
842
844 if (!is_initialized) {
845 std::cout << "Backend not initialized - cannot query GPU memory." << std::endl;
846 return;
847 }
848
849 // Query OptiX memory usage
850 RTsize memory_used;
851 RT_CHECK_ERROR(rtContextGetAttribute(OptiX_Context, RT_CONTEXT_ATTRIBUTE_AVAILABLE_DEVICE_MEMORY, sizeof(RTsize), &memory_used));
852
853 // Memory info available via backend->queryGPUMemory() - removed automatic output for cleaner tests
854}
855
856std::string OptiX6Backend::getBackendName() const {
857 return "OptiX 6.5";
858}
859
860// ========== Error Handling Helpers ==========
861
862static void sutilReportError(const char *message) {
863 fprintf(stderr, "OptiX Error: %s\n", message);
864#if defined(_WIN32) && defined(RELEASE_PUBLIC)
865 {
866 char s[2048];
867 sprintf(s, "OptiX Error: %s", message);
868 MessageBox(0, s, "OptiX Error", MB_OK | MB_ICONWARNING | MB_SYSTEMMODAL);
869 }
870#endif
871}
872
873static void sutilHandleError(RTcontext context, RTresult code, const char *file, int line) {
874 const char *message;
875 char s[2048];
876 rtContextGetErrorString(context, code, &message);
877 sprintf(s, "%s\n(%s:%d)", message, file, line);
878 sutilReportError(s);
879 exit(1);
880}
881
882// ========== Private Helper Methods: Buffer Management ==========
883
884void OptiX6Backend::addBuffer(const char *name, RTbuffer &buffer, RTvariable &variable, RTbuffertype type, RTformat format, size_t dimension) {
885 RT_CHECK_ERROR(rtBufferCreate(OptiX_Context, type, &buffer));
886 RT_CHECK_ERROR(rtBufferSetFormat(buffer, format));
887 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, name, &variable));
888 RT_CHECK_ERROR(rtVariableSetObject(variable, buffer));
889 if (dimension == 1) {
890 zeroBuffer1D(buffer, 1);
891 } else if (dimension == 2) {
892 zeroBuffer2D(buffer, helios::make_int2(1, 1));
893 } else {
894 helios_runtime_error("ERROR (OptiX6Backend::addBuffer): invalid buffer dimension of " + std::to_string(dimension) + ", must be 1 or 2.");
895 }
896}
897
898void OptiX6Backend::zeroBuffer1D(RTbuffer &buffer, size_t bsize) {
899 RTformat format;
900 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
901
902 RT_CHECK_ERROR(rtBufferSetSize1D(buffer, bsize));
903
904 void *ptr;
905 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
906
907 if (format == RT_FORMAT_FLOAT) {
908 float *data = (float *) ptr;
909 for (size_t i = 0; i < bsize; i++) {
910 data[i] = 0.0f;
911 }
912 } else if (format == RT_FORMAT_FLOAT2) {
913 optix::float2 *data = (optix::float2 *) ptr;
914 for (size_t i = 0; i < bsize; i++) {
915 data[i] = optix::make_float2(0, 0);
916 }
917 } else if (format == RT_FORMAT_FLOAT3) {
918 optix::float3 *data = (optix::float3 *) ptr;
919 for (size_t i = 0; i < bsize; i++) {
920 data[i] = optix::make_float3(0, 0, 0);
921 }
922 } else if (format == RT_FORMAT_FLOAT4) {
923 optix::float4 *data = (optix::float4 *) ptr;
924 for (size_t i = 0; i < bsize; i++) {
925 data[i] = optix::make_float4(0, 0, 0, 0);
926 }
927 } else if (format == RT_FORMAT_UNSIGNED_INT) {
928 uint *data = (uint *) ptr;
929 for (size_t i = 0; i < bsize; i++) {
930 data[i] = 0;
931 }
932 } else if (format == RT_FORMAT_INT) {
933 int *data = (int *) ptr;
934 for (size_t i = 0; i < bsize; i++) {
935 data[i] = 0;
936 }
937 } else if (format == RT_FORMAT_INT2) {
938 optix::int2 *data = (optix::int2 *) ptr;
939 for (size_t i = 0; i < bsize; i++) {
940 data[i] = optix::make_int2(0, 0);
941 }
942 } else if (format == RT_FORMAT_BYTE) {
943 char *data = (char *) ptr;
944 for (size_t i = 0; i < bsize; i++) {
945 data[i] = 0;
946 }
947 } else {
948 RT_CHECK_ERROR(rtBufferUnmap(buffer));
949 helios_runtime_error("ERROR (OptiX6Backend::zeroBuffer1D): Unsupported buffer format.");
950 }
951
952 RT_CHECK_ERROR(rtBufferUnmap(buffer));
953}
954
955void OptiX6Backend::zeroBuffer2D(RTbuffer &buffer, const helios::int2 &bsize) {
956 RTformat format;
957 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
958
959 if (format == RT_FORMAT_FLOAT) {
960 std::vector<std::vector<float>> array(bsize.y, std::vector<float>(bsize.x, 0.0f));
961 initializeBuffer2Df(buffer, array);
962 } else if (format == RT_FORMAT_FLOAT2) {
963 std::vector<std::vector<helios::vec2>> array(bsize.y, std::vector<helios::vec2>(bsize.x, helios::make_vec2(0, 0)));
964 initializeBuffer2Dfloat2(buffer, array);
965 } else if (format == RT_FORMAT_FLOAT3) {
966 std::vector<std::vector<optix::float3>> array(bsize.y, std::vector<optix::float3>(bsize.x, optix::make_float3(0, 0, 0)));
967 initializeBuffer2Dfloat3(buffer, array);
968 } else {
969 helios_runtime_error("ERROR (OptiX6Backend::zeroBuffer2D): Unsupported buffer format.");
970 }
971}
972
973void OptiX6Backend::initializeBuffer1Df(RTbuffer &buffer, const std::vector<float> &array) {
974 size_t bsize = array.size();
975 RT_CHECK_ERROR(rtBufferSetSize1D(buffer, bsize));
976
977 RTformat format;
978 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
979 if (format != RT_FORMAT_FLOAT) {
980 helios_runtime_error("ERROR (OptiX6Backend::initializeBuffer1Df): Buffer must have type float.");
981 }
982
983 void *ptr;
984 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
985 float *data = (float *) ptr;
986 for (size_t i = 0; i < bsize; i++) {
987 data[i] = array[i];
988 }
989 RT_CHECK_ERROR(rtBufferUnmap(buffer));
990}
991
992void OptiX6Backend::initializeBuffer1Dui(RTbuffer &buffer, const std::vector<uint> &array) {
993 size_t bsize = array.size();
994 RT_CHECK_ERROR(rtBufferSetSize1D(buffer, bsize));
995
996 RTformat format;
997 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
998 if (format != RT_FORMAT_UNSIGNED_INT) {
999 helios_runtime_error("ERROR (OptiX6Backend::initializeBuffer1Dui): Buffer must have type unsigned int.");
1000 }
1001
1002 void *ptr;
1003 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1004 uint *data = (uint *) ptr;
1005 for (size_t i = 0; i < bsize; i++) {
1006 data[i] = array[i];
1007 }
1008 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1009}
1010
1011void OptiX6Backend::initializeBuffer1Di(RTbuffer &buffer, const std::vector<int> &array) {
1012 size_t bsize = array.size();
1013 RT_CHECK_ERROR(rtBufferSetSize1D(buffer, bsize));
1014
1015 RTformat format;
1016 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
1017 if (format != RT_FORMAT_INT) {
1018 helios_runtime_error("ERROR (OptiX6Backend::initializeBuffer1Di): Buffer must have type int.");
1019 }
1020
1021 void *ptr;
1022 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1023 int *data = (int *) ptr;
1024 for (size_t i = 0; i < bsize; i++) {
1025 data[i] = array[i];
1026 }
1027 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1028}
1029
1030void OptiX6Backend::initializeBuffer1Dchar(RTbuffer &buffer, const std::vector<char> &array) {
1031 size_t bsize = array.size();
1032 RT_CHECK_ERROR(rtBufferSetSize1D(buffer, bsize));
1033
1034 RTformat format;
1035 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
1036 if (format != RT_FORMAT_BYTE) {
1037 helios_runtime_error("ERROR (OptiX6Backend::initializeBuffer1Dchar): Buffer must have type char.");
1038 }
1039
1040 void *ptr;
1041 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1042 char *data = (char *) ptr;
1043 for (size_t i = 0; i < bsize; i++) {
1044 data[i] = array[i];
1045 }
1046 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1047}
1048
1049void OptiX6Backend::initializeBuffer1Dbool(RTbuffer &buffer, const std::vector<bool> &array) {
1050 size_t bsize = array.size();
1051 RT_CHECK_ERROR(rtBufferSetSize1D(buffer, bsize));
1052
1053 void *ptr;
1054 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1055 char *data = (char *) ptr;
1056 for (size_t i = 0; i < bsize; i++) {
1057 data[i] = array[i] ? 1 : 0;
1058 }
1059 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1060}
1061
1062void OptiX6Backend::initializeBuffer1Dfloat2(RTbuffer &buffer, const std::vector<helios::vec2> &array) {
1063 size_t bsize = array.size();
1064 RT_CHECK_ERROR(rtBufferSetSize1D(buffer, bsize));
1065
1066 void *ptr;
1067 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1068 optix::float2 *data = (optix::float2 *) ptr;
1069 for (size_t i = 0; i < bsize; i++) {
1070 data[i] = optix::make_float2(array[i].x, array[i].y);
1071 }
1072 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1073}
1074
1075void OptiX6Backend::initializeBuffer1Dfloat3(RTbuffer &buffer, const std::vector<helios::vec3> &array) {
1076 size_t bsize = array.size();
1077 RT_CHECK_ERROR(rtBufferSetSize1D(buffer, bsize));
1078
1079 void *ptr;
1080 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1081 optix::float3 *data = (optix::float3 *) ptr;
1082 for (size_t i = 0; i < bsize; i++) {
1083 data[i] = optix::make_float3(array[i].x, array[i].y, array[i].z);
1084 }
1085 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1086}
1087
1088void OptiX6Backend::initializeBuffer1Dfloat4(RTbuffer &buffer, const std::vector<helios::vec4> &array) {
1089 size_t bsize = array.size();
1090 RT_CHECK_ERROR(rtBufferSetSize1D(buffer, bsize));
1091
1092 void *ptr;
1093 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1094 optix::float4 *data = (optix::float4 *) ptr;
1095 for (size_t i = 0; i < bsize; i++) {
1096 data[i] = optix::make_float4(array[i].x, array[i].y, array[i].z, array[i].w);
1097 }
1098 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1099}
1100
1101void OptiX6Backend::initializeBuffer1Dint2(RTbuffer &buffer, const std::vector<helios::int2> &array) {
1102 size_t bsize = array.size();
1103 RT_CHECK_ERROR(rtBufferSetSize1D(buffer, bsize));
1104
1105 void *ptr;
1106 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1107 optix::int2 *data = (optix::int2 *) ptr;
1108 for (size_t i = 0; i < bsize; i++) {
1109 data[i] = optix::make_int2(array[i].x, array[i].y);
1110 }
1111 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1112}
1113
1114void OptiX6Backend::initializeBuffer2Dfloat2(RTbuffer &buffer, const std::vector<std::vector<helios::vec2>> &array) {
1115 helios::int2 bsize;
1116 bsize.y = array.size();
1117 bsize.x = (bsize.y == 0) ? 0 : array.front().size();
1118
1119 RT_CHECK_ERROR(rtBufferSetSize2D(buffer, bsize.x, bsize.y));
1120
1121 void *ptr;
1122 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1123 optix::float2 *data = (optix::float2 *) ptr;
1124 for (int j = 0; j < bsize.y; j++) {
1125 for (int i = 0; i < bsize.x; i++) {
1126 data[i + j * bsize.x] = optix::make_float2(array[j][i].x, array[j][i].y);
1127 }
1128 }
1129 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1130}
1131
1132void OptiX6Backend::initializeBuffer2Df(RTbuffer &buffer, const std::vector<std::vector<float>> &array) {
1133 helios::int2 bsize;
1134 bsize.y = array.size();
1135 bsize.x = (bsize.y == 0) ? 0 : array.front().size();
1136
1137 RT_CHECK_ERROR(rtBufferSetSize2D(buffer, bsize.x, bsize.y));
1138
1139 RTformat format;
1140 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
1141 if (format != RT_FORMAT_FLOAT) {
1142 helios_runtime_error("ERROR (OptiX6Backend::initializeBuffer2Df): Buffer must have type float.");
1143 }
1144
1145 void *ptr;
1146 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1147 float *data = (float *) ptr;
1148 for (int j = 0; j < bsize.y; j++) {
1149 for (int i = 0; i < bsize.x; i++) {
1150 data[i + j * bsize.x] = array[j][i];
1151 }
1152 }
1153 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1154}
1155
1156void OptiX6Backend::initializeBuffer2Dui(RTbuffer &buffer, const std::vector<std::vector<uint>> &array) {
1157 helios::int2 bsize;
1158 bsize.y = array.size();
1159 bsize.x = (bsize.y == 0) ? 0 : array.front().size();
1160
1161 RT_CHECK_ERROR(rtBufferSetSize2D(buffer, bsize.x, bsize.y));
1162
1163 RTformat format;
1164 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
1165 if (format != RT_FORMAT_UNSIGNED_INT) {
1166 helios_runtime_error("ERROR (OptiX6Backend::initializeBuffer2Dui): Buffer must have type unsigned int.");
1167 }
1168
1169 void *ptr;
1170 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1171 uint *data = (uint *) ptr;
1172 for (int j = 0; j < bsize.y; j++) {
1173 for (int i = 0; i < bsize.x; i++) {
1174 data[i + j * bsize.x] = array[j][i];
1175 }
1176 }
1177 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1178}
1179
1180void OptiX6Backend::initializeBuffer2Di(RTbuffer &buffer, const std::vector<std::vector<int>> &array) {
1181 helios::int2 bsize;
1182 bsize.y = array.size();
1183 bsize.x = (bsize.y == 0) ? 0 : array.front().size();
1184
1185 RT_CHECK_ERROR(rtBufferSetSize2D(buffer, bsize.x, bsize.y));
1186
1187 RTformat format;
1188 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
1189 if (format != RT_FORMAT_INT) {
1190 helios_runtime_error("ERROR (OptiX6Backend::initializeBuffer2Di): Buffer must have type int.");
1191 }
1192
1193 void *ptr;
1194 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1195 int *data = (int *) ptr;
1196 for (int j = 0; j < bsize.y; j++) {
1197 for (int i = 0; i < bsize.x; i++) {
1198 data[i + j * bsize.x] = array[j][i];
1199 }
1200 }
1201 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1202}
1203
1204void OptiX6Backend::initializeBuffer2Dfloat3(RTbuffer &buffer, const std::vector<std::vector<helios::vec3>> &array) {
1205 // Convert helios::vec3 to optix::float3
1206 std::vector<std::vector<optix::float3>> optix_array;
1207 optix_array.resize(array.size());
1208 for (size_t j = 0; j < array.size(); j++) {
1209 optix_array[j].resize(array[j].size());
1210 for (size_t i = 0; i < array[j].size(); i++) {
1211 optix_array[j][i] = optix::make_float3(array[j][i].x, array[j][i].y, array[j][i].z);
1212 }
1213 }
1214 initializeBuffer2Dfloat3(buffer, optix_array);
1215}
1216
1217void OptiX6Backend::initializeBuffer2Dfloat3(RTbuffer &buffer, const std::vector<std::vector<optix::float3>> &array) {
1218 helios::int2 bsize;
1219 bsize.y = array.size();
1220 bsize.x = (bsize.y == 0) ? 0 : array.front().size();
1221
1222 RT_CHECK_ERROR(rtBufferSetSize2D(buffer, bsize.x, bsize.y));
1223
1224 RTformat format;
1225 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
1226 if (format != RT_FORMAT_FLOAT3) {
1227 helios_runtime_error("ERROR (OptiX6Backend::initializeBuffer2Dfloat3): Buffer must have type float3.");
1228 }
1229
1230 void *ptr;
1231 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1232 optix::float3 *data = (optix::float3 *) ptr;
1233 for (int j = 0; j < bsize.y; j++) {
1234 for (int i = 0; i < bsize.x; i++) {
1235 data[i + j * bsize.x] = array[j][i];
1236 }
1237 }
1238 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1239}
1240
1241void OptiX6Backend::initializeBuffer3Dbool(RTbuffer &buffer, const std::vector<std::vector<std::vector<bool>>> &array) {
1242 // Template implementation for 3D buffers
1243 helios::int3 bsize;
1244 bsize.z = array.size();
1245 bsize.y = (bsize.z == 0) ? 0 : array.front().size();
1246 bsize.x = (bsize.y == 0) ? 0 : array.front().front().size();
1247
1248 RT_CHECK_ERROR(rtBufferSetSize3D(buffer, bsize.x, bsize.y, bsize.z));
1249
1250 void *ptr;
1251 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1252 char *data = (char *) ptr;
1253 for (int k = 0; k < bsize.z; k++) {
1254 for (int j = 0; j < bsize.y; j++) {
1255 for (int i = 0; i < bsize.x; i++) {
1256 data[i + j * bsize.x + k * bsize.x * bsize.y] = array[k][j][i] ? 1 : 0;
1257 }
1258 }
1259 }
1260 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1261}
1262
1263void OptiX6Backend::copyBuffer1D(RTbuffer &source, RTbuffer &dest) {
1264 RTformat format;
1265 RT_CHECK_ERROR(rtBufferGetFormat(source, &format));
1266
1267 RTsize bsize;
1268 rtBufferGetSize1D(source, &bsize);
1269 rtBufferSetSize1D(dest, bsize);
1270
1271 if (format == RT_FORMAT_FLOAT) {
1272 void *ptr_src;
1273 RT_CHECK_ERROR(rtBufferMap(source, &ptr_src));
1274 float *data_src = (float *) ptr_src;
1275
1276 void *ptr_dest;
1277 RT_CHECK_ERROR(rtBufferMap(dest, &ptr_dest));
1278 float *data_dest = (float *) ptr_dest;
1279
1280 for (size_t i = 0; i < bsize; i++) {
1281 data_dest[i] = data_src[i];
1282 }
1283
1284 RT_CHECK_ERROR(rtBufferUnmap(source));
1285 RT_CHECK_ERROR(rtBufferUnmap(dest));
1286 } else {
1287 helios_runtime_error("ERROR (OptiX6Backend::copyBuffer1D): Only float buffers supported currently.");
1288 }
1289}
1290
1291std::vector<float> OptiX6Backend::getOptiXbufferData(RTbuffer buffer) {
1292 RTsize bsize;
1293 RT_CHECK_ERROR(rtBufferGetSize1D(buffer, &bsize));
1294
1295 void *ptr;
1296 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1297 float *data = (float *) ptr;
1298
1299 std::vector<float> result(bsize);
1300 for (size_t i = 0; i < bsize; i++) {
1301 result[i] = data[i];
1302 }
1303
1304 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1305 return result;
1306}
1307
1308std::vector<uint> OptiX6Backend::getOptiXbufferData_ui(RTbuffer buffer) {
1309 RTsize bsize;
1310 RT_CHECK_ERROR(rtBufferGetSize1D(buffer, &bsize));
1311
1312 void *ptr;
1313 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1314 uint *data = (uint *) ptr;
1315
1316 std::vector<uint> result(bsize);
1317 for (size_t i = 0; i < bsize; i++) {
1318 result[i] = data[i];
1319 }
1320
1321 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1322 return result;
1323}
1324
1325void OptiX6Backend::geometryToBuffers(const RayTracingGeometry &geometry) {
1326 // Convert backend-agnostic geometry data to OptiX buffers
1327
1328 // Transform matrices: 1D vector → 2D buffer [primitive+bbox][16]
1329 // Note: includes both real primitives AND bboxes
1330 if (!geometry.transform_matrices.empty()) {
1331 size_t total_count = geometry.primitive_count + geometry.bbox_count;
1332 std::vector<std::vector<float>> transform_2d(total_count);
1333 for (size_t p = 0; p < total_count; p++) {
1334 transform_2d[p].resize(16);
1335 for (int i = 0; i < 16; i++) {
1336 transform_2d[p][i] = geometry.transform_matrices[p * 16 + i];
1337 }
1338 }
1339 initializeBuffer2Df(transform_matrix_RTbuffer, transform_2d);
1340 }
1341
1342 // Primitive types
1343 if (!geometry.primitive_types.empty()) {
1344 initializeBuffer1Dui(primitive_type_RTbuffer, geometry.primitive_types);
1345 }
1346
1347 // Primitive IDs (for sub-patch calculations)
1348 if (!geometry.primitive_IDs.empty()) {
1349 initializeBuffer1Dui(primitiveID_RTbuffer, geometry.primitive_IDs);
1350 }
1351
1352 // Primitive positions (UUID → array position lookup)
1353 if (!geometry.primitive_positions.empty()) {
1354 initializeBuffer1Dui(primitive_positions_RTbuffer, geometry.primitive_positions);
1355 }
1356
1357 // Primitive UUIDs and object IDs
1358 if (!geometry.object_IDs.empty()) {
1359 initializeBuffer1Dui(objectID_RTbuffer, geometry.object_IDs);
1360 }
1361
1362 // Two-sided flags
1363 if (!geometry.twosided_flags.empty()) {
1364 initializeBuffer1Dchar(twosided_flag_RTbuffer, geometry.twosided_flags);
1365 }
1366
1367 // Solid fractions
1368 if (!geometry.solid_fractions.empty()) {
1369 initializeBuffer1Df(primitive_solid_fraction_RTbuffer, geometry.solid_fractions);
1370 }
1371
1372 // Patch vertices: std::vector<vec3> → 2D buffer [patch][4]
1373 if (geometry.patch_count > 0 && !geometry.patches.vertices.empty()) {
1374 std::vector<std::vector<helios::vec3>> patch_verts_2d(geometry.patch_count);
1375 for (size_t p = 0; p < geometry.patch_count; p++) {
1376 patch_verts_2d[p].resize(4);
1377 for (int v = 0; v < 4; v++) {
1378 patch_verts_2d[p][v] = geometry.patches.vertices[p * 4 + v];
1379 }
1380 }
1381 initializeBuffer2Dfloat3(patch_vertices_RTbuffer, patch_verts_2d);
1382
1383 // Patch UUIDs
1384 initializeBuffer1Dui(patch_UUID_RTbuffer, geometry.patches.UUIDs);
1385 }
1386
1387 // Triangle vertices: std::vector<vec3> → 2D buffer [triangle][3]
1388 if (geometry.triangle_count > 0 && !geometry.triangles.vertices.empty()) {
1389 std::vector<std::vector<helios::vec3>> tri_verts_2d(geometry.triangle_count);
1390 for (size_t t = 0; t < geometry.triangle_count; t++) {
1391 tri_verts_2d[t].resize(3);
1392 for (int v = 0; v < 3; v++) {
1393 tri_verts_2d[t][v] = geometry.triangles.vertices[t * 3 + v];
1394 }
1395 }
1396 initializeBuffer2Dfloat3(triangle_vertices_RTbuffer, tri_verts_2d);
1397
1398 // Triangle UUIDs
1399 initializeBuffer1Dui(triangle_UUID_RTbuffer, geometry.triangles.UUIDs);
1400 }
1401
1402 // Disk geometry
1403 if (geometry.disk_count > 0) {
1404 initializeBuffer1Dfloat3(disk_centers_RTbuffer, geometry.disk_centers);
1405 initializeBuffer1Df(disk_radii_RTbuffer, geometry.disk_radii);
1406 initializeBuffer1Dfloat3(disk_normals_RTbuffer, geometry.disk_normals);
1407 initializeBuffer1Dui(disk_UUID_RTbuffer, geometry.disk_UUIDs);
1408 }
1409
1410 // Tile vertices: std::vector<vec3> → 2D buffer [tile][4]
1411 if (geometry.tile_count > 0 && !geometry.tiles.vertices.empty()) {
1412 std::vector<std::vector<helios::vec3>> tile_verts_2d(geometry.tile_count);
1413 for (size_t t = 0; t < geometry.tile_count; t++) {
1414 tile_verts_2d[t].resize(4);
1415 for (int v = 0; v < 4; v++) {
1416 tile_verts_2d[t][v] = geometry.tiles.vertices[t * 4 + v];
1417 }
1418 }
1419 initializeBuffer2Dfloat3(tile_vertices_RTbuffer, tile_verts_2d);
1420
1421 initializeBuffer1Dui(tile_UUID_RTbuffer, geometry.tiles.UUIDs);
1422 }
1423
1424 // Voxel vertices: std::vector<vec3> → 2D buffer [voxel][8]
1425 if (geometry.voxel_count > 0 && !geometry.voxels.vertices.empty()) {
1426 std::vector<std::vector<helios::vec3>> voxel_verts_2d(geometry.voxel_count);
1427 for (size_t v = 0; v < geometry.voxel_count; v++) {
1428 voxel_verts_2d[v].resize(8);
1429 for (int vtx = 0; vtx < 8; vtx++) {
1430 voxel_verts_2d[v][vtx] = geometry.voxels.vertices[v * 8 + vtx];
1431 }
1432 }
1433 initializeBuffer2Dfloat3(voxel_vertices_RTbuffer, voxel_verts_2d);
1434 initializeBuffer1Dui(voxel_UUID_RTbuffer, geometry.voxels.UUIDs);
1435 }
1436
1437 // Bbox vertices: std::vector<vec3> → 2D buffer [bbox][4]
1438 // Bbox faces are 4-vertex rectangles (verified in primitiveIntersection.cu:390-393)
1439 if (geometry.bbox_count > 0 && !geometry.bboxes.vertices.empty()) {
1440 std::vector<std::vector<helios::vec3>> bbox_verts_2d(geometry.bbox_count);
1441 for (size_t b = 0; b < geometry.bbox_count; b++) {
1442 bbox_verts_2d[b].resize(4);
1443 for (int v = 0; v < 4; v++) {
1444 bbox_verts_2d[b][v] = geometry.bboxes.vertices[b * 4 + v];
1445 }
1446 }
1447 initializeBuffer2Dfloat3(bbox_vertices_RTbuffer, bbox_verts_2d);
1448 initializeBuffer1Dui(bbox_UUID_RTbuffer, geometry.bboxes.UUIDs);
1449 }
1450
1451 // Object subdivisions
1452 if (!geometry.object_subdivisions.empty()) {
1453 initializeBuffer1Dint2(object_subdivisions_RTbuffer, geometry.object_subdivisions);
1454 }
1455
1456 // Texture masks
1457 if (!geometry.mask_data.empty()) {
1458 // Convert 1D bool array to 3D structure
1459 // CRITICAL: All masks must have same dimensions for 3D buffer, so pad to max size
1460 int max_width = 0, max_height = 0;
1461 for (const auto &size: geometry.mask_sizes) {
1462 max_width = std::max(max_width, size.x);
1463 max_height = std::max(max_height, size.y);
1464 }
1465
1466 std::vector<std::vector<std::vector<bool>>> mask_3d;
1467 size_t offset = 0;
1468 for (size_t m = 0; m < geometry.mask_sizes.size(); m++) {
1469 int width = geometry.mask_sizes[m].x;
1470 int height = geometry.mask_sizes[m].y;
1471 // Pad to max dimensions (padded regions will be false)
1472 std::vector<std::vector<bool>> mask_2d(max_height, std::vector<bool>(max_width, false));
1473 for (int y = 0; y < height; y++) {
1474 for (int x = 0; x < width; x++) {
1475 mask_2d[y][x] = geometry.mask_data[offset++];
1476 }
1477 }
1478 mask_3d.push_back(mask_2d);
1479 }
1480 initializeBuffer3Dbool(maskdata_RTbuffer, mask_3d);
1481 initializeBuffer1Dint2(masksize_RTbuffer, geometry.mask_sizes);
1482 }
1483
1484 if (!geometry.mask_IDs.empty()) {
1485 initializeBuffer1Di(maskID_RTbuffer, geometry.mask_IDs);
1486 }
1487
1488 // UV data
1489 // uv_IDs contains position indices (not offsets), used by CUDA to access uvdata[vertex][position]
1490 // uv_data is stored sequentially: 4 UVs per primitive that has UVs
1491 if (!geometry.uv_data.empty()) {
1492 // Convert 1D vec2 array to 2D structure: uv_2d[position][vertex]
1493 std::vector<std::vector<helios::vec2>> uv_2d(geometry.primitive_count);
1494 size_t uv_offset = 0;
1495 for (size_t p = 0; p < geometry.primitive_count; p++) {
1496 int uv_id = geometry.uv_IDs[p];
1497 if (uv_id >= 0) {
1498 // This primitive has UVs - read next 4 from uv_data
1499 uv_2d[p].resize(4);
1500 for (int v = 0; v < 4 && uv_offset < geometry.uv_data.size(); v++) {
1501 uv_2d[p][v] = geometry.uv_data[uv_offset++];
1502 }
1503 } else {
1504 // No UVs - use default
1505 uv_2d[p] = {helios::make_vec2(0, 0), helios::make_vec2(1, 0), helios::make_vec2(1, 1), helios::make_vec2(0, 1)};
1506 }
1507 }
1508 initializeBuffer2Dfloat2(uvdata_RTbuffer, uv_2d);
1509 }
1510
1511 if (!geometry.uv_IDs.empty()) {
1512 initializeBuffer1Di(uvID_RTbuffer, geometry.uv_IDs);
1513 }
1514}
1515
1516void OptiX6Backend::materialsToBuffers(const RayTracingMaterial &materials) {
1517 // Upload material properties to OptiX buffers
1518 // Indexing: [source * Nbands * Nprims + prim * Nbands + band]
1519 // This matches CUDA formula: Nprimitives * Nbands_global * source_ID + Nbands_global * origin_UUID + b_global
1520
1521 if (!materials.reflectivity.empty()) {
1522 initializeBuffer1Df(rho_RTbuffer, materials.reflectivity);
1523 }
1524
1525 if (!materials.transmissivity.empty()) {
1526 initializeBuffer1Df(tau_RTbuffer, materials.transmissivity);
1527 }
1528
1529 if (!materials.reflectivity_cam.empty()) {
1530 initializeBuffer1Df(rho_cam_RTbuffer, materials.reflectivity_cam);
1531 }
1532
1533 if (!materials.transmissivity_cam.empty()) {
1534 initializeBuffer1Df(tau_cam_RTbuffer, materials.transmissivity_cam);
1535 }
1536
1537 if (!materials.specular_exponent.empty()) {
1538 initializeBuffer1Df(specular_exponent_RTbuffer, materials.specular_exponent);
1539 }
1540
1541 if (!materials.specular_scale.empty()) {
1542 initializeBuffer1Df(specular_scale_RTbuffer, materials.specular_scale);
1543 }
1544}
1545
1546void OptiX6Backend::sourcesToBuffers(const std::vector<RayTracingSource> &sources) {
1547 // Convert source data to OptiX buffers
1548
1549 if (sources.empty()) {
1550 return;
1551 }
1552
1553 std::vector<helios::vec3> positions;
1554 std::vector<helios::vec2> widths;
1555 std::vector<helios::vec3> rotations;
1556 std::vector<uint> types;
1557 std::vector<float> fluxes;
1558 std::vector<float> fluxes_cam;
1559
1560 for (const auto &source: sources) {
1561 positions.push_back(source.position);
1562 widths.push_back(source.width);
1563 rotations.push_back(source.rotation);
1564 types.push_back(source.type);
1565
1566 // Flatten flux arrays
1567 for (float flux: source.fluxes) {
1568 fluxes.push_back(flux);
1569 }
1570 for (float flux: source.fluxes_cam) {
1571 fluxes_cam.push_back(flux);
1572 }
1573 }
1574
1575 initializeBuffer1Dfloat3(source_positions_RTbuffer, positions);
1576 initializeBuffer1Dfloat2(source_widths_RTbuffer, widths);
1577 initializeBuffer1Dfloat3(source_rotations_RTbuffer, rotations);
1578 initializeBuffer1Dui(source_types_RTbuffer, types);
1579 initializeBuffer1Df(source_fluxes_RTbuffer, fluxes);
1580 initializeBuffer1Df(source_fluxes_cam_RTbuffer, fluxes_cam);
1581}
1582
1583void OptiX6Backend::diffuseToBuffers(const std::vector<float> &flux, const std::vector<float> &extinction, const std::vector<helios::vec3> &peak_dir, const std::vector<float> &dist_norm, const std::vector<float> &sky_energy) {
1584 // Upload diffuse radiation parameters
1585
1586 if (!flux.empty()) {
1587 initializeBuffer1Df(diffuse_flux_RTbuffer, flux);
1588 }
1589
1590 if (!extinction.empty()) {
1591 initializeBuffer1Df(diffuse_extinction_RTbuffer, extinction);
1592 }
1593
1594 if (!peak_dir.empty()) {
1595 initializeBuffer1Dfloat3(diffuse_peak_dir_RTbuffer, peak_dir);
1596 }
1597
1598 if (!dist_norm.empty()) {
1599 initializeBuffer1Df(diffuse_dist_norm_RTbuffer, dist_norm);
1600 }
1601
1602 if (!sky_energy.empty()) {
1603 initializeBuffer1Df(Rsky_RTbuffer, sky_energy);
1604 }
1605}
1606
1607void OptiX6Backend::skyModelToBuffers(const std::vector<helios::vec4> &sky_radiance_params, const std::vector<float> &camera_sky_radiance, const helios::vec3 &sun_direction, const std::vector<float> &solar_disk_radiance, float solar_disk_cos_angle) {
1608 // Upload sky model parameters for camera rendering
1609
1610 if (!sky_radiance_params.empty()) {
1611 initializeBuffer1Dfloat4(sky_radiance_params_RTbuffer, sky_radiance_params);
1612 }
1613
1614 if (!camera_sky_radiance.empty()) {
1615 initializeBuffer1Df(camera_sky_radiance_RTbuffer, camera_sky_radiance);
1616 }
1617
1618 // Set sun direction variable
1619 RT_CHECK_ERROR(rtVariableSet3f(sun_direction_RTvariable, sun_direction.x, sun_direction.y, sun_direction.z));
1620
1621 if (!solar_disk_radiance.empty()) {
1622 initializeBuffer1Df(solar_disk_radiance_RTbuffer, solar_disk_radiance);
1623 }
1624
1625 // Set solar disk angular size
1626 RT_CHECK_ERROR(rtVariableSet1f(solar_disk_cos_angle_RTvariable, solar_disk_cos_angle));
1627}
1628
1629void OptiX6Backend::launchParamsToVariables(const RayTracingLaunchParams &params) {
1630 // Set common launch parameters as OptiX variables
1631
1632 RT_CHECK_ERROR(rtVariableSet1ui(random_seed_RTvariable, params.random_seed));
1633 RT_CHECK_ERROR(rtVariableSet1ui(launch_offset_RTvariable, params.launch_offset));
1634 RT_CHECK_ERROR(rtVariableSet1ui(Nbands_global_RTvariable, params.num_bands_global));
1635 RT_CHECK_ERROR(rtVariableSet1ui(Nbands_launch_RTvariable, params.num_bands_launch));
1636 RT_CHECK_ERROR(rtVariableSet1ui(launch_face_RTvariable, params.launch_face));
1637 RT_CHECK_ERROR(rtVariableSet1ui(scattering_iteration_RTvariable, params.scattering_iteration));
1638
1639 // Band launch flags
1640 if (!params.band_launch_flag.empty()) {
1641 initializeBuffer1Dbool(band_launch_flag_RTbuffer, params.band_launch_flag);
1642 }
1643
1644 // Specular reflection flag
1645 uint specular_enabled = params.specular_reflection_enabled ? 1 : 0;
1646 RT_CHECK_ERROR(rtVariableSet1ui(specular_reflection_enabled_RTvariable, specular_enabled));
1647}
1648
1649void OptiX6Backend::buffersToResults(RayTracingResults &results) {
1650 // Extract radiation results from OptiX buffers
1651
1652 results.radiation_in = getOptiXbufferData(radiation_in_RTbuffer);
1653 results.radiation_out_top = getOptiXbufferData(radiation_out_top_RTbuffer);
1654 results.radiation_out_bottom = getOptiXbufferData(radiation_out_bottom_RTbuffer);
1655 results.scatter_buff_top = getOptiXbufferData(scatter_buff_top_RTbuffer);
1656 results.scatter_buff_bottom = getOptiXbufferData(scatter_buff_bottom_RTbuffer);
1657
1658 // Extract camera scatter buffers (if cameras present)
1659 // Use current_camera_count since results.num_cameras not set yet
1660 if (current_camera_count > 0) {
1661 results.scatter_buff_top_cam = getOptiXbufferData(scatter_buff_top_cam_RTbuffer);
1662 results.scatter_buff_bottom_cam = getOptiXbufferData(scatter_buff_bottom_cam_RTbuffer);
1663 }
1664
1665 results.radiation_specular = getOptiXbufferData(radiation_specular_RTbuffer);
1666 results.sky_energy = getOptiXbufferData(Rsky_RTbuffer);
1667}