Iavra Posted August 15, 2016 Share Posted August 15, 2016 In PIXI v2, i was using the following fragment filter to convert a texture from rectangular to polar coordinates, as part of a process to create a shadowmap: PIXI.AbstractFilter.call(this, [ '#define PI 3.14', 'precision mediump float;', 'varying vec2 vTextureCoord;', 'uniform sampler2D uSampler;', 'void main(void) {', ' vec2 norm = vTextureCoord * 2.0 - 1.0;', ' float theta = PI + norm.x * PI;', ' float r = (1.0 + norm.y) * 0.5;', ' vec2 coord = vec2(-r * sin(theta), -r * cos(theta)) / 2.0 + 0.5;', ' gl_FragColor = texture2D(uSampler, coord);', '}' ]); The first 2 pictures in this thread show an example of how the filter works: http://www.gmlscripts.com/forums/viewtopic.php?id=1657 However, with PIXI v4 (and maybe v3, i skipped that one), the resulting image looks like this: Furthermore, the result changes, depending on the position of the sprite on the screen, which is something i don't think v2 did. Has anyone tried to do something similar in v4 and can point me in the right direction? Quote Link to comment Share on other sites More sharing options...
ivan.popelyshev Posted August 15, 2016 Share Posted August 15, 2016 That can help with filter. The problem is it uses power-of-two temporary textures. May be its better to make it as a shader for Sprite? Or render it to some RenderTexture first, then make create a Sprite, specify its shader, and render it onto other RenderTexture? Then blend it both using new stage where one sprite is original and second is shadowmap. May be even use some awesome blending mode like https://github.com/pixijs/pixi-picture has OVERLAY/HARD_LIGHT or something Quote Link to comment Share on other sites More sharing options...
Iavra Posted August 15, 2016 Author Share Posted August 15, 2016 Hmm, since my test image is 256x256, it should work fine with power-of-two, but i'll give it a try. Haven't worked with shaders in v4 so far, but i'll take a look at it, too. I want to use a RenderTexture in the end, so i can use the wrapped-modified-unwrapped occlusion map as a mask for the light. It will probably contain artifacts, since i lose out on precision during the process, but if i draw soft shadows i should be able to smoothen it out. ivan.popelyshev 1 Quote Link to comment Share on other sites More sharing options...
ivan.popelyshev Posted August 15, 2016 Share Posted August 15, 2016 You should be ok, unless you somehow need to directly copy one texture in the other, that stuff isnt included in pixi-gl-core and you'll have to write some low-level bullshit yourself like i did in pixi-picture and pixi-tilemap Quote Link to comment Share on other sites More sharing options...
Iavra Posted August 16, 2016 Author Share Posted August 16, 2016 Using calculateNormalizedScreenSpaceMatrix doesn't seem to work, the filter still behaves different depending on the sprite's position. I tried using filters as shaders, but it doesn't seem to work? Using the BlurFilter, i tried the following approaches: sprite.filters = [new PIXI.filters.BlurFilter()]; sprite.shader = new PIXI.filters.BlurFilter(); The second one doesn't seem to do anything and i couldn't find an example on how to use shaders in v4. ivan.popelyshev 1 Quote Link to comment Share on other sites More sharing options...
ivan.popelyshev Posted August 16, 2016 Share Posted August 16, 2016 Second one wont work in pixi-v4 anymore. there are no examples for custom shaders, and its kinda difficult because we have multitexturing now and i didnt check out how affects it that shader. I can look it for you pixi-picture for now is the only thing i can recomment to look at. Sprite calls custom renderer that can do many things Quote Link to comment Share on other sites More sharing options...
Iavra Posted August 16, 2016 Author Share Posted August 16, 2016 I think it would be enough, if there is some way to port the filter listed in the OP over to v4, either as filter or shader (i think i actually used it as a shader back then). vTextureCoord used to be in range 0.0-1.0 over the whole sprite area, independent from the sprite's position on the screen. As of now, vTextureCoord seems to change, depending on the sprite's position and even more so if part of the sprite is outside the rendered part of the scene. ivan.popelyshev 1 Quote Link to comment Share on other sites More sharing options...
ivan.popelyshev Posted August 16, 2016 Share Posted August 16, 2016 It changes only for filter, not for shader. To map it to (0.0-1.0) you have to use mapCoord thing from the other thread I mentioned Quote Link to comment Share on other sites More sharing options...
Iavra Posted August 16, 2016 Author Share Posted August 16, 2016 Hmm, since i need to access the texture for the wrapping, i guess i would need to appy the mappedMatrix, calculate polar coordinates and transform them back into non-mapped ones to grab the correct pixels on the texture. How would i do this? Basically, i would: Apply the mappedMatrix to vTextureCoord to get coordinates in range 0.0-1.0. Calculate the polar coordinates. Divide out the matrix again to get the correct coordinates i need to access uSampler. Does this sound correct? Quote Link to comment Share on other sites More sharing options...
ivan.popelyshev Posted August 16, 2016 Share Posted August 16, 2016 Yeah, but matrix isn't that difficult, it doesnt have rotations, you can pass two matrices (A and A^-1) to your filters then I hope it helps. I'll help with pure shader/sprite solution later. That stuff you are coding needs to be converted in a plugin anyway Quote Link to comment Share on other sites More sharing options...
Iavra Posted August 16, 2016 Author Share Posted August 16, 2016 This is my filter so far: var Filter = function() { PIXI.Filter.call(this, null, [ '#define PI 3.14159265358979323846264', 'varying vec2 vTextureCoord;', 'uniform sampler2D uSampler;', 'uniform mat3 mappedMatrix;', 'uniform mat3 unmappedMatrix;', 'void main(void) {', ' vec2 mappedCoord = (vec3(vTextureCoord, 1.0) * mappedMatrix).xy;', ' vec2 norm = mappedCoord * 2.0 - 1.0;', ' float theta = PI + norm.x * PI;', ' float r = (1.0 + norm.y) * 0.5;', ' vec2 coord = vec2(-r * sin(theta), -r * cos(theta)) / 2.0 + 0.5;', ' gl_FragColor = texture2D(uSampler, (vec3(coord, 1.0) * unmappedMatrix).xy);', '}' ].join('\n')); this.uniforms.mappedMatrix = new PIXI.Matrix(); this.uniforms.unmappedMatrix = new PIXI.Matrix(); }; Filter.prototype = Object.create(PIXI.Filter.prototype); Filter.prototype.apply = function(filterManager, input, output) { filterManager.calculateNormalizedScreenSpaceMatrix(this.uniforms.mappedMatrix); this.uniforms.unmappedMatrix = this.uniforms.mappedMatrix.clone().invert(); filterManager.applyFilter(this, input, output); }; It works fine, until the sprite gets moved near or outside the edge of the scene (like negative x/y coordinates), which is when this happens: Theese are, from left to right: Original sprite.x = sprite.y = 0 sprite.x = sprite.y = 10 sprite.x = sprite.y = -10 The second transformed one looks somewhat correct, even though it's still different from the desired result and the dimensions seem to be messed up a bit (because of the mapped matrix?). For comparison, how it should look like: ivan.popelyshev 1 Quote Link to comment Share on other sites More sharing options...
ivan.popelyshev Posted August 16, 2016 Share Posted August 16, 2016 We had a problem with clipspace, please use latest pixi: https://dl.dropboxusercontent.com/u/46636530/pixijs/filter2/pixi.js The relevant commit was yesterday. Quote Link to comment Share on other sites More sharing options...
Iavra Posted August 16, 2016 Author Share Posted August 16, 2016 I see, this works for sprite.x = sprite.y = 0, but not for negative coordinates. Shouldn't matter, if i render the sprite on a RenderTexture, but i guess i would need to either modify the filterArea or part of the projectionMatrix to apply the filter on the whole sprite, not only the visible part? ivan.popelyshev 1 Quote Link to comment Share on other sites More sharing options...
ivan.popelyshev Posted August 16, 2016 Share Posted August 16, 2016 You can add some space to filter. "filter.padding=10" or something like that? That's one of reasons we have that mapping: you can add some pixels to the edge Quote Link to comment Share on other sites More sharing options...
Iavra Posted August 16, 2016 Author Share Posted August 16, 2016 I guess i have a small problem regarding precision when unwrapping. I'm rendering the occlusion map with these 2 filters, assuming the light is at the center of the texture: #define PI 3.1415926535897932384626433832795 varying vec2 vTextureCoord; uniform sampler2D uSampler; uniform mat3 mappedMatrix; uniform mat3 unmappedMatrix; const float h = 256.0; // height of the texture, currently hardcoded void main(void) { vec3 mappedCoord = vec3(vTextureCoord, 1.0) * mappedMatrix; for(float y = 0.0; y < h; y += 1.0) { if(y / h > mappedCoord.y) { break; } vec2 norm = vec2(mappedCoord.x, y / h) * 2.0 - 1.0; float theta = PI + norm.x * PI; float r = (1.0 + norm.y) * 0.5; vec2 coord = vec2(-r * sin(theta), -r * cos(theta)) / 2.0 + 0.5; vec3 unmappedCoord = vec3(coord, 1.0) * unmappedMatrix; if(texture2D(uSampler, unmappedCoord.xy).a > 0.0) { gl_FragColor = vec4(1.0); return; } } gl_FragColor = vec4(0.0); } #define PI 3.1415926535897932384626433832795 varying vec2 vTextureCoord; uniform sampler2D uSampler; uniform mat3 mappedMatrix; uniform mat3 unmappedMatrix; void main(void) { vec2 norm = (vec3(vTextureCoord, 1.0) * mappedMatrix).xy * 2.0 - 1.0; float theta = PI + atan(norm.x, norm.y); float r = length(norm); vec2 coord = vec2(theta / (2.0 * PI), r); gl_FragColor = texture2D(uSampler, (vec3(coord, 1.0) * unmappedMatrix).xy); } Following is a sample image before and after the filter: The transformation seems fine, but you can see the 1px wide gap at the top center. I don't remember if that was there in v2 as well and i currently don't know how to fix it. ivan.popelyshev 1 Quote Link to comment Share on other sites More sharing options...
ivan.popelyshev Posted August 16, 2016 Share Posted August 16, 2016 OK, that means we need high precision for that. "precision highp float;" in the beginning of frag shader. Quote Link to comment Share on other sites More sharing options...
Iavra Posted August 16, 2016 Author Share Posted August 16, 2016 I already tried that and sadly it doesn't do anything ivan.popelyshev 1 Quote Link to comment Share on other sites More sharing options...
ivan.popelyshev Posted August 16, 2016 Share Posted August 16, 2016 Hm. May be the problem with the gap is because linear filtering. Strange you didnt have that in v2. Quote Link to comment Share on other sites More sharing options...
Iavra Posted August 16, 2016 Author Share Posted August 16, 2016 It kinda "works", if i change the last line of filter 2 like this: gl_FragColor = texture2D(uSampler, (vec3(coord, 1.0) * unmappedMatrix).xy * 0.99); Though that also means i would need to add a 3rd render pass reverting that, losing further precision. It also feels really really dirty. /edit: I'm pondering, whether i should drop per-pixel shading and use raycasting, instead. Though that would either limit me to basic shapes or i'll need to cast a whole bunch of rays and still have jittery results. ivan.popelyshev 1 Quote Link to comment Share on other sites More sharing options...
ivan.popelyshev Posted August 16, 2016 Share Posted August 16, 2016 Oh, right, texture is not repeating! What if we actually make some padding... make it not 2*PI but 2*PI+eps range in the first pass? Then the second one will have bonus info for linear filtering on edges theta=0 and theta=PI*2. I think it will work just fine! Quote Link to comment Share on other sites More sharing options...
ivan.popelyshev Posted August 16, 2016 Share Posted August 16, 2016 Found one more solution uniform vec4 filterClamp; clamp(vTextureCoord.xy, filterClamp.xy, filterClamp.zw) clamp coords you pass to texture2d. We arediscussing it in https://github.com/pixijs/pixi.js/issues/2816 , it seems that blur filter cant clamp stuff. Quote Link to comment Share on other sites More sharing options...
Iavra Posted August 16, 2016 Author Share Posted August 16, 2016 Indeed, if i take the unmapped coords in filter 2 and clamp them, it fixes the gap. So, i should be able to take the resulting texture, invert it (i can do that in pass 1, by drawing light instead of shadow) and use it to mask the light texture. I should probably blur the mask and use alpha masking, instead, to hide some of the lost precision. Though i still don't get, why the filtered texture is 260x260, while the original is 256x256. Somewhere in the code, i'm scaling the coordinates slightly. Maybe i should add a clamp to pass 1, too. ivan.popelyshev 1 Quote Link to comment Share on other sites More sharing options...
ivan.popelyshev Posted August 16, 2016 Share Posted August 16, 2016 I think that's because filter has padding=2 by default. Your work is awesome, we can figure out lightmaps and multiple sprites from that Can you give me a background and two lightmaps? I'll try to make an example of lighting /masking. Quote Link to comment Share on other sites More sharing options...
Iavra Posted August 16, 2016 Author Share Posted August 16, 2016 My only example image so far is this one: This is (part of) the occlusion map, which contains all shadowcasting objects. The whole process should work like this: For each light, pick a part of the occlusion map, that contains the outer circle of the light (the filter only works on the inner circle of the shadow texture, so we need to make sure to grab a bigger part). Assuming, that the center of the light is the center of the occlusion map, render it using both filters (for now on a RenderTexture). The last 2-3 lines in pass 1 need to be reversed, so we are drawing light, instead of shadow. Use the RenderTexture as a mask for the light (which is a texture itself). Tint the whole screen in darkness and blend it with the masked lights (i'm not there, yet, so i don't know which blendmode i should use for this). This is my whole code so far: http://pastebin.com/qrFV6ThW /edit: Also, after setting padding = 0 on the first pass, i can even remove the clamp without encountering the filtering issue (the gap). ivan.popelyshev 1 Quote Link to comment Share on other sites More sharing options...
ivan.popelyshev Posted August 16, 2016 Share Posted August 16, 2016 4 can be done either with multiply either with something like overlay. The best way is to add all all lights as Sprites to special container that is above everything else, then assign "VoidFilter" to that container and add blendMode=multiply to that filter. I haven't implement Overlay yet, it works only on single sprites 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.