Pixi.js: No way to render shapes without graphics

Created on 9 Sep 2016  路  18Comments  路  Source: pixijs/pixi.js

Hey, I think I should be able to render rectangles without having to draw to graphics objects. It would make a lot of my code so much easier! :)

Stale 馃捑 v4.x (Legacy) 馃摙 Accepting PRs 馃ザ Low Priority

Most helpful comment

@GoodBoyDigital Of course. The team are currently tied up building a social networking and real time messaging infrastructure for the collaboration/community/social aspect of our app, and we therefore won't be out of beta until December at the earliest, so I can't really let anyone have a play now. But as soon as we are we will be setting up a developers blog describing our journey, challenges and techniques etc. As soon as that's up i'll share with you guys.

And once we have migrated to PIXI version 4, we will start to look at what we can potentially put back into PIXI in terms of plugins etc. I think the text layout and SDF font rendering would be a huge boon for you guys for starters.

All 18 comments

Certainly something we could look at :)

Well, you can use "Graphics._renderSpriteRect" approach. Graphics uses renderTexture and sprites for sipmle rectangles, you can do the same: create renderTexture, then use sprites.

var rect = this.graphicsData[0].shape;
    if(!this._spriteRect)
    {
        if(!Graphics._SPRITE_TEXTURE)
        {
            Graphics._SPRITE_TEXTURE = RenderTexture.create(10, 10);

            var currentRenderTarget = renderer._activeRenderTarget;
            renderer.bindRenderTexture(Graphics._SPRITE_TEXTURE);
            renderer.clear([1,1,1,1]);
            renderer.bindRenderTarget(currentRenderTarget);
        }

        this._spriteRect = new Sprite(Graphics._SPRITE_TEXTURE);
    }
    if (this.tint === 0xffffff) {
        this._spriteRect.tint = this.graphicsData[0].fillColor;
    } else {
        var t1 = tempColor1;
        var t2 = tempColor2;
        utils.hex2rgb(this.graphicsData[0].fillColor, t1);
        utils.hex2rgb(this.tint, t2);
        t1[0] *= t2[0];
        t1[1] *= t2[1];
        t1[2] *= t2[2];
        this._spriteRect.tint = utils.rgb2hex(t1);
    }
    this._spriteRect.alpha = this.graphicsData[0].fillAlpha;
    this._spriteRect.worldAlpha = this.worldAlpha * this._spriteRect.alpha;

    Graphics._SPRITE_TEXTURE._frame.width = rect.width;
    Graphics._SPRITE_TEXTURE._frame.height = rect.height;

    this._spriteRect.transform.worldTransform = this.transform.worldTransform;

    this._spriteRect.anchor.set(-rect.x / rect.width, -rect.y / rect.height);
    this._spriteRect.onAnchorUpdate();

    this._spriteRect._renderWebGL(renderer);

We render rectangles, triangles and circles using only custom shaders. It's much faster than using Pixi Graphics objects and all edges are completely anti-aliased due to using a distance field calculation in the shader.

Adding outlines, glows, shadows and other effects to these shapes is trivial. We simply adjust color, strokeWidth, strokeColor and various effects uniforms for the shaders.

I would suggest looking into shader based shape/primitives rendering for a future version of Pixi, as it is infinitely superior to the current approach.

Yeah but the thing is that render textures are much more cpu heavy than simple primitives (a rectangle is a textureless quad with a shader) should be, and if I have to resize that shape a lot (which i usually do) then either performance will take a hit or it will require a lot more code.

That sounds really interesting @GordoRank ! Would love to see your implementation!

@Dadibom - Graphics Rectangles are super optimised in pixi at the moment as they are batched into the sprite system. This is all done behind the scenes :)

I guess what your asking for is a short hand way of creating a rectangle?

Yeah but I still have to clear a texture and redraw it every frame, instead of just moving the individual rectangles

How comes you need to clear them each frame?

@Dadibom use multiple graphics, change position every time.

As for your primitives, if you have something custom set, please make it a plugin. Especially if your stuff is tied to plots. https://github.com/pixijs/pixi-plugin-example . It will help others too.

Doesn't work if I want to change the size :(

Sure, I can use scale. But it's a lot more code than it has to be

We basically have a complete PIXI powered desktop publishing application (shader based SDF text rendering / formatting, photo editing, page layout, drawing, bezier curves etc.) We simply extended PIXI a lot to meet our needs.

This is an old PIXI 3.0 version of a circle / ellipse shader that lets you set the width, height, opacity, strokeWidth, fillColor and strokeColor.

It's a bit messy and could be improved but it works perfectly for us. We have similar shaders for rectangles, triangles and pointed stars.

These shaders are applied to a custom Pixi renderer that simply applies the shaders to a textureless quad.

PIXI.filters.CircleShader = function(shaderManager) {


    PIXI.Shader.call(this,
        shaderManager,
        // vertex shader
        [
            'precision lowp float;',
            'attribute vec2 aVertexPosition;',
            'attribute vec2 aTextureCoord;',
            'attribute vec4 aColor;',

            'uniform mat3 projectionMatrix;',

            'varying vec2 vTextureCoord;',
            'varying vec4 vColor;',

            'void main(void){',
            '   gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);',
            '   vTextureCoord = aTextureCoord;',
            '   vColor = vec4(aColor.rgb * aColor.a, aColor.a);',
            '}'
        ].join('\n'),
        // fragment shader
        [
            '#extension GL_OES_standard_derivatives : enable',
            'precision mediump float;',

            'varying vec2 vTextureCoord;',

            'uniform float width;',
            'uniform float height;',
            'uniform float opacity;',
            'uniform vec4 fillColor;', 
            'uniform vec4 strokeColor;', 
            'uniform float strokeWidth;',

            'float texelSizeX = 1.0 / width;',
            'float texelSizeY = 1.0 / height;',

            'vec2 px = vec2(texelSizeX, texelSizeY);',
            'vec2 ab = vec2(width, height) / 2.0;',
            'vec2 center = vec2(0.5, 0.5);',


            'void main(void){',

            'vec2 pos = (vTextureCoord - center) / px;',
            'pos *= pos;',

            'float outerDist = dot(pos, 1.0 / (ab * ab));',

            'ab -= strokeWidth;',
            'float innerDist = dot(pos, 1.0 / (ab * ab));',

            'float outerDelta = length(vec2(dFdx(outerDist), dFdy(outerDist))) * 0.70710678118654757;',

            'float innerDelta = length(vec2(dFdx(innerDist), dFdy(innerDist))) * 0.70710678118654757;',

            'float innerAlpha = smoothstep(1.0 - innerDelta, 1.0 + innerDelta, innerDist);',
            'float outerAlpha = smoothstep(1.0 - outerDelta, 1.0 + outerDelta, outerDist);',


            'vec4 stroke = mix(strokeColor, vec4(0, 0, 0, 0), outerAlpha);',
            'vec4 fill = mix(fillColor, vec4(0, 0, 0, 0), innerAlpha);',

            'gl_FragColor = mix(vec4(0.0, 0.0, 0.0, 0.0), mix(fill, stroke, innerAlpha), opacity);',

            '}'
        ].join('\n'),
        // custom uniforms
        {
            dimensions: {
                type: '4fv',
                value: new Float32Array([0, 0, 0, 0])
            },
            projectionMatrix: { type: 'mat3', value: new Float32Array(9) },
            strokeWidth: {
                type: '1f',
                value: 0.0
            },
            strokeColor : { type: '4fv', value: new Float32Array([0, 0, 0, 0]) },
            fillColor : { type: '4fv', value: new Float32Array([0, 0, 0, 0]) },
            width : { type : '1f', value: 1.0},
            height : { type : '1f', value: 1.0},
            opacity : { type : '1f', value: 1.0}
        },
        // custom attributes
        {
            aTextureCoord:0,
            aVertexPosition:0,
            aColor:0
        }

    );


    this.strokeWidth = 0.0;
    this.strokeColor = new Float32Array([0, 0, 0, 0]);
    this.fillColor = new Float32Array([0, 0, 0, 0]);
    this.opacity = 1.0;


};

PIXI.filters.CircleShader.prototype = Object.create(PIXI.Shader.prototype);
PIXI.filters.CircleShader.prototype.constructor = PIXI.filters.CircleShader;

PIXI.ShaderManager.registerPlugin('circleShader', PIXI.filters.CircleShader);

I've stripped out the property definitions to keep this comment concise.

@GoodBoyDigital because i need to get rid of the old rectangle

@Dadibom good. You're on the half-way there to be official pixi plugin.

For v4 you can remove "args" parameter of the shader, arguments will be extracted automagically. Also, you cant set anything to uniforms unless shader is bound . When renderer sets value for uniforms it must be something like that:

myShader.bind();
myShader.uniforms.strokeWidth = ...;

Look at example renderer.

Also, shader code can be separated into "frag" and "vert" files, using glsify, example has it too.

@GordoRank This is really exciting stuff! Would you consider sharing what you have - I think a lot of pixi users would find what you guys have created really awesome :)

@GoodBoyDigital Of course. The team are currently tied up building a social networking and real time messaging infrastructure for the collaboration/community/social aspect of our app, and we therefore won't be out of beta until December at the earliest, so I can't really let anyone have a play now. But as soon as we are we will be setting up a developers blog describing our journey, challenges and techniques etc. As soon as that's up i'll share with you guys.

And once we have migrated to PIXI version 4, we will start to look at what we can potentially put back into PIXI in terms of plugins etc. I think the text layout and SDF font rendering would be a huge boon for you guys for starters.

Amazing! I (and i'm sure the rest of the pixi community) look forward to December :)
Good luck with your beta release!

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Kicking life into this old thread since I am interested in two aspects that @GordoRank mentions.

  1. shader based SDF text rendering
  2. shader based shapes rendering

Did you ever publish any of your work to the public?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

samueller picture samueller  路  3Comments

readygosports picture readygosports  路  3Comments

gaccob picture gaccob  路  3Comments

madroneropaulo picture madroneropaulo  路  3Comments

finscn picture finscn  路  3Comments