<p>Thanks for the input everyone. The problem turned out to be an issue with texture coordinates. I was sampling right on the edge of each pixel in the full-size image, causing causing it to shift to the next pixel in line. To fix this, I translated the texture coordinates left/up by half a pixel and I get the intended result.</p>

CC BY-SA 4.0
2018-11-26T07:39:31.013Z
2018-11-26T07:39:31.013Z
el_zilcho
<p>The downscaled picture is not smooth because you use "nearest" filtering.</p>

<p>Each block of 2x2 pixels in the original image is transformed into a single pixel in the downscaled image. Put another way, each pixel in the downscaled image represents the contents of 4 original pixels. Your current solution considers just one of those 4 pixels and ignores the others. This is what produces blockiness in the downscaled picture.</p>

<p>To fix this, each downscaled pixel must contain information from more than one original pixel. The simplest way to do this is by using a "box filter" where each downscaled pixel is computed as the mean of the 4 original pixels it covers. This can be achieved in a single texture fetch with linear filtering and the proper sampling position:</p>

<pre><code>+---+---+
| A | B |  // ABCD =&gt; original pixels
+---X---+  // X =&gt; sample position in the middle of the 4 pixels (texels)
| C | D |
+---+---+
</code></pre>

<p>Linear filtering sampled at position <code>0</code> will give <code>(A+B+C+D) / 4</code> or the average value.</p>

<p>Another computer graphic term for what you're doing is "mipmap generation". There are other filters which give better quality than the simple box filter I described.</p>

CC BY-SA 4.0
2018-10-30T11:20:32.910Z
2018-10-30T11:20:32.910Z
bernie
<p>I'm working on an image processing app which uses WebGL to apply shaders to a bunch of input photos. At one point in the algorithm, I need to create some <a href="https://en.wikipedia.org/wiki/Pyramid_(image_processing)" rel="nofollow noreferrer">image pyramids</a> where I downscale the input photos by a factor of two each time, until I reach the smallest possible level.</p>

<p>For my downscaling routine, I'm using a really simple process:</p>

<ol>
<li>Create a new, blank texture using <code>texImage2D</code> which is half the size of the original.</li>
<li>Attach this texture to a frame buffer and use it as a render target (<code>bindFrameBuffer</code>). I also set the viewport to match the new texture's size.</li>
<li>Draw a flat quad whose vertices cover the screen-space coordinates (-1 -> 1) using a 'copy-paste' shader which samples the original (full-size) texture. <em>Note that the texture parameters are always NEAREST and CLAMP_TO_EDGE.</em></li>
</ol>

<p>Here's the source code for my fragment shader:</p>

<pre><code>precision mediump float;

uniform sampler2D u_inputImage;  // Original full-size texture

varying vec2 v_texCoords;

void main()
{
    gl_FragColor = vec4(texture2D(u_inputImage, v_texCoords).rgb, 1);
}
</code></pre>

<p>The result looks sensible, but the quality of the downscaled texture is pretty poor. In the following example, you can see how it looks jagged compared to the original, particularly along the edges of the table on the bottom-right. There are also some glitchy pixels on the top of the chair, and under the door handle:</p>

<p><a href="https://i.sstatic.net/7Tpr9.jpg" rel="nofollow noreferrer"><img src="https://i.sstatic.net/7Tpr9.jpg" alt="Original"></a></p>

<p><a href="https://i.sstatic.net/a3UHF.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/a3UHF.png" alt="Downsampled"></a></p>

<p>Does anyone know why this simple method doesn't seem to work? Is there a better way of creating a downsampled texture using WebGL/OpenGL?</p>

CC BY-SA 4.0
2018-10-27T01:56:51.660Z
"*Note that the texture parameters are always NEAREST*" What exactly are you expecting this to do?
Nicol Bolas
CC BY-SA 4.0
2018-10-27T02:49:24.310Z
Well I have another piece of code which runs on the GPU and fills the half-size texture with every other row and column of the original. The results from that are good, with no blocky artifacts or jagged edges. I thought this would be the shader equivalent.
el_zilcho
CC BY-SA 4.0
2018-10-27T02:50:54.620Z
Did you try `GL_LINEAR` nearest neighbour is supposed to give blocked artifacts, linear will interpolate hence will look smoother imo?
gallickgunner
<texture><webgl>
WebGL - issues doing a simple texture downsample
el_zilcho
# WebGL - issues doing a simple texture downsample

(Asked by: username_0 on 2018-10-27)

I'm working on an image processing app which uses WebGL to apply shaders to a bunch of input photos. At one point in the algorithm, I need to create some [image pyramids](https://en.wikipedia.org/wiki/Pyramid_(image_processing)) where I downscale the input photos by a factor of two each time, until I reach the smallest possible level.

For my downscaling routine, I'm using a really simple process:

1. Create a new, blank texture using `texImage2D` which is half the size of the original.
2. Attach this texture to a frame buffer and use it as a render target (`bindFrameBuffer`). I also set the viewport to match the new texture's size.
3. Draw a flat quad whose vertices cover the screen-space coordinates (-1 -> 1) using a 'copy-paste' shader which samples the original (full-size) texture. *Note that the texture parameters are always NEAREST and CLAMP\_TO\_EDGE.*

Here's the source code for my fragment shader:

```
precision mediump float;

uniform sampler2D u_inputImage;  // Original full-size texture

varying vec2 v_texCoords;

void main()
{
    gl_FragColor = vec4(texture2D(u_inputImage, v_texCoords).rgb, 1);
}

```

The result looks sensible, but the quality of the downscaled texture is pretty poor. In the following example, you can see how it looks jagged compared to the original, particularly along the edges of the table on the bottom-right. There are also some glitchy pixels on the top of the chair, and under the door handle:

[![Original](https://i.sstatic.net/7Tpr9.jpg)](https://i.sstatic.net/7Tpr9.jpg)

[![Downsampled](https://i.sstatic.net/a3UHF.png)](https://i.sstatic.net/a3UHF.png)

Does anyone know why this simple method doesn't seem to work? Is there a better way of creating a downsampled texture using WebGL/OpenGL?

**Tags:** texture, webgl

> **Comments:**
> **username_1**: "*Note that the texture parameters are always NEAREST*" What exactly are you expecting this to do?  
> **username_0**: Well I have another piece of code which runs on the GPU and fills the half-size texture with every other row and column of the original. The results from that are good, with no blocky artifacts or jagged edges. I thought this would be the shader equivalent.  
> **username_2**: Did you try `GL_LINEAR` nearest neighbour is supposed to give blocked artifacts, linear will interpolate hence will look smoother imo?  

---

## 2 answers

### Answer by username_3 on 2018-10-30. Score: 2

The downscaled picture is not smooth because you use "nearest" filtering.

Each block of 2x2 pixels in the original image is transformed into a single pixel in the downscaled image. Put another way, each pixel in the downscaled image represents the contents of 4 original pixels. Your current solution considers just one of those 4 pixels and ignores the others. This is what produces blockiness in the downscaled picture.

To fix this, each downscaled pixel must contain information from more than one original pixel. The simplest way to do this is by using a "box filter" where each downscaled pixel is computed as the mean of the 4 original pixels it covers. This can be achieved in a single texture fetch with linear filtering and the proper sampling position:

```
+---+---+
| A | B |  // ABCD => original pixels
+---X---+  // X => sample position in the middle of the 4 pixels (texels)
| C | D |
+---+---+

```

Linear filtering sampled at position `0` will give `(A+B+C+D) / 4` or the average value.

Another computer graphic term for what you're doing is "mipmap generation". There are other filters which give better quality than the simple box filter I described.


---

### Answer by username_0 on 2018-11-26. Score: 1

Thanks for the input everyone. The problem turned out to be an issue with texture coordinates. I was sampling right on the edge of each pixel in the full-size image, causing causing it to shift to the next pixel in line. To fix this, I translated the texture coordinates left/up by half a pixel and I get the intended result.


---

