PROGRESS : OptiXTest split in two, added Opticks/GGeo conversion
NEXT STEPS:
LONGTERM POSSIBILITY : Populate CSGFoundry model direct from Geant4 geometry ? [Disruptive]
CSGFoundry : Instances + Solids
struct CSGFoundry
{
...
void upload();
...
std::vector<CSGSolid> solid ;
std::vector<CSGPrim> prim ;
std::vector<CSGNode> node ;
std::vector<float4> plan ;
std::vector<qat4> tran ;
std::vector<qat4> itra ;
std::vector<qat4> inst ;// instance transforms
CSGPrim* d_prim ;
CSGNode* d_node ;
float4* d_plan ;
qat4* d_itra ; // inverse CSG transforms
};
All solids+constituents created via Foundry (ref by index)
https://github.com/simoncblyth/CSG/blob/main/CSGFoundry.h https://github.com/simoncblyth/CSG/blob/main/qat4.h
geo_OptiX6Test.cu
24 rtBufferprim_buffer; // geometry level context 25 28 rtBuffernode_buffer; // global context 29 rtBuffer .. 40 RT_PROGRAM void intersect(int primIdx) 41 { 42 const CSGPrim* prim = &prim_buffer[primIdx] ; 43 int nodeOffset = prim->nodeOffset() ; 44 int numNode = prim->numNode() ; 45 const CSGNode* node = &node_buffer[nodeOffset] ; 46 const float4* plan = &plan_buffer[0] ; 47 const qat4* itra = &itra_buffer[0] ; 48 49 float4 isect ; 50 if(intersect_prim(isect, numNode, node, plan, itra, ray.tmin , ray.origin, ray.direction )) 51 { 52 if(rtPotentialIntersection(isect.w)) 53 { 55 shading_normal = make_float3( isect ); 57 rtReportIntersection(0); 58 } 59 } 60 }itra_buffer; 30 rtBuffer plan_buffer;
OptiX7Test.cu
150 extern "C" __global__ void __intersection__is()
151 {
152 HitGroupData* hg = (HitGroupData*)optixGetSbtDataPointer();
153 int numNode = hg->numNode ;
154 int nodeOffset = hg->nodeOffset ;
155
156 const CSGNode* node = params.node + nodeOffset ;
157 const float4* plan = params.plan ;
158 const qat4* itra = params.itra ;
159
160 const float t_min = optixGetRayTmin() ;
161 const float3 ray_origin = optixGetObjectRayOrigin();
162 const float3 ray_direction = optixGetObjectRayDirection();
163
164 float4 isect ;
165 if(intersect_prim(isect, numNode, node, plan, itra,
t_min , ray_origin, ray_direction ))
166 {
...
175 optixReportIntersection( isect.w, hitKind, a0, a1, a2, a3 );
176 }
177 }
Minimize code split : 7, pre-7, CPU testing : same intersect_prim
Converter : GGeo/GParts -> CSG
11 struct Converter 12 { 13 CSGFoundry* foundry ; 14 const GGeo* ggeo ; 16 float splay ; 17 18 Converter(CSGFoundry* foundry, const GGeo* ggeo ) ; 19 20 void convert(int repeatIdx, int primIdx, int partIdxRel ); 21 void convert_(); 22 23 CSGSolid* convert_(unsigned repeatIdx ); 24 void addInstances(unsigned repeatIdx ); 25 26 CSGPrim* convert_(const GParts* comp, unsigned primIdx ); 27 CSGNode* convert_(const GParts* comp, unsigned primIdx, unsigned partIdxRel ); 28 };
06 #include "CSGFoundry.h" 07 #include "CSGOptiX.h" 09 #include "Converter.h" 10 11 int main(int argc, char** argv) 12 { 13 int repeatIdx = argc > 1 ? atoi(argv[1]) : 0 ; .. 19 OPTICKS_LOG(argc, argv); 20 Opticks ok(argc, argv); 21 ok.configure(); 22 24 GGeo* ggeo = GGeo::Load(&ok); 26 27 CSGFoundry foundry ; 28 Converter conv(&foundry, ggeo, dump) ; 29 conv.convert(repeatIdx, primIdx, partIdxRel); 30 31 CSGOptiX cx(&foundry); 34 foundry.upload(); // uploads nodes, planes, transforms .. 52 cx.setCE(ce, tmin, tmax); 53 cx.render( tspec ); 55 return 0 ; 56 }
Converter debugging
One JUNO solid (fastener) -> blank
Proof that relative trans not applied
OptiX supports multiple instance levels : IAS->IAS->GAS BUT: Simple two-level is faster : works in hardware RT Cores
SBT : Shader Binding Table
Flexibly binds together:
Hidden in OptiX 1-6 APIs
Optimization : deciding where to draw lines between:
Where those lines are drawn defines the AS
https://developer.nvidia.com/blog/best-practices-using-nvidia-rtx-ray-tracing/
https://developer.nvidia.com/blog/best-practices-using-nvidia-rtx-ray-tracing/
Advantages apply equally to acceleration structures
Equivalent Intersects -> same t
Local Frame Advantages
Geometry Instancing Advantages
Requirements
Outside/Inside Unions
dot(normal,rayDir) -> Enter/Exit
Complete Binary Tree, pick between pairs of nearest intersects:
UNION tA < tB | Enter B | Exit B | Miss B |
---|---|---|---|
Enter A | ReturnA | LoopA | ReturnA |
Exit A | ReturnA | ReturnB | ReturnA |
Miss A | ReturnB | ReturnB | ReturnMiss |
CSG Binary Tree
Primitives combined via binary operators
Simple by construction definition, implicit geometry.
CSG expressions
3D Parametric Ray : ray(t) = r0 + t rDir
Ray Geometry Intersection
How to pick exactly ?
In/On/Out transitions
Classical Roth diagram approach
Computational requirements:
BUT : High performance on GPU requires:
Classical approach not appropriate on GPU
Bit Twiddling Navigation
Geant4 solid -> CSG binary tree (leaf primitives, non-leaf operators, 4x4 transforms on any node)
Serialize to complete binary tree buffer:
Height 3 complete binary tree with level order indices:
depth elevation 1 0 3 10 11 1 2 100 101 110 111 2 1 1000 1001 1010 1011 1100 1101 1110 1111 3 0
postorder_next(i,elevation) = i & 1 ? i >> 1 : (i << elevation) + (1 << elevation) ; // from pattern of bits
Postorder tree traverse visits all nodes, starting from leftmost, such that children are visited prior to their parents.
fullTree = PACK( 1 << height, 1 >> 1 ) // leftmost, parent_of_root(=0) tranche.push(fullTree, ray.tmin) while (!tranche.empty) // stack of begin/end indices { begin, end, tmin <- tranche.pop ; node <- begin ; while( node != end ) // over tranche of postorder traversal { elevation = height - TREE_DEPTH(node) ; if(is_primitive(node)){ isect <- intersect_primitive(node, tmin) ; csg.push(isect) } else{ i_left, i_right = csg.pop, csg.pop // csg stack of intersect normals, t l_state = CLASSIFY(i_left, ray.direction, tmin) r_state = CLASSIFY(i_right, ray.direction, tmin) action = LUT(operator(node), leftIsCloser)(l_state, r_state) if( action is ReturnLeft/Right) csg.push(i_left or i_right) else if( action is LoopLeft/Right) { left = 2*node ; right = 2*node + 1 ; endTranche = PACK( node, end ); leftTranche = PACK( left << (elevation-1), right << (elevation-1) ) rightTranche = PACK( right << (elevation-1), node ) loopTranche = action ? leftTranche : rightTranche tranche.push(endTranche, tmin) tranche.push(loopTranche, tminAdvanced ) // subtree re-traversal with changed tmin break ; // to next tranche } } node <- postorder_next(node, elevation) // bit twiddling postorder } } isect = csg.pop(); // winning intersect
https://bitbucket.org/simoncblyth/opticks/src/tip/optixrap/cu/csg_intersect_boolean.h
Positive form CSG Trees
Apply deMorgan pushing negations down tree
End with only UNION, INTERSECT operators, and some complemented leaves.
COMMUTATIVE -> easily rearranged
1st step to allow balancing : Positivize : remove CSG difference di operators
... ... un cy un cy un cy un cy un cy di cy cy cy
... ... un cy un cy un cy un cy un cy in cy cy !cy