Я полный нуб, когда дело доходит до создания шейдеров. Или лучше сказать, я только что узнал об этом вчера.

Я пытаюсь создать действительно простой круг. Я думал, что я наконец понял это, но это оказывается слишком большим. Он должен соответствовать размеру отображаемого объекта, к которому применяется фильтр.

Фрагмент шейдера:

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);
}

Пример использования 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>
1
Joery 28 Май 2017 в 23:56

2 ответа

Лучший ответ

Кажется, вы наткнулись на странные проблемы точности с плавающей точкой: координаты текстуры (vTextureCoord) в вашем фрагментном шейдере не находятся строго в (0, 1) диапазоне. Вот что я получил, когда добавил строку gl_FragColor = vec4(vTextureCoord, 0, 1):

UV debug shader output

Это кажется хорошим, но если мы внимательно его проверим, нижний правый пиксель должен быть (1, 1, 0), но это не так:

Lower right pixel

Проблема исчезнет, если вместо установки размера 500 на 500 мы используем размер степени двойки (скажем, 512 на 512), проблема исчезнет:

Lower right pixel is (1, 1)

Circle fits!

Другой возможный способ смягчить проблему - попытаться обойти код Pixi, который вычисляет матрицу проекции, и предоставить собственный, который преобразует меньший квад в желаемое положение экрана.

1
Kirill Dmitrenko 29 Май 2017 в 09:00

Pixi.js vTextureCoord не переходят от 0 к 1.

Из документов.

Фильтры V4 отличаются от фильтров V3. Вы не можете просто добавить шейдер и предположить, что координаты текстуры находятся в диапазоне [0,1].

...

Примечание: vTextureCoord, умноженное на filterArea.xy, представляет собой реальный размер ограничительной рамки.

Если вы хотите получить координаты пикселя, используйте единый filterArea, он будет автоматически передан в фильтр.

uniform vec4 filterArea;
...
vec2 pixelCoord = vTextureCoord * filterArea.xy;

Они в пикселях. Это не сработает, если мы хотим что-то вроде «заполнить эллипс в ограничительной рамке». Итак, давайте пропустим размеры! PIXI не делает это автоматически, нам нужно ручное исправление:

filter.apply = function(filterManager, input, output)
{
  this.uniforms.dimensions[0] = input.sourceFrame.width
  this.uniforms.dimensions[1] = input.sourceFrame.height

  // draw the filter...
 filterManager.applyFilter(this, input, output);
}

Позволяет объединить его в шейдер!

uniform vec4 filterArea;
uniform vec2 dimensions;
...
vec2 pixelCoord = vTextureCoord * filterArea.xy;
vec2 normalizedCoord = pixelCoord / dimensions;

Вот ваш обновленный фрагмент.

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;
uniform vec2 dimensions;
uniform vec4 filterArea;

void main() {
    vec2 pixelCoord = vTextureCoord * filterArea.xy;
    vec2 uv = pixelCoord / dimensions;
    uv -= 0.5;
    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.apply = function(filterManager, input, output)
{
  this.uniforms.dimensions[0] = input.sourceFrame.width
  this.uniforms.dimensions[1] = input.sourceFrame.height

  // draw the filter...
  filterManager.applyFilter(this, input, output);
}

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>
1
gman 31 Май 2017 в 19:31