admin管理员组文章数量:1389762
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;
- Am I pre-calculating the rays correctly?
- 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;
- Am I pre-calculating the rays correctly?
- Am I doing the right thing multiplying the camera matrix in order to change the ray directions?
1 Answer
Reset to default 2Typically 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?
本文标签: cSetting Up Octree RaycastingStack Overflow
版权声明:本文标题:c++ - Setting Up Octree Ray-casting - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744578193a2613755.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
new float3[pixels]();
this is C++ so you can/should use std::vector<float3>. Also you seem to be overusingauto
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:35std::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. usingstd::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