Page 1 of 6
How to use Framebuffer:renderTo() properly
Posted: Wed Nov 17, 2010 12:19 am
by TechnoCat
Disclaimer: A lot of the information in this thread only applies to a pre-release version of LOVE (fb's being tied to window dimensions). However, this first post refers to the release version of 0.7.0 (fb's are independent of the window size).
Code: Select all
function love.load()
enabled = false
translate = {x=0, y=0}
image = love.graphics.newImage("image.png")
fb = love.graphics.newFramebuffer(6000,6000)
imageSet = {}
for i=0, 10000, 1 do
local entry = {x=math.random(6000), y=math.random(6000)}
table.insert(imageSet, entry)
--alternative method to draw to framebuffer
fb:renderTo(
function()
love.graphics.draw(image,entry.x,entry.y)
end
)
--]]
end
--[[method to draw to framebuffer
love.graphics.setRenderTarget(fb)
for _,v in ipairs(imageSet) do
love.graphics.draw(image, v.x, v.y)
end
love.graphics.setRenderTarget()
--]]
end
function love.update(dt)
if love.keyboard.isDown("left") then
translate.x = translate.x + 1000*dt
elseif love.keyboard.isDown("right") then
translate.x = translate.x - 1000*dt
end
if love.keyboard.isDown("up") then
translate.y = translate.y + 1000*dt
elseif love.keyboard.isDown("down") then
translate.y = translate.y - 1000*dt
end
end
function love.draw()
love.graphics.push()
love.graphics.translate(translate.x, translate.y)
if enabled then
love.graphics.draw(fb,0,0)
love.graphics.pop()
love.graphics.print("Framebuffer rendering enabled",0,0)
else
for _,v in ipairs(imageSet) do
love.graphics.draw(image, v.x, v.y)
end
love.graphics.pop()
love.graphics.print("Framebuffer rendering disabled",0,0)
end
end
function love.keypressed(k)
if k==" " then
enabled = not enabled
end
end
Disclaimer: It takes a while to generate10,000 random (x,y) pairs.
Re: How to use Framebuffer:renderTo() properly
Posted: Wed Nov 17, 2010 12:57 am
by zac352
Stupid OpenGL. T_T
Code: Select all
Error: [string "main.lua"]:6: Cannot create Framebuffer: Not supported by your O
penGL implementation
stack traceback:
[C]: in function 'newFramebuffer'
[string "main.lua"]:6: in function 'load'
[string "boot.lua"]:305: in function <[string "boot.lua"]:303>
[C]: in function 'xpcall'
Re: How to use Framebuffer:renderTo() properly
Posted: Wed Nov 17, 2010 3:39 am
by TechnoCat
Boolsheet on IRC helped me out and told me about the framebuffer openGL size limitation. Apprently, framebuffers can only be the same dimension as your primary framebuffer. So to work around that, a framebuffer array is used.
Anyways, it works now. Here it is as what I think is a good example of how to use it. Press spacebar to switch between framebuffer mode and non-framebuffer mode.
Code: Select all
function love.load()
enabled = false
image = love.graphics.newImage("image.png")
translate = {x=0, y=0}
screen = {}
screen.width = love.graphics.getWidth()
screen.height = love.graphics.getHeight()
numBuffersX = 4
numBuffersY = 4
numBuffers = numBuffersX*numBuffersY
fbs = {}
--Iniitialize framebuffers
for y = 1, numBuffersY+1 do
fbs[y] = {}
for x = 1, numBuffersX+1 do
fbs[y][x] = love.graphics.newFramebuffer()
end
end
--Create random objects placed everywhere
imageSet = {}
for i = 1, 5000 do
local entry = {}
entry.x = math.random(screen.width*(numBuffersX-1))
entry.y = math.random(screen.height*(numBuffersY-1))
imageSet[i] = entry
end
--Draw them to the framebuffers
for y = 1, numBuffersY do
for x = 1, numBuffersX do
-- This is my preferable method
love.graphics.setRenderTarget(fbs[y][x])
for _,v in ipairs(imageSet) do
love.graphics.draw(image, v.x, v.y)
end
--]]
--[[ This is the other method
fbs[y][x]:renderTo(
function()
for _,v in ipairs(imageSet) do
love.graphics.draw(image, v.x, v.y)
end
end
)
--]]
-- Shift one buffer to the right
love.graphics.translate(-screen.width, 0)
end
-- Go back to the right and then down.
love.graphics.translate(screen.width*numBuffersX, -screen.height)
end
love.graphics.setRenderTarget()
end
function love.update(dt)
if love.keyboard.isDown("left") then
translate.x = translate.x + 1000*dt
elseif love.keyboard.isDown("right") then
translate.x = translate.x - 1000*dt
end
if love.keyboard.isDown("up") then
translate.y = translate.y + 1000*dt
elseif love.keyboard.isDown("down") then
translate.y = translate.y - 1000*dt
end
end
function love.draw()
love.graphics.push()
love.graphics.translate(translate.x, translate.y)
if enabled then
for y = 1, numBuffersY+1 do
for x = 1, numBuffersX+1 do
love.graphics.draw(fbs[y][x], (x-1)*640, (y-1)*480)
end
end
else
for _,v in ipairs(imageSet) do
love.graphics.draw(image, v.x, v.y)
end
end
love.graphics.pop()
fps = love.timer.getFPS()
if enabled then
love.graphics.setCaption(fps.." Framebuffer rendering enabled",0,0)
else
love.graphics.setCaption(fps.." Framebuffer rendering disabled",0,0)
end
end
function love.keypressed(k)
if k==" " then
enabled = not enabled
end
end
DISCLAIMER: Drawing to the framebuffers could really do with some clipping algorithm. And doing clipping on the non-framebuffer mode would help too.
File removed: framebuffer arrays are not a good implementation or method of doing things.
Re: How to use Framebuffer:renderTo() properly
Posted: Wed Nov 17, 2010 9:32 am
by vrld
TechnoCat wrote: So to work around that, a framebuffer array is used.
Don't do that. Your graphics card can only support so may framebuffers...
TechnoCat wrote:Code: Select all
for i=0, 10000, 1 do
local entry = {x=math.random(6000), y=math.random(6000)}
table.insert(imageSet, entry)
--alternative method to draw to framebuffer
fb:renderTo(
function()
love.graphics.draw(image,entry.x,entry.y)
end
)
end
That code is
equivalent to:
Code: Select all
for i = 0, 10000, 1 do
local entry = {x=math.random(6000), y=math.random(6000)}
table.insert(imageSet, entry)
love.graphics.setRenderTarget(fb)
love.graphics.draw(image,entry.x,entry.y)
love.graphics.setRenderTarget()
end
At each setRenderTarget, the framebuffer will be cleared, so what you are doing is painting an image entry, then clearing the buffer, then paining another entry, then clearing again, ...
This should work:
Code: Select all
fb:renderTo(function()
for i = 0, 10000, 1 do
local entry = {x=math.random(6000), y=math.random(6000)}
table.insert(imageSet, entry)
love.graphics.draw(image,entry.x,entry.y)
end
end)
Re: How to use Framebuffer:renderTo() properly
Posted: Wed Nov 17, 2010 1:30 pm
by TechnoCat
vrld wrote:At each setRenderTarget, the framebuffer will be cleared, so what you are doing is painting an image entry, then clearing the buffer, then paining another entry, then clearing again, ...
That is good to know.
vrld wrote:TechnoCat wrote: So to work around that, a framebuffer array is used.
Don't do that. Your graphics card can only support so may framebuffers...
But, what if I want to create an image off-screen that is larger than the main screen?
Re: How to use Framebuffer:renderTo() properly
Posted: Wed Nov 17, 2010 1:44 pm
by vrld
TechnoCat wrote:But, what if I want to create an image off-screen that is larger than the main screen?
I don't see a problem there.
Code: Select all
function love.load()
fbo = love.graphics.newFramebuffer(math.pi*love.graphics.getWidth(),math.exp(1)*love.graphics.getHeight())
love.graphics.setBackgroundColor(0,0,0)
end
function love.draw()
love.graphics.setColor(255,255,255)
love.graphics.setRenderTarget(fbo)
love.graphics.rectangle('fill', 0, 0, 100, 100)
love.graphics.setColor(40,60,150)
love.graphics.circle('fill', 100, 100, 60, 32)
love.graphics.setRenderTarget()
love.graphics.setColor(255,255,255)
love.graphics.draw(fbo, 50,50)
end
Works like a charm. But be aware that though the buffer is bigger, it does
not act as a canvas. Everything you draw will be scaled by
framebuffersize/screensize. To emulate a canvas, you can use
Code: Select all
love.graphics.scale(love.graphics.getWidth()/FB_WIDTH, love.graphics.getHeight()/FB_HEIGHT)
Re: How to use Framebuffer:renderTo() properly
Posted: Wed Nov 17, 2010 4:45 pm
by kikito
vrld wrote:But be aware that though the buffer is bigger, it does
not act as a canvas. Everything you draw will be scaled by
framebuffersize/screensize. To emulate a canvas, you can use
Code: Select all
love.graphics.scale(love.graphics.getWidth()/FB_WIDTH, love.graphics.getHeight()/FB_HEIGHT)
What the ... why?
Is that a bug or a feature?
Re: How to use Framebuffer:renderTo() properly
Posted: Wed Nov 17, 2010 5:23 pm
by vrld
kikito wrote:Is that a bug or a feature?
Neither. It's a constraint set by OpenGL.
To understand why, think of the framebuffer as an offscreen... screen. In a 3D world, you don't handle pixels, but only shapes (that may have some nice textures on them). These shapes are projected onto a screen, like in a shadow theater.
Now you always want to project the shapes in a way that the whole screen will be filled. If you take a bigger screen and set it up so that it will show the same shadows as the small screen, the shadows will obviously be larger.
When creating a framebuffer, OpenGL will ensure you see the same content on the buffer as you do on your main screen.
The way LÖVE handles 2D is basically to have only flat shapes in the same plane. Images are just surfaces with the image as texture on them. If you project that to a bigger screen, the image will appear to be upscaled.
The scaling with love.graphics.scale is somewhat like moving the screen further away (or closer to) from the objects that are projected onto the screen.
Re: How to use Framebuffer:renderTo() properly
Posted: Wed Nov 17, 2010 7:03 pm
by TechnoCat
If my window dimensions are 640x480, 4/3 ratio, and I want a PO2 compliant framebuffer at about 4 times the window size. How do I scale it properly? Since I can't make my framebuffer 4/3 (I think). Therefore, it always stretches the framebuffer when drawing.
Disclaimer: Framebuffer and screen size will be independent soon. http://bitbucket.org/rude/love/changeset/4deb61ae7d2c
Re: How to use Framebuffer:renderTo() properly
Posted: Wed Nov 17, 2010 8:04 pm
by Robin
TechnoCat wrote:If my window dimensions are 640x480, 4/3 ratio, and I want a PO2 compliant framebuffer at about 4 times the window size. How do I scale it properly? Since I can't make my framebuffer 4/3 (I think). Therefore, it always stretches the framebuffer when drawing.
Does it need to be PO2?