Sebi Posted January 28, 2016 Share Posted January 28, 2016 Hey guys, due to the lack of several features in pixi v2, I didn't work much with pixi in the past year but instead ran my own rendering engine. Now with pixi v3, I assume that I can continue using pixi. I'm still going through the source code of v3 and I'm trying to understand all the changes since v2. So far so good. It's really well structured. What I currently need help with are custom shaders. I have a simple full canvas QUAD -1, -1 to 1, 1, a vertex shader and a fragment shader. I also have 3 textures that I pass to my shader. it's trivial in webgl, but how would I go about this in pixi? Can I use a PIXI.Sprite with width and height set to the renderer's width and height and apply a custom AbstractFilter to it, or would I need to write my own RenderObject? I really don't need any of the fancy stuff. No positions, no scaling, no rotation, just a QUAD with a shader applied to it. This is an isometric map renderer that I wrote for my game: precision mediump float; attribute vec2 position; attribute vec2 texture; varying vec2 pixelCoord; uniform vec2 viewOffset; uniform vec2 viewportSize; void main(void) { pixelCoord = (texture * viewportSize) + viewOffset; gl_Position = vec4(position, 0.0, 1.0); } precision mediump float; varying vec2 pixelCoord; uniform sampler2D tiles; uniform sampler2D sprites; uniform sampler2D hitTest; uniform vec2 inverseTileTextureSize; uniform vec2 inverseSpriteTextureSize; uniform vec2 tileSize; uniform vec2 animationStep; uniform vec2 halfTileSize; uniform vec2 inverseTileSize; void main(void) { vec2 spriteCoord = mod(pixelCoord, tileSize); vec2 texCoord = vec2(0.0, 0.0); if (texture2D(hitTest, spriteCoord / tileSize).x > 0.5) { texCoord.x = floor(pixelCoord.x / tileSize.x); texCoord.y = 2.0 * floor(pixelCoord.y / tileSize.y); } else { texCoord.x = floor((pixelCoord.x + tileSize.y) / tileSize.x) - 1.0; texCoord.y = 2.0 * floor((pixelCoord.y + halfTileSize.y) / tileSize.y) - 1.0; spriteCoord = mod(spriteCoord + halfTileSize, tileSize); } vec4 tile = texture2D(tiles, texCoord * inverseTileTextureSize); if(tile.r == 1.0 && tile.g == 1.0) { discard; } vec2 spriteOffset = floor((tile.rg + tile.rb * animationStep) * 256.0) * tileSize; gl_FragColor = texture2D(sprites, (spriteOffset + spriteCoord) * inverseSpriteTextureSize); } As you can see, it's pretty simple, I just need to be able to pass 3 textures to my shader. I also need to access the stencil buffer for a fog of war shader. I guess I would have to study the webgl GraphicsRenderer and roll my own as well. Anything I absolutely need to keep in mind to avoid pixi going crazy? I would really appreciate it if anyone could tell me how I can create a custom sprite without a texture. I can figure out the rest myself, if I get something as simple as this running, I'd be more than happy. QUAD: [ x y u v -1, -1, 0, 1, 1, -1, 1, 1, 1, 1, 1, 0, -1, -1, 0, 1, 1, 1, 1, 0, -1, 1, 0, 0 ]; VERTEX SHADER attribute vec2 aVertexPosition; void main(void) { gl_Position = vec4(aVertexPosition, 0.0, 1.0); } FRAGMENT SHADER precision mediump float; void main(void) { gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0); } That would be my quad drawn via gl.drawArrays(gl.TRIANGLES, 0, 6); Cheers Quote Link to comment Share on other sites More sharing options...
ivan.popelyshev Posted January 28, 2016 Share Posted January 28, 2016 Yes, you need your own ObjectRenderer. Its difficult for the first time. After you do it, you'll understand how pixi works internally, and the process will become easy. https://github.com/pixijs/pixi-tilemap/blob/master/src/pixi-tilemap.js - that's good example of how to create your own renderer class with custom shader that is not using traditional PIXI uniforms. Its also working with tiles. That library also have rpgmaker example and simple one as well. 0. Look at https://github.com/pixijs/pixi.js/blob/master/src/core/renderers/webgl/shaders/TextureShader.js , make your own shader class. 1. Look at https://github.com/pixijs/pixi.js/blob/master/src/core/sprites/webgl/SpriteRenderer.js . Clone it, register in WebGLRenderer with another name, remove all shit about batches. Create your shader (0) somewhere inside. You can use 2. Look at https://github.com/pixijs/pixi.js/blob/master/src/core/sprites/Sprite.js , make a sprite (you know how to extend class, right?) with overriden renderWebGL method, that calls your renderer instead of Sprite one. You also can use https://github.com/pixijs/pixi.js/blob/master/src/core/renderers/webgl/utils/Quad.js for help, just create it inside your ObjectRenderer. UPD. Also, that way you'll be able to make it as library for other PIXI users. The more shaders and renderers we have - the better. Quote Link to comment Share on other sites More sharing options...
Sebi Posted January 28, 2016 Author Share Posted January 28, 2016 Thanks, Ivan! Sounds geat. I will take a look into that, Happy to hear that custom renderObjects are possible. When I checked the v3 source and at renderer.plugins.sprite.render(this); I got lost and didn't know where to continue reading. I will see if I can create a dead simple shader with your code. Quote Link to comment Share on other sites More sharing options...
ivan.popelyshev Posted January 28, 2016 Share Posted January 28, 2016 48 minutes ago, SebastianNette said: I will see if I can create a dead simple shader with your code. My code has two shaders: for both gl.TRIANGLES and gl.POINTS. I dont think the way you made tiles is good for performance, I tried that approach too and it failed. But may be you'll make it faster Quote Link to comment Share on other sites More sharing options...
xerver Posted January 28, 2016 Share Posted January 28, 2016 You don't need to write your own object renderer, sprites are rendered as Quads. You can use the .shader property to override the behavior of how that sprite is drawn. The only catch is that the quad of the sprite is determined by the .texture property. If that makes sense for you, that is the quad will be based on a texture then you're good to go. The only reason you would need a custom ObjectRenderer is if you need to do something the current renderer doesn't support. In fact, if what you want is just to specify geometery and apply a custom shader, use the Mesh class (it can be any geom you want including just a quad) and set the .shader property. Its really rare that you have to implement a custom Object Renderer. You also don't have to make raw Shader classes either, Filters work just fine. Create something extending AbstractFilter like you normally would create a filter, and just assign it to the .shader property. I would be suprised if this was harder than just use sprite/mesh and assign your filter to the .shader property. Quote Link to comment Share on other sites More sharing options...
ivan.popelyshev Posted January 28, 2016 Share Posted January 28, 2016 Yeah, filter assigned to .shader will work too, its useful. Quote Link to comment Share on other sites More sharing options...
Sebi Posted January 29, 2016 Author Share Posted January 29, 2016 13 hours ago, ivan.popelyshev said: My code has two shaders: for both gl.TRIANGLES and gl.POINTS. I dont think the way you made tiles is good for performance, I tried that approach too and it failed. But may be you'll make it faster The performance is actually pretty good, all the texture lookups should be cached by the gpu and the rest is just some simple math. Another approach would be to pre-render 9 chunks at half the viewport width and height and move / redraw them as needed. Not sure yet which would be faster. But the cost of drawing several quads should be greater than drawing the map in one go. I'm still experimenting on this. I want a really fast background shader that ignores all depth. As few quads as possible. My isometric renderer is inspired by Toji's 2d renderer for LTTP. http://media.tojicode.com/zelda/lttp.html Just that I go 2.5d instead of 2d. I think no one can complain about the rendering speed Thanks xerver, that makes sense. I need 3 textures (the map image, the hit test tile and the spritesheet). Is there an example somewhere that shows how I would send more than 1 texture to my shader? For my fog of war shader, I basically just disable the colorMask and enable the stencil buffer, then I draw a batch of white circle textures to all onscreen units, enable the color mask again and draw a fullscreen half transparent black quad and disable the stencil buffer again. gl.enable(gl.STENCIL_TEST); gl.stencilFunc(gl.ALWAYS, 0x1, 0xffffffff); gl.stencilOp(gl.REPLACE, gl.REPLACE, gl.REPLACE); gl.colorMask(false, false, false, false); /* draw units / gl.colorMask(true, true, true, true); gl.depthMask(true); gl.stencilFunc(gl.NOTEQUAL, 1, 1); gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); /* draw quad (0.0, 0.0, 0.0, 0.5) */ gl.disable(gl.STENCIL_TEST); The shader for the fog would then just discard all pixel in the buffer drawn by the circle batch. How would I go about this? I don't want to use a PIXI.Graphics object, especially because I don't want it to calculate the circles as a polygon each frame but instead just batching those circle textures, like a ParticleContainer would do. Is there a way for PIXI, to mask a Sprite by a ParticleContainer? Quote Link to comment Share on other sites More sharing options...
xerver Posted January 29, 2016 Share Posted January 29, 2016 Quote Is there an example somewhere that shows how I would send more than 1 texture to my shader? https://github.com/pixijs/pixi.js/blob/master/src/filters/displacement/DisplacementFilter.js#L27 just have a sampler2D uniform, and assign a pixi texture to it. 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.