RenderKit / embree

Embree ray tracing kernels repository.
Apache License 2.0
2.32k stars 383 forks source link

Rays do not hit triangle #464

Closed Raildex closed 9 months ago

Raildex commented 9 months ago

I use Embree for Lightmap Baking. I shoot rays from the world position of texels into the world. For direct Lighting, I use rtcOccludedX function family to determine Direct Directional Lighting.

I noticed that Embree does not detect hits at all.

I attached a Log that has the following scene: Two Triangles forming a quad at the bottom with Y = 0, Normal.Y = 1 Two Triangles forming a quad at the top with Y = 3, Normal.Y = -1 Sun Direction Y= -1.

To detect a hit, I check for tfar == -std::numeric_limits<float>::infinity();

While the positions from the top positions and bottom positions do not line up (due to texel imprecision), they still form triangles that should be hit.

The scene is set to RTC_SCENE_FLAG_ROBUST | RTC_SCENE_FLAG_COMPACT and the Build Quality is set to RTC_BUILD_QUALITY_HIGH Bake.log

svenwoop commented 9 months ago

Could you please modify the minimal tutorial that comes with Embree with your geometry configuration? If you can reproduce this there we can have a look what is going wrong.

Raildex commented 9 months ago

I noticed when I add a single polygon as some kind of "thickness manipulator", the rays are correctly hit grafik

Could it be because the triangles (black) are all flat?

svenwoop commented 9 months ago

Flat triangles should not be a problem. Please extract some failing configuration in the minimal tutorial for further debug.

Raildex commented 9 months ago

Here is my minimal.cpp:

// Copyright 2009-2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0

#include <embree4/rtcore.h>
#include <stdio.h>
#include <math.h>
#include <limits>
#include <stdio.h>

#if defined(_WIN32)
#  include <conio.h>
#  include <windows.h>
#endif
#include <cassert>

/*
 * A minimal tutorial. 
 *
 * It demonstrates how to intersect a ray with a single triangle. It is
 * meant to get you started as quickly as possible, and does not output
 * an image. 
 *
 * For more complex examples, see the other tutorials.
 *
 * Compile this file using
 *   
 *   gcc -std=c99 \
 *       -I<PATH>/<TO>/<EMBREE>/include \
 *       -o minimal \
 *       minimal.c \
 *       -L<PATH>/<TO>/<EMBREE>/lib \
 *       -lembree4 
 *
 * You should be able to compile this using a C or C++ compiler.
 */

/* 
 * This is only required to make the tutorial compile even when
 * a custom namespace is set.
 */
#if defined(RTC_NAMESPACE_USE)
RTC_NAMESPACE_USE
#endif

/*
 * We will register this error handler with the device in initializeDevice(),
 * so that we are automatically informed on errors.
 * This is extremely helpful for finding bugs in your code, prevents you
 * from having to add explicit error checking to each Embree API call.
 */
void errorFunction(void* userPtr, enum RTCError error, const char* str)
{
  printf("error %d: %s\n", error, str);
}

/*
 * Embree has a notion of devices, which are entities that can run 
 * raytracing kernels.
 * We initialize our device here, and then register the error handler so that 
 * we don't miss any errors.
 *
 * rtcNewDevice() takes a configuration string as an argument. See the API docs
 * for more information.
 *
 * Note that RTCDevice is reference-counted.
 */
RTCDevice initializeDevice()
{
  RTCDevice device = rtcNewDevice(NULL);

  if (!device)
    printf("error %d: cannot create device\n", rtcGetDeviceError(NULL));

  rtcSetDeviceErrorFunction(device, errorFunction, NULL);
  return device;
}

/*
 * Create a scene, which is a collection of geometry objects. Scenes are 
 * what the intersect / occluded functions work on. You can think of a 
 * scene as an acceleration structure, e.g. a bounding-volume hierarchy.
 *
 * Scenes, like devices, are reference-counted.
 */
RTCScene initializeScene(RTCDevice device)
{
  RTCScene scene = rtcNewScene(device);
  rtcSetSceneFlags(scene, RTC_SCENE_FLAG_COMPACT | RTC_SCENE_FLAG_ROBUST);
  rtcSetSceneBuildQuality(scene, RTC_BUILD_QUALITY_HIGH);
  /* 
   * Create a triangle mesh geometry, and initialize a single triangle.
   * You can look up geometry types in the API documentation to
   * find out which type expects which buffers.
   *
   * We create buffers directly on the device, but you can also use
   * shared buffers. For shared buffers, special care must be taken
   * to ensure proper alignment and padding. This is described in
   * more detail in the API documentation.
   */
  RTCGeometry geom = rtcNewGeometry(device, RTC_GEOMETRY_TYPE_TRIANGLE);
  rtcSetGeometryBuildQuality(geom, RTC_BUILD_QUALITY_HIGH);
  /*float* vertices = (float*)rtcSetNewGeometryBuffer(geom,
                                                     RTC_BUFFER_TYPE_VERTEX,
                                                     0,
                                                     RTC_FORMAT_FLOAT3,
                                                     3*sizeof(float),
                                                     24);

  unsigned* indices = (unsigned*) rtcSetNewGeometryBuffer(geom,
                                                          RTC_BUFFER_TYPE_INDEX,
                                                          0,
                                                          RTC_FORMAT_UINT3,
                                                          3*sizeof(unsigned),
                                                          4);
*/

  float vertices[] = {
    -3072.0f, 0.0f,     1024.0f,
    -3072.0f, 0.0f,     0.0f,
    -4096.0f, 0.0f,     1024.0f,
    -4096.0f, 0.0f,     0.0f,
    -4096.0f, 3072.0f,  1024.0f,
    -4096.0f, 3072.0f,  0.0f,
    -3072.0f, 3072.0f,  1024.0f,
    -3072.0f, 3072.0f,  0.0f};
  unsigned indices[] = {
    2,0,1,
    3,2,1,
    6,4,5,
    7,6,5
  };
  auto vertexBuffer = rtcNewBuffer(device, sizeof(float) * 24);
  auto indexBuffer = rtcNewBuffer(device, sizeof(unsigned) * 12);

  void* vtxPtr = rtcGetBufferData(vertexBuffer);
  assert(rtcGetDeviceError(device) == RTC_ERROR_NONE);
  memcpy(vtxPtr, &vertices[0], sizeof(float) * 24);
  void* idxPtr = rtcGetBufferData(indexBuffer);
  assert(rtcGetDeviceError(device) == RTC_ERROR_NONE);
  memcpy(vtxPtr, &indices[0], sizeof(unsigned) * 12);

  rtcSetGeometryBuffer(geom, RTC_BUFFER_TYPE_VERTEX, 0, RTC_FORMAT_FLOAT3, vertexBuffer, 0, sizeof(float) * 3, 8);
  rtcSetGeometryBuffer(geom, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT3, indexBuffer, 0, sizeof(unsigned) * 3, 4);

  /*
   * You must commit geometry objects when you are done setting them up,
   * or you will not get any intersections.
   */
  rtcCommitGeometry(geom);

  /*
   * In rtcAttachGeometry(...), the scene takes ownership of the geom
   * by increasing its reference count. This means that we don't have
   * to hold on to the geom handle, and may release it. The geom object
   * will be released automatically when the scene is destroyed.
   *
   * rtcAttachGeometry() returns a geometry ID. We could use this to
   * identify intersected objects later on.
   */
  rtcAttachGeometry(scene, geom);
  rtcReleaseGeometry(geom);

  /*
   * Like geometry objects, scenes must be committed. This lets
   * Embree know that it may start building an acceleration structure.
   */
  rtcCommitScene(scene);

  return scene;
}

/*
 * Cast a single ray with origin (ox, oy, oz) and direction
 * (dx, dy, dz).
 */
void castRay(RTCScene scene, 
             float ox, float oy, float oz,
             float dx, float dy, float dz)
{
  /*
   * The ray hit structure holds both the ray and the hit.
   * The user must initialize it properly -- see API documentation
   * for rtcIntersect1() for details.
   */
  struct RTCRay ray{};
  ray.org_x = ox;
  ray.org_y = oy;
  ray.org_z = oz;
  ray.dir_x = dx;
  ray.dir_y = dy;
  ray.dir_z = dz;
  ray.tnear = 2.0f;
  ray.tfar = INFINITY;
  ray.mask = 0xffffffff;
  ray.flags = 0;

  /*
   * There are multiple variants of rtcIntersect. This one
   * intersects a single ray with the scene.
   */
  RTCOccludedArguments args;
  rtcInitOccludedArguments(&args);
  rtcOccluded1(scene, &ray,&args);

  assert(ray.tfar == -std::numeric_limits<float>::infinity());
}

void waitForKeyPressedUnderWindows()
{
#if defined(_WIN32)
  HANDLE hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);

  CONSOLE_SCREEN_BUFFER_INFO csbi;
  if (!GetConsoleScreenBufferInfo(hStdOutput, &csbi)) {
    printf("GetConsoleScreenBufferInfo failed: %lu\n", GetLastError());
    return;
  }

  /* do not pause when running on a shell */
  if (csbi.dwCursorPosition.X != 0 || csbi.dwCursorPosition.Y != 0)
    return;

  /* only pause if running in separate console window. */
  printf("\n\tPress any key to exit...\n");
  _getch();
#endif
}

/* -------------------------------------------------------------------------- */

int main()
{
  /* Initialization. All of this may fail, but we will be notified by
   * our errorFunction. */
  RTCDevice device = initializeDevice();
  RTCScene scene = initializeScene(device);
  /*
  * {<-4092.0767, 0, 1022.0383>}
  */
  /* This will not hit the triangle at t=1. */
  castRay(scene, -4092.07666f, 0.0f, 1020.07666f, 0, 1.0f, 0);

  /* Though not strictly necessary in this example, you should
   * always make sure to release resources allocated through Embree. */
  rtcReleaseScene(scene);
  rtcReleaseDevice(device);

  /* wait for user input under Windows when opened in separate window */
  waitForKeyPressedUnderWindows();

  return 0;
}
freibold commented 9 months ago

Hi,

you do two memcpys with vtxPtr as the destination. The 2nd memcpy should copy to idxPtr instead. When I compile your code I get the compiler warning:

[build]minimal.cpp:152:9: warning: unused variable 'idxPtr' [-Wunused-variable] [build] 152 | void* idxPtr = rtcGetBufferData(indexBuffer); [build] | ^~ [build] 1 warning generated.

So, the compiler already told you your problem ;)

Cheers, Florian