OptiX - optix7course Example 09 분석

선비Sunbei·2023년 9월 3일
0

OptiX

목록 보기
23/25
post-thumbnail
https://github.com/ingowald/optix7course

이전과 마찬가지로 변경된 점만 작성한다.

//  LaunchParams.h
amespace osc {
using namespace gdt;

// for this simple example, we have a single ray type
enum { RADIANCE_RAY_TYPE=0, SHADOW_RAY_TYPE, RAY_TYPE_COUNT };
...
}
// SampleRender.cpp
  /*! does all setup for the miss program(s) we are going to use */
  void SampleRenderer::createMissPrograms()
  {
    // we do a single ray gen program in this example:
    missPGs.resize(RAY_TYPE_COUNT);

    char log[2048];
    size_t sizeof_log = sizeof( log );

    OptixProgramGroupOptions pgOptions = {};
    OptixProgramGroupDesc pgDesc    = {};
    pgDesc.kind                     = OPTIX_PROGRAM_GROUP_KIND_MISS;
    pgDesc.miss.module              = module ;           

    // ------------------------------------------------------------------
    // radiance rays
    // ------------------------------------------------------------------
    pgDesc.miss.entryFunctionName = "__miss__radiance";

    OPTIX_CHECK(optixProgramGroupCreate(optixContext,
                                        &pgDesc,
                                        1,
                                        &pgOptions,
                                        log,&sizeof_log,
                                        &missPGs[RADIANCE_RAY_TYPE]
                                        ));
    if (sizeof_log > 1) PRINT(log);

    // ------------------------------------------------------------------
    // shadow rays
    // ------------------------------------------------------------------
    pgDesc.miss.entryFunctionName = "__miss__shadow";

    OPTIX_CHECK(optixProgramGroupCreate(optixContext,
                                        &pgDesc,
                                        1,
                                        &pgOptions,
                                        log,&sizeof_log,
                                        &missPGs[SHADOW_RAY_TYPE]
                                        ));
    if (sizeof_log > 1) PRINT(log);
  }

MissProgram을 2개를 만든다.
각각 ray-gen program에서 발사한 ray가 miss 됐을 시 리턴되는 __miss__radiance 에 대한 Miss program과, shadow-ray로 발사 되었을 때 miss 됐을 시 리턴되는 __miss_shadow에 대한 miss program이다.

// SampleRenderer.cpp
/*! does all setup for the hitgroup program(s) we are going to use */
  void SampleRenderer::createHitgroupPrograms()
  {
    // for this simple example, we set up a single hit group
    hitgroupPGs.resize(RAY_TYPE_COUNT);

    char log[2048];
    size_t sizeof_log = sizeof( log );
      
    OptixProgramGroupOptions pgOptions  = {};
    OptixProgramGroupDesc    pgDesc     = {};
    pgDesc.kind                         = OPTIX_PROGRAM_GROUP_KIND_HITGROUP;
    pgDesc.hitgroup.moduleCH            = module;           
    pgDesc.hitgroup.moduleAH            = module;           

    // -------------------------------------------------------
    // radiance rays
    // -------------------------------------------------------
    pgDesc.hitgroup.entryFunctionNameCH = "__closesthit__radiance";
    pgDesc.hitgroup.entryFunctionNameAH = "__anyhit__radiance";

    OPTIX_CHECK(optixProgramGroupCreate(optixContext,
                                        &pgDesc,
                                        1,
                                        &pgOptions,
                                        log,&sizeof_log,
                                        &hitgroupPGs[RADIANCE_RAY_TYPE]
                                        ));
    if (sizeof_log > 1) PRINT(log);

    // -------------------------------------------------------
    // shadow rays: technically we don't need this hit group,
    // since we just use the miss shader to check if we were not
    // in shadow
    // -------------------------------------------------------
    pgDesc.hitgroup.entryFunctionNameCH = "__closesthit__shadow";
    pgDesc.hitgroup.entryFunctionNameAH = "__anyhit__shadow";

    OPTIX_CHECK(optixProgramGroupCreate(optixContext,
                                        &pgDesc,
                                        1,
                                        &pgOptions,
                                        log,&sizeof_log,
                                        &hitgroupPGs[SHADOW_RAY_TYPE]
                                        ));
    if (sizeof_log > 1) PRINT(log);
  }

miss program과 마찬가지로 shadow ray와 primary ray에 대한 program group을 생성한다.

// SampleRender.cpp
  /*! constructs the shader binding table */
  void SampleRenderer::buildSBT()
  {
    // ------------------------------------------------------------------
    // build raygen records
    // ------------------------------------------------------------------
    std::vector<RaygenRecord> raygenRecords;
    for (int i=0;i<raygenPGs.size();i++) {
      RaygenRecord rec;
      OPTIX_CHECK(optixSbtRecordPackHeader(raygenPGs[i],&rec));
      rec.data = nullptr; /* for now ... */
      raygenRecords.push_back(rec);
    }
    raygenRecordsBuffer.alloc_and_upload(raygenRecords);
    sbt.raygenRecord = raygenRecordsBuffer.d_pointer();

    // ------------------------------------------------------------------
    // build miss records
    // ------------------------------------------------------------------
    std::vector<MissRecord> missRecords;
    for (int i=0;i<missPGs.size();i++) {
      MissRecord rec;
      OPTIX_CHECK(optixSbtRecordPackHeader(missPGs[i],&rec));
      rec.data = nullptr; /* for now ... */
      missRecords.push_back(rec);
    }
    missRecordsBuffer.alloc_and_upload(missRecords);
    sbt.missRecordBase          = missRecordsBuffer.d_pointer();
    sbt.missRecordStrideInBytes = sizeof(MissRecord);
    sbt.missRecordCount         = (int)missRecords.size();

    // ------------------------------------------------------------------
    // build hitgroup records
    // ------------------------------------------------------------------
    int numObjects = (int)model->meshes.size();
    std::vector<HitgroupRecord> hitgroupRecords;
    for (int meshID=0;meshID<numObjects;meshID++) {
      for (int rayID=0;rayID<RAY_TYPE_COUNT;rayID++) {
        auto mesh = model->meshes[meshID];
      
        HitgroupRecord rec;
        OPTIX_CHECK(optixSbtRecordPackHeader(hitgroupPGs[rayID],&rec));
        rec.data.color   = mesh->diffuse;
        if (mesh->diffuseTextureID >= 0) {
          rec.data.hasTexture = true;
          rec.data.texture    = textureObjects[mesh->diffuseTextureID];
        } else {
          rec.data.hasTexture = false;
        }
        rec.data.index    = (vec3i*)indexBuffer[meshID].d_pointer();
        rec.data.vertex   = (vec3f*)vertexBuffer[meshID].d_pointer();
        rec.data.normal   = (vec3f*)normalBuffer[meshID].d_pointer();
        rec.data.texcoord = (vec2f*)texcoordBuffer[meshID].d_pointer();
        hitgroupRecords.push_back(rec);
      }
    }
    hitgroupRecordsBuffer.alloc_and_upload(hitgroupRecords);
    sbt.hitgroupRecordBase          = hitgroupRecordsBuffer.d_pointer();
    sbt.hitgroupRecordStrideInBytes = sizeof(HitgroupRecord);
    sbt.hitgroupRecordCount         = (int)hitgroupRecords.size();
  }

ray-gen은 하나 이므로 그대로이고, miss record는 miss program group이 2개로 되어서 2개의 miss record를 반환한다. 코드 상으로는 변화가 없다.

hit program group 또한 기존에 model의 mesh 수 * 2(primary ray, shadow ray) 만큼 만들어야 한다. 여기서 record 를 넣는 순서가 중요하다.
해당 코드에서는 for(object count) { for(ray count) }이다.
이는 sbt record data를 참조하는 순서에 영향을 주기 때문이다.

// devicePrograms.cu
  extern "C" __global__ void __miss__radiance()
  {
    vec3f &prd = *(vec3f*)getPRD<vec3f>();
    // set to constant white as background color
    prd = vec3f(1.f);
  }

  extern "C" __global__ void __miss__shadow()
  {
    // we didn't hit anything, so the light is visible
    vec3f &prd = *(vec3f*)getPRD<vec3f>();
    prd = vec3f(1.f);
  }

miss program은 그대로 하얀색을 반환한다.

// devicePrograms.cu
  extern "C" __global__ void __anyhit__radiance()
  { /*! for this simple example, this will remain empty */ }

  extern "C" __global__ void __anyhit__shadow()
  { /*! not going to be used */ }

마찬가지로 any-hit에 대한 작업은 이 예제에서는 하지 않는다.

// devicePrograms.cu
 extern "C" __global__ void __closesthit__shadow()
  {
    /* not going to be used ... */
  }
  
  extern "C" __global__ void __closesthit__radiance()
  {
    const TriangleMeshSBTData &sbtData
      = *(const TriangleMeshSBTData*)optixGetSbtDataPointer();
    
    // ------------------------------------------------------------------
    // gather some basic hit information
    // ------------------------------------------------------------------
    const int   primID = optixGetPrimitiveIndex();
    const vec3i index  = sbtData.index[primID];
    const float u = optixGetTriangleBarycentrics().x;
    const float v = optixGetTriangleBarycentrics().y;

    // ------------------------------------------------------------------
    // compute normal, using either shading normal (if avail), or
    // geometry normal (fallback)
    // ------------------------------------------------------------------
    const vec3f &A     = sbtData.vertex[index.x];
    const vec3f &B     = sbtData.vertex[index.y];
    const vec3f &C     = sbtData.vertex[index.z];
    vec3f Ng = cross(B-A,C-A);
    vec3f Ns = (sbtData.normal)
      ? ((1.f-u-v) * sbtData.normal[index.x]
         +       u * sbtData.normal[index.y]
         +       v * sbtData.normal[index.z])
      : Ng;
    
    // ------------------------------------------------------------------
    // face-forward and normalize normals
    // ------------------------------------------------------------------
    const vec3f rayDir = optixGetWorldRayDirection();
    
    if (dot(rayDir,Ng) > 0.f) Ng = -Ng;
    Ng = normalize(Ng);
    
    if (dot(Ng,Ns) < 0.f)
      Ns -= 2.f*dot(Ng,Ns)*Ng;
    Ns = normalize(Ns);

    // ------------------------------------------------------------------
    // compute diffuse material color, including diffuse texture, if
    // available
    // ------------------------------------------------------------------
    vec3f diffuseColor = sbtData.color;
    if (sbtData.hasTexture && sbtData.texcoord) {
      const vec2f tc
        = (1.f-u-v) * sbtData.texcoord[index.x]
        +         u * sbtData.texcoord[index.y]
        +         v * sbtData.texcoord[index.z];
      
      vec4f fromTexture = tex2D<float4>(sbtData.texture,tc.x,tc.y);
      diffuseColor *= (vec3f)fromTexture;
    }
    
    // ------------------------------------------------------------------
    // compute shadow
    // ------------------------------------------------------------------
    const vec3f surfPos
      = (1.f-u-v) * sbtData.vertex[index.x]
      +         u * sbtData.vertex[index.y]
      +         v * sbtData.vertex[index.z];
    const vec3f lightPos(-907.108f, 2205.875f, -400.0267f);
    const vec3f lightDir = lightPos - surfPos;
    
    // trace shadow ray:
    vec3f lightVisibility = 0.f;
    // the values we store the PRD pointer in:
    uint32_t u0, u1;
    packPointer( &lightVisibility, u0, u1 );
    optixTrace(optixLaunchParams.traversable,
               surfPos + 1e-3f * Ng,
               lightDir,
               1e-3f,      // tmin
               1.f-1e-3f,  // tmax
               0.0f,       // rayTime
               OptixVisibilityMask( 255 ),
               // For shadow rays: skip any/closest hit shaders and terminate on first
               // intersection with anything. The miss shader is used to mark if the
               // light was visible.
               OPTIX_RAY_FLAG_DISABLE_ANYHIT
               | OPTIX_RAY_FLAG_TERMINATE_ON_FIRST_HIT
               | OPTIX_RAY_FLAG_DISABLE_CLOSESTHIT,
               SHADOW_RAY_TYPE,            // SBT offset
               RAY_TYPE_COUNT,               // SBT stride
               SHADOW_RAY_TYPE,            // missSBTIndex 
               u0, u1 );

    // ------------------------------------------------------------------
    // final shading: a bit of ambient, a bit of directional ambient,
    // and directional component based on shadowing
    // ------------------------------------------------------------------
    const float cosDN
      = 0.1f
      + .8f*fabsf(dot(rayDir,Ns));
    
    vec3f &prd = *(vec3f*)getPRD<vec3f>();
    prd = (.1f + (.2f + .8f*lightVisibility) * cosDN) * diffuseColor;
  }

shadow ray에 대한 closest hit은 수행하지 않는다.

face-forward and normalize normals 부분부터 보겠다.
optixGetWorldRayDirection()으로 ray direction vector를 얻는다.

ray direction과 vertex cross normal(Ng) 와의 dot product가 90도 미만인 경우 Ng를 -1을 곱해서 Ng가 카메라를 바라보는 방향으로 만들고 정규화를 진행한다.

만약 Ng와 Ns(sbtData에 들어있는 normal 값)가 90도 이상 차이날 경우 Ns vector를 Ng 벡터를 중심으로 반사시키고 정규화를 한다.

diffuseColor에 texture의 색을 곱하여 diffuse material의 최종 색을 정한다.

lightPos는 임의의 값으로 설정했다.
optixTrace로 부딪힌 지점으로부터 위쪽으로 조금 띄운 후 ray를 빛의 방향으로 발사한다. ANY-HIT에 대한 progrma에 대한 ray는 끄고, 첫 번째 hit에 대해서 ray를 종료하고, closest-hit도 종료한다.

miss program이 되면 lightVisibility는 vec3f(1.f)로 초기화 될 것이고 hit 시 lightVisibility = vec3f(0) 으로 끝난다.

여기서 SBT offset와 missSBTIndex가 SHADOW_RAY_TYPE으로 1이다.
이는 record에서 for(object count){ for(ray count) }로 한 이유다.
sbt stride는 object 간의 간격(stride)을 RAY_TYPE_COUNT로 2로 하여 다음 오브젝트로 넘어간다.

cosDN의 0.1은 ambient color이고, .8f*fabsf(dot(rayDir,Ns))는 diffuse color이다.

prd = (.1f + (.2f + .8f*lightVisibility) * cosDN) * diffuseColor;

diffuseColor는 diffuse material 값 Texture 값이고,
.1f은 ambient color에 cosDN은 최대 0.9가 나오고 곱해지는 (.2f + .8f
lightVisibility)는 shadow ray가 된다.

0개의 댓글