diff options
Diffstat (limited to 'intern/cycles/kernel/camera/camera.h')
-rw-r--r-- | intern/cycles/kernel/camera/camera.h | 521 |
1 files changed, 521 insertions, 0 deletions
diff --git a/intern/cycles/kernel/camera/camera.h b/intern/cycles/kernel/camera/camera.h new file mode 100644 index 00000000000..66bc25bb879 --- /dev/null +++ b/intern/cycles/kernel/camera/camera.h @@ -0,0 +1,521 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/camera/camera_projection.h" +#include "kernel/sample/sample_mapping.h" +#include "kernel/util/util_differential.h" +#include "kernel/util/util_lookup_table.h" + +CCL_NAMESPACE_BEGIN + +/* Perspective Camera */ + +ccl_device float2 camera_sample_aperture(ccl_constant KernelCamera *cam, float u, float v) +{ + float blades = cam->blades; + float2 bokeh; + + if (blades == 0.0f) { + /* sample disk */ + bokeh = concentric_sample_disk(u, v); + } + else { + /* sample polygon */ + float rotation = cam->bladesrotation; + bokeh = regular_polygon_sample(blades, rotation, u, v); + } + + /* anamorphic lens bokeh */ + bokeh.x *= cam->inv_aperture_ratio; + + return bokeh; +} + +ccl_device void camera_sample_perspective(KernelGlobals kg, + float raster_x, + float raster_y, + float lens_u, + float lens_v, + ccl_private Ray *ray) +{ + /* create ray form raster position */ + ProjectionTransform rastertocamera = kernel_data.cam.rastertocamera; + float3 raster = make_float3(raster_x, raster_y, 0.0f); + float3 Pcamera = transform_perspective(&rastertocamera, raster); + +#ifdef __CAMERA_MOTION__ + if (kernel_data.cam.have_perspective_motion) { + /* TODO(sergey): Currently we interpolate projected coordinate which + * gives nice looking result and which is simple, but is in fact a bit + * different comparing to constructing projective matrix from an + * interpolated field of view. + */ + if (ray->time < 0.5f) { + ProjectionTransform rastertocamera_pre = kernel_data.cam.perspective_pre; + float3 Pcamera_pre = transform_perspective(&rastertocamera_pre, raster); + Pcamera = interp(Pcamera_pre, Pcamera, ray->time * 2.0f); + } + else { + ProjectionTransform rastertocamera_post = kernel_data.cam.perspective_post; + float3 Pcamera_post = transform_perspective(&rastertocamera_post, raster); + Pcamera = interp(Pcamera, Pcamera_post, (ray->time - 0.5f) * 2.0f); + } + } +#endif + + float3 P = zero_float3(); + float3 D = Pcamera; + + /* modify ray for depth of field */ + float aperturesize = kernel_data.cam.aperturesize; + + if (aperturesize > 0.0f) { + /* sample point on aperture */ + float2 lensuv = camera_sample_aperture(&kernel_data.cam, lens_u, lens_v) * aperturesize; + + /* compute point on plane of focus */ + float ft = kernel_data.cam.focaldistance / D.z; + float3 Pfocus = D * ft; + + /* update ray for effect of lens */ + P = make_float3(lensuv.x, lensuv.y, 0.0f); + D = normalize(Pfocus - P); + } + + /* transform ray from camera to world */ + Transform cameratoworld = kernel_data.cam.cameratoworld; + +#ifdef __CAMERA_MOTION__ + if (kernel_data.cam.num_motion_steps) { + transform_motion_array_interpolate(&cameratoworld, + kernel_tex_array(__camera_motion), + kernel_data.cam.num_motion_steps, + ray->time); + } +#endif + + P = transform_point(&cameratoworld, P); + D = normalize(transform_direction(&cameratoworld, D)); + + bool use_stereo = kernel_data.cam.interocular_offset != 0.0f; + if (!use_stereo) { + /* No stereo */ + ray->P = P; + ray->D = D; + +#ifdef __RAY_DIFFERENTIALS__ + float3 Dcenter = transform_direction(&cameratoworld, Pcamera); + float3 Dcenter_normalized = normalize(Dcenter); + + /* TODO: can this be optimized to give compact differentials directly? */ + ray->dP = differential_zero_compact(); + differential3 dD; + dD.dx = normalize(Dcenter + float4_to_float3(kernel_data.cam.dx)) - Dcenter_normalized; + dD.dy = normalize(Dcenter + float4_to_float3(kernel_data.cam.dy)) - Dcenter_normalized; + ray->dD = differential_make_compact(dD); +#endif + } + else { + /* Spherical stereo */ + spherical_stereo_transform(&kernel_data.cam, &P, &D); + ray->P = P; + ray->D = D; + +#ifdef __RAY_DIFFERENTIALS__ + /* Ray differentials, computed from scratch using the raster coordinates + * because we don't want to be affected by depth of field. We compute + * ray origin and direction for the center and two neighboring pixels + * and simply take their differences. */ + float3 Pnostereo = transform_point(&cameratoworld, zero_float3()); + + float3 Pcenter = Pnostereo; + float3 Dcenter = Pcamera; + Dcenter = normalize(transform_direction(&cameratoworld, Dcenter)); + spherical_stereo_transform(&kernel_data.cam, &Pcenter, &Dcenter); + + float3 Px = Pnostereo; + float3 Dx = transform_perspective(&rastertocamera, + make_float3(raster_x + 1.0f, raster_y, 0.0f)); + Dx = normalize(transform_direction(&cameratoworld, Dx)); + spherical_stereo_transform(&kernel_data.cam, &Px, &Dx); + + differential3 dP, dD; + + dP.dx = Px - Pcenter; + dD.dx = Dx - Dcenter; + + float3 Py = Pnostereo; + float3 Dy = transform_perspective(&rastertocamera, + make_float3(raster_x, raster_y + 1.0f, 0.0f)); + Dy = normalize(transform_direction(&cameratoworld, Dy)); + spherical_stereo_transform(&kernel_data.cam, &Py, &Dy); + + dP.dy = Py - Pcenter; + dD.dy = Dy - Dcenter; + ray->dD = differential_make_compact(dD); + ray->dP = differential_make_compact(dP); +#endif + } + +#ifdef __CAMERA_CLIPPING__ + /* clipping */ + float z_inv = 1.0f / normalize(Pcamera).z; + float nearclip = kernel_data.cam.nearclip * z_inv; + ray->P += nearclip * ray->D; + ray->dP += nearclip * ray->dD; + ray->t = kernel_data.cam.cliplength * z_inv; +#else + ray->t = FLT_MAX; +#endif +} + +/* Orthographic Camera */ +ccl_device void camera_sample_orthographic(KernelGlobals kg, + float raster_x, + float raster_y, + float lens_u, + float lens_v, + ccl_private Ray *ray) +{ + /* create ray form raster position */ + ProjectionTransform rastertocamera = kernel_data.cam.rastertocamera; + float3 Pcamera = transform_perspective(&rastertocamera, make_float3(raster_x, raster_y, 0.0f)); + + float3 P; + float3 D = make_float3(0.0f, 0.0f, 1.0f); + + /* modify ray for depth of field */ + float aperturesize = kernel_data.cam.aperturesize; + + if (aperturesize > 0.0f) { + /* sample point on aperture */ + float2 lensuv = camera_sample_aperture(&kernel_data.cam, lens_u, lens_v) * aperturesize; + + /* compute point on plane of focus */ + float3 Pfocus = D * kernel_data.cam.focaldistance; + + /* update ray for effect of lens */ + float3 lensuvw = make_float3(lensuv.x, lensuv.y, 0.0f); + P = Pcamera + lensuvw; + D = normalize(Pfocus - lensuvw); + } + else { + P = Pcamera; + } + /* transform ray from camera to world */ + Transform cameratoworld = kernel_data.cam.cameratoworld; + +#ifdef __CAMERA_MOTION__ + if (kernel_data.cam.num_motion_steps) { + transform_motion_array_interpolate(&cameratoworld, + kernel_tex_array(__camera_motion), + kernel_data.cam.num_motion_steps, + ray->time); + } +#endif + + ray->P = transform_point(&cameratoworld, P); + ray->D = normalize(transform_direction(&cameratoworld, D)); + +#ifdef __RAY_DIFFERENTIALS__ + /* ray differential */ + differential3 dP; + dP.dx = float4_to_float3(kernel_data.cam.dx); + dP.dy = float4_to_float3(kernel_data.cam.dx); + + ray->dP = differential_make_compact(dP); + ray->dD = differential_zero_compact(); +#endif + +#ifdef __CAMERA_CLIPPING__ + /* clipping */ + ray->t = kernel_data.cam.cliplength; +#else + ray->t = FLT_MAX; +#endif +} + +/* Panorama Camera */ + +ccl_device_inline void camera_sample_panorama(ccl_constant KernelCamera *cam, +#ifdef __CAMERA_MOTION__ + ccl_global const DecomposedTransform *cam_motion, +#endif + float raster_x, + float raster_y, + float lens_u, + float lens_v, + ccl_private Ray *ray) +{ + ProjectionTransform rastertocamera = cam->rastertocamera; + float3 Pcamera = transform_perspective(&rastertocamera, make_float3(raster_x, raster_y, 0.0f)); + + /* create ray form raster position */ + float3 P = zero_float3(); + float3 D = panorama_to_direction(cam, Pcamera.x, Pcamera.y); + + /* indicates ray should not receive any light, outside of the lens */ + if (is_zero(D)) { + ray->t = 0.0f; + return; + } + + /* modify ray for depth of field */ + float aperturesize = cam->aperturesize; + + if (aperturesize > 0.0f) { + /* sample point on aperture */ + float2 lensuv = camera_sample_aperture(cam, lens_u, lens_v) * aperturesize; + + /* compute point on plane of focus */ + float3 Dfocus = normalize(D); + float3 Pfocus = Dfocus * cam->focaldistance; + + /* calculate orthonormal coordinates perpendicular to Dfocus */ + float3 U, V; + U = normalize(make_float3(1.0f, 0.0f, 0.0f) - Dfocus.x * Dfocus); + V = normalize(cross(Dfocus, U)); + + /* update ray for effect of lens */ + P = U * lensuv.x + V * lensuv.y; + D = normalize(Pfocus - P); + } + + /* transform ray from camera to world */ + Transform cameratoworld = cam->cameratoworld; + +#ifdef __CAMERA_MOTION__ + if (cam->num_motion_steps) { + transform_motion_array_interpolate( + &cameratoworld, cam_motion, cam->num_motion_steps, ray->time); + } +#endif + + P = transform_point(&cameratoworld, P); + D = normalize(transform_direction(&cameratoworld, D)); + + /* Stereo transform */ + bool use_stereo = cam->interocular_offset != 0.0f; + if (use_stereo) { + spherical_stereo_transform(cam, &P, &D); + } + + ray->P = P; + ray->D = D; + +#ifdef __RAY_DIFFERENTIALS__ + /* Ray differentials, computed from scratch using the raster coordinates + * because we don't want to be affected by depth of field. We compute + * ray origin and direction for the center and two neighboring pixels + * and simply take their differences. */ + float3 Pcenter = Pcamera; + float3 Dcenter = panorama_to_direction(cam, Pcenter.x, Pcenter.y); + Pcenter = transform_point(&cameratoworld, Pcenter); + Dcenter = normalize(transform_direction(&cameratoworld, Dcenter)); + if (use_stereo) { + spherical_stereo_transform(cam, &Pcenter, &Dcenter); + } + + float3 Px = transform_perspective(&rastertocamera, make_float3(raster_x + 1.0f, raster_y, 0.0f)); + float3 Dx = panorama_to_direction(cam, Px.x, Px.y); + Px = transform_point(&cameratoworld, Px); + Dx = normalize(transform_direction(&cameratoworld, Dx)); + if (use_stereo) { + spherical_stereo_transform(cam, &Px, &Dx); + } + + differential3 dP, dD; + dP.dx = Px - Pcenter; + dD.dx = Dx - Dcenter; + + float3 Py = transform_perspective(&rastertocamera, make_float3(raster_x, raster_y + 1.0f, 0.0f)); + float3 Dy = panorama_to_direction(cam, Py.x, Py.y); + Py = transform_point(&cameratoworld, Py); + Dy = normalize(transform_direction(&cameratoworld, Dy)); + if (use_stereo) { + spherical_stereo_transform(cam, &Py, &Dy); + } + + dP.dy = Py - Pcenter; + dD.dy = Dy - Dcenter; + ray->dD = differential_make_compact(dD); + ray->dP = differential_make_compact(dP); +#endif + +#ifdef __CAMERA_CLIPPING__ + /* clipping */ + float nearclip = cam->nearclip; + ray->P += nearclip * ray->D; + ray->dP += nearclip * ray->dD; + ray->t = cam->cliplength; +#else + ray->t = FLT_MAX; +#endif +} + +/* Common */ + +ccl_device_inline void camera_sample(KernelGlobals kg, + int x, + int y, + float filter_u, + float filter_v, + float lens_u, + float lens_v, + float time, + ccl_private Ray *ray) +{ + /* pixel filter */ + int filter_table_offset = kernel_data.film.filter_table_offset; + float raster_x = x + lookup_table_read(kg, filter_u, filter_table_offset, FILTER_TABLE_SIZE); + float raster_y = y + lookup_table_read(kg, filter_v, filter_table_offset, FILTER_TABLE_SIZE); + +#ifdef __CAMERA_MOTION__ + /* motion blur */ + if (kernel_data.cam.shuttertime == -1.0f) { + ray->time = 0.5f; + } + else { + /* TODO(sergey): Such lookup is unneeded when there's rolling shutter + * effect in use but rolling shutter duration is set to 0.0. + */ + const int shutter_table_offset = kernel_data.cam.shutter_table_offset; + ray->time = lookup_table_read(kg, time, shutter_table_offset, SHUTTER_TABLE_SIZE); + /* TODO(sergey): Currently single rolling shutter effect type only + * where scan-lines are acquired from top to bottom and whole scan-line + * is acquired at once (no delay in acquisition happens between pixels + * of single scan-line). + * + * Might want to support more models in the future. + */ + if (kernel_data.cam.rolling_shutter_type) { + /* Time corresponding to a fully rolling shutter only effect: + * top of the frame is time 0.0, bottom of the frame is time 1.0. + */ + const float time = 1.0f - (float)y / kernel_data.cam.height; + const float duration = kernel_data.cam.rolling_shutter_duration; + if (duration != 0.0f) { + /* This isn't fully physical correct, but lets us to have simple + * controls in the interface. The idea here is basically sort of + * linear interpolation between how much rolling shutter effect + * exist on the frame and how much of it is a motion blur effect. + */ + ray->time = (ray->time - 0.5f) * duration; + ray->time += (time - 0.5f) * (1.0f - duration) + 0.5f; + } + else { + ray->time = time; + } + } + } +#endif + + /* sample */ + if (kernel_data.cam.type == CAMERA_PERSPECTIVE) { + camera_sample_perspective(kg, raster_x, raster_y, lens_u, lens_v, ray); + } + else if (kernel_data.cam.type == CAMERA_ORTHOGRAPHIC) { + camera_sample_orthographic(kg, raster_x, raster_y, lens_u, lens_v, ray); + } + else { +#ifdef __CAMERA_MOTION__ + ccl_global const DecomposedTransform *cam_motion = kernel_tex_array(__camera_motion); + camera_sample_panorama(&kernel_data.cam, cam_motion, raster_x, raster_y, lens_u, lens_v, ray); +#else + camera_sample_panorama(&kernel_data.cam, raster_x, raster_y, lens_u, lens_v, ray); +#endif + } +} + +/* Utilities */ + +ccl_device_inline float3 camera_position(KernelGlobals kg) +{ + Transform cameratoworld = kernel_data.cam.cameratoworld; + return make_float3(cameratoworld.x.w, cameratoworld.y.w, cameratoworld.z.w); +} + +ccl_device_inline float camera_distance(KernelGlobals kg, float3 P) +{ + Transform cameratoworld = kernel_data.cam.cameratoworld; + float3 camP = make_float3(cameratoworld.x.w, cameratoworld.y.w, cameratoworld.z.w); + + if (kernel_data.cam.type == CAMERA_ORTHOGRAPHIC) { + float3 camD = make_float3(cameratoworld.x.z, cameratoworld.y.z, cameratoworld.z.z); + return fabsf(dot((P - camP), camD)); + } + else { + return len(P - camP); + } +} + +ccl_device_inline float camera_z_depth(KernelGlobals kg, float3 P) +{ + if (kernel_data.cam.type != CAMERA_PANORAMA) { + Transform worldtocamera = kernel_data.cam.worldtocamera; + return transform_point(&worldtocamera, P).z; + } + else { + Transform cameratoworld = kernel_data.cam.cameratoworld; + float3 camP = make_float3(cameratoworld.x.w, cameratoworld.y.w, cameratoworld.z.w); + return len(P - camP); + } +} + +ccl_device_inline float3 camera_direction_from_point(KernelGlobals kg, float3 P) +{ + Transform cameratoworld = kernel_data.cam.cameratoworld; + + if (kernel_data.cam.type == CAMERA_ORTHOGRAPHIC) { + float3 camD = make_float3(cameratoworld.x.z, cameratoworld.y.z, cameratoworld.z.z); + return -camD; + } + else { + float3 camP = make_float3(cameratoworld.x.w, cameratoworld.y.w, cameratoworld.z.w); + return normalize(camP - P); + } +} + +ccl_device_inline float3 camera_world_to_ndc(KernelGlobals kg, + ccl_private ShaderData *sd, + float3 P) +{ + if (kernel_data.cam.type != CAMERA_PANORAMA) { + /* perspective / ortho */ + if (sd->object == PRIM_NONE && kernel_data.cam.type == CAMERA_PERSPECTIVE) + P += camera_position(kg); + + ProjectionTransform tfm = kernel_data.cam.worldtondc; + return transform_perspective(&tfm, P); + } + else { + /* panorama */ + Transform tfm = kernel_data.cam.worldtocamera; + + if (sd->object != OBJECT_NONE) + P = normalize(transform_point(&tfm, P)); + else + P = normalize(transform_direction(&tfm, P)); + + float2 uv = direction_to_panorama(&kernel_data.cam, P); + + return make_float3(uv.x, uv.y, 0.0f); + } +} + +CCL_NAMESPACE_END |