Skip to content

Instantly share code, notes, and snippets.

@4aiman
Created June 25, 2016 08:40
Show Gist options
  • Select an option

  • Save 4aiman/9574a8189b32e7c2fa42f35156746ad1 to your computer and use it in GitHub Desktop.

Select an option

Save 4aiman/9574a8189b32e7c2fa42f35156746ad1 to your computer and use it in GitHub Desktop.

Revisions

  1. Doruk Turak revised this gist Jun 24, 2016. 1 changed file with 4 additions and 1 deletion.
    5 changes: 4 additions & 1 deletion fake_sphere.md
    Original file line number Diff line number Diff line change
    @@ -109,4 +109,7 @@ We also divide the multiplied xb*f with aspect ratio here to get the square back

    Lastly, our coordinates are in [0,2] space (ignoring x's division by aspect ratio, it is still higher than 1 anyway) so we divide the uv by 2 just before we se it in Texel.

    The rest is what you wanna make of it. Try and see the result! I'd be glad if someone did and posted a gif because I don't know how to.
    The rest is what you wanna make of it. Try and see the result! I'd be glad if someone did and posted a gif because I don't know how to.

    Sources:
    - http://www.geeks3d.com/20130705/shader-library-circle-disc-fake-sphere-in-glsl-opengl-glslhacker/4/
  2. Doruk Turak created this gist Jun 24, 2016.
    112 changes: 112 additions & 0 deletions fake_sphere.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,112 @@
    # Fake 2D Sphere effect

    Wanna have a rotating planet?

    Prerequisites:
    - Shaders
    - Trigonometry
    - Magic

    ### Lua
    On our lua side, you will need to load an image (ofc), a shader, make a mesh and do some other things.

    You don't need a mesh if your image is square, but for others, we need to tweak texture coordinates and I don't know a better way, so.

    I guess it is redundant to say your image should be seamless on the edge it is rotating. My image is this:
    ![alt text](http://imgur.com/J93wNgv.png "I dont remember where I got this image from.")

    **In love.load:**
    ```lua
    earthimg = love.graphics.newImage("res/PathfinderMap.jpg") -- load the image
    earthimg:setWrap("repeat", "repeat")
    local earthimgw, earthimgh = earthimg:getDimensions()
    earthImageAspectRatio = earthimgw / earthimgh
    ```
    We are going to use the aspect ratio to map the rectangle on the sphere properly.
    Like so:

    ```lua
    local vertices = {
    {0,0, 0,0, 255,255,255}, --topleft
    {500,0, 0 + 1/eia,0, 255,255,255}, --topright
    {500,500, 0 + 1/eia,1, 255,255,255}, --bottomright
    {0,500, 0,1, 255,255,255} --bottomleft
    }
    earth = love.graphics.newMesh(vertices)
    earth:setTexture(earthimg)
    time = 0
    ```
    (Tabs and spaces might be mixed there. I don't care, don't copy paste)

    You should know that texture coordinates are between 0 and 1, so to pick a square area off of our image we have to do that. If your image is already square then you can directly draw the image I suppose.

    **In love.update:**
    ```lua
    t = t + dt
    ```
    **In love.draw:**
    ```lua
    love.graphics.setShader(earthShader)
    earthShader:send("time", t)
    earthShader:send("imageAspectRatio", earthImageAspectRatio)
    love.graphics.draw(earth, 0, 0, 0, 1, 1, 0, 0)
    ```

    That time is going to be used to rotate. Load the shader on your own, there are various ways you can go with that. I use a utility function to load from an external file so I didn't put that here.

    Aspect ratio is going to be used again to fix square-rectangle stuff.

    Now that we are done here, let's move on to the shader.

    ### Shader
    You don't need to modify the vertex shader, all of the magic happens in the vertex. First the declarations:
    ```glsl
    extern number time;
    extern number imageAspectRatio;
    vec4 effect( vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords ){
    //Init
    vec4 pixel;
    ```
    We are gonna need to forward declare a vec4, that I named "pixel" here. This is gonna be our color result. The main part:

    ```glsl
    //Fake Sphere
    number xb = 2.0 * (texture_coords.x * imageAspectRatio - 0.5);
    number yb = 2.0 * (texture_coords.y - 0.5);
    number r = sqrt(xb * xb + yb * yb); //value between 0 and sqrt(2)
    ```

    Firt of all, we multiply our x coordinate with the aspect ratio to push it back to [0,1] space. We will need this for the algorith, and we will divide it back after the algorithm, to put it back to square-space.

    As we take the x and y coordinates, and subtract 0.5 from both, this shifts the coordinate space from [0,1] , [0,1] to [-0.5,-0.5] , [0.5,0.5]. And when we multiply by two, this expands to [-1,-1] , [1,1]. Why we do this is simple: We are going to put a unit circle in the middle of it. To test whether or not a point is in our unit circle, our coordinate space needs to be the same. The "r" here is the "length", the norm of our final texture_coords vector, and if its length isnt more than 1, so inside the unit cicle, we render it:

    ```glsl
    if(r>1.0) {
    pixel = vec4(0);
    } else {
    number f = (1.0-sqrt(1.0-r))/(r);
    vec2 uv;
    uv.x = (xb * f) /imageAspectRatio + time;
    uv.y = yb * f + 1;
    pixel = Texel(texture, uv / 2);
    pixel = pixel;
    }
    return pixel * color;
    }
    ```

    What we do here with f is a little more complex. That is what makes our "fake sphere" effect. Plotting "(1.0-sqrt(1.0-r))/r" on [Wolfram Alpha](https://www.wolframalpha.com) describes it visually better than I can verbally: See that immediate increase nearing 1.0 on real values? As r nears 1.0, so the closer the pixel is to the edge, f is higher. Using f as a denominator there makes texture coordinates move slower near the centre and faster near the edges, simulating a fake tangented look for the edges, making it seem like a sphere.

    This is just one way to make it seem like a sphere, I'm sure there would be other ways. This particular math equation is chosen only for that falloff property and nothing else.


    As xb and yb are in [-1,-1] , [1,1] coordinate space, after we multiply them with f for the effect, we should move them back into [0,1] space before we use them as texture coordinates in Texel function. We add 1 to the y, moving it to [0,2] space, and normally we need to add 1 to x too but that isnt very important now, as x axis wrapping is seamless and we add "time" anyway. If your sphere isnt rotating around its x-axis you should add 1 here.

    We also divide the multiplied xb*f with aspect ratio here to get the square back. Theoretically, you can divide the x coordinate with aspect ratio anywhere after you multiplied it with f and added 1 to it. As we dont need to add 1 here, I chose to do that there.

    Lastly, our coordinates are in [0,2] space (ignoring x's division by aspect ratio, it is still higher than 1 anyway) so we divide the uv by 2 just before we se it in Texel.

    The rest is what you wanna make of it. Try and see the result! I'd be glad if someone did and posted a gif because I don't know how to.