diff --git a/src/libcamera/shaders/bayer_unpacked.frag b/src/libcamera/shaders/bayer_unpacked.frag
index 10c5e941b..455d1fe78 100644
--- a/src/libcamera/shaders/bayer_unpacked.frag
+++ b/src/libcamera/shaders/bayer_unpacked.frag
@@ -21,9 +21,10 @@ precision highp float;
 
 /** Monochrome RGBA or GL_LUMINANCE Bayer encoded texture.*/
 uniform sampler2D       tex_y;
-varying vec4            center;
-varying vec4            yCoord;
-varying vec4            xCoord;
+uniform vec2            tex_size;
+uniform float           stride_factor;
+uniform vec2            tex_bayer_first_red;
+varying vec2            pixelPos;
 uniform vec3            awb;
 uniform mat3            ccm;
 uniform vec3            blacklevel;
@@ -52,11 +53,23 @@ void main(void) {
     #define fetch(x, y) texture2D(tex_y, vec2(x, y)).r
     #endif
 
+    /*
+     * Derive the parity and every fetch coordinate from one floor() of the
+     * pixel position, snapped to texel centres. Computed separately they
+     * can round to different texels when downscaling lands a sample on a
+     * texel boundary.
+     */
+    vec2 texStep = vec2(stride_factor / tex_size.x, 1.0 / tex_size.y);
+    vec2 pix = floor(pixelPos);
+    vec2 center = (pix + 0.5) * texStep;
+    vec4 xCoord = center.x + vec4(-2.0, -1.0, 1.0, 2.0) * texStep.x;
+    vec4 yCoord = center.y + vec4(-2.0, -1.0, 1.0, 2.0) * texStep.y;
+
     float C = fetch(center.x, center.y); // ( 0, 0)
     const vec4 kC = vec4( 4.0,  6.0,  5.0,  5.0) / 8.0;
 
     // Determine which of four types of pixels we are on.
-    vec2 alternate = mod(floor(center.zw), 2.0);
+    vec2 alternate = mod(pix + tex_bayer_first_red, 2.0);
 
     vec4 Dvec = vec4(
         fetch(xCoord[1], yCoord[1]),  // (-1,-1)
diff --git a/src/libcamera/shaders/bayer_unpacked.vert b/src/libcamera/shaders/bayer_unpacked.vert
index 423dde0fa..be1268b62 100644
--- a/src/libcamera/shaders/bayer_unpacked.vert
+++ b/src/libcamera/shaders/bayer_unpacked.vert
@@ -22,34 +22,12 @@ attribute vec2 textureIn;
 uniform mat4 proj_matrix;
 
 uniform vec2 tex_size;  /* The texture size in pixels */
-uniform vec2 tex_step;
 
-/** Pixel position of the first red pixel in the */
-/**  Bayer pattern.  [{0,1}, {0, 1}]*/
-uniform vec2            tex_bayer_first_red;
-
-/** .xy = Pixel being sampled in the fragment shader on the range [0, 1]
-    .zw = ...on the range [0, sourceSize], offset by firstRed */
-varying vec4            center;
-
-/** center.x + (-2/w, -1/w, 1/w, 2/w); These are the x-positions */
-/** of the adjacent pixels.*/
-varying vec4            xCoord;
-
-/** center.y + (-2/h, -1/h, 1/h, 2/h); These are the y-positions */
-/** of the adjacent pixels.*/
-varying vec4            yCoord;
-
-uniform float stride_factor;
+/** Position of the pixel being sampled, in image pixels. */
+varying vec2            pixelPos;
 
 void main(void) {
-    center.xy = vec2(textureIn.x * stride_factor, textureIn.y);
-    center.zw = textureIn * tex_size + tex_bayer_first_red;
-
-    xCoord = center.x + vec4(-2.0 * tex_step.x,
-                             -tex_step.x, tex_step.x, 2.0 * tex_step.x);
-    yCoord = center.y + vec4(-2.0 * tex_step.y,
-                              -tex_step.y, tex_step.y, 2.0 * tex_step.y);
+    pixelPos = textureIn * tex_size;
 
     gl_Position = proj_matrix * vertexIn;
 }
diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp
index 97aa03793..6db07c284 100644
--- a/src/libcamera/software_isp/debayer_egl.cpp
+++ b/src/libcamera/software_isp/debayer_egl.cpp
@@ -432,11 +432,11 @@ void DebayerEGL::setShaderVariableValues(eGLImage &eglImageIn, const DebayerPara
 
 	/*
 	 * These values are:
-	 * firstRed = tex_bayer_first_red - bayer_8.vert
-	 * imgSize = tex_size - bayer_8.vert
-	 * step = tex_step - bayer_8.vert
-	 * Stride = stride_factor identity.vert
-	 * textureUniformProjMatri = No scaling
+	 * firstRed = tex_bayer_first_red - bayer_unpacked.frag, bayer_1x_packed.frag
+	 * imgSize = tex_size - bayer_unpacked.vert, bayer_unpacked.frag, bayer_1x_packed.frag
+	 * step = tex_step - bayer_1x_packed.frag
+	 * Stride = stride_factor - identity.vert, bayer_unpacked.frag
+	 * projMatrix = proj_matrix - identity.vert, bayer_unpacked.vert
 	 */
 	glUniform2fv(textureUniformBayerFirstRed_, 1, firstRed);
 	glUniform2fv(textureUniformSize_, 1, imgSize);
