diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 4f9865230..64639ed26 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -476,20 +476,14 @@ void RasterizerOpenGL::SetShader() {
         std::unique_ptr<TEVShader> shader = Common::make_unique<TEVShader>();
 
         shader->shader.Create(GLShader::GenerateVertexShader().c_str(), GLShader::GenerateFragmentShader(config).c_str());
-        shader->uniform_alphatest_ref = glGetUniformLocation(shader->shader.handle, "alphatest_ref");
-        shader->uniform_tex = glGetUniformLocation(shader->shader.handle, "tex");
-        shader->uniform_tev_combiner_buffer_color = glGetUniformLocation(shader->shader.handle, "tev_combiner_buffer_color");
-        shader->uniform_tev_const_colors = glGetUniformLocation(shader->shader.handle, "const_color");
 
         state.draw.shader_program = shader->shader.handle;
         state.Apply();
 
         // Set the texture samplers to correspond to different texture units
-        if (shader->uniform_tex != -1) {
-            glUniform1i(shader->uniform_tex, 0);
-            glUniform1i(shader->uniform_tex + 1, 1);
-            glUniform1i(shader->uniform_tex + 2, 2);
-        }
+        glUniform1i(PicaShader::Uniform::Texture0, 0);
+        glUniform1i(PicaShader::Uniform::Texture1, 1);
+        glUniform1i(PicaShader::Uniform::Texture2, 2);
 
         current_shader = shader_cache.emplace(config, std::move(shader)).first->second.get();
     }
@@ -622,8 +616,7 @@ void RasterizerOpenGL::SyncBlendColor() {
 
 void RasterizerOpenGL::SyncAlphaTest() {
     const auto& regs = Pica::g_state.regs;
-    if (current_shader->uniform_alphatest_ref != -1)
-        glUniform1i(current_shader->uniform_alphatest_ref, regs.output_merger.alpha_test.ref);
+    glUniform1i(PicaShader::Uniform::AlphaTestRef, regs.output_merger.alpha_test.ref);
 }
 
 void RasterizerOpenGL::SyncLogicOp() {
@@ -654,17 +647,13 @@ void RasterizerOpenGL::SyncDepthTest() {
 }
 
 void RasterizerOpenGL::SyncCombinerColor() {
-    if (current_shader->uniform_tev_combiner_buffer_color != -1) {
-        auto combiner_color = PicaToGL::ColorRGBA8(Pica::g_state.regs.tev_combiner_buffer_color.raw);
-        glUniform4fv(current_shader->uniform_tev_combiner_buffer_color, 1, combiner_color.data());
-    }
+    auto combiner_color = PicaToGL::ColorRGBA8(Pica::g_state.regs.tev_combiner_buffer_color.raw);
+    glUniform4fv(PicaShader::Uniform::TevCombinerBufferColor, 1, combiner_color.data());
 }
 
 void RasterizerOpenGL::SyncTevConstColor(int stage_index, const Pica::Regs::TevStageConfig& tev_stage) {
-    if (current_shader->uniform_tev_const_colors != -1) {
-        auto const_color = PicaToGL::ColorRGBA8(tev_stage.const_color);
-        glUniform4fv(current_shader->uniform_tev_const_colors + stage_index, 1, const_color.data());
-    }
+    auto const_color = PicaToGL::ColorRGBA8(tev_stage.const_color);
+    glUniform4fv(PicaShader::Uniform::TevConstColors + stage_index, 1, const_color.data());
 }
 
 void RasterizerOpenGL::SyncDrawState() {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 484579d82..79c34944a 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -153,36 +153,24 @@ public:
     /// Notify rasterizer that a 3DS memory region has been changed
     void NotifyFlush(PAddr addr, u32 size) override;
 
-private:
-    /// Structure used for managing texture environment states
-    struct TEVConfigUniforms {
-        GLuint enabled;
-        GLuint color_sources;
-        GLuint alpha_sources;
-        GLuint color_modifiers;
-        GLuint alpha_modifiers;
-        GLuint color_alpha_op;
-        GLuint color_alpha_multiplier;
-        GLuint const_color;
-        GLuint updates_combiner_buffer_color_alpha;
-    };
-
-    struct TEVShader {
+    /// OpenGL shader generated for a given Pica register state
+    struct PicaShader {
+        /// OpenGL shader resource
         OGLShader shader;
 
-        // Hardware fragment shader
-        GLuint uniform_alphatest_ref;
-        GLuint uniform_tex;
-        GLuint uniform_tev_combiner_buffer_color;
-        GLuint uniform_tev_const_colors;
-
-        TEVShader() = default;
-        TEVShader(TEVShader&& o) : shader(std::move(o.shader)),
-            uniform_alphatest_ref(o.uniform_alphatest_ref), uniform_tex(o.uniform_tex),
-            uniform_tev_combiner_buffer_color(o.uniform_tev_combiner_buffer_color),
-            uniform_tev_const_colors(o.uniform_tev_const_colors) {}
+        /// Fragment shader uniforms
+        enum Uniform : GLuint {
+            AlphaTestRef = 0,
+            TevConstColors = 1,
+            Texture0 = 7,
+            Texture1 = 8,
+            Texture2 = 9,
+            TevCombinerBufferColor = 10,
+        };
     };
 
+private:
+
     /// Structure used for storing information about color textures
     struct TextureInfo {
         OGLTexture texture;
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index 79c690e76..50bb2e3cc 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -321,25 +321,26 @@ static void WriteTevStage(std::string& out, const ShaderCacheKey& config, unsign
 
 std::string GenerateFragmentShader(const ShaderCacheKey& config) {
     std::string out = R"(
-#version 150 core
+#version 330
+#extension GL_ARB_explicit_uniform_location : require
 
 #define NUM_VTX_ATTR 7
 #define NUM_TEV_STAGES 6
 
 in vec4 attr[NUM_VTX_ATTR];
 out vec4 color;
-
-uniform int alphatest_ref;
-uniform vec4 const_color[NUM_TEV_STAGES];
-uniform sampler2D tex[3];
-
-uniform vec4 tev_combiner_buffer_color;
-
-void main(void) {
-vec4 g_combiner_buffer = tev_combiner_buffer_color;
-vec4 g_last_tex_env_out = vec4(0.0, 0.0, 0.0, 0.0);
 )";
 
+    using Uniform = RasterizerOpenGL::PicaShader::Uniform;
+    out += "layout(location = " + std::to_string(Uniform::AlphaTestRef) + ") uniform int alphatest_ref;\n";
+    out += "layout(location = " + std::to_string(Uniform::TevConstColors) + ") uniform vec4 const_color[NUM_TEV_STAGES];\n";
+    out += "layout(location = " + std::to_string(Uniform::Texture0) + ") uniform sampler2D tex[3];\n";
+    out += "layout(location = " + std::to_string(Uniform::TevCombinerBufferColor) + ") uniform vec4 tev_combiner_buffer_color;\n";
+
+    out += "void main() {\n";
+    out += "vec4 combiner_buffer = tev_combiner_buffer_color;\n";
+    out += "vec4 last_tex_env_out = vec4(0.0);\n";
+
     // Do not do any sort of processing if it's obvious we're not going to pass the alpha test
     if (config.alpha_test_func == Regs::CompareFunc::Never) {
         out += "discard; }";
@@ -362,7 +363,7 @@ vec4 g_last_tex_env_out = vec4(0.0, 0.0, 0.0, 0.0);
 
 std::string GenerateVertexShader() {
     static const std::string out = R"(
-#version 150 core
+#version 330
 
 #define NUM_VTX_ATTR 7