How to use Framebuffer:renderTo() properly

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
User avatar
TechnoCat
Inner party member
Posts: 1611
Joined: Thu Jul 30, 2009 12:31 am
Location: Denver, CO
Contact:

How to use Framebuffer:renderTo() properly

Post by TechnoCat » Wed Nov 17, 2010 12:19 am

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.

User avatar
zac352
Party member
Posts: 496
Joined: Sat Aug 28, 2010 8:13 pm
Location: In your head.
Contact:

Re: How to use Framebuffer:renderTo() properly

Post by zac352 » Wed Nov 17, 2010 12:57 am

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'
Hello, I am not dead.

User avatar
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

Post by TechnoCat » Wed Nov 17, 2010 3:39 am

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.

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

Re: How to use Framebuffer:renderTo() properly

Post by vrld » Wed Nov 17, 2010 9:32 am

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

User avatar
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

Post by TechnoCat » Wed Nov 17, 2010 1:30 pm

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?

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

Re: How to use Framebuffer:renderTo() properly

Post by vrld » Wed Nov 17, 2010 1:44 pm

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

User avatar
kikito
Inner party member
Posts: 3152
Joined: Sat Oct 03, 2009 5:22 pm
Location: Madrid, Spain
Contact:

Re: How to use Framebuffer:renderTo() properly

Post by kikito » Wed Nov 17, 2010 4:45 pm

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.

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

Re: How to use Framebuffer:renderTo() properly

Post by vrld » Wed Nov 17, 2010 5:23 pm

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

User avatar
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

Post by TechnoCat » Wed Nov 17, 2010 7:03 pm

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.

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

Re: How to use Framebuffer:renderTo() properly

Post by Robin » Wed Nov 17, 2010 8:04 pm

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?
Help us help you: attach a .love.

Post Reply

Who is online

Users browsing this forum: No registered users and 8 guests