Cleanup: Cycles: Restructure camera viewplane calculation

This started with investigating a render issue that appears to be caused by
GCC 15. From what I can tell, it was caused by
`*viewplane = (*viewplane) * bcam->zoom;`.

I'm not entirely sure what the root cause is (potentially pointer aliasing?),
but the restructured code works fine now.

Pull Request: https://projects.blender.org/blender/blender/pulls/139416
This commit is contained in:
Lukas Stockner
2025-05-26 22:24:20 +02:00
parent 1e18fab838
commit 507267393e
3 changed files with 64 additions and 92 deletions

View File

@@ -329,101 +329,63 @@ static Transform blender_camera_matrix(const Transform &tfm,
static void blender_camera_viewplane(BlenderCamera *bcam,
const int width,
const int height,
BoundBox2D *viewplane,
float *aspectratio,
float *sensor_size)
BoundBox2D &viewplane,
float &aspectratio,
float &sensor_size)
{
/* dimensions */
const float xratio = (float)width * bcam->pixelaspect.x;
const float yratio = (float)height * bcam->pixelaspect.y;
/* compute x/y aspect and ratio */
float xaspect;
float yaspect;
float2 aspect;
bool horizontal_fit;
/* sensor fitting */
if (bcam->sensor_fit == BlenderCamera::AUTO) {
horizontal_fit = (xratio > yratio);
if (sensor_size != nullptr) {
*sensor_size = bcam->sensor_width;
}
sensor_size = bcam->sensor_width;
}
else if (bcam->sensor_fit == BlenderCamera::HORIZONTAL) {
horizontal_fit = true;
if (sensor_size != nullptr) {
*sensor_size = bcam->sensor_width;
}
sensor_size = bcam->sensor_width;
}
else {
horizontal_fit = false;
if (sensor_size != nullptr) {
*sensor_size = bcam->sensor_height;
}
sensor_size = bcam->sensor_height;
}
if (horizontal_fit) {
if (aspectratio != nullptr) {
*aspectratio = xratio / yratio;
}
xaspect = *aspectratio;
yaspect = 1.0f;
aspectratio = xratio / yratio;
aspect = make_float2(aspectratio, 1.0f);
}
else {
if (aspectratio != nullptr) {
*aspectratio = yratio / xratio;
}
xaspect = 1.0f;
yaspect = *aspectratio;
aspectratio = yratio / xratio;
aspect = make_float2(1.0f, aspectratio);
}
/* modify aspect for orthographic scale */
if (bcam->type == CAMERA_ORTHOGRAPHIC) {
xaspect = xaspect * bcam->ortho_scale / (*aspectratio * 2.0f);
yaspect = yaspect * bcam->ortho_scale / (*aspectratio * 2.0f);
if (aspectratio != nullptr) {
*aspectratio = bcam->ortho_scale / 2.0f;
}
aspect *= bcam->ortho_scale / (aspectratio * 2.0f);
aspectratio = bcam->ortho_scale / 2.0f;
}
if (bcam->type == CAMERA_PANORAMA || bcam->type == CAMERA_CUSTOM) {
/* Set viewplane for panoramic or custom camera. */
if (viewplane != nullptr) {
*viewplane = bcam->pano_viewplane;
/* Modify viewplane for camera shift. */
const float shift_factor = (bcam->pano_aspectratio == 0.0f) ?
1.0f :
*aspectratio / bcam->pano_aspectratio;
const float dx = bcam->shift.x * shift_factor;
const float dy = bcam->shift.y * shift_factor;
viewplane->left += dx;
viewplane->right += dx;
viewplane->bottom += dy;
viewplane->top += dy;
/* Account for camera shift. */
float2 dv = bcam->shift;
if (bcam->pano_aspectratio != 0.0f) {
dv *= aspectratio / bcam->pano_aspectratio;
}
/* Set viewplane for panoramic or custom camera. */
viewplane = bcam->pano_viewplane.offset(dv);
}
else {
/* set viewplane */
if (viewplane != nullptr) {
viewplane->left = -xaspect;
viewplane->right = xaspect;
viewplane->bottom = -yaspect;
viewplane->top = yaspect;
/* Account for camera shift and 3d camera view offset. */
const float2 dv = 2.0f * (aspectratio * bcam->shift + bcam->offset * aspect * 2.0f);
/* zoom for 3d camera view */
*viewplane = (*viewplane) * bcam->zoom;
/* modify viewplane with camera shift and 3d camera view offset */
const float dx = 2.0f * (*aspectratio * bcam->shift.x + bcam->offset.x * xaspect * 2.0f);
const float dy = 2.0f * (*aspectratio * bcam->shift.y + bcam->offset.y * yaspect * 2.0f);
viewplane->left += dx;
viewplane->right += dx;
viewplane->bottom += dy;
viewplane->top += dy;
}
/* Set viewplane for perspective or orthographic camera. */
viewplane = (BoundBox2D(aspect) * bcam->zoom).offset(dv);
}
}
@@ -516,7 +478,7 @@ static void blender_camera_sync(Camera *cam,
/* viewplane */
BoundBox2D viewplane;
blender_camera_viewplane(bcam, width, height, &viewplane, &aspectratio, &sensor_size);
blender_camera_viewplane(bcam, width, height, viewplane, aspectratio, sensor_size);
cam->set_viewplane_left(viewplane.left);
cam->set_viewplane_right(viewplane.right);
@@ -792,9 +754,11 @@ void BlenderSync::sync_camera_motion(BL::RenderSettings &b_render,
bcam.pixelaspect.y = b_render.pixel_aspect_y();
blender_camera_from_object(&bcam, b_engine, b_ob, b_data);
BoundBox2D viewplane;
float aspectratio;
float sensor_size;
blender_camera_viewplane(&bcam, width, height, nullptr, &aspectratio, &sensor_size);
blender_camera_viewplane(&bcam, width, height, viewplane, aspectratio, sensor_size);
/* TODO(sergey): De-duplicate calculation with camera sync. */
const float fov = 2.0f * atanf((0.5f * sensor_size) / bcam.lens / aspectratio);
if (fov != cam->get_fov()) {
@@ -825,9 +789,9 @@ static void blender_camera_view_subset(BL::RenderEngine &b_engine,
BL::RegionView3D &b_rv3d,
const int width,
const int height,
BoundBox2D *view_box,
BoundBox2D *cam_box,
float *view_aspect);
BoundBox2D &view_box,
BoundBox2D &cam_box,
float &view_aspect);
static void blender_camera_from_view(BlenderCamera *bcam,
BL::RenderEngine &b_engine,
@@ -871,9 +835,9 @@ static void blender_camera_from_view(BlenderCamera *bcam,
b_rv3d,
width,
height,
&view_box,
&cam_box,
&view_aspect);
view_box,
cam_box,
view_aspect);
bcam->pano_viewplane = view_box.make_relative_to(cam_box);
bcam->pano_aspectratio = view_aspect;
@@ -926,9 +890,9 @@ static void blender_camera_view_subset(BL::RenderEngine &b_engine,
BL::RegionView3D &b_rv3d,
const int width,
const int height,
BoundBox2D *view_box,
BoundBox2D *cam_box,
float *view_aspect)
BoundBox2D &view_box,
BoundBox2D &cam_box,
float &view_aspect)
{
BoundBox2D cam;
BoundBox2D view;
@@ -940,7 +904,7 @@ static void blender_camera_view_subset(BL::RenderEngine &b_engine,
blender_camera_from_view(
&view_bcam, b_engine, b_scene, b_data, b_v3d, b_rv3d, width, height, true);
blender_camera_viewplane(&view_bcam, width, height, &view, view_aspect, &sensor_size);
blender_camera_viewplane(&view_bcam, width, height, view, view_aspect, sensor_size);
/* Get camera viewplane. */
BlenderCamera cam_bcam(b_render);
@@ -951,11 +915,11 @@ static void blender_camera_view_subset(BL::RenderEngine &b_engine,
cam_bcam.pixelaspect.y = b_render.pixel_aspect_y();
blender_camera_viewplane(
&cam_bcam, cam_bcam.full_width, cam_bcam.full_height, &cam, &cam_aspect, &sensor_size);
&cam_bcam, cam_bcam.full_width, cam_bcam.full_height, cam, cam_aspect, sensor_size);
/* Return */
*view_box = view * (1.0f / *view_aspect);
*cam_box = cam * (1.0f / cam_aspect);
view_box = view * (1.0f / view_aspect);
cam_box = cam * (1.0f / cam_aspect);
}
static void blender_camera_border_subset(BL::RenderEngine &b_engine,
@@ -983,9 +947,9 @@ static void blender_camera_border_subset(BL::RenderEngine &b_engine,
b_rv3d,
width,
height,
&view_box,
&cam_box,
&view_aspect);
view_box,
cam_box,
view_aspect);
/* Determine viewport subset matching given border. */
cam_box = cam_box.make_relative_to(view_box);

View File

@@ -196,24 +196,15 @@ Camera::~Camera() = default;
void Camera::compute_auto_viewplane()
{
if (camera_type == CAMERA_PANORAMA || camera_type == CAMERA_CUSTOM) {
viewplane.left = 0.0f;
viewplane.right = 1.0f;
viewplane.bottom = 0.0f;
viewplane.top = 1.0f;
viewplane = BoundBox2D();
}
else {
const float aspect = (float)full_width / (float)full_height;
if (full_width >= full_height) {
viewplane.left = -aspect;
viewplane.right = aspect;
viewplane.bottom = -1.0f;
viewplane.top = 1.0f;
viewplane = BoundBox2D(make_float2(aspect, 1.0f));
}
else {
viewplane.left = -1.0f;
viewplane.right = 1.0f;
viewplane.bottom = -1.0f / aspect;
viewplane.top = 1.0f / aspect;
viewplane = BoundBox2D(make_float2(1.0f, 1.0f / aspect));
}
}
}

View File

@@ -202,6 +202,11 @@ class BoundBox2D {
BoundBox2D() = default;
BoundBox2D(const float2 radius)
: left(-radius.x), right(radius.x), bottom(-radius.y), top(radius.y)
{
}
bool operator==(const BoundBox2D &other) const
{
return (left == other.left && right == other.right && bottom == other.bottom &&
@@ -265,6 +270,18 @@ class BoundBox2D {
return result;
}
BoundBox2D offset(const float2 offset) const
{
BoundBox2D result;
result.left = left + offset.x;
result.right = right + offset.x;
result.bottom = bottom + offset.y;
result.top = top + offset.y;
return result;
}
};
CCL_NAMESPACE_END