## How to use Framebuffer:renderTo() properly

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
TechnoCat
Inner party member
Posts: 1611
Joined: Thu Jul 30, 2009 12:31 am
Location: Denver, CO
Contact:

### How to use Framebuffer:renderTo() properly

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.
Last edited by TechnoCat on Wed Dec 08, 2010 5:38 pm, edited 11 times in total.

zac352
Party member
Posts: 496
Joined: Sat Aug 28, 2010 8:13 pm
Contact:

### Re: How to use Framebuffer:renderTo() properly

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 "boot.lua"]:305: in function <[string "boot.lua"]:303>
[C]: in function 'xpcall'

TechnoCat
Inner party member
Posts: 1611
Joined: Thu Jul 30, 2009 12:31 am
Location: Denver, CO
Contact:

### Re: How to use Framebuffer:renderTo() properly

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.
Last edited by TechnoCat on Wed Nov 17, 2010 6:53 pm, edited 1 time in total.

vrld
Party member
Posts: 917
Joined: Sun Apr 04, 2010 9:14 pm
Location: Germany
Contact:

### Re: How to use Framebuffer:renderTo() properly

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)
I have come here to chew bubblegum and kick ass... and I'm all out of bubblegum.

hump | HC | SUIT | moonshine

TechnoCat
Inner party member
Posts: 1611
Joined: Thu Jul 30, 2009 12:31 am
Location: Denver, CO
Contact:

### Re: How to use Framebuffer:renderTo() properly

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?

vrld
Party member
Posts: 917
Joined: Sun Apr 04, 2010 9:14 pm
Location: Germany
Contact:

### Re: How to use Framebuffer:renderTo() properly

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)
I have come here to chew bubblegum and kick ass... and I'm all out of bubblegum.

hump | HC | SUIT | moonshine

kikito
Inner party member
Posts: 3153
Joined: Sat Oct 03, 2009 5:22 pm
Contact:

### Re: How to use Framebuffer:renderTo() properly

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?
When I write def I mean function.

vrld
Party member
Posts: 917
Joined: Sun Apr 04, 2010 9:14 pm
Location: Germany
Contact:

### Re: How to use Framebuffer:renderTo() properly

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.
I have come here to chew bubblegum and kick ass... and I'm all out of bubblegum.

hump | HC | SUIT | moonshine

TechnoCat
Inner party member
Posts: 1611
Joined: Thu Jul 30, 2009 12:31 am
Location: Denver, CO
Contact:

### Re: How to use Framebuffer:renderTo() properly

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
Last edited by TechnoCat on Thu Nov 18, 2010 3:32 pm, edited 2 times in total.

Robin
The Omniscient
Posts: 6506
Joined: Fri Feb 20, 2009 4:29 pm
Location: The Netherlands
Contact:

### Re: How to use Framebuffer:renderTo() properly

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?