Drawing background in setup function and changing renderer to P2D/P3D

Hello everyone,
I use python mode and Processing 3.5.4.
I would like to use the setup function to draw background elements once before animating in the draw function. However, have an issue when I choose the P2D or P3D renderer.

def setup():
    size(400,400)
    ellipse(200,200,30,30)

def draw():
    rect(100,100,20,20)

In this case, the circle is drawn once in the setup function, and then the rectangle in the draw loop. However, if I change rendering modes by using: size(400,400,P2D) or P3D, the circle is overdrawn and disappears.

How can I avoid that if I want to draw something just once, out of the drawing loop? Is drawing in setup a bad practice?

To circumvent that problem, I tried to use a method I found in a tutorial, using loadPixels but it doesn’t work either.

background_image = None

def setup():
    size(400,400,P2D)

    global background_image

    background_image= createImage(400,400,RGB)

    ellipse(200,200,30,30)

    loadPixels()
    background_image.loadPixels()
    background_image.pixels = pixels
    background_image.updatePixels()

def draw():

    background(background_image)
    rect(100,100,20,20)


What am I doing wrong?
Thanks!

1 Like

Cheers @Setsuna!

I think it could be some bug/issue involving the P2D/P3D renderer, I have seen them interact badly with some video card setups recently, but I don’t have much information to add unfortunately. :frowning:

It did not disapear for me:

See if this at the start of the sketch helps:

from java.lang import System 
System.setProperty("jogl.disable.openglcore", "false") 

Your method of drawing to a PImage image buffer (background_image) should work without any .pixel load/updates, but when used as background inside draw ()it seems to me it would not result different to directly drawing the ellipse again and again in draw() :thinking:

1 Like

Like @villares, I couldn’t replicate this issue either (on Linux).

Perhaps you could separate your code into settings() and setup() functions. I’ve no idea if this would help, but you could try –

def settings():
    size(400,400,P2D)

def setup():
    ellipse(200,200,30,30)

def draw():
    rect(100,100,20,20)

To be honest, I’m confused why you just don’t move the circle line to the draw() function. The visual result is identical –

def setup():
    size(400,400,P2D)
    
def draw():
    ellipse(200,200,30,30)
    rect(100,100,20,20)

But perhaps there’s some particular reason? if you can explain that use-case, maybe there’s another approach we can devise.

2 Likes

Thank you @villares and @tabreturn for trying to replicate my issue. I use windows 10 and have an NVIDIA GeForce RTX 2060, it that’s worth anything. I still don’t get why the loadPixel solution doesn’t work.

I tried both of your solutions and it didn’t change the result. I see the setup frame with the circle for a split second and then it disappears.

My reason for trying this way is that in my actual program, my background image is made of many pixels, and the drawing loop animates many elements. The performance becomes bad if I draw the background in every loop. I also need to use P3D because I want to use ModelX and ModelY to get absolute coordinates. The actual project has become quite big so that’s why I posted just a simple example.

2 Likes

I now understand why you need the fixed background! I’ll will try some other pre-draw alternatives. :open_mouth:

Another wild thing to try, one of my students, if I remember correctly, improved on his P3D problems by adding the seemingly useless frameRate(60) to the code. You might want to try that. :slight_smile:

Another desperate thing would be to try another Processing+Python experimental tool: py5.ixora.io and see if you get the same renderer issues :thinking:

Cheers @Setsuna !

See if this helps, back with the image buffer option, it worked for me:

def setup():
    global bg
    size(400, 400, P3D)
    
    bg = createGraphics(400, 400)
    bg.beginDraw()  # important!
    bg.clear()            # transparent background if you like
    bg.ellipse(200, 200, 30, 30)   # drawing on the buffer/offscreen surface
    bg.endDraw()    # also needed!

def draw():
    background(bg)
    rect(100, 100, 20, 20)

Another performance optimization for not redrawing too much stuff in draw could be using a PShape group. You can do polygons with beginShape() & endShape() and etc. Check the createShape() reference!

def setup():
    global bg
    size(400, 400, P3D)
    
    bg = createShape(GROUP)
    e = createShape(ELLIPSE, 200, 200, 30, 30)
    bg.addChild(e)
    

def draw():
    shape(bg, 0, 0)
    rect(100, 100, 20, 20)
3 Likes

Yay, both solutions work! I will use createGraphics for now and look into PShape if I need further optimization.
Thank you so much @villares , hope you have a great day!

1 Like