https://raytracing-docs.nvidia.com/optix7/guide/index.html#curves#curves-and-spheres
OptiX를 사용한 ray tracing curve 또는 sphere tracing, triangle의 절차와 유사하다. curve, sphere, triangle의 차이점은 다음과 같다.
triangle build input의 index buffer는 선택 사항이다. curve build input에서는 필수이다. sphere build input는 존재하지 않는다.
각 curve build input은 단 하나의 SBT record만 참조한다. triangle이나 sphere와 달리 primitive별 SBT index가 없다. material당 하나씩 여러 build input을 사용하여 동일한 BVH의 Curve에 여러 material을 사용할 수 있다. (SBT record가 하나뿐이므로 OptixGeometryFlags는 정수의 배열이 아닌 하나의 정수로만 지정된다.)
Curve 또는 Sphere에 대한 사전 변환 필드는 없다. 만약 있다면, ununiform scale, sphere은 instance transform과 prior trainsform에서 다른 결과를 산출할 것이다. Curve의 ununiform instance transform은 타원형 단면을 생성하지만, prior transform은 여전히 원형 단면을 갖는다.
Curve에 대한 각 SBT record에는 Curve에 내장된 intersection program을 사용하는 hit group이 필요하다. OptixProgramGroupHitGroup에서 moduleIS의 경우 optixBuiltinISModuleGet()이 반환환 모듈을 사용해야 하며, entryFunctionNameIS의 경우 nullptr을 사용해야 한다. 이는 sphere에도 적용된다.
Curve 또는 Sphere는 pipeline에서 명시적으로 활성화해야 렌더링할 수 있으며, triangle과 custom primitive는 기본적으로 활성화되어 있다. OptixPipelineCompileOptions::usesPrimitiveTypeFlags에서 optixPrimitveTypeFlags 관련 비트를 설정하여 활성화 할 수 있다.
OptiX는 curve segment를 여러 하위 세그먼트로 분할하고 하위 세그먼트를 개별적으로 바인딩할 수 있다. 이렇게 하면 성능이 빨라지지만 메모리를 더 많이 사용한다. 분할은 OptixBuildFlag를 통해 제어할 수 있으며 기본값보다 높은 분할 계수를 원할 경우 OPTIX_BUILD_FLAG_PREFER_FAST_TRACE를, 낮은 분할 계수를 원할 경우 OPTIX_BUILD_FLAG_PREFER_FAST_BUILD를 사용할 수 있다.
분할은 동일한 primitive(동일한 curve segment)가 광선에 여러 번 맞을 수 있음을 의미한다. OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL이 설정된 geometrt는 분할되지 않으며, 이 경우 각 세그먼트는 한 번만 hit할 수 있지만 여러 세그먼트로 구성된 긴 curve strand는 여전히 두 번 이상 hit할 수 있다.
hit program에서 리본이 아닌 curve hit는 단일 attribute인 교차점에 해당하는 세그먼트 내의 curve parameter("u")를 제공하며, optixGetCurveParameter()가 반환된다. endcap에 닿을 때만 반환되는 "u" 파라미터는 정확히 0.0 또는 1.0이다. 리본 hit의 경우 optixGetRibbonParameters()에서 두 가지 속성을 제공한다. Curve 축을 따라 매개변수 "u"와 함께 너비를 따라 매개변수 "v"가 반환된다. 파라미터 "v"의 범위는 -1.0~1.0이다. 모든 hit program과 마찬가지로 optixGetRayTmax()는 적중점을 계산할 수 있는 ray parameter("t")를 반환하고, optixGetPrimitiveIndex()는 세그먼트 primitive index를 반환한다. 성능을 극대화하기 위해 다른 geometry 속성은 전달되거나 미리 계산되지 않는다. 대신 program은 vertex data를 사용하여 필요한 curve geometry를 계산해야 한다. 리본의 경우, 기본 index와 리본 파라미터 optixGetRibbonNormal()에 전달하여 기하 노멀을 계산할 수 있다.
optixGetRibbonNormal
OptiX shader(예: closest hit program)에서 vertex data를 사용할 수 있도록 하려면 GAS build input에 OPTIX_BUILD_FLAG_ALLOW_RANDOM_VERTEX_ACCES를 포함해야 한다. 세그먼트 vertex 위치 및 반지름 값을 가져오려면 curve 유형에 적합한 함수에 primitive index를 전달한다.
optixGetLinearCurveVertexData
optixGetQuadraticBSplineVertexData
optixGetCubicBSplineVertexData
optixGetCatmullRomVertexData
optixGetCubicBezierVertexData
optixGetRibbonVertexData
커브 점과 반지름을 보간하고 접선, 노멀, 도함수를 계산하는 샘플 코드가 제공된다(SDK/cuda/curve.h). 단일 hit program은 optixGetPtimitiveType() 값을 확인하여 여러 curve primitive 유형을 처리할 수 있다. 예를 들어 optixHair 코드 샘플의 optixHair.cu를 참조할 수 있다.
optixGetCurveParameter 및 optixGetRiboonParameters 함ㅅ수는 curve의 단일 다항식 세그먼트 기준으로 "u" 파라미터 값을 반환한다. 필요한 경우 hit program에서 이를 전체 다중 세그먼트 strand에 대한 파라미터 값에 매핑할 수 있다. 예를 들어 머리카락의 뿌리와 끝에 지정된 두 가지 색상을 보간하는 데 사용할 수 있다. 이 세그먼트 간 매핑은 Application의 책임이며, 구현 예제는 optixHair code sample을 참조하면 된다.
sphere에 대한 hit program은 ray와 sphere의 최대 두 개의 교차를 ray를 따라 정렬하여 알려준다. 두 번째 교차는 구체 적중의 단일 속성, 즉 두 번째 적중이 존재하면 구체와 두 번째 적중의 광선 매개변수, 그렇지 않으면 0으로 표시된다. optixGetRayTmax()는 첫 번째 적중점을 계산할 수 있는 ray 매개변수 t를 적중 유형과 함께 반환한다.
optixGetPrimitiveIndex() 함수는 구의 primitive index를 반환한다. curve와 마찬가지로 다른 geometry attribute는 전달되거나 미리 계산되징 않는다. 대신 program은 정점 데이터를 사용하여 필요한 sphere geometry(normal 등)을 계산해야 한다.
optix shader(ex> closest hit program)에서 vetex 데이터를 사용할 수 있도록 하려면 GAS의 빌드 flag에 OPTIX_BUILD_FLAG_ALLOW_RANDOM_VERTEX_ACCESS를 포함해야 한다. 이 flag를 포함하면 optixGetSphereData 함수가 sphere의 정점 위치와 반지름 값을 획득할 수 있다.
B-spline curve는 일반적으로 제어점을 보간하지 않는다.(즉, 닿지 않는다). 특히 베지어 곡선과 달리 첫 번째와 마지막 제어점까지 닿지 않는다.
원하는 경우 Application은 각 끝에 제어점을 추가하여 이러한 점을 보간하도록 제어점 sequence를 수정할 수 있다.
Universal Scene 문서에 따라 이러한 팬텀 포인트를 phantom point라고 부르며, 그 결과 pinned curve가 생성된다. phantom point는 endpoint를 통해 그 앞의 포인트를 반영하여 구성한다. 제어점이(p1,p2,...,pn) 주어지면 phantom point는 다음과 같이 기하학적으로 정의할 수 있다.
끝점을 반복하여 보간 할 수도 있다. qubic b-spline curve의 경우 3번 반복되는 제어점이 보간된다. optix에서도 이를 허용하지만 phantom point 방식이 더 안정성을 보인다.
추가 포인트는 Application에서 build input에 추가해야 한다. optix 자체에서는 추가되지 않는다.
curve primitive의 cube는 속이 비어있는 것으로 간주되며, 뒷면은 컬링된다. 따라서 ray가 curve primitive 내부에서 시작하면 해당 primitive는 닿지 않는다. 이는 보조 ray 및 transparent에 편리하다 그러나 아래의 제한 사항을 참고해야 한다.
spher의 뒷면은 culling되지 않는다. ray는 구체 내부에서 시작할 수 있다. 이 경우 보고된 (단일) hit는 뒷면 hit가 된다. 어던 면이 부딪혔는지 판단하기 위해 optixIsBackFaceHit 또는 optixIsFrontFaceHit 함수를 사용할 수 있다. 고보 ray의 경우 backface hit를 무시하는 것이 유용할 수 있다.
curve 및 curve hit program을 사용할 때 주의사항이 몇 가지 있다.
Multiple hits
단일 curve segment와 두 번 이상 교차하는 광선의 경우 optix는 모든 교차점이 적중으로 보고되는 것이 보장되지 않는다. 특히 모든 hit program은 일반적으로 주어진 세그먼트에 대해 두 번 이상 호출되지 않는다. 투명성을 구현하기 위해 모든 hit program을 사용하는 대신 가장 가까운 적중을 사용하고 각 적중에서 연속된 ray를 쏘는 것이 더 안정적인 접근 방식이다.
Back-face culling
tube는 대부분 속이 비어 있지만, 현재 구현에서는 ray이 내부 endcap, 즉 가닥의 두 세그먼트 사이의 endcap에 부딪힐 수 있다. 폭보다 훨씬 긴 일반적인 curve의 경우 이러한 현상이 눈에 띄지 않는 경우가 많다. 내부 endcap에 부딪히는 것을 방지하려면 보조 ray가 튜브 외부에서 발사되도록 조정한다.
Triple control points
curve의 끝점의 보간하기에서 언급했듯이 OptiX에서는 제어점을 복제할 수 있지만 phantom point를 사용하는 것이 좋다. 끝 제어점을 복제하는 것은 수치적으로 어렵고(미분0) 첫 번째 세그먼트는 강제로 직선이어야 한다.
Convoluted cases
머리카락, 모피, 천 섬유 등의 curve는 일반적으로 비교적 완만한 곡률로 얇다. 특히 가까이에서 렌더링할 경우 곡률이 촘촘하거나(너비 대비), 자체 교차점이 있거나, 반경이 급변하는 curve segment를 구성할 경우 아티팩트가 나타날 수 있다. 대부분의 경우 세그먼트를 3개의 작은 세그먼트로 분할하면 이러한 문제를 해결할 수 있다.
Ribbon basis type and representation
리본은 방향이 지정된 커브로, 기저 유형이 균일한 2차 B-spline으로 제한된다. curve build input에 설명된 대로, 리본 strand의 세그먼트에는 제어점이 겹친다고 가정한다. 리본 build input의 첫 번째 인덱스는 0이어야 하며, vertex 데이터의 시작 부분에 사용되지 않은 제어점이 없어야 한다.
Ribbon with user-specified normals(orientations)
리본은 지정된 방향 없이도 사용할 수 있다. 방향은 가닥을 따라 접하는 점을 고려하여 curve 축의 모양에서 자동으로 계산된다. 잔디 모델링과 같은 많은 응용 프로그램에서 이 계산은 의도한 방향을 생성하므로 선호되는 방법이다. 또는 리본의 방향을 지정하기 위해 법선을 제공할 수 있다. 법선은 2차 B-spline의 제어점이 아니라 segment를 따라 선형적으로 보간된다. 법선이 curve geometry에 충분히 맞아야 하며, 그렇지 않으면 세그먼트 간의 연결이 매끄럽지 않을 수 있다. 직선 리본 strand의 경우 세그먼트 테두리가 매끄럽지 않을 수 있다. 이러한 문제는 strand 제어점을 약간 변형하여 해결할 수 있다.