filterwebglpixi.js

Drawing a circle in fragment shader


I am a complete noob when it comes to creating shaders. Or better said, I just learned about it yesterday.

I am trying to create a really simple circle. I thouht I finally figured it out but it turns out to be to large. It should match the DisplayObject size where the filter is applied to.

The fragment shader:

precision mediump float;

varying vec2 vTextureCoord;
vec2 resolution = vec2(1.0, 1.0);

void main() {
    vec2 uv = vTextureCoord.xy / resolution.xy;
    uv -= 0.5;
    uv.x *= resolution.x / resolution.y;
    float r = 0.5;
    float d = length(uv);
    float c = smoothstep(d,d+0.003,r);
    gl_FragColor = vec4(vec3(c,0.5,0.0),1.0);
}

Example using Pixi.js:

var app = new PIXI.Application();
document.body.appendChild(app.view);

var background = PIXI.Sprite.fromImage("required/assets/bkg-grass.jpg");
background.width = 200;
background.height = 200;
app.stage.addChild(background);

var vertexShader = `
attribute vec2 aVertexPosition;
attribute vec2 aTextureCoord;

uniform mat3 projectionMatrix;

varying vec2 vTextureCoord;

void main(void)
{
    gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
    vTextureCoord = aTextureCoord;
}
`;

var fragShader = `
precision mediump float;

varying vec2 vTextureCoord;
vec2 resolution = vec2(1.0, 1.0);

void main() {
    vec2 uv = vTextureCoord.xy / resolution.xy;
    uv -= 0.5;
    uv.x *= resolution.x / resolution.y;
    float r = 0.5;
    float d = length(uv);
    float c = smoothstep(d,d+0.003,r);
    gl_FragColor = vec4(vec3(c,0.5,0.),1.0);
}
`;
var filter = new PIXI.Filter(vertexShader, fragShader);
filter.padding = 0;
background.filters = [filter];
body { margin: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/4.5.2/pixi.js"></script>


Solution

  • It seems you've stumbled upon weird floating point precision problems: texture coordinates (vTextureCoord) in your fragment shader aren't strictly in (0, 1) range. Here's what I've got when I've added line gl_FragColor = vec4(vTextureCoord, 0, 1):

    UV debug shader output

    It seems good, but if we inspect it closely, lower right pixel should be (1, 1, 0), but it isn't:

    Lower right pixel

    The problem goes away if instead of setting size to 500 by 500 we use power-of-two size (say, 512 by 512), the problem goes away:

    Lower right pixel is (1, 1)

    Circle fits!

    The other possible way to mitigate the problem would be to try to circumvent Pixi's code that computes projection matrix and provide your own that transforms smaller quad into desired screen position.