Cycles: support for camera rendering an environment map with equirectangular
authorBrecht Van Lommel <brechtvanlommel@pandora.be>
Tue, 28 Feb 2012 16:44:54 +0000 (16:44 +0000)
committerBrecht Van Lommel <brechtvanlommel@pandora.be>
Tue, 28 Feb 2012 16:44:54 +0000 (16:44 +0000)
environment map, by enabling the Panorama option in the camera.

http://wiki.blender.org/index.php/Doc:2.6/Manual/Render/Cycles/Camera#Panorama

The focal length or sensor settings are not used, the UI can be tweaked still to
communicate this, also panorama should probably become a proper camera type like
perspective or ortho.

intern/cycles/app/cycles_xml.cpp
intern/cycles/blender/blender_camera.cpp
intern/cycles/kernel/kernel_camera.h
intern/cycles/kernel/kernel_displace.h
intern/cycles/kernel/kernel_light.h
intern/cycles/kernel/kernel_montecarlo.h
intern/cycles/kernel/kernel_types.h
intern/cycles/render/camera.cpp
intern/cycles/render/camera.h
intern/cycles/render/light.cpp
release/scripts/startup/bl_ui/properties_data_camera.py

index 5e4b3da071dcf67b9ef21bd754eebbc86474886a..283bdf52e0317ca2a91557dabf0c8d1401723c94 100644 (file)
@@ -288,9 +288,11 @@ static void xml_read_camera(const XMLReadState& state, pugi::xml_node node)
        xml_read_float(&cam->shutterclose, node, "shutterclose");
 
        if(xml_equal_string(node, "type", "orthographic"))
-               cam->ortho = true;
+               cam->type = CAMERA_ORTHOGRAPHIC;
        else if(xml_equal_string(node, "type", "perspective"))
-               cam->ortho = false;
+               cam->type = CAMERA_PERSPECTIVE;
+       else if(xml_equal_string(node, "type", "environment"))
+               cam->type = CAMERA_ENVIRONMENT;
 
        cam->matrix = state.tfm;
 
index cbc430d9240a800ae1253c48e313d4920d1137c1..7c9c162f8b1080c4566f313b1b57651298c9d0bc 100644 (file)
@@ -31,7 +31,7 @@ struct BlenderCamera {
        float nearclip;
        float farclip;
 
-       bool ortho;
+       CameraType type;
        float ortho_scale;
 
        float lens;
@@ -58,6 +58,7 @@ static void blender_camera_init(BlenderCamera *bcam)
 {
        memset(bcam, 0, sizeof(BlenderCamera));
 
+       bcam->type = CAMERA_PERSPECTIVE;
        bcam->zoom = 1.0f;
        bcam->pixelaspect = make_float2(1.0f, 1.0f);
        bcam->sensor_width = 32.0f;
@@ -91,7 +92,9 @@ 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->ortho = (b_camera.type() == BL::Camera::type_ORTHO);
+               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;
                bcam->ortho_scale = b_camera.ortho_scale();
 
                bcam->lens = b_camera.lens();
@@ -159,39 +162,48 @@ static void blender_camera_sync(Camera *cam, BlenderCamera *bcam, int width, int
        }
 
        /* modify aspect for orthographic scale */
-       if(bcam->ortho) {
+       if(bcam->type == CAMERA_ORTHOGRAPHIC) {
                xaspect = xaspect*bcam->ortho_scale/(aspectratio*2.0f);
                yaspect = yaspect*bcam->ortho_scale/(aspectratio*2.0f);
                aspectratio = bcam->ortho_scale/2.0f;
        }
 
-       /* set viewplane */
-       cam->left = -xaspect;
-       cam->right = xaspect;
-       cam->bottom = -yaspect;
-       cam->top = yaspect;
-
-       /* zoom for 3d camera view */
-       cam->left *= bcam->zoom;
-       cam->right *= bcam->zoom;
-       cam->bottom *= bcam->zoom;
-       cam->top *= bcam->zoom;
-
-       /* modify viewplane with camera shift and 3d camera view offset */
-       float dx = 2.0f*(aspectratio*bcam->shift.x + bcam->offset.x*xaspect*2.0f);
-       float dy = 2.0f*(aspectratio*bcam->shift.y + bcam->offset.y*yaspect*2.0f);
-
-       cam->left += dx;
-       cam->right += dx;
-       cam->bottom += dy;
-       cam->top += dy;
+       if(bcam->type == CAMERA_ENVIRONMENT) {
+               /* set viewplane */
+               cam->left = 0.0f;
+               cam->right = 1.0f;
+               cam->bottom = 0.0f;
+               cam->top = 1.0f;
+       }
+       else {
+               /* set viewplane */
+               cam->left = -xaspect;
+               cam->right = xaspect;
+               cam->bottom = -yaspect;
+               cam->top = yaspect;
+
+               /* zoom for 3d camera view */
+               cam->left *= bcam->zoom;
+               cam->right *= bcam->zoom;
+               cam->bottom *= bcam->zoom;
+               cam->top *= bcam->zoom;
+
+               /* modify viewplane with camera shift and 3d camera view offset */
+               float dx = 2.0f*(aspectratio*bcam->shift.x + bcam->offset.x*xaspect*2.0f);
+               float dy = 2.0f*(aspectratio*bcam->shift.y + bcam->offset.y*yaspect*2.0f);
+
+               cam->left += dx;
+               cam->right += dx;
+               cam->bottom += dy;
+               cam->top += dy;
+       }
 
        /* clipping distances */
        cam->nearclip = bcam->nearclip;
        cam->farclip = bcam->farclip;
 
-       /* orthographic */
-       cam->ortho = bcam->ortho;
+       /* type */
+       cam->type = bcam->type;
 
        /* perspective */
        cam->fov = 2.0f*atan((0.5f*sensor_size)/bcam->lens/aspectratio);
@@ -200,8 +212,24 @@ static void blender_camera_sync(Camera *cam, BlenderCamera *bcam, int width, int
        cam->blades = bcam->apertureblades;
        cam->bladesrotation = bcam->aperturerotation;
 
-       /* transform, note the blender camera points along the negative z-axis */
-       cam->matrix = bcam->matrix * transform_scale(1.0f, 1.0f, -1.0f);
+       /* transform */
+       cam->matrix = bcam->matrix;
+
+       if(bcam->type == CAMERA_ENVIRONMENT) {
+               /* 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 */
+               cam->matrix = cam->matrix *
+                       make_transform( 0.0f, -1.0f, 0.0f, 0.0f,
+                                       0.0f,  0.0f, 1.0f, 0.0f,
+                                      -1.0f,  0.0f, 0.0f, 0.0f,
+                                       0.0f,  0.0f, 0.0f, 1.0f);
+       }
+       else {
+               /* note the blender camera points along the negative z-axis */
+               cam->matrix = cam->matrix * transform_scale(1.0f, 1.0f, -1.0f);
+       }
+
        cam->matrix = transform_clear_scale(cam->matrix);
 
        /* set update flag */
@@ -269,7 +297,7 @@ void BlenderSync::sync_view(BL::SpaceView3D b_v3d, BL::RegionView3D b_rv3d, int
                bcam.farclip *= 0.5;
                bcam.nearclip = -bcam.farclip;
 
-               bcam.ortho = true;
+               bcam.type = CAMERA_ORTHOGRAPHIC;
                bcam.ortho_scale = b_rv3d.view_distance();
        }
 
index 2dbdd07689190cea7262e065f6169369497a3580..251b2c280dafebdaaaca98ee450d88089fdef43b 100644 (file)
@@ -122,6 +122,44 @@ __device void camera_sample_orthographic(KernelGlobals *kg, float raster_x, floa
 #endif
 }
 
+/* Environment Camera */
+
+__device void camera_sample_environment(KernelGlobals *kg, float raster_x, float raster_y, Ray *ray)
+{
+       Transform rastertocamera = kernel_data.cam.rastertocamera;
+       float3 Pcamera = transform(&rastertocamera, make_float3(raster_x, raster_y, 0.0f));
+
+       /* create ray form raster position */
+       ray->P = make_float3(0.0, 0.0f, 0.0f);
+       ray->D = equirectangular_to_direction(Pcamera.x, Pcamera.y);
+
+       /* transform ray from camera to world */
+       Transform cameratoworld = kernel_data.cam.cameratoworld;
+
+       ray->P = transform(&cameratoworld, ray->P);
+       ray->D = transform_direction(&cameratoworld, ray->D);
+       ray->D = normalize(ray->D);
+
+#ifdef __RAY_DIFFERENTIALS__
+       /* ray differential */
+       ray->dP.dx = make_float3(0.0f, 0.0f, 0.0f);
+       ray->dP.dy = make_float3(0.0f, 0.0f, 0.0f);
+
+       Pcamera = transform(&rastertocamera, make_float3(raster_x + 1.0f, raster_y, 0.0f));
+       ray->dD.dx = equirectangular_to_direction(Pcamera.x, Pcamera.y) - ray->D;
+
+       Pcamera = transform(&rastertocamera, make_float3(raster_x, raster_y + 1.0f, 0.0f));
+       ray->dD.dy = equirectangular_to_direction(Pcamera.x, Pcamera.y) - ray->D;
+#endif
+
+#ifdef __CAMERA_CLIPPING__
+       /* clipping */
+       ray->t = kernel_data.cam.cliplength;
+#else
+       ray->t = FLT_MAX;
+#endif
+}
+
 /* Common */
 
 __device void camera_sample(KernelGlobals *kg, int x, int y, float filter_u, float filter_v, float lens_u, float lens_v, Ray *ray)
@@ -134,10 +172,12 @@ __device void camera_sample(KernelGlobals *kg, int x, int y, float filter_u, flo
        //ray->time = lerp(time_t, kernel_data.cam.shutter_open, kernel_data.cam.shutter_close);
 
        /* sample */
-       if(kernel_data.cam.ortho)
+       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, ray);
        else
-               camera_sample_perspective(kg, raster_x, raster_y, lens_u, lens_v, ray);
+               camera_sample_environment(kg, raster_x, raster_y, ray);
 }
 
 CCL_NAMESPACE_END
index 73666892cf3d7e6e4722b77334ea6b859ffbdf0c..f4b33605f5be5b6b1475994f3030ee1e8eb8b833 100644 (file)
@@ -41,9 +41,11 @@ __device void kernel_shader_evaluate(KernelGlobals *kg, uint4 *input, float4 *ou
        else { // SHADER_EVAL_BACKGROUND
                /* setup ray */
                Ray ray;
+               float u = __int_as_float(in.x);
+               float v = __int_as_float(in.y);
 
                ray.P = make_float3(0.0f, 0.0f, 0.0f);
-               ray.D = make_float3(__int_as_float(in.x), __int_as_float(in.y), __int_as_float(in.z));
+               ray.D = equirectangular_to_direction(u, v);
                ray.t = 0.0f;
 
 #ifdef __RAY_DIFFERENTIALS__
index aa125180a94310cda1b61872644e54115feb34c1..42260577069324d4672dee9230c9d54b08f9c86e 100644 (file)
@@ -120,13 +120,9 @@ __device float3 background_light_sample(KernelGlobals *kg, float randu, float ra
        float du = (randu - cdf_u.y) / (cdf_next_u.y - cdf_u.y);
        float u = (index_u + du) / res;
 
-       /* spherical coordinates */
-       float theta = v * M_PI_F;
-       float phi = u * M_PI_F * 2.0f;
-
        /* compute pdf */
        float denom = cdf_last_u.x * cdf_last_v.x;
-       float sin_theta = sinf(theta);
+       float sin_theta = sinf(M_PI_F * v);
 
        if(sin_theta == 0.0f || denom == 0.0f)
                *pdf = 0.0f;
@@ -136,7 +132,7 @@ __device float3 background_light_sample(KernelGlobals *kg, float randu, float ra
        *pdf *= kernel_data.integrator.pdf_lights;
 
        /* compute direction */
-       return spherical_to_direction(theta, phi);
+       return -equirectangular_to_direction(u, v);
 }
 
 __device float background_light_pdf(KernelGlobals *kg, float3 direction)
index 9776baf65e4dc984fc477fd2da7604a95e7024ba..66bd0ee9998a94b2557f8a192e33ed892339df8c 100644 (file)
@@ -185,7 +185,7 @@ __device float2 regular_polygon_sample(float corners, float rotation, float u, f
        return make_float2(cr*p.x - sr*p.y, sr*p.x + cr*p.y);
 }
 
-/* Spherical coordinates <-> Cartesion direction  */
+/* Spherical coordinates <-> Cartesian direction  */
 
 __device float2 direction_to_spherical(float3 dir)
 {
@@ -203,11 +203,11 @@ __device float3 spherical_to_direction(float theta, float phi)
                cosf(theta));
 }
 
-/* Equirectangular */
+/* Equirectangular coordinates <-> Cartesian direction */
 
 __device float2 direction_to_equirectangular(float3 dir)
 {
-       float u = (atan2f(dir.y, dir.x) + M_PI_F)/(2.0f*M_PI_F);
+       float u = -atan2f(dir.y, dir.x)/(2.0f*M_PI_F) + 0.5f;
        float v = atan2f(dir.z, hypotf(dir.x, dir.y))/M_PI_F + 0.5f;
 
        return make_float2(u, v);
@@ -215,9 +215,8 @@ __device float2 direction_to_equirectangular(float3 dir)
 
 __device float3 equirectangular_to_direction(float u, float v)
 {
-       /* XXX check correctness? */
-       float theta = M_PI_F*v;
-       float phi = 2.0f*M_PI_F*u;
+       float phi = M_PI_F*(1.0f - 2.0f*u);
+       float theta = M_PI_F*(1.0f - v);
 
        return make_float3(
                sin(theta)*cos(phi),
index 9f4d5b00e2ca2e779c68952c252c03896b28a444..61335d5f9fad51285bcce8a9e682e0f3dd5a9741 100644 (file)
@@ -240,6 +240,14 @@ typedef enum LightType {
        LIGHT_AREA
 } LightType;
 
+/* Camera Type */
+
+enum CameraType {
+       CAMERA_PERSPECTIVE,
+       CAMERA_ORTHOGRAPHIC,
+       CAMERA_ENVIRONMENT
+};
+
 /* Differential */
 
 typedef struct differential3 {
@@ -387,7 +395,7 @@ typedef struct ShaderData {
 
 typedef struct KernelCamera {
        /* type */
-       int ortho;
+       int type;
        int pad1, pad2, pad3;
 
        /* matrices */
index a83ae81844cbcb805cc734a0c289897671e13080..6edf9c66f1d7f37d605cbab5c17d5e5bbcedbfde 100644 (file)
@@ -35,7 +35,7 @@ Camera::Camera()
 
        matrix = transform_identity();
 
-       ortho = false;
+       type = CAMERA_PERSPECTIVE;
        fov = M_PI_F/4.0f;
 
        nearclip = 1e-5f;
@@ -77,17 +77,21 @@ void Camera::update()
        Transform ndctoraster = transform_scale(width, height, 1.0f);
 
        /* raster to screen */
-       Transform screentoraster = ndctoraster *
+       Transform screentoraster = ndctoraster;
+       
+       screentoraster = ndctoraster *
                transform_scale(1.0f/(right - left), 1.0f/(top - bottom), 1.0f) *
                transform_translate(-left, -bottom, 0.0f);
 
        Transform rastertoscreen = transform_inverse(screentoraster);
 
        /* screen to camera */
-       if(ortho)
+       if(type == CAMERA_PERSPECTIVE)
+               screentocamera = transform_inverse(transform_perspective(fov, nearclip, farclip));
+       else if(type == CAMERA_ORTHOGRAPHIC)
                screentocamera = transform_inverse(transform_orthographic(nearclip, farclip));
        else
-               screentocamera = transform_inverse(transform_perspective(fov, nearclip, farclip));
+               screentocamera = transform_identity();
 
        rastertocamera = screentocamera * rastertoscreen;
 
@@ -98,16 +102,20 @@ void Camera::update()
        worldtoraster = transform_inverse(rastertoworld);
 
        /* differentials */
-       if(ortho) {
+       if(type == CAMERA_ORTHOGRAPHIC) {
                dx = transform_direction(&rastertocamera, make_float3(1, 0, 0));
                dy = transform_direction(&rastertocamera, make_float3(0, 1, 0));
        }
-       else {
+       else if(type == CAMERA_PERSPECTIVE) {
                dx = transform(&rastertocamera, make_float3(1, 0, 0)) -
                     transform(&rastertocamera, make_float3(0, 0, 0));
                dy = transform(&rastertocamera, make_float3(0, 1, 0)) -
                     transform(&rastertocamera, make_float3(0, 0, 0));
        }
+       else {
+               dx = make_float3(0, 0, 0);
+               dy = make_float3(0, 0, 0);
+       }
 
        dx = transform_direction(&cameratoworld, dx);
        dy = transform_direction(&cameratoworld, dy);
@@ -147,7 +155,7 @@ void Camera::device_update(Device *device, DeviceScene *dscene)
        kcam->shutterclose = shutterclose;
 
        /* type */
-       kcam->ortho = ortho;
+       kcam->type = type;
 
        /* store differentials */
        kcam->dx = float3_to_float4(dx);
@@ -173,7 +181,7 @@ bool Camera::modified(const Camera& cam)
                (blades == cam.blades) &&
                (bladesrotation == cam.bladesrotation) &&
                (focaldistance == cam.focaldistance) &&
-               (ortho == cam.ortho) &&
+               (type == cam.type) &&
                (fov == cam.fov) &&
                (nearclip == cam.nearclip) &&
                (farclip == cam.farclip) &&
index 43537ce8c3c7a3fc0b67c54373c41019c244194e..cfcc5406ee35c16fbe3cc6251a0c006604e22df3 100644 (file)
@@ -19,6 +19,8 @@
 #ifndef __CAMERA_H__
 #define __CAMERA_H__
 
+#include "kernel_types.h"
+
 #include "util_transform.h"
 #include "util_types.h"
 
@@ -44,8 +46,8 @@ public:
        uint blades;
        float bladesrotation;
 
-       /* orthographic/perspective */
-       bool ortho;
+       /* type */
+       CameraType type;
        float fov;
 
        /* clipping */
index 405aa8004571ceab159b4b79fcc1a8b3f5066dca..bd3acb00f6d8c863b7f1eff352c89b474db7f46d 100644 (file)
@@ -45,9 +45,8 @@ static void dump_background_pixels(Device *device, DeviceScene *dscene, int res,
                for(int x = 0; x < width; x++) {
                        float u = x/(float)width;
                        float v = y/(float)height;
-                       float3 D = -equirectangular_to_direction(u, v);
 
-                       uint4 in = make_uint4(__float_as_int(D.x), __float_as_int(D.y), __float_as_int(D.z), 0);
+                       uint4 in = make_uint4(__float_as_int(u), __float_as_int(v), 0, 0);
                        d_input_data[x + y*width] = in;
                }
        }
index 1d7559b2cebae2222edab18a5b23de452f240adb..484719e4dece2ea33d2c8000566c746557389c50 100644 (file)
@@ -88,11 +88,7 @@ class DATA_PT_lens(CameraButtonsPanel, Panel):
             col.prop(cam, "ortho_scale")
 
         col = layout.column()
-        if cam.type == 'ORTHO':
-            if cam.use_panorama:
-                col.alert = True
-            else:
-                col.enabled = False
+        col.enabled = cam.type == 'PERSPECTIVE'
 
         col.prop(cam, "use_panorama")