#c #raytracing
#c #трассировка лучей
Вопрос:
Я пишу трассировщик лучей с нуля. Примером является рендеринг двух сфер с использованием обнаружения пересечения лучей и сфер. Когда сферы расположены близко к центру экрана, они выглядят нормально. Однако, когда я перемещаю камеру или регулирую положение сфер так, чтобы они были ближе к краю, они искажаются.
Это код преобразования лучей:
void Renderer::RenderThread(int start, int span)
{
// pCamera holds the position, rotation, and fov of the camera
// pRenderTarget is the screen to render to
// calculate the camera space to world space matrix
Mat4 camSpaceMatrix = Mat4::Get3DTranslation(pCamera->position.x, pCamera->position.y, pCamera->position.z) *
Mat4::GetRotation(pCamera->rotation.x, pCamera->rotation.y, pCamera->rotation.z);
// use the cameras origin as the rays origin
Vec3 origin(0, 0, 0);
origin = (camSpaceMatrix * origin.Vec4()).Vec3();
// this for loop loops over all the pixels on the screen
for ( int p = start; p < start span; p ) {
// get the pixel coordinates on the screen
int px = p % pRenderTarget->GetWidth();
int py = p / pRenderTarget->GetWidth();
// in ray tracing, ndc space is from [0, 1]
Vec2 ndc((px 0.75f) / pRenderTarget->GetWidth(), (py 0.75f) / pRenderTarget->GetHeight());
// in ray tracing, screen space is [-1, 1]
Vec2 screen(2 * ndc.x - 1, 1 - 2 * ndc.y);
// scale x by aspect ratio
screen.x *= (float)pRenderTarget->GetWidth() / pRenderTarget->GetHeight();
// scale screen by the field of view
// fov is currently set to 90
screen *= tan((pCamera->fov / 2) * (PI / 180));
// screen point is the pixels point in camera space,
// give a z value of -1
Vec3 camSpace(screen.x, screen.y, -1);
camSpace = (camSpaceMatrix * camSpace.Vec4()).Vec3();
// the rays direction is its point on the cameras viewing plane
// minus the cameras origin
Vec3 dir = (camSpace - origin).Normalized();
Ray ray = { origin, dir };
// find where the ray intersects with the spheres
// using ray-sphere intersection algorithm
Vec4 color = TraceRay(ray);
pRenderTarget->PutPixel(px, py, color);
}
}
Поле обзора установлено на 90. Я видел, где у других людей была эта проблема, но это было потому, что они использовали очень высокое значение поля обзора. Я не думаю, что должны быть проблемы с 90. Эта проблема сохраняется, даже если камера вообще не перемещается. Любой объект, расположенный близко к краю экрана, выглядит искаженным.
Комментарии:
1. Вы уверены, что это не просто обычные искажения перспективы? Без полной сцены для справки обычные преобразования перспективы могут выглядеть странно.
2. Проецирование 3D в 2D всегда будет иметь некоторые искажения. Насколько это зависит от поля зрения. Более высокие значения означают большее искажение по краям.
3. Если это просто обычное искажение перспективы, почему оно проявляется в такой высокой степени только при 90 FOV? В недавно созданном мной растровом рендерере 90 выглядело великолепно. Проблема действительно начинает исчезать примерно при 40 FOV, но я не хочу использовать такое низкое FOV.
4. 90 градусов поля зрения на самом деле довольно высоки. Как я уже сказал, если вы добавите декорации или движение для справки, это не будет выглядеть так очевидно. Я не уверен, почему ваш raster renderer выглядел лучше. Я подозреваю, что это просто когнитивный перекос. Другими словами, возможно, вы просто помните это лучше, чем было, возможно, вы просто не замечали этого раньше или контекст сделал это менее очевидным. По моему опыту, когда трассировка лучей идет плохо, она либо идет очень плохо, либо идет незаметно плохо. Редактировать: Можете ли вы выставить растрированную версию для сравнения?
5. ОК. Я думал, что 90 — это своего рода стандарт
Ответ №1:
Если вы сомневаетесь, вы всегда можете проверить, что делают другие средства визуализации. Я всегда сравниваю свои результаты и настройки с Blender. Например, Blender 2.82 имеет поле зрения по умолчанию 39,6 градусов.
Я также склонен указать, что это неправильно:
Vec2 ndc((px 0.75f) / pRenderTarget->GetWidth(), (py 0.75f) / pRenderTarget->GetHeight());
Если вы хотите получить центр пикселя, то он должен быть 0.5f
:
Vec2 ndc((px 0.5f) / pRenderTarget->GetWidth(), (py 0.5f) / pRenderTarget->GetHeight());
Кроме того, и это действительно придирчивая вещь, ваши интервалы — это открытые интервалы, а не закрытые (как вы упомянули в комментариях к источнику).) Координаты плоскости изображения никогда не достигают 0 или 1, а пространственные координаты вашей камеры никогда не равны полностью -1 или 1. В конце концов, координаты плоскости изображения преобразуются в пиксельные координаты, остается замкнутый интервал [0, ширина) и [0, высота).
Удачи с вашим трассировщиком лучей!