【问题标题】:Drawing a circle in fragment shader在片段着色器中画一个圆圈
【发布时间】:2017-06-01 03:31:55
【问题描述】:

在创建着色器方面,我完全是个菜鸟。或者更确切地说,我昨天才知道。

我正在尝试创建一个非常简单的圆圈。我以为我终于弄明白了,但事实证明它太大了。它应该与应用过滤器的 DisplayObject 大小相匹配。

片段着色器:

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>

【问题讨论】:

    标签: filter webgl pixi.js


    【解决方案1】:

    Pixi.js 的 vTextureCoord 不会从 0 变为 1。

    来自the docs

    V4 过滤器不同于 V3。您不能只添加着色器并假设纹理坐标在 [0,1] 范围内。

    ...

    注意:vTextureCoord 乘以 filterArea.xy 是边界框的实际大小。

    如果要获取像素坐标,使用uniform filterArea,它会自动传递给filter。

    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;
    

    这是您的 sn-p 更新。

    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>

    【讨论】:

      【解决方案2】:

      您似乎偶然发现了奇怪的浮点精度问题:片段着色器中的纹理坐标 (vTextureCoord) 并不严格在 (0, 1) 范围内。这是我添加gl_FragColor = vec4(vTextureCoord, 0, 1) 行后得到的结果:

      看起来不错,但如果我们仔细观察,右下像素应该是 (1, 1, 0),但不是:

      如果不是将大小设置为 500 x 500,而是使用 2 的幂大小(例如,512 x 512),问题就会消失:

      缓解此问题的另一种可能方法是尝试绕过 Pixi 计算投影矩阵的代码,并提供您自己的代码,将较小的四边形转换为所需的屏幕位置。

      【讨论】:

      • 谢谢!你让我意识到我忘记了什么。我已经更新了小提琴。 jsfiddle.net/08pdhrff/1
      • @gman 好吧,问题还是一样:片段着色器依赖(依赖?)vTextureCoord 在整个图像中从 0 变为 1 以适应圆圈,但事实并非如此。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-21
      • 1970-01-01
      • 2012-11-12
      • 1970-01-01
      • 2018-12-25
      • 1970-01-01
      相关资源
      最近更新 更多