Obj material transparency - lens.

Hi,

I have always had this problem with Optix and material transparency defined in obj files. This problem has always happened in Optix version 2, 3 and now 4 and is always changing.

If you look at this image you can see that it gets the transparency for the sunglasses lens.

https://www.sites.google.com/site/billyaraujo/_/rsrc/1472781451300/other-software/real-time-raytracing/2014-12-28%2014_52_04-Real-time%20raytracer.png

This was made using Optix 3.5.1. I changed the obj_material.cu file to include alpha. In Optix 2 I did some different method and now in 4.5 I have no idea how this should be done. Looking at OptixMesh.cpp it doesn’t seem to consider parameters “d” or “Tr”.

OptixMesh.cpp

optix::Material createOptiXMaterial(
    optix::Context         context,
    optix::Program         closest_hit,
    optix::Program         any_hit,
    const MaterialParams&  mat_params,
    bool                   use_textures
    )
{
  optix::Material mat = context->createMaterial();
  mat->setClosestHitProgram( 0u, closest_hit );             
  mat->setAnyHitProgram( 1u, any_hit ) ;    

  if( use_textures )
    mat[ "Kd_map"]->setTextureSampler( sutil::loadTexture( context, mat_params.Kd_map, optix::make_float3(mat_params.Kd) ) );
  else
    mat[ "Kd_map"]->setTextureSampler( sutil::loadTexture( context, "", optix::make_float3(mat_params.Kd) ) );

  mat[ "Kd_mapped" ]->setInt( use_textures  );
  mat[ "Kd"        ]->set3fv( mat_params.Kd );
  mat[ "Ks"        ]->set3fv( mat_params.Ks );
  mat[ "Kr"        ]->set3fv( mat_params.Kr );
  mat[ "Ka"        ]->set3fv( mat_params.Ka );
  mat[ "phong_exp" ]->setFloat( mat_params.exp );

  return mat;
}

Does anyone know how this should be done in versions >= 4.5.

obj_material.cu

/*
 * Copyright (c) 2008 - 2009 NVIDIA Corporation.  All rights reserved.
 *
 * NVIDIA Corporation and its licensors retain all intellectual property and proprietary
 * rights in and to this software, related documentation and any modifications thereto.
 * Any use, reproduction, disclosure or distribution of this software and related
 * documentation without an express license agreement from NVIDIA Corporation is strictly
 * prohibited.
 *
 * TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THIS SOFTWARE IS PROVIDED *AS IS*
 * AND NVIDIA AND ITS SUPPLIERS DISCLAIM ALL WARRANTIES, EITHER EXPRESS OR IMPLIED,
 * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE.  IN NO EVENT SHALL NVIDIA OR ITS SUPPLIERS BE LIABLE FOR ANY
 * SPECIAL, INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, WITHOUT
 * LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF
 * BUSINESS INFORMATION, OR ANY OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OF OR
 * INABILITY TO USE THIS SOFTWARE, EVEN IF NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGES
 */

#include <optix.h>
#include <optixu/optixu_math_namespace.h>
#include "commonStructs.h"

struct PerRayData_radiance
{
  float3 result;
  float importance;
  int depth;
};

struct PerRayData_shadow
{
  float3 attenuation;
};

using namespace optix;

rtDeclareVariable(float3,            ambient_light_color, , );
rtBuffer<BasicLight>                 lights;
rtDeclareVariable(int,               max_depth, , );
rtDeclareVariable(unsigned int,      radiance_ray_type, , );
rtDeclareVariable(unsigned int,      shadow_ray_type, , );
rtDeclareVariable(float,             scene_epsilon, , );
rtDeclareVariable(rtObject,          top_object, , );
rtDeclareVariable(rtObject,          top_shadower, , );

rtTextureSampler<float4, 2>   ambient_map;         // 
rtTextureSampler<float4, 2>   diffuse_map;         // Correspond to OBJ mtl params
rtTextureSampler<float4, 2>   specular_map;        // 
rtDeclareVariable(float,      phong_exp, , );          //
rtDeclareVariable(float,      alpha, , );              //
rtDeclareVariable(int,        illum, , );              //

rtDeclareVariable(optix::Ray, ray, rtCurrentRay, );
rtDeclareVariable(float, t_hit, rtIntersectionDistance, );
rtDeclareVariable(float3, texcoord, attribute texcoord, ); 
rtDeclareVariable(float3, geometric_normal, attribute geometric_normal, ); 
rtDeclareVariable(float3, shading_normal, attribute shading_normal, ); 
rtDeclareVariable(PerRayData_radiance, prd_radiance, rtPayload, );
rtDeclareVariable(PerRayData_shadow,   prd_shadow, rtPayload, );

rtDeclareVariable(float3, bg_color, , );

rtTextureSampler<float4, 2> envmap;

static __device__ __inline__ float3 exp( const float3& x )
{
  return make_float3(exp(x.x), exp(x.y), exp(x.z));
}

RT_PROGRAM void any_hit_shadow()
{

  // this material is opaque, so it fully attenuates all shadow rays
  prd_shadow.attenuation = optix::make_float3(1.0 - alpha);
  rtTerminateRay();

}

RT_PROGRAM void closest_hit_radiance()
{

  float3 direction              = ray.direction;
  float3 hit_point              = ray.origin + t_hit * ray.direction;
  float3 world_shading_normal   = normalize( rtTransformNormal( RT_OBJECT_TO_WORLD, shading_normal ) );
  float3 world_geometric_normal = normalize( rtTransformNormal( RT_OBJECT_TO_WORLD, geometric_normal ) );
  float3 ffnormal               = faceforward( world_shading_normal, -direction, world_geometric_normal );
  float3 uv                     = texcoord;

  float u = ray.direction.x;
  float v = ray.direction.y;

  float3 env_color = make_float3( tex2D(envmap, u, v) );

  float3 black = make_float3(0.0f, 0.0f, 0.0f);
  float3 reflectivity_n = make_float3(0.2f, 0.2f, 0.2f);
  float importance_cutoff = 1e-2f;

  // grab values from textures
  // support only MTL illumination modes 0-3 (Ks is for now used as reflectivity)
  // ( see http://local.wasp.uwa.edu.au/~pbourke/dataformats/mtl/ )
  float3 Kd =                       make_float3( tex2D( diffuse_map,  uv.x, uv.y ) );
  float3 Ka = (illum < 1) ? black : make_float3( tex2D( ambient_map,  uv.x, uv.y ) );
  float3 Ks = (illum < 2) ? black : make_float3( tex2D( specular_map, uv.x, uv.y ) );
  float3 Kr = make_float3(0.2f, 0.2f, 0.2f);

  // ambient contribution
  float3 result = Ks * ambient_light_color;

  if (alpha > 0.01 && alpha < 1.0)
  {

	  // intersection vectors

      float reflection = 1.0f;
      float3 cutoff_color = env_color;
      float fresnel_exponent = 3.0f;
      float fresnel_minimum = 0.1f;
      float fresnel_maximum = 1.0f;
      float refraction_index = 1.1f;
      float3 refraction_color = make_float3(1.0f - alpha);
      float3 reflection_color = make_float3(0.2f);
      int refraction_maxdepth = 100;
      int reflection_maxdepth = 100;
      float3 extinction_constant = make_float3(log(.80f), log(.89f), log(.75f));

      float3 beer_attenuation;
      if(dot(world_shading_normal, ray.direction) > 0){
        // Beer's law attenuation
        beer_attenuation = exp(extinction_constant * t_hit);
      } else {
        beer_attenuation = make_float3(1);
      }

      // refraction
      if (prd_radiance.depth < min(refraction_maxdepth, max_depth))
      {
        float3 t;                                                            // transmission direction
        if ( refract(t, direction, world_shading_normal, refraction_index) )
        {

          // check for external or internal reflection
          float cos_theta = dot(direction, world_shading_normal);
          if (cos_theta < 0.0f)
            cos_theta = -cos_theta;
          else
            cos_theta = dot(t, world_shading_normal);

          reflection = fresnel_schlick(cos_theta, fresnel_exponent, fresnel_minimum, fresnel_maximum);

          float importance = prd_radiance.importance * (1.0f-reflection) * optix::luminance( refraction_color * beer_attenuation );
          if ( importance > importance_cutoff ) {
            optix::Ray ray( hit_point, t, radiance_ray_type, scene_epsilon );
            PerRayData_radiance refr_prd;
            refr_prd.depth = prd_radiance.depth+1;
            refr_prd.importance = importance;

            rtTrace( top_object, ray, refr_prd );
            result += (1.0f - reflection) * refraction_color * refr_prd.result;
          } else 
          {
            result += (1.0f - reflection) * refraction_color * cutoff_color;
          }
        }
        // else TIR
      }

      // reflection
      if (prd_radiance.depth < min(reflection_maxdepth, max_depth))
      {
        float3 r = reflect(direction, world_shading_normal);

        float importance = prd_radiance.importance * reflection * optix::luminance( reflection_color * beer_attenuation );
        if ( importance > importance_cutoff ) {
          optix::Ray ray( hit_point, r, radiance_ray_type, scene_epsilon );
          PerRayData_radiance refl_prd;
          refl_prd.depth = prd_radiance.depth+1;
          refl_prd.importance = importance;

          rtTrace( top_object, ray, refl_prd );
          result += reflection * reflection_color * refl_prd.result;
        } else {
          result += reflection * reflection_color * cutoff_color;
        }
      }

      result = result * beer_attenuation;

  }
  else
  {
    
    // compute direct lighting
    unsigned int num_lights = lights.size();
    for(int i = 0; i < num_lights; ++i) {
    BasicLight light = lights[i];
    float Ldist = optix::length(light.pos - hit_point);
    float3 L = optix::normalize(light.pos - hit_point);
    float nDl = optix::dot( ffnormal, L);

    // cast shadow ray
    float3 light_attenuation = make_float3(static_cast<float>( nDl > 0.0f ));
    if ( nDl > 0.0f && light.casts_shadow ) 
    {
        PerRayData_shadow shadow_prd;
        shadow_prd.attenuation = make_float3(1.0f);
        optix::Ray shadow_ray = optix::make_Ray( hit_point, L, shadow_ray_type, scene_epsilon, Ldist );
        rtTrace(top_object, shadow_ray, shadow_prd);
        light_attenuation = shadow_prd.attenuation;
    }

    // If not completely shadowed, light the hit point
    if( fmaxf(light_attenuation) > 0.0f ) {
        float3 Lc = light.color * light_attenuation;

        result += Kd * nDl * Lc;

        float3 H = optix::normalize(L - ray.direction);
        float nDh = optix::dot( ffnormal, H );
        if(nDh > 0) {
        float power = pow(nDh, phong_exp);
        result += Ks * power * Lc;
        }
    }
    }

    if( fmaxf( Kr ) > 0 ) 
    {

    // ray tree attenuation
    PerRayData_radiance new_prd;
    new_prd.importance = prd_radiance.importance * optix::luminance( Kr );
    new_prd.depth = prd_radiance.depth + 1;

    // reflection ray
    if( new_prd.importance >= 0.01f && new_prd.depth <= max_depth) 
    {
        float3 R = optix::reflect( ray.direction, ffnormal );
        optix::Ray refl_ray = optix::make_Ray( hit_point, R, radiance_ray_type, scene_epsilon, RT_DEFAULT_MAX );
        rtTrace(top_object, refl_ray, new_prd);
        result += Kr * new_prd.result;
    }
    }

}

  if (alpha < 0.01)
  {
    if (result.x < 0.5 && result.y < 0.5 && result.z < 0.5)
        result *= env_color;
    else
        result = env_color;
  }

  // pass the color back up the tree
  prd_radiance.result = result;

}

The OBJ reader in OptiX 4 (SDK/sutil/tinyobjloader) reads the ‘Tr’ and ‘d’ parameters into the ‘dissolve’ field of tinyobj::material_t. We don’t do anything with the ‘dissolve’ field by default.

If you want to expose it to your custom material, you could do something like this:

  • add an ‘alpha’ field (or whatever name) to MaterialParams, defined in sutil/Mesh.h
  • set the ‘alpha’ field in loadMeshOBJ, defined in sutil/Mesh.cpp, using the ‘dissolve’ field of the tinyobj::material_t struct.
  • set up a custom material, rather than the default phong material. You can do this by setting the ‘closest_hit’ and ‘any_hit’ fields of OptiXMesh to something like what you have in obj_material above, before calling loadMesh. You could modify optixMeshViewer.cpp for example:
// in optixMeshViewer.cpp
void loadMesh( const std::string& filename )
{
    // New: read closest-hit and any-hit programs from obj_material.cu or similar file
    Program closest_hit_program = context->createProgramFromPTXFile(...);
    Program any_hit_program = ...;

    OptiXMesh mesh;
    mesh.context = context;

    // New: override phong programs
    mesh.closest_hit = closest_hit_program;
    mesh.any_hit = any_hit_program;

    // Read OBJ with modified OptiXMesh code that sets 'alpha'
    loadMesh( filename, mesh );
    ...
}

I may have missed a step somewhere, but hopefully this is enough to get you going. If you really get stuck, email optix-help, preferably with a working code sample in hand, and we can help you fix it up.

A cautionary note: the OptiX headers are very stable across releases. The SDK samples, on the other hand, can change any time. It kind of looks like you’re depending on sample code for a rendering application. Modifying a sample is a great way to learn OptiX, but you should ultimately write your own application without depending on anything in the samples directly.

Hi,

Thanks for your answer. Will try the solution provided.