pixelWidth/pixelHeight: a miracle

0 favourites
  • 4 posts
  • hi there!

    i'm pretty much into pixel shader lately, and it's going pretty well so far. but one thing that bothers me, is that i don't have any clue about antialiasing/smoothing hard edges, which occur, when i want to tile ranges between 0.0 and 1.0. a common practice when tiling values is using mod or fract.

    currently, i try to develope a shader which can tile/offset/rotate a sprite's texture like on the following image. as you can see, fract introduces nasty jaggies, which i like to get rid off.

    <center><img src="https://dl.dropbox.com/u/6200498/Construct%202/wishlist/jaggies.png" border="0"></center>

    on my research, i found a pretty interesting tutorial that deals with a halftone shader, which can be found here and sheds light on antialiasing as well. in step "3. Anti-aliasing is required" it is mentioned that we normally use dFdx() and dFdy() or fwidth to determine automatic derivatives which can interpolate our rough fract values painlessly. but since this cool webgl extension is not enabled within C2, we have to find other options to smooth out jaggies by finding customized versions of our functions.

    so my question is, perhaps Ashley , how are the pre-defined uniforms pixelWidth/pixelHeight connected to those unavailable functions dFdx/dFdy/fWidth and how can i achieve a smoothing on hard edges between 0.0 and 1.0 maybe with the help of pixelWidth/Height? i really like to offer some generative shaders to the community with some sort of antialiasing... please help me out on this one.

  • I can't help much, but since you don't seem to get an answer, I'll give that little bit of information I have.

    I'm not working with C2, and my experiences are from HLSL and Ashley's implementation of pixelWidth/Height there, but since GLSL is very close to HLSL and there's probably no reason for Ashley to change the behavior of pixelWidth/Height (I'll omit 'pixelHeight' now), here's my two cents.

    pixelWidth is nothing more than the width relative to the normalized display size. Or, in other words, pixelWidth = 1/display size and unnormalized display size = 1/pixelWidth.

    You should be able to use smoothstep, since it is available from v1.3 up. The example, you linked to, contains an alternate method to aa when dFdx/dFdy are not available. It explains that you need to know the "window size, the view transformation, the gradient in texture space and the relation between texture coordinates and world coordinates".

    Well, the texture coords are absolute values of the normalized world (display) coordinates, like 0.25, 0.25, 0.5, 0.5 (a texture half of the display size, centered on screen). You get the window (display) size from 1/pixelWidth, you should already know, how you transform the view (or expect it to be untransformed), but I don't exactly understand "gradient in texture space". Maybe that's the range of grey shades from the texture's colors? That's the last point you have to find out by yourself.

    Maybe this doesn't help you, or you already knew. But better told twice than never ;)

  • thanks for your effort in explaining the problem to me, tulamide!

    well, i knew what pixelWidth meant in general. the thing i didn't get was the relation to dFdx/dFdy/fWidth.

    back to problem itself. Stefan Gustavson is calculating black and white circles and therefore speaks of gradient which describes antialiasing the circles' egdges.

    in my shader, i like to tile & rotate textures. so i need to interpolate the transition areas between one texture and the follow next. to illustrate my problem a bit more, i like show the code i use:

    precision mediump float;
    varying mediump vec2 vTex;
    uniform lowp sampler2D samplerFront;
    uniform float pixelWidth;
    uniform float pixelHeight;
    
    uniform float offx;
    uniform float offy; <font color=GREEN>//Offset the texture</font>
    uniform float tilex;
    uniform float tiley; <font color=GREEN>//Tiling parameter for X and Y</font>
    uniform float rot; <font color=GREEN>//Rotation factor</font>
    
    void main(void)
    {
         float ro = radians(rot); <font color=GREEN>//degree to radians</font>
         mat2 r = mat2(cos(ro), -sin(ro), sin(ro), cos(ro)); <font color=GREEN>//simple rotation matrix</font>
         vec2 tile = vec2(tilex,tiley); <font color=GREEN>//tiling vector</font>
         vec2 off = vTex*tile+vec2(offx,offy);<font color=GREEN>//apply tiling & offset</font>
         vec2 pos = fract(off*r); <font color=GREEN>//new position to sample from</font>
    
         vec4 front = texture2D(samplerFront, pos);
         gl_FragColor = front;
         <font color=GREEN>//sampling & output</font>
    }

    this code produces jaggies of course:

    <img src="https://dl.dropbox.com/u/6200498/Construct%202/wishlist/aa_problem.png" border="0" />

    if we knew the exact location of the texture transition area, we could sample there and do the interpolation. currently we jump from the end of texture one (gradient = 1.0) to the beginning of texture two (gradient = 0.0). a smoothing factor provided as a variable could describe that interpolation area (threshold) on the basis of pixelWidth/pixelHeight.

    my main problem is that i can't imagine how to implement this gap area with my current code. Gustavson suggest taking into account the variables uScale cos(uYrot), but i don't know exactly which part of my code corresponds to those.

  • Try Construct 3

    Develop games in your browser. Powerful, performant & highly capable.

    Try Now Construct 3 users don't see these ads
  • the thing i didn't get was the relation to dFdx/dFdy/fWidth.There is no direct relation. dFdx/dFdy tell you the difference in x- or y-direction (based on a 2x2 or 3x3 block) and fWidth tells you the overall difference to the neighboring blocks. So, in this context they are variable while pixelWidth/pixelHeight are constant.

    if we knew the exact location of the texture transition area, we could sample there and do the interpolation. currently we jump from the end of texture one (gradient = 1.0) to the beginning of texture two (gradient = 0.0). a smoothing factor provided as a variable could describe that interpolation area (threshold) on the basis of pixelWidth/pixelHeight.You shouldn't work pixel-based when you want to smooth edges. But if you want to give it a try: You get the adjacent pixel positions from

    (floor(x/pixelWidth) [+, -] 1) * pixelWidth

    (floor(y/pixelHeight) [+, -] 1) * pixelHeight

    As an example, the next pixel on the right from the current position would be located at

    vec2 right = vec2((floor(vTex.x/pixelWidth) + 1) * pixelWidth, floor(vTex.y/pixelHeight) * pixelHeight)

    my main problem is that i can't imagine how to implement this gap area with my current code. Gustavson suggest taking into account the variables uScale cos(uYrot), but i don't know exactly which part of my code corresponds to those.From what I see you're not scaling, so uScale will be 1, while uYrot should be equivalent to 'ro'.

    <font color=Blue>

    EDIT

    oppenheimer

    I made a big mistake! The only excuse I have is that I was awake for almost 30 hours, when I wrote it. I'm very sorry. I corrected the error and add it here again:</font>

    <font color=Green>
    (floor(x/pixelWidth) (+, -) 1) * pixelWidth
    (floor(y/pixelHeight) (+, -) 1) * pixelHeight
    
    vec2 right = vec2((floor(vTex.x/pixelWidth) + 1) * pixelWidth, floor(vTex.y/pixelHeight) * pixelHeight)
    </font>
    

    <font color=Blue>1. Keep in mind that you might work with subpixels. For example, when a sprite's texture is 32x32 but its size stretched to 128x128, you'll have 4 subpixels per pixel, and therefore your shader will be called 4 times per absolute texture pixel. (You're working in screenspace)

    2. The above formula is only true when a pixel's "hotspot" is at upper-left (which should be the default). If it is centered, try replacing floor with round.</font>

Jump to:
Active Users
There are 1 visitors browsing this topic (0 users and 1 guests)