DNIWE Posted April 10, 2019 Share Posted April 10, 2019 Hello. I'm making example filter with custom fragment shader. This is what I have in terms of shader code so far: //fragShader struct SomeInfo { vec4 color; }; uniform SomeInfo c; void main() { gl_FragColor = c.color; } The idea is to (eventually) make c into an array of Infos, which would be then operated on by the shader. What I'm struggling with is the definition of filter: how do I declare the uniform to be of type SomeInfo in Js code? I assume in plain WebGL, I'd have to bind uniform location (of c's property) by calling gl.getUniformLocation(program, "c.color") and create a uniform with appropriate gl.uniform4f(location, /* bunch of values*/), but can I do something similar via the existing filters means? Relevant part of my Js code looks like this: //Define base filter class for our future shaders/filters PIXI.filters.CustomFilterBase = class CustomFilterBase extends PIXI.Filter { constructor({ vertexSrc = null, fragmentSrc = null, uniforms = {}, enabled = true, debug = false, name = null } = {}) { if(debug && fragmentSrc !== null) { fragmentSrc = "#define DEBUG \r\n" + fragmentSrc; } //Add dimensions for scaling uniforms.dimensions = { type: 'vec2', value: { x: 0.0, y: 0.0 } }; super(vertexSrc, fragmentSrc, uniforms); name ? this._name = name : this._name = "CustomFilterBase"; this.autoFit = false; this.enabled = enabled; } apply(filterManager, input, output) { this.uniforms.dimensions.x = input.sourceFrame.width; this.uniforms.dimensions.y = input.sourceFrame.height; // draw the filter... filterManager.applyFilter(this, input, output); } } //Shader for prototyping and testing PIXI.filters.TestFilter = class TestFilter extends PIXI.filters.CustomFilterBase { constructor() { let fragmentSrc = document.getElementById('fragShader').innerHTML; let uniforms = { //What do I do here?! c: { type: 'vec4', //Judging by GLSL_SINGLE_SETTERS, only GLSL's primitives are recognized value: new Float32Array([0.0, 1.0, 0.0, 1.0]) } }; super({ vertexSrc: null, fragmentSrc: fragmentSrc, uniforms: uniforms, name: 'testfilter' }); } } (using pixijs v4.8.7) The expected result is green screen, as it is if I declare c as vec4 in shader code, but alas the screen is black, hinting on c's value being default constructed / not properly assigned Any help is appreciated, cheers! P.S. I tried to find similar cases from this forum and stackoverflow, but it seems that few people use structs in GLSL code. P.P.S. If it is of any help, I found that PIXI.glCore.shader removes specific characters from uniform's name (which looks like a hotfix rather than a feature) and that in fact one of iterations uniformData's name is 'c.color'. /** * Extracts the uniforms * @class * @memberof PIXI.glCore.shader * @param gl {WebGLRenderingContext} The current WebGL rendering context * @param program {WebGLProgram} The shader program to get the uniforms from * @return uniforms {Object} */ var extractUniforms = function(gl, program) { var uniforms = {}; var totalUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); for (var i = 0; i < totalUniforms; i++) { var uniformData = gl.getActiveUniform(program, i); var name = uniformData.name.replace(/\[.*?\]/, ""); //<----- Here it is!! var type = mapType(gl, uniformData.type ); uniforms[name] = { type:type, size:uniformData.size, location:gl.getUniformLocation(program, name), value:defaultValue(type, uniformData.size) }; } return uniforms; }; Quote Link to comment Share on other sites More sharing options...
ivan.popelyshev Posted April 10, 2019 Share Posted April 10, 2019 wait a moment, making a demo. I believe we have something like that in v5, i dont remember about v4. DNIWE 1 Quote Link to comment Share on other sites More sharing options...
DNIWE Posted April 10, 2019 Author Share Posted April 10, 2019 8 minutes ago, ivan.popelyshev said: wait a moment, making a demo. I believe we have something like that in v5, i dont remember about v4. Thanks and no rush Quote Link to comment Share on other sites More sharing options...
ivan.popelyshev Posted April 10, 2019 Share Posted April 10, 2019 OK, I made it to the dead end. Now I asked @Mat Groves if he can fix my demos. He owns most of code regarding uniforms. If we succeed, result will be here: <PLAYGROUND DOENST WORK > No guarantees about v4 though, I just dont remember if anyone discussed uniform struct in it. Quote Link to comment Share on other sites More sharing options...
DNIWE Posted April 10, 2019 Author Share Posted April 10, 2019 Thank you. I'm not bound to use v4, it's just the latest tag I checked out from repo that looked like a full release rather than rc. Quote Link to comment Share on other sites More sharing options...
ivan.popelyshev Posted April 10, 2019 Share Posted April 10, 2019 PixiJS philosophy allows to use webgl hacks when you really know what are you doing and how it affects PixiJS state. You can hack "apply" function of the filter or "applyFilter" function of FilterManager: bind the shader there and assign your uniforms How filterManager handles it : https://github.com/pixijs/pixi.js/blob/v4.x/src/core/renderers/webgl/managers/FilterManager.js#L242 How filter calls filterManager: https://github.com/pixijs/pixi.js/blob/v4.x/src/filters/displacement/DisplacementFilter.js#L64 It looks like "syncUniforms" is the best place to apply the hack: shader is already bound. Just in case if you need whats inside GLShader: https://github.com/pixijs/pixi-gl-core/blob/master/src/GLShader.js#L40 var oldSyncUniforms = renderer.filterManager.syncUniforms; renderer.filterManager.syncUniforms = function(shader, filter) { oldSyncUniforms.call(this, shader, filter); if (filter.syncUniforms) { filter.syncUniforms(shader); } } myFilter.syncUniforms = function(shader) { var gl = shader.gl; gl.uniform1f(...); } Quote Link to comment Share on other sites More sharing options...
botmaster Posted April 17, 2019 Share Posted April 17, 2019 it could be worth mentioning this found in the PIXI 5.0.0 code: // currently this does not extract structs only default types Quote Link to comment Share on other sites More sharing options...
botmaster Posted April 17, 2019 Share Posted April 17, 2019 struct don't have location so you can't set them directly in code. What has location are the struct properties so in your case you can set the uniform for "c.color". This is a valid GLSL syntax for setting unifroms. Quote Link to comment Share on other sites More sharing options...
DNIWE Posted May 6, 2019 Author Share Posted May 6, 2019 (edited) Too bad that the forum was down for so long... anyway I peeked at the problem that Ivan encountered in his V5 demos, and found out that syncUniformsGeneration function generates empty function (full story can be found here). Having done that I went back to v4 and got the thing working, with one little caveat that I feel like is worth mentioning. When testing out filters I found out by default the last shader is set active.So if you have multiple shaders setup, you'd have to manually "activate" appropriate shader before writing to uniforms, otherwise webgl would swear at you in console with red errors. In order to do that, however, you'd need access to shader created by this line (cached one will also be fine) 19478: shader = new _Shader2.default(this.gl, filter.vertexSrc, filter.fragmentSrc); However, it is a local variable of FilterManager's applyFilter function, so I ended up using this monstrocity as a workaround PIXI.InjectedApplication = class InjectedApplication extends PIXI.Application { constructor({width = arguments[0], height = arguments[1], options = arguments[2], noWebGL = arguments[3], sharedTicker = arguments[4], sharedLoader = arguments[5]}) { super(width, height, options, noWebGL, sharedTicker, sharedLoader); var oldSyncUniforms = this.renderer.filterManager.syncUniforms; this.renderer.filterManager.syncUniforms = function(shader, filter) { //shader is NOT of type PIXI.Shader but of _Shader2 ! //Call "vanilla" sync uniforms oldSyncUniforms.call(this, shader, filter); //Then call custom one if(filter.syncUniforms) { filter.syncUniforms(shader); } //Save references to filter //in order to be able to switch active shader. filter.__program = shader.program; filter.__shader = shader; }; } updateShader(shader, func) { //this.shaders is just a map of PIXI.Filter shaders let shdr = this.shaders[shader] || shader; let prev = this.renderer._activeShader; this.renderer.bindShader(shdr.__shader); //Make shader use program (openGL terms) func(shdr); //Execute function app.renderer.bindShader(prev); //Bind previous original shader back } } And the use example is simply this: function mouseMoveHandler(e) { let newpos = e.data.getLocalPosition(app.stage); app.updateShader('squareshader', function(shader) { //_uniforms are my custom uniforms and squares is array of structs in GLSL shader._uniforms['squares[0].coords'] = new Float32Array([newpos.x, newpos.y]); }); } On 4/17/2019 at 4:43 PM, botmaster said: struct don't have location so you can't set them directly in code. What has location are the struct properties so in your case you can set the uniform for "c.color". This is a valid GLSL syntax for setting unifroms. Thank you, I already knew that. To my knowelage this also applies to arrays, ex. struct SomeInfo { vec4 color; }; uniform SomeInfo c[2]; to access 1st color the correct string for position getter would be "c[0].color" Edited May 6, 2019 by DNIWE Added use case example ivan.popelyshev 1 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.