最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c++ - Setting Up Octree Ray-casting - Stack Overflow

programmeradmin4浏览0评论

I'm working on ray-casting an octree and am unsure on the correct method of pre-calculating a bunch of rays, one for each pixel. This is what I have at the moment. Please ignore the ortho projection stuff, I'm currently just working on getting perspective working.

float3* Renderer::CalculateRays(int width, int height, float fov, float range_far, bool ortho) {
    auto clip_plane_size = 2.0f;
    const int pixels = width * height;
    auto aspect_ratio = (float)width / (float)height;
    auto ray_id = 0;
    auto hx = width / 2;
    auto hy = height / 2;
    auto plane_width = clip_plane_size;
    auto plane_height = clip_plane_size / aspect_ratio;

    float3* ray_directions = new float3[pixels]();
    float3* ray_dir_frac = new float3[pixels]();

    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {

            if (ortho == true) {
                auto wx = x * (plane_width / width);
                auto wy = y * (plane_height / height);
                auto wz = 0.0f;

                Vec3 ray_start = { wx, wy, wz };
                Vec3 ray_dir = { 0.0f, 0.0f, 1.0f };

                ray_directions[ray_id] = { ray_dir.x, ray_dir.y, ray_dir.z };
                ray_dir_frac[ray_id] = { 1.0f / ray_dir.x, 1.0f / ray_dir.y, 1.0f / ray_dir.z };
            }
            else {
                Vec3 ray_dir = CalculateRayDirection(x, y, width, height, fov);

                ray_directions[ray_id] = { ray_dir.x, ray_dir.y, ray_dir.z };
                ray_dir_frac[ray_id] = { 1.0f / ray_dir.x, 1.0f / ray_dir.y, 1.0f / ray_dir.z };
            }
            ray_id++;
        }
    }

    return ray_directions;
}

Vec3 Renderer::CalculateRayDirection(float x, float y, float width, float height, float fov) {
    const float ASPECT_RATIO = (float)width / (float)height;
    const float NEAR_PLANE = 1.0f;

    float px = (2 * ((x + 0.5f) / width) - 1) * std::tan(fov / 2 * M_PI / 180) * ASPECT_RATIO;
    float py = (1 - 2 * ((y + 0.5f) / height)) * std::tan(fov / 2 * M_PI / 180);

    return Vec3(px, py, NEAR_PLANE).Normalized();
}

This is how the octree is searched in the render loop.

//For loop for x & y pixels

 int pixel_index = y * world->screen_width + x;

 auto cam_pos = world->camera_position;
 auto cam_mat = world->camera_matrix;

 float3 ray_dir = world->ray_directions[pixel_index];
 ray_dir = cam_mat.MultiplyVector(ray_dir);
 float3 ray_frac = { 1.0f / ray_dir.x, 1.0f / ray_dir.y, 1.0f / ray_dir.z };

 float3 from = { 0,0,0 };
 from = cam_mat.MultiplyVector(from);


 float3 normal;

 if (IntersectsOctree(root, from, ray_frac, normal) == true) {
     output[pixel_index].x = 255.0f;
     output[pixel_index].y = 0;
     output[pixel_index].z = 0;
 }
 else {
     output[pixel_index].x = world->clear_color.x;
     output[pixel_index].y = world->clear_color.y;
     output[pixel_index].z = world->clear_color.z;
 }

I haven't posted "IntersectsOctree()" as it's not really relevant to creating and manipulating the rays themselves.

Results appear to be correct until the rays are transformed with the camera matrix. Without any rotation the camera can move around along the world axis just fine, however with camera rotation things become skewed the further I get away from the objects. I initially thought barrel distortion but am I uncertain.

With "ray_dir = cam_mat.MultiplyVector(ray_dir);" commented out (so the ray un-modified by the camera matrix), this is the result.

Without Matrix Multiplication

With "ray_dir = cam_mat.MultiplyVector(ray_dir);" used to modify the rays, this is the result.

With Matrix Multiplication

I could be doing my matrix wrong, but first I'd like to see if I've got this much right!

My main queries are this;

  1. Am I pre-calculating the rays correctly?
  2. Am I doing the right thing multiplying the camera matrix in order to change the ray directions?

I'm working on ray-casting an octree and am unsure on the correct method of pre-calculating a bunch of rays, one for each pixel. This is what I have at the moment. Please ignore the ortho projection stuff, I'm currently just working on getting perspective working.

float3* Renderer::CalculateRays(int width, int height, float fov, float range_far, bool ortho) {
    auto clip_plane_size = 2.0f;
    const int pixels = width * height;
    auto aspect_ratio = (float)width / (float)height;
    auto ray_id = 0;
    auto hx = width / 2;
    auto hy = height / 2;
    auto plane_width = clip_plane_size;
    auto plane_height = clip_plane_size / aspect_ratio;

    float3* ray_directions = new float3[pixels]();
    float3* ray_dir_frac = new float3[pixels]();

    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {

            if (ortho == true) {
                auto wx = x * (plane_width / width);
                auto wy = y * (plane_height / height);
                auto wz = 0.0f;

                Vec3 ray_start = { wx, wy, wz };
                Vec3 ray_dir = { 0.0f, 0.0f, 1.0f };

                ray_directions[ray_id] = { ray_dir.x, ray_dir.y, ray_dir.z };
                ray_dir_frac[ray_id] = { 1.0f / ray_dir.x, 1.0f / ray_dir.y, 1.0f / ray_dir.z };
            }
            else {
                Vec3 ray_dir = CalculateRayDirection(x, y, width, height, fov);

                ray_directions[ray_id] = { ray_dir.x, ray_dir.y, ray_dir.z };
                ray_dir_frac[ray_id] = { 1.0f / ray_dir.x, 1.0f / ray_dir.y, 1.0f / ray_dir.z };
            }
            ray_id++;
        }
    }

    return ray_directions;
}

Vec3 Renderer::CalculateRayDirection(float x, float y, float width, float height, float fov) {
    const float ASPECT_RATIO = (float)width / (float)height;
    const float NEAR_PLANE = 1.0f;

    float px = (2 * ((x + 0.5f) / width) - 1) * std::tan(fov / 2 * M_PI / 180) * ASPECT_RATIO;
    float py = (1 - 2 * ((y + 0.5f) / height)) * std::tan(fov / 2 * M_PI / 180);

    return Vec3(px, py, NEAR_PLANE).Normalized();
}

This is how the octree is searched in the render loop.

//For loop for x & y pixels

 int pixel_index = y * world->screen_width + x;

 auto cam_pos = world->camera_position;
 auto cam_mat = world->camera_matrix;

 float3 ray_dir = world->ray_directions[pixel_index];
 ray_dir = cam_mat.MultiplyVector(ray_dir);
 float3 ray_frac = { 1.0f / ray_dir.x, 1.0f / ray_dir.y, 1.0f / ray_dir.z };

 float3 from = { 0,0,0 };
 from = cam_mat.MultiplyVector(from);


 float3 normal;

 if (IntersectsOctree(root, from, ray_frac, normal) == true) {
     output[pixel_index].x = 255.0f;
     output[pixel_index].y = 0;
     output[pixel_index].z = 0;
 }
 else {
     output[pixel_index].x = world->clear_color.x;
     output[pixel_index].y = world->clear_color.y;
     output[pixel_index].z = world->clear_color.z;
 }

I haven't posted "IntersectsOctree()" as it's not really relevant to creating and manipulating the rays themselves.

Results appear to be correct until the rays are transformed with the camera matrix. Without any rotation the camera can move around along the world axis just fine, however with camera rotation things become skewed the further I get away from the objects. I initially thought barrel distortion but am I uncertain.

With "ray_dir = cam_mat.MultiplyVector(ray_dir);" commented out (so the ray un-modified by the camera matrix), this is the result.

Without Matrix Multiplication

With "ray_dir = cam_mat.MultiplyVector(ray_dir);" used to modify the rays, this is the result.

With Matrix Multiplication

I could be doing my matrix wrong, but first I'd like to see if I've got this much right!

My main queries are this;

  1. Am I pre-calculating the rays correctly?
  2. Am I doing the right thing multiplying the camera matrix in order to change the ray directions?
Share Improve this question edited Mar 17 at 20:04 genpfault 52.2k12 gold badges91 silver badges151 bronze badges asked Mar 17 at 5:28 Spider PigSpider Pig 231 silver badge3 bronze badges 4
  • Side note : new float3[pixels](); this is C++ so you can/should use std::vector<float3>. Also you seem to be overusing auto when declaring your local variables, make sure you do that with concrete types (now some of your variables are ints where floats would be better) – Pepijn Kramer Commented Mar 17 at 5:35
  • @PepijnKramer When you're writing graphics code, using std::vector is just plain silly. Chances are you'll end up wanting to move portion of the execution to the GPU, at which point you're going to need code that can work on a mem mapped GPU buffer. using std::vector in this case, just shoots yourself in the foot. Admittedly, the memory leak (by not freeing ray_dir_frac ever is an issue), however see my response below. Algorithmic optimisations, always trump pedantic C++ trivialities. There are no auto->integers in use here. They are all floats. Which ones do you mean exactly? – robthebloke Commented Mar 17 at 8:05
  • @robthebloke I totally agree with you that even allocating the rays is not needed. As for using std::vector for shared mem, that's indeed clumsy... but from the code I got the impression this data is not shared (I could be wrong ;) ) – Pepijn Kramer Commented Mar 17 at 8:59
  • 1 Hi, when your code seems to work okay but you're not sure if it's really correct, codereview.stackexchange is the site you should consider to use. – tevemadar Commented Mar 22 at 11:17
Add a comment  | 

1 Answer 1

Reset to default 2

Typically when you do this stuff, you store 2 pre-calculated values on your camera:

htan = std::tan(fov / 2 * M_PI / 180) * ASPECT_RATIO
vtan = std::tan(fov / 2 * M_PI / 180)

(Since they only need to be recalculated when your fov or aspect ratio changes, which isn't often). Other than that, the ray calculation you have looks right.

Personally I'd recommend NOT pre-calculating the rays. Once you remove the calls to std::tan, the cost of calculating the rays is cheap (compared to the L1 cache hit you'll take from storing all of those rays!). For a 4K image, that's about 8 million rays!

Your ray-dir calculation is correct, however from is a position (w=1), not a vector (w=0). Just assign the cam-matrix translation into from, and that should fix the problem I'm guessing?

发布评论

评论列表(0)

  1. 暂无评论