├── LICENSE ├── README.md ├── main.lua ├── preview.gif ├── tiles-map_by_Luis_Zuno_(ansimuz).png └── wipe_background.png /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Screen Wipe Shader 2 | LÖVE example of a screen wipe shader, for use with in-game cinematics, screen transitions and such. 3 | 4 | Download the .love package: [ScreenWipeShader.v1.0.0.love](https://github.com/RNavega/ScreenWipeShader-Love2D/releases/download/1.0.0/ScreenWipeShader.v1.0.0.love) 5 | 6 | ![preview.gif](preview.gif) 7 | -------------------------------------------------------------------------------- /main.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Screen wipe shader example, for use with in-game cinematics, screen transitions and such. 3 | Version 1.0.0. 4 | 5 | Code: 6 | Rafael Navega (2020) 7 | LICENSE: Public domain. 8 | 9 | Other assets with their own licenses: 10 | Pixel art image: 11 | Luiz Zuno (ansimuz.com) on OpenGameArt.com 12 | Wipe background image: 13 | Based on the LÖVE logo by Rude (https://love2d.org/wiki/L%C3%B6ve_Logo_Graphics) 14 | ]] 15 | io.stdout:setvbuf("no") 16 | 17 | local image = nil 18 | local mesh = nil 19 | local currentShader = 'circle' 20 | local useBlack = false 21 | 22 | local dir = -1 23 | local value = 0.0 24 | local wipeCenter = {0.5, 0.5} 25 | local maxRadius = 1.415 -- Start with the maximum radius possible, sqrt(2). 26 | 27 | 28 | local circleWipePixelShaderCode = [[ 29 | // Comment the define below if you don't want a feathered edge. 30 | #define FEATHER 0.05 31 | 32 | // Uniform float in range [0.0, 1.0] that controls the wipe effect. The wipe 33 | // will be invisible at 0.0 and fully formed at 1.0. 34 | uniform float time; 35 | 36 | // A 2D location on the UV space (ie normalized screen coordinates, (0,0) -> (1,1)), where 37 | // the wipe circle should close at. 38 | uniform vec2 wipeCenter; 39 | 40 | // The BIGGEST distance, in UV units, from the 'wipeCenter' point to any other point on the UV space, 41 | // as the reference radius. Since the UV space is a square, this is going to be the biggest 42 | // distance from 'wipeCenter' to any of the 4 corners of the screen. 43 | uniform float maxRadius; 44 | 45 | vec4 effect(vec4 color, Image tex, vec2 texture_coords, vec2 screen_coords) 46 | { 47 | vec2 centerOffset = texture_coords - wipeCenter; 48 | // Make the iris shape a circle, otherwise it's an ellipse with the same ratio as the window. 49 | // 'love_ScreenSize' is an internal uniform set in (LÖVE v11.3): 50 | // https://github.com/love2d/love/blob/master/src/modules/graphics/opengl/Shader.cpp#L718 51 | centerOffset.y *= (love_ScreenSize.y / love_ScreenSize.x); 52 | 53 | #ifdef FEATHER 54 | // To avoid division-by-zero, we only divide by FEATHER if it's defined at all. 55 | float shiftedRadius = maxRadius * (1.0 - time); 56 | float centerOffsetLength = length(centerOffset) + FEATHER * time; 57 | float alpha = smoothstep(shiftedRadius, shiftedRadius + FEATHER, centerOffsetLength); 58 | #else 59 | float shiftedRadius = maxRadius * (1.0 - time); 60 | float centerOffsetLength = length(centerOffset); 61 | float alpha = 1.0 - step(centerOffsetLength, shiftedRadius); 62 | #endif 63 | 64 | vec4 texturecolor = Texel(tex, texture_coords); 65 | return vec4(texturecolor.rgb, alpha) * color; 66 | } 67 | ]] 68 | 69 | 70 | local horizontalWipePixelShaderCode = [[ 71 | // Comment the define below if you don't want a feathered edge. 72 | #define FEATHER 0.25 73 | 74 | uniform float time; 75 | 76 | vec4 effect(vec4 color, Image tex, vec2 texture_coords, vec2 screen_coords) 77 | { 78 | #ifdef FEATHER 79 | float shiftedRadius = (1.0 - time); 80 | float shiftedU = texture_coords.x + FEATHER * time; 81 | float alpha = smoothstep(shiftedRadius, shiftedRadius + FEATHER, shiftedU); 82 | #else 83 | float shiftedRadius = 1.0 - time; 84 | float alpha = 1.0 - step(texture_coords.x, shiftedRadius); 85 | #endif 86 | 87 | vec4 texturecolor = Texel(tex, texture_coords); 88 | return vec4(texturecolor.rgb, alpha) * color; 89 | } 90 | ]] 91 | 92 | 93 | function love.load() 94 | love.window.setTitle('Screen Wipe Shader Example') 95 | love.window.setMode(704, 576 + 30) 96 | 97 | image = love.graphics.newImage('tiles-map_by_Luis_Zuno_(ansimuz).png') 98 | image:setFilter('nearest', 'nearest') 99 | pixelSize = {image:getPixelWidth()*2, image:getPixelHeight()*2} 100 | 101 | wipeBackground = love.graphics.newImage('wipe_background.png') 102 | wipeBackground:setFilter('nearest', 'nearest') 103 | 104 | -- Simple quad mesh with position and UV data. 105 | mesh = love.graphics.newMesh( 106 | { 107 | {'VertexPosition', 'float', 2}, 108 | {'VertexTexCoord', 'float', 2}, 109 | }, 110 | { 111 | {0.0, 0.0, 0.0, 0.0}, 112 | {pixelSize[1], 0.0, 1.0, 0.0}, 113 | {pixelSize[1], pixelSize[2], 1.0, 1.0}, 114 | {0.0, pixelSize[2], 0.0, 1.0} 115 | }, 116 | 'fan', 117 | 'static' 118 | ) 119 | -- Use a texture for the wipe background. 120 | mesh:setTexture(wipeBackground) 121 | 122 | circleWipeShader = love.graphics.newShader(circleWipePixelShaderCode) 123 | horizontalWipeShader = love.graphics.newShader(horizontalWipePixelShaderCode) 124 | end 125 | 126 | 127 | function love.update(dt) 128 | value = value + dir*dt*0.75 129 | value = math.max(0.0, math.min(1.0, value)) 130 | end 131 | 132 | 133 | function love.draw() 134 | love.graphics.setColor(1.0, 1.0, 1.0) 135 | love.graphics.draw(image, 0, 30, 0, 2.0, 2.0) 136 | 137 | if currentShader == 'circle' then 138 | love.graphics.setShader(circleWipeShader) 139 | circleWipeShader:send('time', value) 140 | circleWipeShader:send('wipeCenter', wipeCenter) 141 | circleWipeShader:send('maxRadius', maxRadius) 142 | elseif currentShader == 'horizontal' then 143 | love.graphics.setShader(horizontalWipeShader) 144 | horizontalWipeShader:send('time', value) 145 | --elseif currentShader == (...) if more shader types are to be added. 146 | end 147 | 148 | -- Set a flat color if no texture is being used. 149 | if useBlack then 150 | love.graphics.setColor(0.0, 0.0, 0.0) 151 | end 152 | love.graphics.draw(mesh, 0, 30) 153 | 154 | love.graphics.setShader(nil) 155 | love.graphics.setColor(0.0, 0.0, 0.0) 156 | love.graphics.rectangle('fill', 0, 0, love.graphics.getWidth(), 30) 157 | love.graphics.setColor(1.0, 1.0, 1.0) 158 | love.graphics.print( 159 | 'Press Left/Right for the circle wipe, Up/Down for the horizontal wipe, hold Space for black color.', 9, 9 160 | ) 161 | end 162 | 163 | 164 | function love.keypressed(key) 165 | if key == 'escape' then 166 | love.event.quit() 167 | elseif key == 'space' then 168 | useBlack = true 169 | mesh:setTexture(nil) 170 | else 171 | dir = -dir 172 | if key == 'left' or key == 'right' then 173 | currentShader = 'circle' 174 | if value == 0.0 or value == 1.0 then 175 | -- Recalculate some values for the circle wipe shader. 176 | wipeCenter[1], wipeCenter[2] = 0.5, 0.5 177 | -- The center of the circular wipe can be any 2D point on the UV space, that is, using 178 | -- normalized coordinates in range [0.0, 1.0]. 179 | --wipeCenter[1], wipeCenter[2] = math.random(), math.random() 180 | -- Get the farthest distance from the wipe center to any of the four UV-space corners. 181 | local _farthestOffsetU = math.max(wipeCenter[1], 1.0-wipeCenter[1]) 182 | local _farthestOffsetV = math.max(wipeCenter[2], 1.0-wipeCenter[2]) 183 | maxRadius = math.sqrt(_farthestOffsetU*_farthestOffsetU + _farthestOffsetV*_farthestOffsetV) 184 | end 185 | elseif key == 'up' or key == 'down' then 186 | currentShader = 'horizontal' 187 | end 188 | end 189 | end 190 | 191 | 192 | function love.keyreleased(key) 193 | if key == 'space' then 194 | useBlack = false 195 | mesh:setTexture(wipeBackground) 196 | end 197 | end 198 | -------------------------------------------------------------------------------- /preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RNavega/ScreenWipeShader-Love/ac689468aa7187d58dfcb9bf488869cb06286498/preview.gif -------------------------------------------------------------------------------- /tiles-map_by_Luis_Zuno_(ansimuz).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RNavega/ScreenWipeShader-Love/ac689468aa7187d58dfcb9bf488869cb06286498/tiles-map_by_Luis_Zuno_(ansimuz).png -------------------------------------------------------------------------------- /wipe_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RNavega/ScreenWipeShader-Love/ac689468aa7187d58dfcb9bf488869cb06286498/wipe_background.png --------------------------------------------------------------------------------