From d7fbe03a8a128408c86687ef34273adddccdb347 Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Fri, 4 May 2012 16:20:51 +0000 Subject: Fisheye Camera for Cycles For sample images see: http://www.dalaifelinto.com/?p=399 (equisolid) http://www.dalaifelinto.com/?p=389 (equidistant) The 'use_panorama' option is now part of a new Camera type: 'Panorama'. Created two other panorama cameras: - Equisolid: most of lens in the market simulate this lens - e.g. Nikon, Canon, ...) this works as a real lens up to an extent. The final result takes the sensor dimensions into account also. .:. to simulate a Nikon DX2S with a 10.5mm lens do: sensor: 23.7 x 15.7 fisheye lens: 10.5 fisheye fov: 180 render dimensions: 4288 x 2848 - Equidistant: this is not a real lens model. Although the old equidistant lens simulate this lens. The result is always as a circular fisheye that takes the whole sensor (in other words, it doesn't take the sensor into consideration). This is perfect for fulldomes ;) For the UI we have 10 to 360 as soft values and 10 to 3600 as hard values (because we can). Reference material: http://www.hdrlabs.com/tutorials/downloads_files/HDRI%20for%20CGI.pdf http://www.bobatkins.com/photography/technical/field_of_view.html Note, this is not a real simulation of the light path through the lens. The ideal solution would be this: https://graphics.stanford.edu/wikis/cs348b-11/Assignment3 http://www.graphics.stanford.edu/papers/camera/ Thanks Brecht for the fix, suggestions and code review. Kudos for the dome community for keeping me stimulated on the topic since 2009 ;) Patch partly implemented during lab time at VisGraf, IMPA - Rio de Janeiro. --- intern/cycles/app/cycles_xml.cpp | 18 +++++++++-- intern/cycles/blender/addon/enums.py | 6 ++++ intern/cycles/blender/addon/properties.py | 19 ++++++++++++ intern/cycles/blender/blender_camera.cpp | 50 +++++++++++++++++++++++++++--- intern/cycles/kernel/kernel_camera.h | 42 +++++++++++++++++-------- intern/cycles/kernel/kernel_montecarlo.h | 51 +++++++++++++++++++++++++++++++ intern/cycles/kernel/kernel_path.h | 7 ++++- intern/cycles/kernel/kernel_types.h | 21 +++++++++++-- intern/cycles/render/camera.cpp | 22 ++++++++++++- intern/cycles/render/camera.h | 9 ++++++ 10 files changed, 222 insertions(+), 23 deletions(-) (limited to 'intern') diff --git a/intern/cycles/app/cycles_xml.cpp b/intern/cycles/app/cycles_xml.cpp index 82f1338d86b..db3592f1227 100644 --- a/intern/cycles/app/cycles_xml.cpp +++ b/intern/cycles/app/cycles_xml.cpp @@ -290,8 +290,22 @@ static void xml_read_camera(const XMLReadState& state, pugi::xml_node node) cam->type = CAMERA_ORTHOGRAPHIC; else if(xml_equal_string(node, "type", "perspective")) cam->type = CAMERA_PERSPECTIVE; - else if(xml_equal_string(node, "type", "environment")) - cam->type = CAMERA_ENVIRONMENT; + else if(xml_equal_string(node, "type", "panorama")) + cam->type = CAMERA_PANORAMA; + + if(xml_equal_string(node, "panorama_type", "equirectangular")) + cam->panorama_type = PANORAMA_EQUIRECTANGULAR; + else if(xml_equal_string(node, "panorama_type", "fisheye_equidistant")) + cam->panorama_type = PANORAMA_FISHEYE_EQUIDISTANT; + else if(xml_equal_string(node, "panorama_type", "fisheye_equisolid")) + cam->panorama_type = PANORAMA_FISHEYE_EQUISOLID; + + xml_read_float(&cam->fisheye_fov, node, "fisheye_fov"); + xml_read_float(&cam->fisheye_lens, node, "fisheye_lens"); + + xml_read_float(&cam->sensorwidth, node, "sensorwidth"); + xml_read_float(&cam->sensorheight, node, "sensorheight"); + cam->matrix = state.tfm; diff --git a/intern/cycles/blender/addon/enums.py b/intern/cycles/blender/addon/enums.py index b4b1646c10d..6cc3010eb0e 100644 --- a/intern/cycles/blender/addon/enums.py +++ b/intern/cycles/blender/addon/enums.py @@ -54,3 +54,9 @@ aperture_types = ( ('RADIUS', "Radius", "Directly change the size of the aperture"), ('FSTOP', "F/stop", "Change the size of the aperture by f/stops"), ) + +panorama_types = ( + ('EQUIRECTANGULAR', "Equirectangular", "Render the scene with a spherical camera, also known as Lat Long panorama"), + ('FISHEYE_EQUIDISTANT', "Fisheye Equidistant", "Ideal for fulldomes, ignore the sensor dimensions"), + ('FISHEYE_EQUISOLID', "Fisheye Equisolid", "Similar to most fisheye modern lens, take sensor dimensions into consideration"), + ) diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 35f97bf629f..fb066a3a939 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -278,6 +278,25 @@ class CyclesCameraSettings(bpy.types.PropertyGroup): subtype='ANGLE', default=0, ) + cls.panorama_type = EnumProperty( + name="Panorama Type", + description="Distortion to use for the calculation", + items=enums.panorama_types, + default='FISHEYE_EQUISOLID', + ) + cls.fisheye_fov = FloatProperty( + name="Field of View", + description="Field of view for the fisheye lens", + min=0.1745, soft_max=2*math.pi, max=10.0*math.pi, + subtype='ANGLE', + default=math.pi, + ) + cls.fisheye_lens = FloatProperty( + name="Fisheye Lens", + description="Lens focal length (mm))", + min=0.01, soft_max=15.0, max=100.0, + default=10.5, + ) @classmethod def unregister(cls): diff --git a/intern/cycles/blender/blender_camera.cpp b/intern/cycles/blender/blender_camera.cpp index 55a32d8fc10..bdd02bb5086 100644 --- a/intern/cycles/blender/blender_camera.cpp +++ b/intern/cycles/blender/blender_camera.cpp @@ -48,6 +48,10 @@ struct BlenderCamera { float2 pixelaspect; + PanoramaType panorama_type; + float fisheye_fov; + float fisheye_lens; + enum { AUTO, HORIZONTAL, VERTICAL } sensor_fit; float sensor_width; float sensor_height; @@ -94,9 +98,37 @@ static void blender_camera_from_object(BlenderCamera *bcam, BL::Object b_ob) bcam->nearclip = b_camera.clip_start(); bcam->farclip = b_camera.clip_end(); - bcam->type = (b_camera.type() == BL::Camera::type_ORTHO)? CAMERA_ORTHOGRAPHIC: CAMERA_PERSPECTIVE; - if(bcam->type == CAMERA_PERSPECTIVE && b_camera.use_panorama()) - bcam->type = CAMERA_ENVIRONMENT; + switch(b_camera.type()) + { + case BL::Camera::type_ORTHO: + bcam->type = CAMERA_ORTHOGRAPHIC; + break; + case BL::Camera::type_PANO: + bcam->type = CAMERA_PANORAMA; + break; + case BL::Camera::type_PERSP: + default: + bcam->type = CAMERA_PERSPECTIVE; + break; + } + + switch(RNA_enum_get(&ccamera, "panorama_type")) + { + case 1: + bcam->panorama_type = PANORAMA_FISHEYE_EQUIDISTANT; + break; + case 2: + bcam->panorama_type = PANORAMA_FISHEYE_EQUISOLID; + break; + case 0: + default: + bcam->panorama_type = PANORAMA_EQUIRECTANGULAR; + break; + } + + bcam->fisheye_fov = RNA_float_get(&ccamera, "fisheye_fov"); + bcam->fisheye_lens = RNA_float_get(&ccamera, "fisheye_lens"); + bcam->ortho_scale = b_camera.ortho_scale(); bcam->lens = b_camera.lens(); @@ -138,7 +170,7 @@ static Transform blender_camera_matrix(const Transform& tfm, CameraType type) { Transform result; - if(type == CAMERA_ENVIRONMENT) { + if(type == CAMERA_PANORAMA) { /* make it so environment camera needs to be pointed in the direction of the positive x-axis to match an environment texture, this way it is looking at the center of the texture */ @@ -172,6 +204,9 @@ static void blender_camera_sync(Camera *cam, BlenderCamera *bcam, int width, int bool horizontal_fit; float sensor_size; + cam->sensorwidth = bcam->sensor_width; + cam->sensorheight = bcam->sensor_height; + if(bcam->sensor_fit == BlenderCamera::AUTO) { horizontal_fit = (xratio > yratio); sensor_size = bcam->sensor_width; @@ -203,7 +238,7 @@ static void blender_camera_sync(Camera *cam, BlenderCamera *bcam, int width, int aspectratio = bcam->ortho_scale/2.0f; } - if(bcam->type == CAMERA_ENVIRONMENT) { + if(bcam->type == CAMERA_PANORAMA) { /* set viewplane */ cam->left = 0.0f; cam->right = 1.0f; @@ -240,6 +275,11 @@ static void blender_camera_sync(Camera *cam, BlenderCamera *bcam, int width, int /* type */ cam->type = bcam->type; + /* panorama */ + cam->panorama_type = bcam->panorama_type; + cam->fisheye_fov = bcam->fisheye_fov; + cam->fisheye_lens = bcam->fisheye_lens; + /* perspective */ cam->fov = 2.0f*atan((0.5f*sensor_size)/bcam->lens/aspectratio); cam->focaldistance = bcam->focaldistance; diff --git a/intern/cycles/kernel/kernel_camera.h b/intern/cycles/kernel/kernel_camera.h index e4b10f6151c..6d49fd96dd7 100644 --- a/intern/cycles/kernel/kernel_camera.h +++ b/intern/cycles/kernel/kernel_camera.h @@ -132,16 +132,40 @@ __device void camera_sample_orthographic(KernelGlobals *kg, float raster_x, floa #endif } -/* Environment Camera */ +/* Panorama Camera */ -__device void camera_sample_environment(KernelGlobals *kg, float raster_x, float raster_y, Ray *ray) +__device float3 panorama_to_direction(KernelGlobals *kg, float u, float v, Ray *ray) +{ + switch (kernel_data.cam.panorama_type) { + case PANORAMA_EQUIRECTANGULAR: + return equirectangular_to_direction(u, v); + break; + case PANORAMA_FISHEYE_EQUIDISTANT: + return fisheye_to_direction(u, v, kernel_data.cam.fisheye_fov, ray); + break; + case PANORAMA_FISHEYE_EQUISOLID: + default: + return fisheye_equisolid_to_direction(u, v, kernel_data.cam.fisheye_lens, kernel_data.cam.fisheye_fov, kernel_data.cam.sensorwidth, kernel_data.cam.sensorheight, ray); + break; + } +} + +__device void camera_sample_panorama(KernelGlobals *kg, float raster_x, float raster_y, Ray *ray) { Transform rastertocamera = kernel_data.cam.rastertocamera; float3 Pcamera = transform_perspective(&rastertocamera, make_float3(raster_x, raster_y, 0.0f)); /* create ray form raster position */ ray->P = make_float3(0.0f, 0.0f, 0.0f); - ray->D = equirectangular_to_direction(Pcamera.x, Pcamera.y); + +#ifdef __CAMERA_CLIPPING__ + /* clipping */ + ray->t = kernel_data.cam.cliplength; +#else + ray->t = FLT_MAX; +#endif + + ray->D = panorama_to_direction(kg, Pcamera.x, Pcamera.y, ray); /* transform ray from camera to world */ Transform cameratoworld = kernel_data.cam.cameratoworld; @@ -161,17 +185,11 @@ __device void camera_sample_environment(KernelGlobals *kg, float raster_x, float ray->dP.dy = make_float3(0.0f, 0.0f, 0.0f); Pcamera = transform_perspective(&rastertocamera, make_float3(raster_x + 1.0f, raster_y, 0.0f)); - ray->dD.dx = normalize(transform_direction(&cameratoworld, equirectangular_to_direction(Pcamera.x, Pcamera.y))) - ray->D; + ray->dD.dx = normalize(transform_direction(&cameratoworld, panorama_to_direction(kg, Pcamera.x, Pcamera.y, ray))) - ray->D; Pcamera = transform_perspective(&rastertocamera, make_float3(raster_x, raster_y + 1.0f, 0.0f)); - ray->dD.dy = normalize(transform_direction(&cameratoworld, equirectangular_to_direction(Pcamera.x, Pcamera.y))) - ray->D; -#endif + ray->dD.dy = normalize(transform_direction(&cameratoworld, panorama_to_direction(kg, Pcamera.x, Pcamera.y, ray))) - ray->D; -#ifdef __CAMERA_CLIPPING__ - /* clipping */ - ray->t = kernel_data.cam.cliplength; -#else - ray->t = FLT_MAX; #endif } @@ -198,7 +216,7 @@ __device void camera_sample(KernelGlobals *kg, int x, int y, float filter_u, flo else if(kernel_data.cam.type == CAMERA_ORTHOGRAPHIC) camera_sample_orthographic(kg, raster_x, raster_y, ray); else - camera_sample_environment(kg, raster_x, raster_y, ray); + camera_sample_panorama(kg, raster_x, raster_y, ray); } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_montecarlo.h b/intern/cycles/kernel/kernel_montecarlo.h index 68f007cfd97..3fb4d41ce06 100644 --- a/intern/cycles/kernel/kernel_montecarlo.h +++ b/intern/cycles/kernel/kernel_montecarlo.h @@ -224,6 +224,57 @@ __device float3 equirectangular_to_direction(float u, float v) cos(theta)); } +/* Fisheye <- Cartesian direction */ + +__device float3 fisheye_to_direction(float u, float v, float fov, Ray *ray) +{ + u = (u - 0.5f) * 2.f; + v = (v - 0.5f) * 2.f; + + float r = sqrt(u*u + v*v); + + if (r > 1.0) { + ray->t = 0.f; + return make_float3(0.f,0.f,0.f); + } + + float phi = acosf((r!=0.f)?u/r:0.f); + float theta = asinf(r) * (fov / M_PI_F); + + if (v < 0.f) phi = -phi; + + return make_float3( + cosf(theta), + -cosf(phi)*sinf(theta), + sinf(phi)*sinf(theta) + ); +} + +__device float3 fisheye_equisolid_to_direction(float u, float v, float lens, float fov, float width, float height, Ray *ray) +{ + u = (u - 0.5f) * width; + v = (v - 0.5f) * height; + + float rmax = 2.f * lens * sinf(fov * 0.5f); + float r = sqrt(u*u + v*v); + + if (r > rmax) { + ray->t = 0.f; + return make_float3(0.f,0.f,0.f); + } + + float phi = acosf((r!=0.f)?u/r:0.f); + float theta = 2.f * asinf(r/(2.f * lens)); + + if (v < 0.f) phi = -phi; + + return make_float3( + cosf(theta), + -cosf(phi)*sinf(theta), + sinf(phi)*sinf(theta) + ); +} + /* Mirror Ball <-> Cartesion direction */ __device float3 mirrorball_to_direction(float u, float v) diff --git a/intern/cycles/kernel/kernel_path.h b/intern/cycles/kernel/kernel_path.h index 87d996ef9e2..d53951a1f34 100644 --- a/intern/cycles/kernel/kernel_path.h +++ b/intern/cycles/kernel/kernel_path.h @@ -474,7 +474,12 @@ __device void kernel_path_trace(KernelGlobals *kg, camera_sample(kg, x, y, filter_u, filter_v, lens_u, lens_v, time, &ray); /* integrate */ - float4 L = kernel_path_integrate(kg, &rng, sample, ray, buffer); + float4 L; + + if (ray.t != 0.f) + L = kernel_path_integrate(kg, &rng, sample, ray, buffer); + else + L = make_float4(0.f, 0.f, 0.f, 0.f); /* accumulate result in output buffer */ kernel_write_pass_float4(buffer, sample, L); diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 85ee16fc5c6..25ff7f73888 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -268,7 +268,15 @@ typedef enum LightType { enum CameraType { CAMERA_PERSPECTIVE, CAMERA_ORTHOGRAPHIC, - CAMERA_ENVIRONMENT + CAMERA_PANORAMA +}; + +/* Panorama Type */ + +enum PanoramaType { + PANORAMA_EQUIRECTANGULAR, + PANORAMA_FISHEYE_EQUIDISTANT, + PANORAMA_FISHEYE_EQUISOLID }; /* Differential */ @@ -452,7 +460,11 @@ typedef struct ShaderData { typedef struct KernelCamera { /* type */ int type; - int pad1, pad2, pad3; + + /* panorama */ + int panorama_type; + float fisheye_fov; + float fisheye_lens; /* matrices */ Transform cameratoworld; @@ -476,6 +488,11 @@ typedef struct KernelCamera { float nearclip; float cliplength; + /* sensor size */ + float sensorwidth; + float sensorheight; + int pad1, pad2; + /* more matrices */ Transform screentoworld; Transform rastertoworld; diff --git a/intern/cycles/render/camera.cpp b/intern/cycles/render/camera.cpp index f0b77871130..95405519cc0 100644 --- a/intern/cycles/render/camera.cpp +++ b/intern/cycles/render/camera.cpp @@ -39,8 +39,14 @@ Camera::Camera() use_motion = false; type = CAMERA_PERSPECTIVE; + panorama_type = PANORAMA_EQUIRECTANGULAR; + fisheye_fov = M_PI_F; + fisheye_lens = 10.5f; fov = M_PI_F/4.0f; + sensorwidth = 0.036; + sensorheight = 0.024; + nearclip = 1e-5f; farclip = 1e5f; @@ -181,6 +187,15 @@ void Camera::device_update(Device *device, DeviceScene *dscene, Scene *scene) /* type */ kcam->type = type; + /* panorama */ + kcam->panorama_type = panorama_type; + kcam->fisheye_fov = fisheye_fov; + kcam->fisheye_lens = fisheye_lens; + + /* sensor size */ + kcam->sensorwidth = sensorwidth; + kcam->sensorheight = sensorheight; + /* store differentials */ kcam->dx = float3_to_float4(dx); kcam->dy = float3_to_float4(dy); @@ -208,6 +223,8 @@ bool Camera::modified(const Camera& cam) (fov == cam.fov) && (nearclip == cam.nearclip) && (farclip == cam.farclip) && + (sensorwidth == cam.sensorwidth) && + (sensorheight == cam.sensorheight) && // modified for progressive render // (width == cam.width) && // (height == cam.height) && @@ -217,7 +234,10 @@ bool Camera::modified(const Camera& cam) (top == cam.top) && (matrix == cam.matrix) && (motion == cam.motion) && - (use_motion == cam.use_motion)); + (use_motion == cam.use_motion) && + (panorama_type == cam.panorama_type) && + (fisheye_fov == cam.fisheye_fov) && + (fisheye_lens == cam.fisheye_lens)); } void Camera::tag_update() diff --git a/intern/cycles/render/camera.h b/intern/cycles/render/camera.h index 935489711c8..7a09b5981e4 100644 --- a/intern/cycles/render/camera.h +++ b/intern/cycles/render/camera.h @@ -50,6 +50,15 @@ public: CameraType type; float fov; + /* panorama */ + PanoramaType panorama_type; + float fisheye_fov; + float fisheye_lens; + + /* sensor */ + float sensorwidth; + float sensorheight; + /* clipping */ float nearclip; float farclip; -- cgit v1.2.3