diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 718c709e5..3c6ea42be 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -33,6 +33,7 @@ set(HEADERS
             rasterizer.h
             rasterizer_interface.h
             regs_framebuffer.h
+            regs_lighting.h
             regs_rasterizer.h
             regs_texturing.h
             renderer_base.h
diff --git a/src/video_core/pica.h b/src/video_core/pica.h
index 50a549c42..371bcdb84 100644
--- a/src/video_core/pica.h
+++ b/src/video_core/pica.h
@@ -19,6 +19,7 @@
 #include "common/logging/log.h"
 #include "common/vector_math.h"
 #include "video_core/regs_framebuffer.h"
+#include "video_core/regs_lighting.h"
 #include "video_core/regs_rasterizer.h"
 #include "video_core/regs_texturing.h"
 
@@ -53,281 +54,7 @@ struct Regs {
     RasterizerRegs rasterizer;
     TexturingRegs texturing;
     FramebufferRegs framebuffer;
-
-    enum class LightingSampler {
-        Distribution0 = 0,
-        Distribution1 = 1,
-        Fresnel = 3,
-        ReflectBlue = 4,
-        ReflectGreen = 5,
-        ReflectRed = 6,
-        SpotlightAttenuation = 8,
-        DistanceAttenuation = 16,
-    };
-
-    /**
-     * Pica fragment lighting supports using different LUTs for each lighting component:
-     * Reflectance R, G, and B channels, distribution function for specular components 0 and 1,
-     * fresnel factor, and spotlight attenuation. Furthermore, which LUTs are used for each channel
-     * (or whether a channel is enabled at all) is specified by various pre-defined lighting
-     * configurations. With configurations that require more LUTs, more cycles are required on HW to
-     * perform lighting computations.
-     */
-    enum class LightingConfig {
-        Config0 = 0, ///< Reflect Red, Distribution 0, Spotlight
-        Config1 = 1, ///< Reflect Red, Fresnel, Spotlight
-        Config2 = 2, ///< Reflect Red, Distribution 0/1
-        Config3 = 3, ///< Distribution 0/1, Fresnel
-        Config4 = 4, ///< Reflect Red/Green/Blue, Distribution 0/1, Spotlight
-        Config5 = 5, ///< Reflect Red/Green/Blue, Distribution 0, Fresnel, Spotlight
-        Config6 = 6, ///< Reflect Red, Distribution 0/1, Fresnel, Spotlight
-        Config7 = 8, ///< Reflect Red/Green/Blue, Distribution 0/1, Fresnel, Spotlight
-                     ///< NOTE: '8' is intentional, '7' does not appear to be a valid configuration
-    };
-
-    /// Selects which lighting components are affected by fresnel
-    enum class LightingFresnelSelector {
-        None = 0,           ///< Fresnel is disabled
-        PrimaryAlpha = 1,   ///< Primary (diffuse) lighting alpha is affected by fresnel
-        SecondaryAlpha = 2, ///< Secondary (specular) lighting alpha is affected by fresnel
-        Both =
-            PrimaryAlpha |
-            SecondaryAlpha, ///< Both primary and secondary lighting alphas are affected by fresnel
-    };
-
-    /// Factor used to scale the output of a lighting LUT
-    enum class LightingScale {
-        Scale1 = 0,   ///< Scale is 1x
-        Scale2 = 1,   ///< Scale is 2x
-        Scale4 = 2,   ///< Scale is 4x
-        Scale8 = 3,   ///< Scale is 8x
-        Scale1_4 = 6, ///< Scale is 0.25x
-        Scale1_2 = 7, ///< Scale is 0.5x
-    };
-
-    enum class LightingLutInput {
-        NH = 0, // Cosine of the angle between the normal and half-angle vectors
-        VH = 1, // Cosine of the angle between the view and half-angle vectors
-        NV = 2, // Cosine of the angle between the normal and the view vector
-        LN = 3, // Cosine of the angle between the light and the normal vectors
-    };
-
-    enum class LightingBumpMode : u32 {
-        None = 0,
-        NormalMap = 1,
-        TangentMap = 2,
-    };
-
-    union LightColor {
-        BitField<0, 10, u32> b;
-        BitField<10, 10, u32> g;
-        BitField<20, 10, u32> r;
-
-        Math::Vec3f ToVec3f() const {
-            // These fields are 10 bits wide, however 255 corresponds to 1.0f for each color
-            // component
-            return Math::MakeVec((f32)r / 255.f, (f32)g / 255.f, (f32)b / 255.f);
-        }
-    };
-
-    /// Returns true if the specified lighting sampler is supported by the current Pica lighting
-    /// configuration
-    static bool IsLightingSamplerSupported(LightingConfig config, LightingSampler sampler) {
-        switch (sampler) {
-        case LightingSampler::Distribution0:
-            return (config != LightingConfig::Config1);
-
-        case LightingSampler::Distribution1:
-            return (config != LightingConfig::Config0) && (config != LightingConfig::Config1) &&
-                   (config != LightingConfig::Config5);
-
-        case LightingSampler::Fresnel:
-            return (config != LightingConfig::Config0) && (config != LightingConfig::Config2) &&
-                   (config != LightingConfig::Config4);
-
-        case LightingSampler::ReflectRed:
-            return (config != LightingConfig::Config3);
-
-        case LightingSampler::ReflectGreen:
-        case LightingSampler::ReflectBlue:
-            return (config == LightingConfig::Config4) || (config == LightingConfig::Config5) ||
-                   (config == LightingConfig::Config7);
-        default:
-            UNREACHABLE_MSG("Regs::IsLightingSamplerSupported: Reached "
-                            "unreachable section, sampler should be one "
-                            "of Distribution0, Distribution1, Fresnel, "
-                            "ReflectRed, ReflectGreen or ReflectBlue, instead "
-                            "got %i",
-                            static_cast<int>(config));
-        }
-    }
-
-    struct {
-        struct LightSrc {
-            LightColor specular_0; // material.specular_0 * light.specular_0
-            LightColor specular_1; // material.specular_1 * light.specular_1
-            LightColor diffuse;    // material.diffuse * light.diffuse
-            LightColor ambient;    // material.ambient * light.ambient
-
-            // Encoded as 16-bit floating point
-            union {
-                BitField<0, 16, u32> x;
-                BitField<16, 16, u32> y;
-            };
-            union {
-                BitField<0, 16, u32> z;
-            };
-
-            INSERT_PADDING_WORDS(0x3);
-
-            union {
-                BitField<0, 1, u32> directional;
-                BitField<1, 1, u32> two_sided_diffuse; // When disabled, clamp dot-product to 0
-            } config;
-
-            BitField<0, 20, u32> dist_atten_bias;
-            BitField<0, 20, u32> dist_atten_scale;
-
-            INSERT_PADDING_WORDS(0x4);
-        };
-        static_assert(sizeof(LightSrc) == 0x10 * sizeof(u32),
-                      "LightSrc structure must be 0x10 words");
-
-        LightSrc light[8];
-        LightColor global_ambient; // Emission + (material.ambient * lighting.ambient)
-        INSERT_PADDING_WORDS(0x1);
-        BitField<0, 3, u32> max_light_index; // Number of enabled lights - 1
-
-        union {
-            BitField<2, 2, LightingFresnelSelector> fresnel_selector;
-            BitField<4, 4, LightingConfig> config;
-            BitField<22, 2, u32> bump_selector; // 0: Texture 0, 1: Texture 1, 2: Texture 2
-            BitField<27, 1, u32> clamp_highlights;
-            BitField<28, 2, LightingBumpMode> bump_mode;
-            BitField<30, 1, u32> disable_bump_renorm;
-        } config0;
-
-        union {
-            BitField<16, 1, u32> disable_lut_d0;
-            BitField<17, 1, u32> disable_lut_d1;
-            BitField<19, 1, u32> disable_lut_fr;
-            BitField<20, 1, u32> disable_lut_rr;
-            BitField<21, 1, u32> disable_lut_rg;
-            BitField<22, 1, u32> disable_lut_rb;
-
-            // Each bit specifies whether distance attenuation should be applied for the
-            // corresponding light
-
-            BitField<24, 1, u32> disable_dist_atten_light_0;
-            BitField<25, 1, u32> disable_dist_atten_light_1;
-            BitField<26, 1, u32> disable_dist_atten_light_2;
-            BitField<27, 1, u32> disable_dist_atten_light_3;
-            BitField<28, 1, u32> disable_dist_atten_light_4;
-            BitField<29, 1, u32> disable_dist_atten_light_5;
-            BitField<30, 1, u32> disable_dist_atten_light_6;
-            BitField<31, 1, u32> disable_dist_atten_light_7;
-        } config1;
-
-        bool IsDistAttenDisabled(unsigned index) const {
-            const unsigned disable[] = {
-                config1.disable_dist_atten_light_0, config1.disable_dist_atten_light_1,
-                config1.disable_dist_atten_light_2, config1.disable_dist_atten_light_3,
-                config1.disable_dist_atten_light_4, config1.disable_dist_atten_light_5,
-                config1.disable_dist_atten_light_6, config1.disable_dist_atten_light_7};
-            return disable[index] != 0;
-        }
-
-        union {
-            BitField<0, 8, u32> index; ///< Index at which to set data in the LUT
-            BitField<8, 5, u32> type;  ///< Type of LUT for which to set data
-        } lut_config;
-
-        BitField<0, 1, u32> disable;
-        INSERT_PADDING_WORDS(0x1);
-
-        // When data is written to any of these registers, it gets written to the lookup table of
-        // the selected type at the selected index, specified above in the `lut_config` register.
-        // With each write, `lut_config.index` is incremented. It does not matter which of these
-        // registers is written to, the behavior will be the same.
-        u32 lut_data[8];
-
-        // These are used to specify if absolute (abs) value should be used for each LUT index. When
-        // abs mode is disabled, LUT indexes are in the range of (-1.0, 1.0). Otherwise, they are in
-        // the range of (0.0, 1.0).
-        union {
-            BitField<1, 1, u32> disable_d0;
-            BitField<5, 1, u32> disable_d1;
-            BitField<9, 1, u32> disable_sp;
-            BitField<13, 1, u32> disable_fr;
-            BitField<17, 1, u32> disable_rb;
-            BitField<21, 1, u32> disable_rg;
-            BitField<25, 1, u32> disable_rr;
-        } abs_lut_input;
-
-        union {
-            BitField<0, 3, LightingLutInput> d0;
-            BitField<4, 3, LightingLutInput> d1;
-            BitField<8, 3, LightingLutInput> sp;
-            BitField<12, 3, LightingLutInput> fr;
-            BitField<16, 3, LightingLutInput> rb;
-            BitField<20, 3, LightingLutInput> rg;
-            BitField<24, 3, LightingLutInput> rr;
-        } lut_input;
-
-        union {
-            BitField<0, 3, LightingScale> d0;
-            BitField<4, 3, LightingScale> d1;
-            BitField<8, 3, LightingScale> sp;
-            BitField<12, 3, LightingScale> fr;
-            BitField<16, 3, LightingScale> rb;
-            BitField<20, 3, LightingScale> rg;
-            BitField<24, 3, LightingScale> rr;
-
-            static float GetScale(LightingScale scale) {
-                switch (scale) {
-                case LightingScale::Scale1:
-                    return 1.0f;
-                case LightingScale::Scale2:
-                    return 2.0f;
-                case LightingScale::Scale4:
-                    return 4.0f;
-                case LightingScale::Scale8:
-                    return 8.0f;
-                case LightingScale::Scale1_4:
-                    return 0.25f;
-                case LightingScale::Scale1_2:
-                    return 0.5f;
-                }
-                return 0.0f;
-            }
-        } lut_scale;
-
-        INSERT_PADDING_WORDS(0x6);
-
-        union {
-            // There are 8 light enable "slots", corresponding to the total number of lights
-            // supported by Pica. For N enabled lights (specified by register 0x1c2, or 'src_num'
-            // above), the first N slots below will be set to integers within the range of 0-7,
-            // corresponding to the actual light that is enabled for each slot.
-
-            BitField<0, 3, u32> slot_0;
-            BitField<4, 3, u32> slot_1;
-            BitField<8, 3, u32> slot_2;
-            BitField<12, 3, u32> slot_3;
-            BitField<16, 3, u32> slot_4;
-            BitField<20, 3, u32> slot_5;
-            BitField<24, 3, u32> slot_6;
-            BitField<28, 3, u32> slot_7;
-
-            unsigned GetNum(unsigned index) const {
-                const unsigned enable_slots[] = {slot_0, slot_1, slot_2, slot_3,
-                                                 slot_4, slot_5, slot_6, slot_7};
-                return enable_slots[index];
-            }
-        } light_enable;
-    } lighting;
-
-    INSERT_PADDING_WORDS(0x26);
+    LightingRegs lighting;
 
     enum class VertexAttributeFormat : u64 {
         BYTE = 0,
@@ -702,6 +429,7 @@ ASSERT_REG_POSITION(framebuffer.output_merger, 0x100);
 ASSERT_REG_POSITION(framebuffer.framebuffer, 0x110);
 
 ASSERT_REG_POSITION(lighting, 0x140);
+
 ASSERT_REG_POSITION(vertex_attributes, 0x200);
 ASSERT_REG_POSITION(index_array, 0x227);
 ASSERT_REG_POSITION(num_vertices, 0x228);
diff --git a/src/video_core/regs_lighting.h b/src/video_core/regs_lighting.h
new file mode 100644
index 000000000..b14500ff7
--- /dev/null
+++ b/src/video_core/regs_lighting.h
@@ -0,0 +1,292 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+
+#include "common/bit_field.h"
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+
+namespace Pica {
+
+struct LightingRegs {
+    enum class LightingSampler {
+        Distribution0 = 0,
+        Distribution1 = 1,
+        Fresnel = 3,
+        ReflectBlue = 4,
+        ReflectGreen = 5,
+        ReflectRed = 6,
+        SpotlightAttenuation = 8,
+        DistanceAttenuation = 16,
+    };
+
+    /**
+    * Pica fragment lighting supports using different LUTs for each lighting component:  Reflectance
+    * R, G, and B channels, distribution function for specular components 0 and 1, fresnel factor,
+    * and spotlight attenuation.  Furthermore, which LUTs are used for each channel (or whether a
+    * channel is enabled at all) is specified by various pre-defined lighting configurations.  With
+    * configurations that require more LUTs, more cycles are required on HW to perform lighting
+    * computations.
+    */
+    enum class LightingConfig {
+        Config0 = 0, ///< Reflect Red, Distribution 0, Spotlight
+        Config1 = 1, ///< Reflect Red, Fresnel, Spotlight
+        Config2 = 2, ///< Reflect Red, Distribution 0/1
+        Config3 = 3, ///< Distribution 0/1, Fresnel
+        Config4 = 4, ///< Reflect Red/Green/Blue, Distribution 0/1, Spotlight
+        Config5 = 5, ///< Reflect Red/Green/Blue, Distribution 0, Fresnel, Spotlight
+        Config6 = 6, ///< Reflect Red, Distribution 0/1, Fresnel, Spotlight
+
+        Config7 = 8, ///< Reflect Red/Green/Blue, Distribution 0/1, Fresnel, Spotlight
+                     ///< NOTE: '8' is intentional, '7' does not appear to be a valid configuration
+    };
+
+    /// Selects which lighting components are affected by fresnel
+    enum class LightingFresnelSelector {
+        None = 0,           ///< Fresnel is disabled
+        PrimaryAlpha = 1,   ///< Primary (diffuse) lighting alpha is affected by fresnel
+        SecondaryAlpha = 2, ///< Secondary (specular) lighting alpha is affected by fresnel
+        Both =
+            PrimaryAlpha |
+            SecondaryAlpha, ///< Both primary and secondary lighting alphas are affected by fresnel
+    };
+
+    /// Factor used to scale the output of a lighting LUT
+    enum class LightingScale {
+        Scale1 = 0, ///< Scale is 1x
+        Scale2 = 1, ///< Scale is 2x
+        Scale4 = 2, ///< Scale is 4x
+        Scale8 = 3, ///< Scale is 8x
+
+        Scale1_4 = 6, ///< Scale is 0.25x
+        Scale1_2 = 7, ///< Scale is 0.5x
+    };
+
+    enum class LightingLutInput {
+        NH = 0, // Cosine of the angle between the normal and half-angle vectors
+        VH = 1, // Cosine of the angle between the view and half-angle vectors
+        NV = 2, // Cosine of the angle between the normal and the view vector
+        LN = 3, // Cosine of the angle between the light and the normal vectors
+    };
+
+    enum class LightingBumpMode : u32 {
+        None = 0,
+        NormalMap = 1,
+        TangentMap = 2,
+    };
+
+    union LightColor {
+        BitField<0, 10, u32> b;
+        BitField<10, 10, u32> g;
+        BitField<20, 10, u32> r;
+
+        Math::Vec3f ToVec3f() const {
+            // These fields are 10 bits wide, however 255 corresponds to 1.0f for each color
+            // component
+            return Math::MakeVec((f32)r / 255.f, (f32)g / 255.f, (f32)b / 255.f);
+        }
+    };
+
+    /// Returns true if the specified lighting sampler is supported by the current Pica lighting
+    /// configuration
+    static bool IsLightingSamplerSupported(LightingConfig config, LightingSampler sampler) {
+        switch (sampler) {
+        case LightingSampler::Distribution0:
+            return (config != LightingConfig::Config1);
+
+        case LightingSampler::Distribution1:
+            return (config != LightingConfig::Config0) && (config != LightingConfig::Config1) &&
+                   (config != LightingConfig::Config5);
+
+        case LightingSampler::Fresnel:
+            return (config != LightingConfig::Config0) && (config != LightingConfig::Config2) &&
+                   (config != LightingConfig::Config4);
+
+        case LightingSampler::ReflectRed:
+            return (config != LightingConfig::Config3);
+
+        case LightingSampler::ReflectGreen:
+        case LightingSampler::ReflectBlue:
+            return (config == LightingConfig::Config4) || (config == LightingConfig::Config5) ||
+                   (config == LightingConfig::Config7);
+        default:
+            UNREACHABLE_MSG("Regs::IsLightingSamplerSupported: Reached "
+                            "unreachable section, sampler should be one "
+                            "of Distribution0, Distribution1, Fresnel, "
+                            "ReflectRed, ReflectGreen or ReflectBlue, instead "
+                            "got %i",
+                            static_cast<int>(config));
+        }
+    }
+
+    struct LightSrc {
+        LightColor specular_0; // material.specular_0 * light.specular_0
+        LightColor specular_1; // material.specular_1 * light.specular_1
+        LightColor diffuse;    // material.diffuse * light.diffuse
+        LightColor ambient;    // material.ambient * light.ambient
+
+        // Encoded as 16-bit floating point
+        union {
+            BitField<0, 16, u32> x;
+            BitField<16, 16, u32> y;
+        };
+        union {
+            BitField<0, 16, u32> z;
+        };
+
+        INSERT_PADDING_WORDS(0x3);
+
+        union {
+            BitField<0, 1, u32> directional;
+            BitField<1, 1, u32> two_sided_diffuse; // When disabled, clamp dot-product to 0
+        } config;
+
+        BitField<0, 20, u32> dist_atten_bias;
+        BitField<0, 20, u32> dist_atten_scale;
+
+        INSERT_PADDING_WORDS(0x4);
+    };
+    static_assert(sizeof(LightSrc) == 0x10 * sizeof(u32), "LightSrc structure must be 0x10 words");
+
+    LightSrc light[8];
+    LightColor global_ambient; // Emission + (material.ambient * lighting.ambient)
+    INSERT_PADDING_WORDS(0x1);
+    BitField<0, 3, u32> max_light_index; // Number of enabled lights - 1
+
+    union {
+        BitField<2, 2, LightingFresnelSelector> fresnel_selector;
+        BitField<4, 4, LightingConfig> config;
+        BitField<22, 2, u32> bump_selector; // 0: Texture 0, 1: Texture 1, 2: Texture 2
+        BitField<27, 1, u32> clamp_highlights;
+        BitField<28, 2, LightingBumpMode> bump_mode;
+        BitField<30, 1, u32> disable_bump_renorm;
+    } config0;
+
+    union {
+        BitField<16, 1, u32> disable_lut_d0;
+        BitField<17, 1, u32> disable_lut_d1;
+        BitField<19, 1, u32> disable_lut_fr;
+        BitField<20, 1, u32> disable_lut_rr;
+        BitField<21, 1, u32> disable_lut_rg;
+        BitField<22, 1, u32> disable_lut_rb;
+
+        // Each bit specifies whether distance attenuation should be applied for the corresponding
+        // light.
+        BitField<24, 1, u32> disable_dist_atten_light_0;
+        BitField<25, 1, u32> disable_dist_atten_light_1;
+        BitField<26, 1, u32> disable_dist_atten_light_2;
+        BitField<27, 1, u32> disable_dist_atten_light_3;
+        BitField<28, 1, u32> disable_dist_atten_light_4;
+        BitField<29, 1, u32> disable_dist_atten_light_5;
+        BitField<30, 1, u32> disable_dist_atten_light_6;
+        BitField<31, 1, u32> disable_dist_atten_light_7;
+    } config1;
+
+    bool IsDistAttenDisabled(unsigned index) const {
+        const unsigned disable[] = {
+            config1.disable_dist_atten_light_0, config1.disable_dist_atten_light_1,
+            config1.disable_dist_atten_light_2, config1.disable_dist_atten_light_3,
+            config1.disable_dist_atten_light_4, config1.disable_dist_atten_light_5,
+            config1.disable_dist_atten_light_6, config1.disable_dist_atten_light_7};
+        return disable[index] != 0;
+    }
+
+    union {
+        BitField<0, 8, u32> index; ///< Index at which to set data in the LUT
+        BitField<8, 5, u32> type;  ///< Type of LUT for which to set data
+    } lut_config;
+
+    BitField<0, 1, u32> disable;
+    INSERT_PADDING_WORDS(0x1);
+
+    // When data is written to any of these registers, it gets written to the lookup table of the
+    // selected type at the selected index, specified above in the `lut_config` register.  With each
+    // write, `lut_config.index` is incremented.  It does not matter which of these registers is
+    // written to, the behavior will be the same.
+    u32 lut_data[8];
+
+    // These are used to specify if absolute (abs) value should be used for each LUT index.  When
+    // abs mode is disabled, LUT indexes are in the range of (-1.0, 1.0).  Otherwise, they are in
+    // the range of (0.0, 1.0).
+    union {
+        BitField<1, 1, u32> disable_d0;
+        BitField<5, 1, u32> disable_d1;
+        BitField<9, 1, u32> disable_sp;
+        BitField<13, 1, u32> disable_fr;
+        BitField<17, 1, u32> disable_rb;
+        BitField<21, 1, u32> disable_rg;
+        BitField<25, 1, u32> disable_rr;
+    } abs_lut_input;
+
+    union {
+        BitField<0, 3, LightingLutInput> d0;
+        BitField<4, 3, LightingLutInput> d1;
+        BitField<8, 3, LightingLutInput> sp;
+        BitField<12, 3, LightingLutInput> fr;
+        BitField<16, 3, LightingLutInput> rb;
+        BitField<20, 3, LightingLutInput> rg;
+        BitField<24, 3, LightingLutInput> rr;
+    } lut_input;
+
+    union {
+        BitField<0, 3, LightingScale> d0;
+        BitField<4, 3, LightingScale> d1;
+        BitField<8, 3, LightingScale> sp;
+        BitField<12, 3, LightingScale> fr;
+        BitField<16, 3, LightingScale> rb;
+        BitField<20, 3, LightingScale> rg;
+        BitField<24, 3, LightingScale> rr;
+
+        static float GetScale(LightingScale scale) {
+            switch (scale) {
+            case LightingScale::Scale1:
+                return 1.0f;
+            case LightingScale::Scale2:
+                return 2.0f;
+            case LightingScale::Scale4:
+                return 4.0f;
+            case LightingScale::Scale8:
+                return 8.0f;
+            case LightingScale::Scale1_4:
+                return 0.25f;
+            case LightingScale::Scale1_2:
+                return 0.5f;
+            }
+            return 0.0f;
+        }
+    } lut_scale;
+
+    INSERT_PADDING_WORDS(0x6);
+
+    union {
+        // There are 8 light enable "slots", corresponding to the total number of lights supported
+        // by Pica.  For N enabled lights (specified by register 0x1c2, or 'src_num' above), the
+        // first N slots below will be set to integers within the range of 0-7, corresponding to the
+        // actual light that is enabled for each slot.
+
+        BitField<0, 3, u32> slot_0;
+        BitField<4, 3, u32> slot_1;
+        BitField<8, 3, u32> slot_2;
+        BitField<12, 3, u32> slot_3;
+        BitField<16, 3, u32> slot_4;
+        BitField<20, 3, u32> slot_5;
+        BitField<24, 3, u32> slot_6;
+        BitField<28, 3, u32> slot_7;
+
+        unsigned GetNum(unsigned index) const {
+            const unsigned enable_slots[] = {slot_0, slot_1, slot_2, slot_3,
+                                             slot_4, slot_5, slot_6, slot_7};
+            return enable_slots[index];
+        }
+    } light_enable;
+
+    INSERT_PADDING_WORDS(0x26);
+};
+
+static_assert(sizeof(LightingRegs) == 0xC0 * sizeof(u32), "LightingRegs struct has incorrect size");
+
+} // namespace Pica
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index bfdc0c8a4..3e6850302 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -192,18 +192,18 @@ union PicaShaderConfig {
 
             bool enable;
             unsigned src_num;
-            Pica::Regs::LightingBumpMode bump_mode;
+            Pica::LightingRegs::LightingBumpMode bump_mode;
             unsigned bump_selector;
             bool bump_renorm;
             bool clamp_highlights;
 
-            Pica::Regs::LightingConfig config;
-            Pica::Regs::LightingFresnelSelector fresnel_selector;
+            Pica::LightingRegs::LightingConfig config;
+            Pica::LightingRegs::LightingFresnelSelector fresnel_selector;
 
             struct {
                 bool enable;
                 bool abs_input;
-                Pica::Regs::LightingLutInput type;
+                Pica::LightingRegs::LightingLutInput type;
                 float scale;
             } lut_d0, lut_d1, lut_fr, lut_rr, lut_rg, lut_rb;
         } lighting;
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index 9c7687f62..0467cccfd 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -14,6 +14,7 @@
 
 using Pica::Regs;
 using Pica::RasterizerRegs;
+using Pica::LightingRegs;
 using TevStageConfig = Pica::TexturingRegs::TevStageConfig;
 
 namespace GLShader {
@@ -365,7 +366,7 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
            "vec3 refl_value = vec3(0.0);\n";
 
     // Compute fragment normals
-    if (lighting.bump_mode == Pica::Regs::LightingBumpMode::NormalMap) {
+    if (lighting.bump_mode == LightingRegs::LightingBumpMode::NormalMap) {
         // Bump mapping is enabled using a normal map, read perturbation vector from the selected
         // texture
         std::string bump_selector = std::to_string(lighting.bump_selector);
@@ -379,7 +380,7 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
                 "(1.0 - (surface_normal.x*surface_normal.x + surface_normal.y*surface_normal.y))";
             out += "surface_normal.z = sqrt(max(" + val + ", 0.0));\n";
         }
-    } else if (lighting.bump_mode == Pica::Regs::LightingBumpMode::TangentMap) {
+    } else if (lighting.bump_mode == LightingRegs::LightingBumpMode::TangentMap) {
         // Bump mapping is enabled using a tangent map
         LOG_CRITICAL(HW_GPU, "unimplemented bump mapping mode (tangent mapping)");
         UNIMPLEMENTED();
@@ -393,23 +394,24 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
     out += "vec3 normal = normalize(quaternion_rotate(normquat, surface_normal));\n";
 
     // Gets the index into the specified lookup table for specular lighting
-    auto GetLutIndex = [&lighting](unsigned light_num, Regs::LightingLutInput input, bool abs) {
+    auto GetLutIndex = [&lighting](unsigned light_num, LightingRegs::LightingLutInput input,
+                                   bool abs) {
         const std::string half_angle = "normalize(normalize(view) + light_vector)";
         std::string index;
         switch (input) {
-        case Regs::LightingLutInput::NH:
+        case LightingRegs::LightingLutInput::NH:
             index = "dot(normal, " + half_angle + ")";
             break;
 
-        case Regs::LightingLutInput::VH:
+        case LightingRegs::LightingLutInput::VH:
             index = std::string("dot(normalize(view), " + half_angle + ")");
             break;
 
-        case Regs::LightingLutInput::NV:
+        case LightingRegs::LightingLutInput::NV:
             index = std::string("dot(normal, normalize(view))");
             break;
 
-        case Regs::LightingLutInput::LN:
+        case LightingRegs::LightingLutInput::LN:
             index = std::string("dot(light_vector, normal)");
             break;
 
@@ -433,7 +435,7 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
     };
 
     // Gets the lighting lookup table value given the specified sampler and index
-    auto GetLutValue = [](Regs::LightingSampler sampler, std::string lut_index) {
+    auto GetLutValue = [](LightingRegs::LightingSampler sampler, std::string lut_index) {
         return std::string("texture(lut[" + std::to_string((unsigned)sampler / 4) + "], " +
                            lut_index + ")[" + std::to_string((unsigned)sampler & 3) + "]");
     };
@@ -462,8 +464,8 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
                                 light_src + ".position) + " + light_src + ".dist_atten_bias)";
             index = "(OFFSET_256 + SCALE_256 * clamp(" + index + ", 0.0, 1.0))";
             const unsigned lut_num =
-                ((unsigned)Regs::LightingSampler::DistanceAttenuation + light_config.num);
-            dist_atten = GetLutValue((Regs::LightingSampler)lut_num, index);
+                ((unsigned)LightingRegs::LightingSampler::DistanceAttenuation + light_config.num);
+            dist_atten = GetLutValue((LightingRegs::LightingSampler)lut_num, index);
         }
 
         // If enabled, clamp specular component if lighting result is negative
@@ -473,24 +475,24 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
         // Specular 0 component
         std::string d0_lut_value = "1.0";
         if (lighting.lut_d0.enable &&
-            Pica::Regs::IsLightingSamplerSupported(lighting.config,
-                                                   Pica::Regs::LightingSampler::Distribution0)) {
+            LightingRegs::IsLightingSamplerSupported(
+                lighting.config, LightingRegs::LightingSampler::Distribution0)) {
             // Lookup specular "distribution 0" LUT value
             std::string index =
                 GetLutIndex(light_config.num, lighting.lut_d0.type, lighting.lut_d0.abs_input);
             d0_lut_value = "(" + std::to_string(lighting.lut_d0.scale) + " * " +
-                           GetLutValue(Regs::LightingSampler::Distribution0, index) + ")";
+                           GetLutValue(LightingRegs::LightingSampler::Distribution0, index) + ")";
         }
         std::string specular_0 = "(" + d0_lut_value + " * " + light_src + ".specular_0)";
 
         // If enabled, lookup ReflectRed value, otherwise, 1.0 is used
         if (lighting.lut_rr.enable &&
-            Pica::Regs::IsLightingSamplerSupported(lighting.config,
-                                                   Pica::Regs::LightingSampler::ReflectRed)) {
+            LightingRegs::IsLightingSamplerSupported(lighting.config,
+                                                     LightingRegs::LightingSampler::ReflectRed)) {
             std::string index =
                 GetLutIndex(light_config.num, lighting.lut_rr.type, lighting.lut_rr.abs_input);
             std::string value = "(" + std::to_string(lighting.lut_rr.scale) + " * " +
-                                GetLutValue(Regs::LightingSampler::ReflectRed, index) + ")";
+                                GetLutValue(LightingRegs::LightingSampler::ReflectRed, index) + ")";
             out += "refl_value.r = " + value + ";\n";
         } else {
             out += "refl_value.r = 1.0;\n";
@@ -498,12 +500,13 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
 
         // If enabled, lookup ReflectGreen value, otherwise, ReflectRed value is used
         if (lighting.lut_rg.enable &&
-            Pica::Regs::IsLightingSamplerSupported(lighting.config,
-                                                   Pica::Regs::LightingSampler::ReflectGreen)) {
+            LightingRegs::IsLightingSamplerSupported(lighting.config,
+                                                     LightingRegs::LightingSampler::ReflectGreen)) {
             std::string index =
                 GetLutIndex(light_config.num, lighting.lut_rg.type, lighting.lut_rg.abs_input);
             std::string value = "(" + std::to_string(lighting.lut_rg.scale) + " * " +
-                                GetLutValue(Regs::LightingSampler::ReflectGreen, index) + ")";
+                                GetLutValue(LightingRegs::LightingSampler::ReflectGreen, index) +
+                                ")";
             out += "refl_value.g = " + value + ";\n";
         } else {
             out += "refl_value.g = refl_value.r;\n";
@@ -511,12 +514,13 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
 
         // If enabled, lookup ReflectBlue value, otherwise, ReflectRed value is used
         if (lighting.lut_rb.enable &&
-            Pica::Regs::IsLightingSamplerSupported(lighting.config,
-                                                   Pica::Regs::LightingSampler::ReflectBlue)) {
+            LightingRegs::IsLightingSamplerSupported(lighting.config,
+                                                     LightingRegs::LightingSampler::ReflectBlue)) {
             std::string index =
                 GetLutIndex(light_config.num, lighting.lut_rb.type, lighting.lut_rb.abs_input);
             std::string value = "(" + std::to_string(lighting.lut_rb.scale) + " * " +
-                                GetLutValue(Regs::LightingSampler::ReflectBlue, index) + ")";
+                                GetLutValue(LightingRegs::LightingSampler::ReflectBlue, index) +
+                                ")";
             out += "refl_value.b = " + value + ";\n";
         } else {
             out += "refl_value.b = refl_value.r;\n";
@@ -525,35 +529,39 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
         // Specular 1 component
         std::string d1_lut_value = "1.0";
         if (lighting.lut_d1.enable &&
-            Pica::Regs::IsLightingSamplerSupported(lighting.config,
-                                                   Pica::Regs::LightingSampler::Distribution1)) {
+            LightingRegs::IsLightingSamplerSupported(
+                lighting.config, LightingRegs::LightingSampler::Distribution1)) {
             // Lookup specular "distribution 1" LUT value
             std::string index =
                 GetLutIndex(light_config.num, lighting.lut_d1.type, lighting.lut_d1.abs_input);
             d1_lut_value = "(" + std::to_string(lighting.lut_d1.scale) + " * " +
-                           GetLutValue(Regs::LightingSampler::Distribution1, index) + ")";
+                           GetLutValue(LightingRegs::LightingSampler::Distribution1, index) + ")";
         }
         std::string specular_1 =
             "(" + d1_lut_value + " * refl_value * " + light_src + ".specular_1)";
 
         // Fresnel
-        if (lighting.lut_fr.enable && Pica::Regs::IsLightingSamplerSupported(
-                                          lighting.config, Pica::Regs::LightingSampler::Fresnel)) {
+        if (lighting.lut_fr.enable &&
+            LightingRegs::IsLightingSamplerSupported(lighting.config,
+                                                     LightingRegs::LightingSampler::Fresnel)) {
             // Lookup fresnel LUT value
             std::string index =
                 GetLutIndex(light_config.num, lighting.lut_fr.type, lighting.lut_fr.abs_input);
             std::string value = "(" + std::to_string(lighting.lut_fr.scale) + " * " +
-                                GetLutValue(Regs::LightingSampler::Fresnel, index) + ")";
+                                GetLutValue(LightingRegs::LightingSampler::Fresnel, index) + ")";
 
             // Enabled for difffuse lighting alpha component
-            if (lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::PrimaryAlpha ||
-                lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::Both)
+            if (lighting.fresnel_selector == LightingRegs::LightingFresnelSelector::PrimaryAlpha ||
+                lighting.fresnel_selector == LightingRegs::LightingFresnelSelector::Both) {
                 out += "diffuse_sum.a  *= " + value + ";\n";
+            }
 
             // Enabled for the specular lighting alpha component
-            if (lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::SecondaryAlpha ||
-                lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::Both)
+            if (lighting.fresnel_selector ==
+                    LightingRegs::LightingFresnelSelector::SecondaryAlpha ||
+                lighting.fresnel_selector == LightingRegs::LightingFresnelSelector::Both) {
                 out += "specular_sum.a *= " + value + ";\n";
+            }
         }
 
         // Compute primary fragment color (diffuse lighting) function
diff --git a/src/video_core/renderer_opengl/pica_to_gl.h b/src/video_core/renderer_opengl/pica_to_gl.h
index c1bf3dc24..97c044918 100644
--- a/src/video_core/renderer_opengl/pica_to_gl.h
+++ b/src/video_core/renderer_opengl/pica_to_gl.h
@@ -210,7 +210,7 @@ inline GLvec4 ColorRGBA8(const u32 color) {
     }};
 }
 
-inline std::array<GLfloat, 3> LightColor(const Pica::Regs::LightColor& color) {
+inline std::array<GLfloat, 3> LightColor(const Pica::LightingRegs::LightColor& color) {
     return {{
         color.r / 255.0f, color.g / 255.0f, color.b / 255.0f,
     }};