Cycles: Automatically detect HDRI resolution by default and use non-square sampling map
authorLukas Stockner <lukas.stockner@freenet.de>
Thu, 14 Jun 2018 14:18:34 +0000 (16:18 +0200)
committerLukas Stockner <lukas.stockner@freenet.de>
Thu, 14 Jun 2018 20:07:07 +0000 (22:07 +0200)
The automatic mode checks all Enviroment Texture nodes and picks the largest image's resolution.
If there are no Enviroment Textures, it just uses the old default.

Also, the sampling map now isn't limited to square shapes. The automatic detection uses the exact image size,
the manual UI option now halves the value to get the height.

A default aspect ratio of 2:1 makes sense since this is what most HDRIs use.

Reviewers: brecht, sergey

Differential Revision: https://developer.blender.org/D3477

intern/cycles/blender/addon/properties.py
intern/cycles/blender/addon/ui.py
intern/cycles/blender/addon/version_update.py
intern/cycles/blender/blender_object.cpp
intern/cycles/kernel/kernel_emission.h
intern/cycles/kernel/kernel_light.h
intern/cycles/kernel/kernel_types.h
intern/cycles/render/image.cpp
intern/cycles/render/image.h
intern/cycles/render/light.cpp

index 8dbd80f37470dbb79e8a95b0967fb7bab58b4e15..4484dcfbfd74d84ebfaac813844e00e304ccbe24 100644 (file)
@@ -128,6 +128,12 @@ enum_volume_interpolation = (
     ('CUBIC', "Cubic", "Smoothed high quality interpolation, but slower")
     )
 
+enum_world_mis = (
+    ('NONE', "None", "Don't sample the background, faster but might cause noise for non-solid backgrounds"),
+    ('AUTOMATIC', "Auto", "Automatically try to determine the best setting"),
+    ('MANUAL', "Manual", "Manually set the resolution of the sampling map, higher values are slower and require more memory but reduce noise")
+    )
+
 enum_device_type = (
     ('CPU', "CPU", "CPU", 0),
     ('CUDA', "CUDA", "CUDA", 1),
@@ -938,15 +944,15 @@ class CyclesWorldSettings(bpy.types.PropertyGroup):
                 description="Cycles world settings",
                 type=cls,
                 )
-        cls.sample_as_light = BoolProperty(
-                name="Multiple Importance Sample",
-                description="Use multiple importance sampling for the environment, "
-                            "enabling for non-solid colors is recommended",
-                default=True,
+        cls.sampling_method = EnumProperty(
+                name="Sampling method",
+                description="How to sample the background light",
+                items=enum_world_mis,
+                default='AUTOMATIC',
                 )
         cls.sample_map_resolution = IntProperty(
                 name="Map Resolution",
-                description="Importance map size is resolution x resolution; "
+                description="Importance map size is resolution x resolution/2; "
                             "higher values potentially produce less noise, at the cost of memory and speed",
                 min=4, max=8192,
                 default=1024,
index 707f8756f6f923cc5f39fdf6b21a980009323ee3..2b11a2eefb0088f5ddf607efccc463ecf34827a3 100644 (file)
@@ -1214,11 +1214,13 @@ class CYCLES_WORLD_PT_settings(CyclesButtonsPanel, Panel):
         col = split.column()
 
         col.label(text="Surface:")
-        col.prop(cworld, "sample_as_light", text="Multiple Importance")
+        col.prop(cworld, "sampling_method", text="Sampling")
 
         sub = col.column(align=True)
-        sub.active = cworld.sample_as_light
-        sub.prop(cworld, "sample_map_resolution")
+        sub.active = cworld.sampling_method != 'NONE'
+        subsub = sub.row(align=True)
+        subsub.active = cworld.sampling_method == 'MANUAL'
+        subsub.prop(cworld, "sample_map_resolution")
         if use_branched_path(context):
             subsub = sub.row(align=True)
             subsub.active = use_sample_all_lights(context)
index 292f0a1fa90c2b22113f6a0ba8569b793ab735fa..dc28bc647b59897168a937487d28f7097646715a 100644 (file)
@@ -377,10 +377,6 @@ def do_versions(self):
         for world in bpy.data.worlds:
             cworld = world.cycles
 
-            # World MIS
-            if not cworld.is_property_set("sample_as_light"):
-                cworld.sample_as_light = False
-
             # World MIS Samples
             if not cworld.is_property_set("samples"):
                 cworld.samples = 4
@@ -431,3 +427,12 @@ def do_versions(self):
     if bpy.data.version <= (2, 79, 3):
         # Switch to squared roughness convention
         square_roughness_nodes_insert()
+
+    for world in bpy.data.worlds:
+        cworld = world.cycles
+        # World MIS
+        if not cworld.is_property_set("sampling_method"):
+            if cworld.get("sample_as_light", False):
+                cworld.sampling_method = 'MANUAL'
+            else:
+                cworld.sampling_method = 'NONE'
index 4919bc4325f722817f4163779ee143f5cbcfca09..86b04f5030cdc3c0048e70a205bd6621e2f5fa23 100644 (file)
@@ -227,7 +227,15 @@ void BlenderSync::sync_background_light(bool use_portal)
        if(b_world) {
                PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
                PointerRNA cworld = RNA_pointer_get(&b_world.ptr, "cycles");
-               bool sample_as_light = get_boolean(cworld, "sample_as_light");
+
+               enum SamplingMethod {
+                       SAMPLING_NONE = 0,
+                       SAMPLING_AUTOMATIC,
+                       SAMPLING_MANUAL,
+                       SAMPLING_NUM
+               };
+               int sampling_method = get_enum(cworld, "sampling_method", SAMPLING_NUM, SAMPLING_AUTOMATIC);
+               bool sample_as_light = (sampling_method != SAMPLING_NONE);
 
                if(sample_as_light || use_portal) {
                        /* test if we need to sync */
@@ -239,7 +247,12 @@ void BlenderSync::sync_background_light(bool use_portal)
                            b_world.ptr.data != world_map)
                        {
                                light->type = LIGHT_BACKGROUND;
-                               light->map_resolution  = get_int(cworld, "sample_map_resolution");
+                               if(sampling_method == SAMPLING_MANUAL) {
+                                       light->map_resolution = get_int(cworld, "sample_map_resolution");
+                               }
+                               else {
+                                       light->map_resolution = 0;
+                               }
                                light->shader = scene->default_background;
                                light->use_mis = sample_as_light;
                                light->max_bounces = get_int(cworld, "max_bounces");
index a5556c3be8fbe3d51164c94708e245a703dd69e0..524e2467ebc507318147baa321b993d77903bfb3 100644 (file)
@@ -319,9 +319,9 @@ ccl_device_noinline float3 indirect_background(KernelGlobals *kg,
 
 #ifdef __BACKGROUND_MIS__
        /* check if background light exists or if we should skip pdf */
-       int res = kernel_data.integrator.pdf_background_res;
+       int res_x = kernel_data.integrator.pdf_background_res_x;
 
-       if(!(state->flag & PATH_RAY_MIS_SKIP) && res) {
+       if(!(state->flag & PATH_RAY_MIS_SKIP) && res_x) {
                /* multiple importance sampling, get background light pdf for ray
                 * direction, and compute weight with respect to BSDF pdf */
                float pdf = background_light_pdf(kg, ray->P, ray->D);
index ec7203d36eb1a570a23bc8202e2e7d57a741dda1..bb182ef1f259cd0774d95d2867d17cac12d5c8e2 100644 (file)
@@ -143,12 +143,13 @@ float3 background_map_sample(KernelGlobals *kg, float randu, float randv, float
        /* for the following, the CDF values are actually a pair of floats, with the
         * function value as X and the actual CDF as Y.  The last entry's function
         * value is the CDF total. */
-       int res = kernel_data.integrator.pdf_background_res;
-       int cdf_count = res + 1;
+       int res_x = kernel_data.integrator.pdf_background_res_x;
+       int res_y = kernel_data.integrator.pdf_background_res_y;
+       int cdf_width = res_x + 1;
 
        /* this is basically std::lower_bound as used by pbrt */
        int first = 0;
-       int count = res;
+       int count = res_y;
 
        while(count > 0) {
                int step = count >> 1;
@@ -163,24 +164,24 @@ float3 background_map_sample(KernelGlobals *kg, float randu, float randv, float
        }
 
        int index_v = max(0, first - 1);
-       kernel_assert(index_v >= 0 && index_v < res);
+       kernel_assert(index_v >= 0 && index_v < res_y);
 
        float2 cdf_v = kernel_tex_fetch(__light_background_marginal_cdf, index_v);
        float2 cdf_next_v = kernel_tex_fetch(__light_background_marginal_cdf, index_v + 1);
-       float2 cdf_last_v = kernel_tex_fetch(__light_background_marginal_cdf, res);
+       float2 cdf_last_v = kernel_tex_fetch(__light_background_marginal_cdf, res_y);
 
        /* importance-sampled V direction */
        float dv = inverse_lerp(cdf_v.y, cdf_next_v.y, randv);
-       float v = (index_v + dv) / res;
+       float v = (index_v + dv) / res_y;
 
        /* this is basically std::lower_bound as used by pbrt */
        first = 0;
-       count = res;
+       count = res_x;
        while(count > 0) {
                int step = count >> 1;
                int middle = first + step;
 
-               if(kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_count + middle).y < randu) {
+               if(kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_width + middle).y < randu) {
                        first = middle + 1;
                        count -= step + 1;
                }
@@ -189,15 +190,15 @@ float3 background_map_sample(KernelGlobals *kg, float randu, float randv, float
        }
 
        int index_u = max(0, first - 1);
-       kernel_assert(index_u >= 0 && index_u < res);
+       kernel_assert(index_u >= 0 && index_u < res_x);
 
-       float2 cdf_u = kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_count + index_u);
-       float2 cdf_next_u = kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_count + index_u + 1);
-       float2 cdf_last_u = kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_count + res);
+       float2 cdf_u = kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_width + index_u);
+       float2 cdf_next_u = kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_width + index_u + 1);
+       float2 cdf_last_u = kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_width + res_x);
 
        /* importance-sampled U direction */
        float du = inverse_lerp(cdf_u.y, cdf_next_u.y, randu);
-       float u = (index_u + du) / res;
+       float u = (index_u + du) / res_x;
 
        /* compute pdf */
        float denom = cdf_last_u.x * cdf_last_v.x;
@@ -223,19 +224,21 @@ ccl_device
 float background_map_pdf(KernelGlobals *kg, float3 direction)
 {
        float2 uv = direction_to_equirectangular(direction);
-       int res = kernel_data.integrator.pdf_background_res;
+       int res_x = kernel_data.integrator.pdf_background_res_x;
+       int res_y = kernel_data.integrator.pdf_background_res_y;
+       int cdf_width = res_x + 1;
 
        float sin_theta = sinf(uv.y * M_PI_F);
 
        if(sin_theta == 0.0f)
                return 0.0f;
 
-       int index_u = clamp(float_to_int(uv.x * res), 0, res - 1);
-       int index_v = clamp(float_to_int(uv.y * res), 0, res - 1);
+       int index_u = clamp(float_to_int(uv.x * res_x), 0, res_x - 1);
+       int index_v = clamp(float_to_int(uv.y * res_y), 0, res_y - 1);
 
        /* pdfs in V direction */
-       float2 cdf_last_u = kernel_tex_fetch(__light_background_conditional_cdf, index_v * (res + 1) + res);
-       float2 cdf_last_v = kernel_tex_fetch(__light_background_marginal_cdf, res);
+       float2 cdf_last_u = kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_width + res_x);
+       float2 cdf_last_v = kernel_tex_fetch(__light_background_marginal_cdf, res_y);
 
        float denom = cdf_last_u.x * cdf_last_v.x;
 
@@ -243,7 +246,7 @@ float background_map_pdf(KernelGlobals *kg, float3 direction)
                return 0.0f;
 
        /* pdfs in U direction */
-       float2 cdf_u = kernel_tex_fetch(__light_background_conditional_cdf, index_v * (res + 1) + index_u);
+       float2 cdf_u = kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_width + index_u);
        float2 cdf_v = kernel_tex_fetch(__light_background_marginal_cdf, index_v);
 
        return (cdf_u.x * cdf_v.x)/(M_2PI_F * M_PI_F * sin_theta * denom);
index 72fbf7be557a70da3e74cc4aeffeee7b4afe2c21..5382213e6f7dd6b75a5d2b1eebe4992f1d7e458f 100644 (file)
@@ -1306,7 +1306,8 @@ typedef struct KernelIntegrator {
        int num_all_lights;
        float pdf_triangles;
        float pdf_lights;
-       int pdf_background_res;
+       int pdf_background_res_x;
+       int pdf_background_res_y;
        float light_inv_rr_threshold;
 
        /* light portals */
@@ -1368,6 +1369,8 @@ typedef struct KernelIntegrator {
        int start_sample;
 
        int max_closures;
+
+       int pad1, pad2, pad3;
 } KernelIntegrator;
 static_assert_align(KernelIntegrator, 16);
 
index 9c5e32e8219db4fd58fb831e143fae49a2dac653..9c6536edc4f2ed946f0effd0eb449dfc50794ac3 100644 (file)
@@ -94,6 +94,25 @@ device_memory *ImageManager::image_memory(int flat_slot)
           return img->mem;
 }
 
+bool ImageManager::get_image_metadata(int flat_slot,
+                                      ImageMetaData& metadata)
+{
+       if(flat_slot == -1) {
+               return false;
+       }
+
+       ImageDataType type;
+       int slot = flattened_slot_to_type_index(flat_slot, &type);
+
+       Image *img = images[type][slot];
+       if(img) {
+               metadata = img->metadata;
+               return true;
+       }
+
+       return false;
+}
+
 bool ImageManager::get_image_metadata(const string& filename,
                                       void *builtin_data,
                                       ImageMetaData& metadata)
@@ -329,7 +348,7 @@ int ImageManager::add_image(const string& filename,
        img = new Image();
        img->filename = filename;
        img->builtin_data = builtin_data;
-       img->builtin_free_cache = metadata.builtin_free_cache;
+       img->metadata = metadata;
        img->need_load = true;
        img->animated = animated;
        img->frame = frame;
@@ -417,11 +436,7 @@ void ImageManager::tag_reload_image(const string& filename,
 }
 
 bool ImageManager::file_load_image_generic(Image *img,
-                                           ImageInput **in,
-                                           int &width,
-                                           int &height,
-                                           int &depth,
-                                           int &components)
+                                           ImageInput **in)
 {
        if(img->filename == "")
                return false;
@@ -449,28 +464,15 @@ bool ImageManager::file_load_image_generic(Image *img,
                        *in = NULL;
                        return false;
                }
-
-               width = spec.width;
-               height = spec.height;
-               depth = spec.depth;
-               components = spec.nchannels;
        }
        else {
                /* load image using builtin images callbacks */
                if(!builtin_image_info_cb || !builtin_image_pixels_cb)
                        return false;
-
-               ImageMetaData metadata;
-               builtin_image_info_cb(img->filename, img->builtin_data, metadata);
-
-               width = metadata.width;
-               height = metadata.height;
-               depth = metadata.depth;
-               components = metadata.channels;
        }
 
        /* we only handle certain number of components */
-       if(!(components >= 1 && components <= 4)) {
+       if(!(img->metadata.channels >= 1 && img->metadata.channels <= 4)) {
                if(*in) {
                        (*in)->close();
                        delete *in;
@@ -493,10 +495,16 @@ bool ImageManager::file_load_image(Image *img,
 {
        const StorageType alpha_one = (FileFormat == TypeDesc::UINT8)? 255 : 1;
        ImageInput *in = NULL;
-       int width, height, depth, components;
-       if(!file_load_image_generic(img, &in, width, height, depth, components)) {
+       if(!file_load_image_generic(img, &in)) {
                return false;
        }
+
+       /* Get metadata. */
+       int width = img->metadata.width;
+       int height = img->metadata.height;
+       int depth = img->metadata.depth;
+       int components = img->metadata.channels;
+
        /* Read RGBA pixels. */
        vector<StorageType> pixels_storage;
        StorageType *pixels;
@@ -557,14 +565,14 @@ bool ImageManager::file_load_image(Image *img,
                                                      img->builtin_data,
                                                      (float*)&pixels[0],
                                                      num_pixels * components,
-                                                     img->builtin_free_cache);
+                                                     img->metadata.builtin_free_cache);
                }
                else if(FileFormat == TypeDesc::UINT8) {
                        builtin_image_pixels_cb(img->filename,
                                                img->builtin_data,
                                                (uchar*)&pixels[0],
                                                num_pixels * components,
-                                               img->builtin_free_cache);
+                                               img->metadata.builtin_free_cache);
                }
                else {
                        /* TODO(dingto): Support half for ImBuf. */
index 5391490d993d392745accdaf9fa4132845527158..4fd09adaa6446810d18f32709e0f66b974eed0dd 100644 (file)
@@ -71,6 +71,8 @@ public:
        bool get_image_metadata(const string& filename,
                                void *builtin_data,
                                ImageMetaData& metadata);
+       bool get_image_metadata(int flat_slot,
+                               ImageMetaData& metadata);
 
        void device_update(Device *device,
                           Scene *scene,
@@ -110,7 +112,7 @@ public:
        struct Image {
                string filename;
                void *builtin_data;
-               bool builtin_free_cache;
+               ImageMetaData metadata;
 
                bool use_alpha;
                bool need_load;
@@ -137,11 +139,7 @@ private:
        void *osl_texture_system;
 
        bool file_load_image_generic(Image *img,
-                                    ImageInput **in,
-                                    int &width,
-                                    int &height,
-                                    int &depth,
-                                    int &components);
+                                    ImageInput **in);
 
        template<TypeDesc::BASETYPE FileFormat,
                 typename StorageType,
index cbcd2564e6e684a95ce2a3e1d3fac764752d92bc..20c6a53e58fb3f79b078366006681ca0e3d60163 100644 (file)
 #include "device/device.h"
 #include "render/integrator.h"
 #include "render/film.h"
+#include "render/graph.h"
 #include "render/light.h"
 #include "render/mesh.h"
+#include "render/nodes.h"
 #include "render/object.h"
 #include "render/scene.h"
 #include "render/shader.h"
 
 CCL_NAMESPACE_BEGIN
 
-static void shade_background_pixels(Device *device, DeviceScene *dscene, int res, vector<float3>& pixels, Progress& progress)
+static void shade_background_pixels(Device *device, DeviceScene *dscene, int width, int height, vector<float3>& pixels, Progress& progress)
 {
        /* create input */
-       int width = res;
-       int height = res;
-
        device_vector<uint4> d_input(device, "background_input", MEM_READ_ONLY);
        device_vector<float4> d_output(device, "background_output", MEM_READ_WRITE);
 
@@ -120,7 +119,7 @@ NODE_DEFINE(Light)
        SOCKET_VECTOR(axisv, "Axis V", make_float3(0.0f, 0.0f, 0.0f));
        SOCKET_FLOAT(sizev, "Size V", 1.0f);
 
-       SOCKET_INT(map_resolution, "Map Resolution", 512);
+       SOCKET_INT(map_resolution, "Map Resolution", 0);
 
        SOCKET_FLOAT(spot_angle, "Spot Angle", M_PI_4_F);
        SOCKET_FLOAT(spot_smooth, "Spot Smooth", 0.0f);
@@ -481,40 +480,41 @@ void LightManager::device_update_distribution(Device *, DeviceScene *dscene, Sce
 
 static void background_cdf(int start,
                            int end,
-                           int res,
-                           int cdf_count,
+                           int res_x,
+                           int res_y,
                            const vector<float3> *pixels,
                            float2 *cond_cdf)
 {
+       int cdf_width = res_x+1;
        /* Conditional CDFs (rows, U direction). */
        for(int i = start; i < end; i++) {
-               float sin_theta = sinf(M_PI_F * (i + 0.5f) / res);
-               float3 env_color = (*pixels)[i * res];
+               float sin_theta = sinf(M_PI_F * (i + 0.5f) / res_y);
+               float3 env_color = (*pixels)[i * res_x];
                float ave_luminance = average(env_color);
 
-               cond_cdf[i * cdf_count].x = ave_luminance * sin_theta;
-               cond_cdf[i * cdf_count].y = 0.0f;
+               cond_cdf[i * cdf_width].x = ave_luminance * sin_theta;
+               cond_cdf[i * cdf_width].y = 0.0f;
 
-               for(int j = 1; j < res; j++) {
-                       env_color = (*pixels)[i * res + j];
+               for(int j = 1; j < res_x; j++) {
+                       env_color = (*pixels)[i * res_x + j];
                        ave_luminance = average(env_color);
 
-                       cond_cdf[i * cdf_count + j].x = ave_luminance * sin_theta;
-                       cond_cdf[i * cdf_count + j].y = cond_cdf[i * cdf_count + j - 1].y + cond_cdf[i * cdf_count + j - 1].x / res;
+                       cond_cdf[i * cdf_width + j].x = ave_luminance * sin_theta;
+                       cond_cdf[i * cdf_width + j].y = cond_cdf[i * cdf_width + j - 1].y + cond_cdf[i * cdf_width + j - 1].x / res_x;
                }
 
-               float cdf_total = cond_cdf[i * cdf_count + res - 1].y + cond_cdf[i * cdf_count + res - 1].x / res;
+               float cdf_total = cond_cdf[i * cdf_width + res_x - 1].y + cond_cdf[i * cdf_width + res_x - 1].x / res_x;
                float cdf_total_inv = 1.0f / cdf_total;
 
                /* stuff the total into the brightness value for the last entry, because
                 * we are going to normalize the CDFs to 0.0 to 1.0 afterwards */
-               cond_cdf[i * cdf_count + res].x = cdf_total;
+               cond_cdf[i * cdf_width + res_x].x = cdf_total;
 
                if(cdf_total > 0.0f)
-                       for(int j = 1; j < res; j++)
-                               cond_cdf[i * cdf_count + j].y *= cdf_total_inv;
+                       for(int j = 1; j < res_x; j++)
+                               cond_cdf[i * cdf_width + j].y *= cdf_total_inv;
 
-               cond_cdf[i * cdf_count + res].y = 1.0f;
+               cond_cdf[i * cdf_width + res_x].y = 1.0f;
        }
 }
 
@@ -536,7 +536,8 @@ void LightManager::device_update_background(Device *device,
 
        /* no background light found, signal renderer to skip sampling */
        if(!background_light || !background_light->is_enabled) {
-               kintegrator->pdf_background_res = 0;
+               kintegrator->pdf_background_res_x = 0;
+               kintegrator->pdf_background_res_y = 0;
                return;
        }
 
@@ -545,41 +546,62 @@ void LightManager::device_update_background(Device *device,
        assert(kintegrator->use_direct_light);
 
        /* get the resolution from the light's size (we stuff it in there) */
-       int res = background_light->map_resolution;
-       kintegrator->pdf_background_res = res;
-
-       assert(res > 0);
+       int2 res = make_int2(background_light->map_resolution, background_light->map_resolution/2);
+       /* If the resolution isn't set manually, try to find an environment texture. */
+       if (res.x == 0) {
+               Shader *shader = (scene->background->shader) ? scene->background->shader : scene->default_background;
+               foreach(ShaderNode *node, shader->graph->nodes) {
+                       if(node->type == EnvironmentTextureNode::node_type) {
+                               EnvironmentTextureNode *env = (EnvironmentTextureNode*) node;
+                               ImageMetaData metadata;
+                               if(env->image_manager && env->image_manager->get_image_metadata(env->slot, metadata)) {
+                                       res.x = max(res.x, metadata.width);
+                                       res.y = max(res.y, metadata.height);
+                               }
+                       }
+               }
+               if (res.x > 0 && res.y > 0) {
+                       VLOG(2) << "Automatically set World MIS resolution to " << res.x << " by " << res.y << "\n";
+               }
+       }
+       /* If it's still unknown, just use the default. */
+       if (res.x == 0 || res.y == 0) {
+               res = make_int2(1024, 512);
+               VLOG(2) << "Setting World MIS resolution to default\n";
+       }
+       kintegrator->pdf_background_res_x = res.x;
+       kintegrator->pdf_background_res_y = res.y;
 
        vector<float3> pixels;
-       shade_background_pixels(device, dscene, res, pixels, progress);
+       shade_background_pixels(device, dscene, res.x, res.y, pixels, progress);
 
        if(progress.get_cancel())
                return;
 
        /* build row distributions and column distribution for the infinite area environment light */
-       int cdf_count = res + 1;
-       float2 *marg_cdf = dscene->light_background_marginal_cdf.alloc(cdf_count);
-       float2 *cond_cdf = dscene->light_background_conditional_cdf.alloc(cdf_count * cdf_count);
+       int cdf_width = res.x+1;
+       float2 *marg_cdf = dscene->light_background_marginal_cdf.alloc(res.y + 1);
+       float2 *cond_cdf = dscene->light_background_conditional_cdf.alloc(cdf_width * res.y);
 
        double time_start = time_dt();
-       if(res < 512) {
+       if(max(res.x, res.y) < 512) {
                /* Small enough resolution, faster to do single-threaded. */
-               background_cdf(0, res, res, cdf_count, &pixels, cond_cdf);
+               background_cdf(0, res.x, res.x, res.y, &pixels, cond_cdf);
        }
        else {
                /* Threaded evaluation for large resolution. */
                const int num_blocks = TaskScheduler::num_threads();
-               const int chunk_size = res / num_blocks;
+               const int chunk_size = res.y / num_blocks;
                int start_row = 0;
                TaskPool pool;
                for(int i = 0; i < num_blocks; ++i) {
                        const int current_chunk_size =
                            (i != num_blocks - 1) ? chunk_size
-                                                 : (res - i * chunk_size);
+                                                 : (res.y - i * chunk_size);
                        pool.push(function_bind(&background_cdf,
                                                start_row, start_row + current_chunk_size,
-                                               res,
-                                               cdf_count,
+                                               res.x,
+                                               res.y,
                                                &pixels,
                                                cond_cdf));
                        start_row += current_chunk_size;
@@ -588,22 +610,22 @@ void LightManager::device_update_background(Device *device,
        }
 
        /* marginal CDFs (column, V direction, sum of rows) */
-       marg_cdf[0].x = cond_cdf[res].x;
+       marg_cdf[0].x = cond_cdf[res.x].x;
        marg_cdf[0].y = 0.0f;
 
-       for(int i = 1; i < res; i++) {
-               marg_cdf[i].x = cond_cdf[i * cdf_count + res].x;
-               marg_cdf[i].y = marg_cdf[i - 1].y + marg_cdf[i - 1].x / res;
+       for(int i = 1; i < res.y; i++) {
+               marg_cdf[i].x = cond_cdf[i * cdf_width + res.x].x;
+               marg_cdf[i].y = marg_cdf[i - 1].y + marg_cdf[i - 1].x / res.y;
        }
 
-       float cdf_total = marg_cdf[res - 1].y + marg_cdf[res - 1].x / res;
-       marg_cdf[res].x = cdf_total;
+       float cdf_total = marg_cdf[res.y - 1].y + marg_cdf[res.y - 1].x / res.y;
+       marg_cdf[res.y].x = cdf_total;
 
        if(cdf_total > 0.0f)
-               for(int i = 1; i < res; i++)
+               for(int i = 1; i < res.y; i++)
                        marg_cdf[i].y /= cdf_total;
 
-       marg_cdf[res].y = 1.0f;
+       marg_cdf[res.y].y = 1.0f;
 
        VLOG(2) << "Background MIS build time " << time_dt() - time_start << "\n";