flowen

beginner question: using shaders in pixi v4 (custom filter)

Recommended Posts

Hi, this is my first time using shaders and the new filters in pixi v4, so please bare with me :ph34r:

First of all I'm a bit confused by the term (custom) filter.. Is that the same as the user of a shader? I assume it is. 

Second I tried using this example. I got it to work in pixi 3.x but somehow I can't figure out what i'm doing wrong in v4. no error messages, just a black canvas. 

My goal is to create custom button hovers with PIXI, using shaders, etc. I tried 2 different ways but... alas, same black canvas

HTML:

<a href="#" class="btn btn-one">
  <canvas></canvas>
  <span class="title">button effect one</span>
</a>

shader.frag:

  precision mediump float;
  
  uniform vec2 mouse;
  uniform vec2 resolution;
  uniform float time;

  void main() {
    gl_FragColor = vec4( sin(time), mouse.x/resolution.x, mouse.y/resolution.y, 1.0);
  }

JS:

    var btnOne = document.querySelector('.btn-one');
    var width = btnOne.clientWidth;
    var height = btnOne.clientHeight;
    var app = new PIXI.Application({
      width: width,
      height: height,
      view: btnOne.querySelector('canvas')
    });
    
    btnOne.append(app.view);

    // create rect to fill the button and apply shaders to
    
    const rect = new PIXI.Graphics()
        .beginFill(0x00ff00)
        .drawRect(0, 0, width, height);

    app.stage.addChild(rect);

    // Stop application wait for load to finish
    app.stop();

    PIXI.loader.add('shader', 'shader.frag')
        .load(onLoaded);

    var simpleShader;
    var uniforms = {};
    uniforms.mouse = {
      type: 'v2',
      value: { x:0, y:0 }
    }
    
    uniforms.time = {
      type: 'f',
      value: 0
    }

    uniforms.resolution = {
      type: 'v2',
      value:{ x: width, y: height}
    }

    function onLoaded (loader,res) {
      // Create the new filter, arguments: (vertexShader, fragmentShader, uniforms)
      simpleShader = new PIXI.Filter(null, res.shader.data, uniforms);

      rect.filters = [simpleShader];
      app.start();

      // bind mouse to shader effects
      btnOne.onmousemove = function(evt) {
        // Get the mouse position
        mousePos = { x: evt.clientX, y: evt.clientY }
        // Assigning a new value lets Pixi know to update the uniform in the shader
        // But doing something like uniforms.mouse.x = 0, won't update in this current version of Pixi
        simpleShader.uniforms.mouse.value = mousePos;
      }

      // apply the filter
      rect.filters = [simpleShader];

      // Animate the filter
      app.ticker.add(function(delta) {
        simpleShader.uniforms.time.value += 0.1
      });
    }

 

I read the post about creating filters in v4, but to a beginner it's just confusing as I don't understand a lot of the terminology used. Anyone can (hint how to) fix this so I can continue my explorations programming shaders? :)

Share this post


Link to post
Share on other sites

I don't think you can apply a Pixi filter to an HTML element like that. Is there an example/tutorial that does that?

You could also use PIXI.Text and respond to its mouse events to manually create the behavior of anchor button. And apply/remove filter to the text on mouse over/out.

Share this post


Link to post
Share on other sites

@Jinz Thanks for your reply!

My thought is that Pixi injects a canvas element within the .button. Rendering sprites was not a problem at all, but now with a shader I have no clue what's going (wr)on(g). Even without uniforms it won't display anything so I think i've been setting things up incorrectly or perhaps there's some limitation within pixi?

 

Later on I want to automate it and inject it in every .button and thus create some crazy button effects.. If someone has a different suggestion to do this, would appreciate it! :)

Share this post


Link to post
Share on other sites

Neat I didn't know you could do that. I spotted a problem in your code. You're using Pixi's loader to load a file named 'shader.frag', but your shader code isn't in a separate file named 'shader.frag', it's in your HTML file. I think you need to either 1) make the file 'shader.frag' and put your shader code there or 2) leave the shader code where it is, but get the script element and pass it's innerHtml as the shader source parameter.

Share this post


Link to post
Share on other sites

When I simply display a color for the shader, it's working. But once I try mixing in uniforms, I only get a black canvas. There's surely something simple I'm not seeing :huh: so I just keep on trying B)

 

updated the code in topic

Share this post


Link to post
Share on other sites

If you're using V4 then the uniforms should be used without .value. So for example simpleShader.mouse = mousePos;

You can also skip defining the uniforms if you want. Pixi detects all uniforms in shader code and populates the uniforms -object with correct ones.

Share this post


Link to post
Share on other sites

Open http://pixijs.github.io/examples/?v=v4.5.2#/basics/custom-filter.js , paste it there. I have background sprite, and on top im placing container with filter, and specify filterArea as whole screen. In update i modify the uniforms of the filter. I also use "app.screen" instead of "app.renderer" for width and height, because it will take care of resolution stuff (devicePixelRatio), and that rectangle can be assigned as filter area.

And if you really want to use grass texture, apply filter to background and start using textureCoord and sampler and other stuff... I dont remember exactly, look in the demo shader (http://pixijs.github.io/examples/required/assets/basics/shader.frag)

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

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

var shaderFrag = `
 precision mediump float;
  
  uniform vec2 mouse;
  uniform vec2 resolution;
  uniform float time;

  void main() {
    gl_FragColor = vec4( sin(time), mouse.x/resolution.x, mouse.y/resolution.y, 0.5);
  }
`;


var container = new PIXI.Container();
container.filterArea = app.screen;
app.stage.addChild(container);
var filter = new PIXI.Filter(null, shaderFrag);
container.filters = [filter];

// Animate the filter
app.ticker.add(function(delta) {
    var v2 = filter.uniforms.mouse;
    var global = app.renderer.plugins.interaction.mouse.global;
    v2[0] = global.x; v2[1] = global.y;
    filter.uniforms.mouse = v2;
  
    v2 = filter.uniforms.resolution;
    v2[0] = app.screen.width;
    v2[1] = app.screen.height;
    filter.uniforms.resolution = v2;
});

Share this post


Link to post
Share on other sites
5 hours ago, Exca said:

If you're using V4 then the uniforms should be used without .value. So for example simpleShader.mouse = mousePos;

You can also skip defining the uniforms if you want. Pixi detects all uniforms in shader code and populates the uniforms -object with correct ones.

I see thanks!

 

@ivan.popelyshev Ivan! Thanks so much for fixing the problem :) To be honest it does look a little wonky.. would've never thought (and didn't read anywhere) that I would need an filterArea. I tried mixing your code with my 'setup' code, but that gave me another error ( Uncaught TypeError: Cannot read property 'value' of undefined ) which I guess is some uniform that cannot be read.. Anyways, enough for today!

Thanks again for answering, even though you're tired. You da man!

Share this post


Link to post
Share on other sites

Glad that helped\. Look, i've added it in examples: http://pixijs.github.io/examples/?v=v4.5.3#/filters/filter-mouse.js

FilterArea is a sure way to set the size of filter rectangle. I dont like how "container.width" and "container.height" is calculated. "app.screen" was added by me exactly for that - if screen resizes, that rectangle will also be changed.

Share this post


Link to post
Share on other sites

Sample at http://pixijs.github.io/examples/?v=v4.5.3#/filters/filter-mouse.js fails for me with error Cannot ready property 'location' of undefined at VertexArrayObject.js:171 and 'location' is shader.attributes.aTextureCoord passed from Quad.js:98. But sample http://pixijs.io/examples/?v=v4.5.2#/basics/custom-filter.js works somehow. When I change any line at filter-mouse.js sample with vTextureCoord to anything else it also stops working. WebGL Inspector shows that that custom filter (when it's not working) is the only having no aTextureCoord attribute, so shader.attributes.aTextureCoord is undefined. Shader builder program optimizes it somehow so that it doesn't have such attribute anymore maybe?

Edit: oh, I guess it's the same issue as described in https://github.com/pixijs/pixi.js/issues/4476

Ivan was mentioning about it occurring because some sampler optimizes out aTextureCoord attribute. Is there a way trick the optimizer so that it won't remove it? Thanks in advance ^^ (eagerly waiting for pixi v5 in the meantime)

Edited by Slaus
reference found

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.