vtk-m/vtkm/rendering/raytracing/Camera.cxx
Kenneth Moreland 3e1339f9a7 Remove deprecated features from VTK-m
With the major revision 2.0 of VTK-m, many items previously marked as
deprecated were removed. If updating to a new version of VTK-m, it is
recommended to first update to VTK-m 1.9, which will include the deprecated
features but provide warnings (with the right compiler) that will point to
the replacement code. Once the deprecations have been fixed, updating to
2.0 should be smoother.
2022-11-17 07:12:31 -06:00

1106 lines
33 KiB
C++

//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
#include <vtkm/VectorAnalysis.h>
#include <vtkm/cont/Algorithm.h>
#include <vtkm/cont/ErrorBadValue.h>
#include <vtkm/cont/Timer.h>
#include <vtkm/cont/TryExecute.h>
#include <vtkm/rendering/raytracing/Camera.h>
#include <vtkm/rendering/raytracing/Logger.h>
#include <vtkm/rendering/raytracing/RayOperations.h>
#include <vtkm/rendering/raytracing/RayTracingTypeDefs.h>
#include <vtkm/rendering/raytracing/Sampler.h>
#include <vtkm/rendering/raytracing/Worklets.h>
#include <vtkm/worklet/DispatcherMapField.h>
#include <vtkm/worklet/WorkletMapField.h>
#include <limits>
namespace vtkm
{
namespace rendering
{
namespace raytracing
{
class PixelData : public vtkm::worklet::WorkletMapField
{
public:
vtkm::Int32 w;
vtkm::Int32 h;
vtkm::Int32 Minx;
vtkm::Int32 Miny;
vtkm::Int32 SubsetWidth;
vtkm::Vec3f_32 nlook; // normalized look
vtkm::Vec3f_32 delta_x;
vtkm::Vec3f_32 delta_y;
vtkm::Vec3f_32 Origin;
vtkm::Bounds BoundingBox;
VTKM_CONT
PixelData(vtkm::Int32 width,
vtkm::Int32 height,
vtkm::Float32 fovX,
vtkm::Float32 fovY,
vtkm::Vec3f_32 look,
vtkm::Vec3f_32 up,
vtkm::Float32 _zoom,
vtkm::Int32 subsetWidth,
vtkm::Int32 minx,
vtkm::Int32 miny,
vtkm::Vec3f_32 origin,
vtkm::Bounds boundingBox)
: w(width)
, h(height)
, Minx(minx)
, Miny(miny)
, SubsetWidth(subsetWidth)
, Origin(origin)
, BoundingBox(boundingBox)
{
vtkm::Float32 thx = tanf((fovX * vtkm::Pi_180f()) * .5f);
vtkm::Float32 thy = tanf((fovY * vtkm::Pi_180f()) * .5f);
vtkm::Vec3f_32 ru = vtkm::Cross(look, up);
vtkm::Normalize(ru);
vtkm::Vec3f_32 rv = vtkm::Cross(ru, look);
vtkm::Normalize(rv);
delta_x = ru * (2 * thx / (float)w);
delta_y = rv * (2 * thy / (float)h);
if (_zoom > 0)
{
delta_x[0] = delta_x[0] / _zoom;
delta_x[1] = delta_x[1] / _zoom;
delta_x[2] = delta_x[2] / _zoom;
delta_y[0] = delta_y[0] / _zoom;
delta_y[1] = delta_y[1] / _zoom;
delta_y[2] = delta_y[2] / _zoom;
}
nlook = look;
vtkm::Normalize(nlook);
}
VTKM_EXEC inline vtkm::Float32 rcp(vtkm::Float32 f) const { return 1.0f / f; }
VTKM_EXEC inline vtkm::Float32 rcp_safe(vtkm::Float32 f) const
{
return rcp((fabs(f) < 1e-8f) ? 1e-8f : f);
}
using ControlSignature = void(FieldOut, FieldOut);
using ExecutionSignature = void(WorkIndex, _1, _2);
VTKM_EXEC
void operator()(const vtkm::Id idx, vtkm::Int32& hit, vtkm::Float32& distance) const
{
vtkm::Vec3f_32 ray_dir;
int i = vtkm::Int32(idx) % SubsetWidth;
int j = vtkm::Int32(idx) / SubsetWidth;
i += Minx;
j += Miny;
// Write out the global pixelId
ray_dir = nlook + delta_x * ((2.f * vtkm::Float32(i) - vtkm::Float32(w)) / 2.0f) +
delta_y * ((2.f * vtkm::Float32(j) - vtkm::Float32(h)) / 2.0f);
vtkm::Float32 dot = vtkm::Dot(ray_dir, ray_dir);
vtkm::Float32 sq_mag = vtkm::Sqrt(dot);
ray_dir[0] = ray_dir[0] / sq_mag;
ray_dir[1] = ray_dir[1] / sq_mag;
ray_dir[2] = ray_dir[2] / sq_mag;
vtkm::Float32 invDirx = rcp_safe(ray_dir[0]);
vtkm::Float32 invDiry = rcp_safe(ray_dir[1]);
vtkm::Float32 invDirz = rcp_safe(ray_dir[2]);
vtkm::Float32 odirx = Origin[0] * invDirx;
vtkm::Float32 odiry = Origin[1] * invDiry;
vtkm::Float32 odirz = Origin[2] * invDirz;
vtkm::Float32 xmin = vtkm::Float32(BoundingBox.X.Min) * invDirx - odirx;
vtkm::Float32 ymin = vtkm::Float32(BoundingBox.Y.Min) * invDiry - odiry;
vtkm::Float32 zmin = vtkm::Float32(BoundingBox.Z.Min) * invDirz - odirz;
vtkm::Float32 xmax = vtkm::Float32(BoundingBox.X.Max) * invDirx - odirx;
vtkm::Float32 ymax = vtkm::Float32(BoundingBox.Y.Max) * invDiry - odiry;
vtkm::Float32 zmax = vtkm::Float32(BoundingBox.Z.Max) * invDirz - odirz;
vtkm::Float32 mind = vtkm::Max(
vtkm::Max(vtkm::Max(vtkm::Min(ymin, ymax), vtkm::Min(xmin, xmax)), vtkm::Min(zmin, zmax)),
0.f);
vtkm::Float32 maxd =
vtkm::Min(vtkm::Min(vtkm::Max(ymin, ymax), vtkm::Max(xmin, xmax)), vtkm::Max(zmin, zmax));
if (maxd < mind)
{
hit = 0;
distance = 0;
}
else
{
distance = maxd - mind;
hit = 1;
}
}
}; // class pixelData
class PerspectiveRayGenJitter : public vtkm::worklet::WorkletMapField
{
public:
vtkm::Int32 w;
vtkm::Int32 h;
vtkm::Vec3f_32 nlook; // normalized look
vtkm::Vec3f_32 delta_x;
vtkm::Vec3f_32 delta_y;
vtkm::Int32 CurrentSample;
VTKM_CONT_EXPORT
PerspectiveRayGenJitter(vtkm::Int32 width,
vtkm::Int32 height,
vtkm::Float32 fovX,
vtkm::Float32 fovY,
vtkm::Vec3f_32 look,
vtkm::Vec3f_32 up,
vtkm::Float32 _zoom,
vtkm::Int32 currentSample)
: w(width)
, h(height)
{
vtkm::Float32 thx = tanf((fovX * 3.1415926f / 180.f) * .5f);
vtkm::Float32 thy = tanf((fovY * 3.1415926f / 180.f) * .5f);
vtkm::Vec3f_32 ru = vtkm::Cross(up, look);
vtkm::Normalize(ru);
vtkm::Vec3f_32 rv = vtkm::Cross(ru, look);
vtkm::Normalize(rv);
delta_x = ru * (2 * thx / (float)w);
delta_y = rv * (2 * thy / (float)h);
if (_zoom > 0)
{
delta_x[0] = delta_x[0] / _zoom;
delta_x[1] = delta_x[1] / _zoom;
delta_x[2] = delta_x[2] / _zoom;
delta_y[0] = delta_y[0] / _zoom;
delta_y[1] = delta_y[1] / _zoom;
delta_y[2] = delta_y[2] / _zoom;
}
nlook = look;
vtkm::Normalize(nlook);
CurrentSample = currentSample;
}
typedef void ControlSignature(FieldOut, FieldOut, FieldOut, FieldIn);
typedef void ExecutionSignature(WorkIndex, _1, _2, _3, _4);
VTKM_EXEC
void operator()(vtkm::Id idx,
vtkm::Float32& rayDirX,
vtkm::Float32& rayDirY,
vtkm::Float32& rayDirZ,
const vtkm::Int32& seed) const
{
vtkm::Vec2f_32 xy;
Halton2D<3>(CurrentSample + seed, xy);
xy[0] -= .5f;
xy[1] -= .5f;
vtkm::Vec3f_32 ray_dir(rayDirX, rayDirY, rayDirZ);
vtkm::Float32 i = static_cast<vtkm::Float32>(vtkm::Int32(idx) % w);
vtkm::Float32 j = static_cast<vtkm::Float32>(vtkm::Int32(idx) / w);
i += xy[0];
j += xy[1];
ray_dir = nlook + delta_x * ((2.f * i - vtkm::Float32(w)) / 2.0f) +
delta_y * ((2.f * j - vtkm::Float32(h)) / 2.0f);
vtkm::Normalize(ray_dir);
rayDirX = ray_dir[0];
rayDirY = ray_dir[1];
rayDirZ = ray_dir[2];
}
}; // class perspective ray gen jitter
class Camera::Ortho2DRayGen : public vtkm::worklet::WorkletMapField
{
public:
vtkm::Int32 w;
vtkm::Int32 h;
vtkm::Int32 Minx;
vtkm::Int32 Miny;
vtkm::Int32 SubsetWidth;
vtkm::Vec3f_32 PixelDelta;
vtkm::Vec3f_32 StartOffset;
VTKM_CONT
Ortho2DRayGen(vtkm::Int32 width,
vtkm::Int32 height,
vtkm::Float32 vtkmNotUsed(_zoom),
vtkm::Int32 subsetWidth,
vtkm::Int32 minx,
vtkm::Int32 miny,
const vtkm::rendering::Camera& camera)
: w(width)
, h(height)
, Minx(minx)
, Miny(miny)
, SubsetWidth(subsetWidth)
{
vtkm::Float32 left, right, bottom, top;
camera.GetViewRange2D(left, right, bottom, top);
vtkm::Float32 vl, vr, vb, vt;
camera.GetRealViewport(width, height, vl, vr, vb, vt);
vtkm::Float32 _w = static_cast<vtkm::Float32>(width) * (vr - vl) / 2.f;
vtkm::Float32 _h = static_cast<vtkm::Float32>(height) * (vt - vb) / 2.f;
vtkm::Vec2f_32 minPoint(left, bottom);
vtkm::Vec2f_32 maxPoint(right, top);
vtkm::Vec2f_32 delta = maxPoint - minPoint;
//delta[0] /= vtkm::Float32(width);
//delta[1] /= vtkm::Float32(height);
delta[0] /= vtkm::Float32(_w);
delta[1] /= vtkm::Float32(_h);
PixelDelta[0] = delta[0];
PixelDelta[1] = delta[1];
PixelDelta[2] = 0.f;
vtkm::Vec2f_32 startOffset = minPoint + delta / 2.f;
StartOffset[0] = startOffset[0];
StartOffset[1] = startOffset[1];
// always push the rays back from the origin
StartOffset[2] = -1.f;
}
using ControlSignature =
void(FieldOut, FieldOut, FieldOut, FieldOut, FieldOut, FieldOut, FieldOut);
using ExecutionSignature = void(WorkIndex, _1, _2, _3, _4, _5, _6, _7);
template <typename Precision>
VTKM_EXEC void operator()(vtkm::Id idx,
Precision& rayDirX,
Precision& rayDirY,
Precision& rayDirZ,
Precision& rayOriginX,
Precision& rayOriginY,
Precision& rayOriginZ,
vtkm::Id& pixelIndex) const
{
// this is 2d so always look down z
rayDirX = 0.f;
rayDirY = 0.f;
rayDirZ = 1.f;
//
// Pixel subset is the pixels in the 2d viewport
// not where the rays might intersect data like
// the perspective ray gen
//
int i = vtkm::Int32(idx) % SubsetWidth;
int j = vtkm::Int32(idx) / SubsetWidth;
vtkm::Vec3f_32 pos;
pos[0] = vtkm::Float32(i);
pos[1] = vtkm::Float32(j);
pos[2] = 0.f;
vtkm::Vec3f_32 origin = StartOffset + pos * PixelDelta;
rayOriginX = origin[0];
rayOriginY = origin[1];
rayOriginZ = origin[2];
i += Minx;
j += Miny;
pixelIndex = static_cast<vtkm::Id>(j * w + i);
}
}; // class perspective ray gen
class Camera::PerspectiveRayGen : public vtkm::worklet::WorkletMapField
{
public:
vtkm::Int32 w;
vtkm::Int32 h;
vtkm::Int32 Minx;
vtkm::Int32 Miny;
vtkm::Int32 SubsetWidth;
vtkm::Vec3f_32 nlook; // normalized look
vtkm::Vec3f_32 delta_x;
vtkm::Vec3f_32 delta_y;
VTKM_CONT
PerspectiveRayGen(vtkm::Int32 width,
vtkm::Int32 height,
vtkm::Float32 fovX,
vtkm::Float32 fovY,
vtkm::Vec3f_32 look,
vtkm::Vec3f_32 up,
vtkm::Float32 _zoom,
vtkm::Int32 subsetWidth,
vtkm::Int32 minx,
vtkm::Int32 miny)
: w(width)
, h(height)
, Minx(minx)
, Miny(miny)
, SubsetWidth(subsetWidth)
{
vtkm::Float32 thx = tanf((fovX * vtkm::Pi_180f()) * .5f);
vtkm::Float32 thy = tanf((fovY * vtkm::Pi_180f()) * .5f);
vtkm::Vec3f_32 ru = vtkm::Cross(look, up);
vtkm::Normalize(ru);
vtkm::Vec3f_32 rv = vtkm::Cross(ru, look);
vtkm::Normalize(rv);
delta_x = ru * (2 * thx / (float)w);
delta_y = rv * (2 * thy / (float)h);
if (_zoom > 0)
{
delta_x[0] = delta_x[0] / _zoom;
delta_x[1] = delta_x[1] / _zoom;
delta_x[2] = delta_x[2] / _zoom;
delta_y[0] = delta_y[0] / _zoom;
delta_y[1] = delta_y[1] / _zoom;
delta_y[2] = delta_y[2] / _zoom;
}
nlook = look;
vtkm::Normalize(nlook);
}
using ControlSignature = void(FieldOut, FieldOut, FieldOut, FieldOut);
using ExecutionSignature = void(WorkIndex, _1, _2, _3, _4);
template <typename Precision>
VTKM_EXEC void operator()(vtkm::Id idx,
Precision& rayDirX,
Precision& rayDirY,
Precision& rayDirZ,
vtkm::Id& pixelIndex) const
{
vtkm::Vec<Precision, 3> ray_dir(rayDirX, rayDirY, rayDirZ);
int i = vtkm::Int32(idx) % SubsetWidth;
int j = vtkm::Int32(idx) / SubsetWidth;
i += Minx;
j += Miny;
// Write out the global pixelId
pixelIndex = static_cast<vtkm::Id>(j * w + i);
ray_dir = nlook + delta_x * ((2.f * Precision(i) - Precision(w)) / 2.0f) +
delta_y * ((2.f * Precision(j) - Precision(h)) / 2.0f);
// avoid some numerical issues
for (vtkm::Int32 d = 0; d < 3; ++d)
{
if (ray_dir[d] == 0.f)
ray_dir[d] += 0.0000001f;
}
Precision dot = vtkm::Dot(ray_dir, ray_dir);
Precision sq_mag = vtkm::Sqrt(dot);
rayDirX = ray_dir[0] / sq_mag;
rayDirY = ray_dir[1] / sq_mag;
rayDirZ = ray_dir[2] / sq_mag;
}
}; // class perspective ray gen
bool Camera::operator==(const Camera& other) const
{
if (this->Height != other.Height)
return false;
if (this->Width != other.Width)
return false;
if (this->SubsetWidth != other.SubsetWidth)
return false;
if (this->SubsetHeight != other.SubsetHeight)
return false;
if (this->SubsetMinX != other.SubsetMinX)
return false;
if (this->SubsetMinY != other.SubsetMinY)
return false;
if (this->FovY != other.FovY)
return false;
if (this->FovX != other.FovX)
return false;
if (this->Zoom != other.Zoom)
return false;
if (this->Look[0] != other.Look[0])
return false;
if (this->Look[1] != other.Look[1])
return false;
if (this->Look[2] != other.Look[2])
return false;
if (this->LookAt[0] != other.LookAt[0])
return false;
if (this->LookAt[1] != other.LookAt[1])
return false;
if (this->LookAt[2] != other.LookAt[2])
return false;
if (this->Up[0] != other.Up[0])
return false;
if (this->Up[1] != other.Up[1])
return false;
if (this->Up[2] != other.Up[2])
return false;
if (this->Position[0] != other.Position[0])
return false;
if (this->Position[1] != other.Position[1])
return false;
if (this->Position[2] != other.Position[2])
return false;
return true;
}
VTKM_CONT
Camera::Camera()
{
this->Height = 500;
this->Width = 500;
this->SubsetWidth = 500;
this->SubsetHeight = 500;
this->SubsetMinX = 0;
this->SubsetMinY = 0;
this->FovY = 30.f;
this->FovX = 30.f;
this->Zoom = 1.f;
this->Look[0] = 0.f;
this->Look[1] = 0.f;
this->Look[2] = -1.f;
this->LookAt[0] = 0.f;
this->LookAt[1] = 0.f;
this->LookAt[2] = -1.f;
this->Up[0] = 0.f;
this->Up[1] = 1.f;
this->Up[2] = 0.f;
this->Position[0] = 0.f;
this->Position[1] = 0.f;
this->Position[2] = 0.f;
this->IsViewDirty = true;
}
VTKM_CONT
Camera::~Camera() {}
VTKM_CONT
void Camera::SetParameters(const vtkm::rendering::Camera& camera,
const vtkm::Int32 width,
const vtkm::Int32 height)
{
this->SetUp(camera.GetViewUp());
this->SetLookAt(camera.GetLookAt());
this->SetPosition(camera.GetPosition());
this->SetZoom(camera.GetZoom());
this->SetFieldOfView(camera.GetFieldOfView());
this->SetHeight(height);
this->SetWidth(width);
this->CameraView = camera;
}
VTKM_CONT
void Camera::SetHeight(const vtkm::Int32& height)
{
if (height <= 0)
{
throw vtkm::cont::ErrorBadValue("Camera height must be greater than zero.");
}
if (Height != height)
{
this->Height = height;
this->SetFieldOfView(this->FovY);
}
}
VTKM_CONT
vtkm::Int32 Camera::GetHeight() const
{
return this->Height;
}
VTKM_CONT
void Camera::SetWidth(const vtkm::Int32& width)
{
if (width <= 0)
{
throw vtkm::cont::ErrorBadValue("Camera width must be greater than zero.");
}
if (this->Width != width)
{
this->Width = width;
this->SetFieldOfView(this->FovY);
}
}
VTKM_CONT
vtkm::Int32 Camera::GetWidth() const
{
return this->Width;
}
VTKM_CONT
vtkm::Int32 Camera::GetSubsetWidth() const
{
return this->SubsetWidth;
}
VTKM_CONT
vtkm::Int32 Camera::GetSubsetHeight() const
{
return this->SubsetHeight;
}
VTKM_CONT
void Camera::SetZoom(const vtkm::Float32& zoom)
{
if (zoom <= 0)
{
throw vtkm::cont::ErrorBadValue("Camera zoom must be greater than zero.");
}
if (this->Zoom != zoom)
{
this->IsViewDirty = true;
this->Zoom = zoom;
}
}
VTKM_CONT
vtkm::Float32 Camera::GetZoom() const
{
return this->Zoom;
}
VTKM_CONT
void Camera::SetFieldOfView(const vtkm::Float32& degrees)
{
if (degrees <= 0)
{
throw vtkm::cont::ErrorBadValue("Camera feild of view must be greater than zero.");
}
if (degrees > 180)
{
throw vtkm::cont::ErrorBadValue("Camera feild of view must be less than 180.");
}
vtkm::Float32 newFOVY = degrees;
vtkm::Float32 newFOVX;
if (this->Width != this->Height)
{
vtkm::Float32 fovyRad = newFOVY * vtkm::Pi_180f();
// Use the tan function to find the distance from the center of the image to the top (or
// bottom). (Actually, we are finding the ratio of this distance to the near plane distance,
// but since we scale everything by the near plane distance, we can use this ratio as a scaled
// proxy of the distances we need.)
vtkm::Float32 verticalDistance = vtkm::Tan(0.5f * fovyRad);
// Scale the vertical distance by the aspect ratio to get the horizontal distance.
vtkm::Float32 aspectRatio = vtkm::Float32(this->Width) / vtkm::Float32(this->Height);
vtkm::Float32 horizontalDistance = aspectRatio * verticalDistance;
// Now use the arctan function to get the proper field of view in the x direction.
vtkm::Float32 fovxRad = 2.0f * vtkm::ATan(horizontalDistance);
newFOVX = fovxRad / vtkm::Pi_180f();
}
else
{
newFOVX = newFOVY;
}
if (newFOVX != this->FovX)
{
this->IsViewDirty = true;
}
if (newFOVY != this->FovY)
{
this->IsViewDirty = true;
}
this->FovX = newFOVX;
this->FovY = newFOVY;
this->CameraView.SetFieldOfView(this->FovY);
}
VTKM_CONT
vtkm::Float32 Camera::GetFieldOfView() const
{
return this->FovY;
}
VTKM_CONT
void Camera::SetUp(const vtkm::Vec3f_32& up)
{
if (this->Up != up)
{
this->Up = up;
vtkm::Normalize(this->Up);
this->IsViewDirty = true;
}
}
VTKM_CONT
vtkm::Vec3f_32 Camera::GetUp() const
{
return this->Up;
}
VTKM_CONT
void Camera::SetLookAt(const vtkm::Vec3f_32& lookAt)
{
if (this->LookAt != lookAt)
{
this->LookAt = lookAt;
this->IsViewDirty = true;
}
}
VTKM_CONT
vtkm::Vec3f_32 Camera::GetLookAt() const
{
return this->LookAt;
}
VTKM_CONT
void Camera::SetPosition(const vtkm::Vec3f_32& position)
{
if (this->Position != position)
{
this->Position = position;
this->IsViewDirty = true;
}
}
VTKM_CONT
vtkm::Vec3f_32 Camera::GetPosition() const
{
return this->Position;
}
VTKM_CONT
void Camera::ResetIsViewDirty()
{
this->IsViewDirty = false;
}
VTKM_CONT
bool Camera::GetIsViewDirty() const
{
return this->IsViewDirty;
}
void Camera::GetPixelData(const vtkm::cont::CoordinateSystem& coords,
vtkm::Int32& activePixels,
vtkm::Float32& aveRayDistance)
{
vtkm::Bounds boundingBox = coords.GetBounds();
this->FindSubset(boundingBox);
//Reset the camera look vector
this->Look = this->LookAt - this->Position;
vtkm::Normalize(this->Look);
const int size = this->SubsetWidth * this->SubsetHeight;
vtkm::cont::ArrayHandle<vtkm::Float32> dists;
vtkm::cont::ArrayHandle<vtkm::Int32> hits;
dists.Allocate(size);
hits.Allocate(size);
//Create the ray direction
vtkm::worklet::DispatcherMapField<PixelData>(PixelData(this->Width,
this->Height,
this->FovX,
this->FovY,
this->Look,
this->Up,
this->Zoom,
this->SubsetWidth,
this->SubsetMinX,
this->SubsetMinY,
this->Position,
boundingBox))
.Invoke(hits, dists); //X Y Z
activePixels = vtkm::cont::Algorithm::Reduce(hits, vtkm::Int32(0));
aveRayDistance =
vtkm::cont::Algorithm::Reduce(dists, vtkm::Float32(0)) / vtkm::Float32(activePixels);
}
VTKM_CONT
void Camera::CreateRays(Ray<vtkm::Float32>& rays, vtkm::Bounds bounds)
{
CreateRaysImpl(rays, bounds);
}
VTKM_CONT
void Camera::CreateRays(Ray<vtkm::Float64>& rays, vtkm::Bounds bounds)
{
CreateRaysImpl(rays, bounds);
}
template <typename Precision>
VTKM_CONT void Camera::CreateRaysImpl(Ray<Precision>& rays, const vtkm::Bounds boundingBox)
{
Logger* logger = Logger::GetInstance();
vtkm::cont::Timer createTimer;
createTimer.Start();
logger->OpenLogEntry("ray_camera");
bool ortho = this->CameraView.GetMode() == vtkm::rendering::Camera::Mode::TwoD;
this->UpdateDimensions(rays, boundingBox, ortho);
this->WriteSettingsToLog();
vtkm::cont::Timer timer;
timer.Start();
//Set the origin of the ray back to the camera position
Precision infinity;
GetInfinity(infinity);
vtkm::cont::ArrayHandleConstant<Precision> inf(infinity, rays.NumRays);
vtkm::cont::Algorithm::Copy(inf, rays.MaxDistance);
vtkm::cont::ArrayHandleConstant<Precision> zero(0, rays.NumRays);
vtkm::cont::Algorithm::Copy(zero, rays.MinDistance);
vtkm::cont::Algorithm::Copy(zero, rays.Distance);
vtkm::cont::ArrayHandleConstant<vtkm::Id> initHit(-2, rays.NumRays);
vtkm::cont::Algorithm::Copy(initHit, rays.HitIdx);
vtkm::Float64 time = timer.GetElapsedTime();
logger->AddLogData("camera_memset", time);
timer.Start();
//Reset the camera look vector
this->Look = this->LookAt - this->Position;
vtkm::Normalize(this->Look);
if (ortho)
{
vtkm::worklet::DispatcherMapField<Ortho2DRayGen> dispatcher(Ortho2DRayGen(this->Width,
this->Height,
this->Zoom,
this->SubsetWidth,
this->SubsetMinX,
this->SubsetMinY,
this->CameraView));
dispatcher.Invoke(rays.DirX,
rays.DirY,
rays.DirZ,
rays.OriginX,
rays.OriginY,
rays.OriginZ,
rays.PixelIdx); //X Y Z
}
else
{
//Create the ray direction
vtkm::worklet::DispatcherMapField<PerspectiveRayGen> dispatcher(
PerspectiveRayGen(this->Width,
this->Height,
this->FovX,
this->FovY,
this->Look,
this->Up,
this->Zoom,
this->SubsetWidth,
this->SubsetMinX,
this->SubsetMinY));
dispatcher.Invoke(rays.DirX, rays.DirY, rays.DirZ, rays.PixelIdx); //X Y Z
vtkm::cont::ArrayHandleConstant<Precision> posX(this->Position[0], rays.NumRays);
vtkm::cont::Algorithm::Copy(posX, rays.OriginX);
vtkm::cont::ArrayHandleConstant<Precision> posY(this->Position[1], rays.NumRays);
vtkm::cont::Algorithm::Copy(posY, rays.OriginY);
vtkm::cont::ArrayHandleConstant<Precision> posZ(this->Position[2], rays.NumRays);
vtkm::cont::Algorithm::Copy(posZ, rays.OriginZ);
}
time = timer.GetElapsedTime();
logger->AddLogData("ray_gen", time);
time = createTimer.GetElapsedTime();
logger->CloseLogEntry(time);
} //create rays
VTKM_CONT
void Camera::FindSubset(const vtkm::Bounds& bounds)
{
this->ViewProjectionMat =
vtkm::MatrixMultiply(this->CameraView.CreateProjectionMatrix(this->Width, this->Height),
this->CameraView.CreateViewMatrix());
vtkm::Float32 x[2], y[2], z[2];
x[0] = static_cast<vtkm::Float32>(bounds.X.Min);
x[1] = static_cast<vtkm::Float32>(bounds.X.Max);
y[0] = static_cast<vtkm::Float32>(bounds.Y.Min);
y[1] = static_cast<vtkm::Float32>(bounds.Y.Max);
z[0] = static_cast<vtkm::Float32>(bounds.Z.Min);
z[1] = static_cast<vtkm::Float32>(bounds.Z.Max);
//Inise the data bounds
if (this->Position[0] >= x[0] && this->Position[0] <= x[1] && this->Position[1] >= y[0] &&
this->Position[1] <= y[1] && this->Position[2] >= z[0] && this->Position[2] <= z[1])
{
this->SubsetWidth = this->Width;
this->SubsetHeight = this->Height;
this->SubsetMinY = 0;
this->SubsetMinX = 0;
return;
}
vtkm::Float32 xmin, ymin, xmax, ymax, zmin, zmax;
xmin = vtkm::Infinity32();
ymin = vtkm::Infinity32();
zmin = vtkm::Infinity32();
xmax = vtkm::NegativeInfinity32();
ymax = vtkm::NegativeInfinity32();
zmax = vtkm::NegativeInfinity32();
vtkm::Vec4f_32 extentPoint;
for (vtkm::Int32 i = 0; i < 2; ++i)
for (vtkm::Int32 j = 0; j < 2; ++j)
for (vtkm::Int32 k = 0; k < 2; ++k)
{
extentPoint[0] = x[i];
extentPoint[1] = y[j];
extentPoint[2] = z[k];
extentPoint[3] = 1.f;
vtkm::Vec4f_32 transformed = vtkm::MatrixMultiply(this->ViewProjectionMat, extentPoint);
// perform the perspective divide
for (vtkm::Int32 a = 0; a < 3; ++a)
{
transformed[a] = transformed[a] / transformed[3];
}
transformed[0] = (transformed[0] * 0.5f + 0.5f) * static_cast<vtkm::Float32>(Width);
transformed[1] = (transformed[1] * 0.5f + 0.5f) * static_cast<vtkm::Float32>(Height);
transformed[2] = (transformed[2] * 0.5f + 0.5f);
zmin = vtkm::Min(zmin, transformed[2]);
zmax = vtkm::Max(zmax, transformed[2]);
if (transformed[2] < 0 || transformed[2] > 1)
{
continue;
}
xmin = vtkm::Min(xmin, transformed[0]);
ymin = vtkm::Min(ymin, transformed[1]);
xmax = vtkm::Max(xmax, transformed[0]);
ymax = vtkm::Max(ymax, transformed[1]);
}
xmin -= .001f;
xmax += .001f;
ymin -= .001f;
ymax += .001f;
xmin = vtkm::Floor(vtkm::Min(vtkm::Max(0.f, xmin), vtkm::Float32(Width)));
xmax = vtkm::Ceil(vtkm::Min(vtkm::Max(0.f, xmax), vtkm::Float32(Width)));
ymin = vtkm::Floor(vtkm::Min(vtkm::Max(0.f, ymin), vtkm::Float32(Height)));
ymax = vtkm::Ceil(vtkm::Min(vtkm::Max(0.f, ymax), vtkm::Float32(Height)));
Logger* logger = Logger::GetInstance();
std::stringstream ss;
ss << "(" << xmin << "," << ymin << "," << zmin << ")-";
ss << "(" << xmax << "," << ymax << "," << zmax << ")";
logger->AddLogData("pixel_range", ss.str());
vtkm::Int32 dx = vtkm::Int32(xmax) - vtkm::Int32(xmin);
vtkm::Int32 dy = vtkm::Int32(ymax) - vtkm::Int32(ymin);
//
// scene is behind the camera
//
if (zmax < 0 || xmin >= xmax || ymin >= ymax)
{
this->SubsetWidth = 1;
this->SubsetHeight = 1;
this->SubsetMinX = 0;
this->SubsetMinY = 0;
}
else
{
this->SubsetWidth = dx;
this->SubsetHeight = dy;
this->SubsetMinX = vtkm::Int32(xmin);
this->SubsetMinY = vtkm::Int32(ymin);
}
logger->AddLogData("subset_width", dx);
logger->AddLogData("subset_height", dy);
}
template <typename Precision>
VTKM_CONT void Camera::UpdateDimensions(Ray<Precision>& rays,
const vtkm::Bounds& boundingBox,
bool ortho2D)
{
// If bounds have been provided, only cast rays that could hit the data
bool imageSubsetModeOn = boundingBox.IsNonEmpty();
//Find the pixel footprint
if (imageSubsetModeOn && !ortho2D)
{
//Create a transform matrix using the rendering::camera class
vtkm::rendering::Camera camera = this->CameraView;
camera.SetFieldOfView(this->GetFieldOfView());
camera.SetLookAt(this->GetLookAt());
camera.SetPosition(this->GetPosition());
camera.SetViewUp(this->GetUp());
//
// Just create come clipping range, we ignore the zmax value in subsetting
//
vtkm::Float64 maxDim = vtkm::Max(
boundingBox.X.Max - boundingBox.X.Min,
vtkm::Max(boundingBox.Y.Max - boundingBox.Y.Min, boundingBox.Z.Max - boundingBox.Z.Min));
maxDim *= 100;
camera.SetClippingRange(.0001, maxDim);
//Update our ViewProjection matrix
this->ViewProjectionMat =
vtkm::MatrixMultiply(this->CameraView.CreateProjectionMatrix(this->Width, this->Height),
this->CameraView.CreateViewMatrix());
this->FindSubset(boundingBox);
}
else if (ortho2D)
{
// 2D rendering has a viewport that represents the area of the canvas where the image
// is drawn. Thus, we have to create rays cooresponding to that region of the
// canvas, so annotations are correctly rendered
vtkm::Float32 vl, vr, vb, vt;
this->CameraView.GetRealViewport(this->GetWidth(), this->GetHeight(), vl, vr, vb, vt);
vtkm::Float32 _x = static_cast<vtkm::Float32>(this->GetWidth()) * (1.f + vl) / 2.f;
vtkm::Float32 _y = static_cast<vtkm::Float32>(this->GetHeight()) * (1.f + vb) / 2.f;
vtkm::Float32 _w = static_cast<vtkm::Float32>(this->GetWidth()) * (vr - vl) / 2.f;
vtkm::Float32 _h = static_cast<vtkm::Float32>(this->GetHeight()) * (vt - vb) / 2.f;
this->SubsetWidth = static_cast<vtkm::Int32>(_w);
this->SubsetHeight = static_cast<vtkm::Int32>(_h);
this->SubsetMinY = static_cast<vtkm::Int32>(_y);
this->SubsetMinX = static_cast<vtkm::Int32>(_x);
}
else
{
//Update the image dimensions
this->SubsetWidth = this->Width;
this->SubsetHeight = this->Height;
this->SubsetMinY = 0;
this->SubsetMinX = 0;
}
// resize rays and buffers
if (rays.NumRays != SubsetWidth * SubsetHeight)
{
RayOperations::Resize(
rays, this->SubsetHeight * this->SubsetWidth, vtkm::cont::DeviceAdapterTagSerial());
}
}
void Camera::CreateDebugRay(vtkm::Vec2i_32 pixel, Ray<vtkm::Float64>& rays)
{
CreateDebugRayImp(pixel, rays);
}
void Camera::CreateDebugRay(vtkm::Vec2i_32 pixel, Ray<vtkm::Float32>& rays)
{
CreateDebugRayImp(pixel, rays);
}
template <typename Precision>
void Camera::CreateDebugRayImp(vtkm::Vec2i_32 pixel, Ray<Precision>& rays)
{
RayOperations::Resize(rays, 1, vtkm::cont::DeviceAdapterTagSerial());
vtkm::Int32 pixelIndex = this->Width * (this->Height - pixel[1]) + pixel[0];
rays.PixelIdx.WritePortal().Set(0, pixelIndex);
rays.OriginX.WritePortal().Set(0, this->Position[0]);
rays.OriginY.WritePortal().Set(0, this->Position[1]);
rays.OriginZ.WritePortal().Set(0, this->Position[2]);
vtkm::Float32 infinity;
GetInfinity(infinity);
rays.MaxDistance.WritePortal().Set(0, infinity);
rays.MinDistance.WritePortal().Set(0, 0.f);
rays.HitIdx.WritePortal().Set(0, -2);
vtkm::Float32 thx = tanf((this->FovX * vtkm::Pi_180f()) * .5f);
vtkm::Float32 thy = tanf((this->FovY * vtkm::Pi_180f()) * .5f);
vtkm::Vec3f_32 ru = vtkm::Cross(this->Look, this->Up);
vtkm::Normalize(ru);
vtkm::Vec3f_32 rv = vtkm::Cross(ru, this->Look);
vtkm::Vec3f_32 delta_x, delta_y;
vtkm::Normalize(rv);
delta_x = ru * (2 * thx / (float)this->Width);
delta_y = rv * (2 * thy / (float)this->Height);
if (this->Zoom > 0)
{
vtkm::Float32 _zoom = this->Zoom;
delta_x[0] = delta_x[0] / _zoom;
delta_x[1] = delta_x[1] / _zoom;
delta_x[2] = delta_x[2] / _zoom;
delta_y[0] = delta_y[0] / _zoom;
delta_y[1] = delta_y[1] / _zoom;
delta_y[2] = delta_y[2] / _zoom;
}
vtkm::Vec3f_32 nlook = this->Look;
vtkm::Normalize(nlook);
vtkm::Vec<Precision, 3> ray_dir;
int i = vtkm::Int32(pixelIndex) % this->Width;
int j = vtkm::Int32(pixelIndex) / this->Height;
ray_dir = nlook + delta_x * ((2.f * Precision(i) - Precision(this->Width)) / 2.0f) +
delta_y * ((2.f * Precision(j) - Precision(this->Height)) / 2.0f);
Precision dot = vtkm::Dot(ray_dir, ray_dir);
Precision sq_mag = vtkm::Sqrt(dot);
ray_dir[0] = ray_dir[0] / sq_mag;
ray_dir[1] = ray_dir[1] / sq_mag;
ray_dir[2] = ray_dir[2] / sq_mag;
rays.DirX.WritePortal().Set(0, ray_dir[0]);
rays.DirY.WritePortal().Set(0, ray_dir[1]);
rays.DirZ.WritePortal().Set(0, ray_dir[2]);
}
void Camera::WriteSettingsToLog()
{
Logger* logger = Logger::GetInstance();
logger->AddLogData("position_x", Position[0]);
logger->AddLogData("position_y", Position[1]);
logger->AddLogData("position_z", Position[2]);
logger->AddLogData("lookat_x", LookAt[0]);
logger->AddLogData("lookat_y", LookAt[1]);
logger->AddLogData("lookat_z", LookAt[2]);
logger->AddLogData("up_x", Up[0]);
logger->AddLogData("up_y", Up[1]);
logger->AddLogData("up_z", Up[2]);
logger->AddLogData("fov_x", FovX);
logger->AddLogData("fov_y", FovY);
logger->AddLogData("width", Width);
logger->AddLogData("height", Height);
logger->AddLogData("subset_height", SubsetHeight);
logger->AddLogData("subset_width", SubsetWidth);
logger->AddLogData("num_rays", SubsetWidth * SubsetHeight);
}
std::string Camera::ToString()
{
std::stringstream sstream;
sstream << "------------------------------------------------------------\n";
sstream << "Position : [" << this->Position[0] << ",";
sstream << this->Position[1] << ",";
sstream << this->Position[2] << "]\n";
sstream << "LookAt : [" << this->LookAt[0] << ",";
sstream << this->LookAt[1] << ",";
sstream << this->LookAt[2] << "]\n";
sstream << "FOV_X : " << this->FovX << "\n";
sstream << "Up : [" << this->Up[0] << ",";
sstream << this->Up[1] << ",";
sstream << this->Up[2] << "]\n";
sstream << "Width : " << this->Width << "\n";
sstream << "Height : " << this->Height << "\n";
sstream << "------------------------------------------------------------\n";
return sstream.str();
}
}
}
} //namespace vtkm::rendering::raytracing