Jump to content

Structs as uniform type (in fragment shader)


DNIWE
 Share

Recommended Posts

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

 

Link to comment
Share on other sites

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

 

Link to comment
Share on other sites

  • 3 weeks later...

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 by DNIWE
Added use case example
Link to comment
Share on other sites

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.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...