[SOLVED] Render only one frame and other questions

Return to Scripting Issues

[SOLVED] Render only one frame and other questions

Postby lrq3000 » Fri Oct 19, 2012 2:04 pm

Hello,

I would like to use Panda3D for my personal project, but after reading the documentation and some example sourcecodes, I still have a few questions:

- How can I render just one frame and save it in a file?
In fact I would need to render 2 different images: a single object, and a scene of multiple objects including the previous single object, but just one frame for each and they both need to be saved as image files.

- The application will be coded in Python, and needs to be very scalable (be used by thousand of users). Would Panda3D fit the bill here? (about my program in Python, it's almost a constant complexity so no problem here, and 3D models will be low-poly and about 5 to 20 per scene).

- I need to calculate the perspective projection of every object to the camera. Is it possible to directly access the vertexes and faces (position, parameters, etc..)?

- Can I recolor my 3D objects? I need to set a simple color for the whole object, but a different color per object. Is it possible?

Please also note that I'm quite a newbie in the field of graphical and game development, but I know some bits of 3D modelling and 3D theory, as well as computer imaging theory.

Thank you for reading me.

PS: My main alternative currently is to use Soya3D or PySoy, but they don't seem to be very actively developped nor optimized, so although they would both have a smaller memory footprints, I don't know if they would really perform faster than Panda3D since they're not very optimized...
Last edited by lrq3000 on Sun Nov 04, 2012 1:43 pm, edited 1 time in total.
lrq3000
 
Posts: 16
Joined: Fri Oct 19, 2012 1:46 pm

Re: Render only one frame and other questions

Postby Nemesis#13 » Sat Oct 20, 2012 12:00 pm

lrq3000 wrote:-- How can I render just one frame and save it in a file?

Take a look at ShowBase.screenshot() and ShowBase.graphicsEngine.renderFrame(). (A global instance of ShowBase is usually stored in "base".)
lrq3000 wrote:- The application will be coded in Python, and needs to be very scalable (be used by thousand of users). Would Panda3D fit the bill here? (about my program in Python, it's almost a constant complexity so no problem here, and 3D models will be low-poly and about 5 to 20 per scene).

Nobody can really answer such a question, but if it makes you sleep better.. Panda has been used for MMOG by Disney for Toontown and Pirates of the Carribean. Still this has little to do with the engine, but rather with your understanding of networking and the server you'll use.

lrq3000 wrote:- I need to calculate the perspective projection of every object to the camera. Is it possible to directly access the vertexes and faces (position, parameters, etc..)?

I don't quite get the question. Could you elaborate on this? Maybe this reference page also serves you some help: http://www.panda3d.org/reference/devel/ ... e.Geom.php
It's about the geom class, which you can get easily out of any loaded model.

lrq3000 wrote:- Can I recolor my 3D objects? I need to set a simple color for the whole object, but a different color per object. Is it possible?

No problem. Look through the api reference page for NodePath and search for "color".
User avatar
Nemesis#13
 
Posts: 1041
Joined: Mon Aug 04, 2008 8:09 pm
Location: Germany

Postby lrq3000 » Sat Oct 20, 2012 2:39 pm

Thank's a lot, this greatly helps.

However, my application is really only a render-one-frame-and-go, rendering a frame and saving it to an image is really the only thing it does, so there's no worry about networking and stuffs.

When I asked about performances, I was more specifically wondering if using Panda3D wouldn't be overkill in my situation, where I just render 1 frame for every access to the script, but there can be maaaaanyyyy concurrent access at once. In the end, I think that the optimizations and the modularity makes Panda3D a perfect fit, as long as I load only the very specific modules I need.

Anyway, I'm still stuck at trying to do my rendering.

Your solution work (thank's a lot!):

Code: Select all
import direct.directbase.DirectStart

base.graphicsEngine.renderFrame()
base.screenshot(namePrefix='screenshot', defaultFilename=1, source=None, imageComment="")



But it still opens a window whenever I launch the script.

So I've stumbled over a few other threads on the subject, and this is what I pieced together:

Code: Select all
from panda3d.core import loadPrcFileData
loadPrcFileData("", "window-type none" ) # Make sure we don't need a graphics engine (Will also prevent X errors / Display errors when starting on linux without X server)
loadPrcFileData("", "audio-library-name null" ) # Prevent ALSA errors

import direct.directbase.DirectStart

base.graphicsEngine.renderFrame()
base.screenshot(namePrefix='screenshot', defaultFilename=1, source=None, imageComment="")


NB: I think this tip should be in the manual for those who want to make a server-side application. This is a common application.

Anyway now the screenshot doesn't work, as I expected, because it cannot find a window anymore!

So I'm stuck here, I'm trying to re-read the manual (I've spent the whole day reading it, it's quite nice but long!) and the reference API, but I'm kinda stuck... Any help is welcome!
lrq3000
 
Posts: 16
Joined: Fri Oct 19, 2012 1:46 pm

Postby Nemesis#13 » Sat Oct 20, 2012 7:02 pm

I think it should be possible to render to a buffer and make a screenshot from that. Still, I can't figure out the code right now.
User avatar
Nemesis#13
 
Posts: 1041
Joined: Mon Aug 04, 2008 8:09 pm
Location: Germany


Postby lrq3000 » Sun Oct 21, 2012 4:02 am

FOUND IT!

I used window-type none, when I should have used window-type offscreen which then create an offscreen buffer!

Here is my test code:
Code: Select all
from panda3d.core import loadPrcFileData
loadPrcFileData("", "window-type offscreen" ) # Spawn an offscreen buffer
loadPrcFileData("", "audio-library-name null" ) # Prevent ALSA errors
from direct.showbase.ShowBase import ShowBase
#import direct.directbase.DirectStart


base = ShowBase()

base.graphicsEngine.renderFrame()
base.screenshot(namePrefix='screenshot', defaultFilename=1, source=None, imageComment="")


Just one more question: if there is no graphic card available, will Panda3D fallback to the CPU? This is important to me, because I will use my application on servers where there may not be any graphic card available, or at least access will be forbidden.
lrq3000
 
Posts: 16
Joined: Fri Oct 19, 2012 1:46 pm

Postby Nemesis#13 » Sun Oct 21, 2012 4:36 am

For me it looks like this functionality is somewhat broken at the moment.

I tried with
Code: Select all
win = base.openWindow(type="offscreen")

but it throws an error.
Then I tried to do by hand what ShowBase does:
Code: Select all
from panda3d.core import loadPrcFileData
loadPrcFileData("", "window-type none" )
loadPrcFileData("", "audio-library-name null" )

from panda3d.core import GraphicsPipe, WindowProperties, FrameBufferProperties
import direct.directbase.DirectStart

flags = GraphicsPipe.BFFbPropsOptional | GraphicsPipe.BFRefuseWindow
props = WindowProperties.getDefault()
fbprops = FrameBufferProperties.getDefault()
base.makeDefaultPipe()
buff = base.graphicsEngine.makeOutput(pipe=base.pipe, name="rendering buffer", sort=0, fb_prop=fbprops, win_prop=props, flags=flags)
assert buff is not None # AssertionError

but still it seems Panda is unable to open an offscreen buffer only.
User avatar
Nemesis#13
 
Posts: 1041
Joined: Mon Aug 04, 2008 8:09 pm
Location: Germany

Postby lrq3000 » Sun Oct 21, 2012 4:52 am

Thank's Nemesis. Indeed I also tried to it that way and it doesn't work for the same reasons as you, the only way to do it is to define window-type offscreen instead of window-type none.

In fact when window-type is none, it seems that the engine doesn't even bother to load a Pipe, thus base.makeDefaultPipe() just returns None, hence why the rest of the code can't work.

So we would also need to manually create a Pipe, but I don't know at all how to do that...

Anyway for now, it seems that the window-type offscreen trick works, so I'll continue with that. We'll see if it produces errors later on.

----

To answer my own question about CPU only rendering, it seems that tinydisplay does just that. It was renamed p3tinydisplay in Panda3D v1.8.0, see:
http://www.panda3d.org/forums/viewtopic.php?t=13023

So I just added at the top of my script:
Code: Select all
loadPrcFileData("", "load-display p3tinydisplay"); # to force CPU only (or at least make it available)


Now I guess the script should work even on servers where there's no GPU.

Now let's get to work! Thank's Nemesis and to the Panda3D developpers, this is a really great open source engine!
lrq3000
 
Posts: 16
Joined: Fri Oct 19, 2012 1:46 pm

Server-side rendering without any visible window Panda3D

Postby lrq3000 » Sun Oct 21, 2012 4:55 am

To summary for people who may have similar needs, here is how you can use Panda3D as a render engine for one frame on CPU, server-side without any visible window and which will save the rendering result as an image file:

Code: Select all
from panda3d.core import loadPrcFileData
loadPrcFileData("",
"""
   load-display p3tinydisplay # to force CPU only rendering (to make it available as an option if everything else fail, use aux-display p3tinydisplay)
   window-type offscreen # Spawn an offscreen buffer (use window-type none if you don't need any rendering)
   audio-library-name null # Prevent ALSA errors
   show-frame-rate-meter 0
   sync-video 0
""")
from direct.showbase.ShowBase import ShowBase


base = ShowBase()

base.graphicsEngine.renderFrame()
base.screenshot(namePrefix='screenshot', defaultFilename=1, source=None, imageComment="")


Thank's to H3LLB0Y for the multi config call, this avoids calling loadPrcFileData() multiple times.

I think this information should be added to the manual, a chapter about how to use Panda3D as a server-side only renderer.
lrq3000
 
Posts: 16
Joined: Fri Oct 19, 2012 1:46 pm

Postby rdb » Sun Oct 21, 2012 10:02 am

You may use the aux-display config variable to specify additional renderers to fall back onto if the load-display one doesn't work.
rdb
 
Posts: 9563
Joined: Mon Dec 04, 2006 5:58 am
Location: Netherlands

Postby lrq3000 » Mon Oct 22, 2012 6:31 am

Thank's rdb, but I've already specified it as a comment in my code above :) For now I prefer to force p3tinydisplay to be sure that everything I want to do is rendered correctly using this API, and later I will use aux-display.

----

Follow-up on my question: I would now like to fetch the rendered image directly to a Python variable (a matrix containing the intensity of every pixel).

I can already do that by saving the rendered image on disk and then load it using Python, but I would prefer to economize that additional cost on drive by fetching the rendered image directly from RAM.

I've found the method extractTextureData(), I'll try to use that, but I'm not sure it will work out as expected, so if anyone has got a better idea on the subject...
lrq3000
 
Posts: 16
Joined: Fri Oct 19, 2012 1:46 pm

Postby lrq3000 » Mon Oct 22, 2012 7:38 am

Thank's to ThomasEgi on IRC, here is how to move the rendered frame image from GPU to RAM, and then get it in a PNMImage object

Code: Select all
from panda3d.core import PNMImage, PNMImageHeader
from direct.showbase.ShowBase import ShowBase

base = ShowBase()

# Rendering the frame
base.graphicsEngine.renderFrame()

# Creating a PNMImage (this is NOT a file format, it's a class provided by Panda3D for accessing and doing operations on image)
screenshot = PNMImage()
# Set the display region
dr = base.camNode.getDisplayRegion(0)
# Store the image from GPU to the RAM variable screenshot
dr.getScreenshot(screenshot)

# Example of operations: making an histogram
hist = PNMImage().Histogram() # Create an histogram object
screenshot.makeHistogram(hist) # create the histogram and store it in hist
print(hist.getNumPixels()) # print the number of unique pixels colors
print(hist.getCount(PNMImageHeader.PixelSpec(255,255,255, 255))) # print the count of all white opaque pixels

# To access a single pixel
# intensity = screenshot.getPixel(x, y)


For more infos:
http://www.panda3d.org/reference/devel/ ... MImage.php

Also, be careful with Google, the manual pages referenced by the search engine are outdated, and for example you won't find the makeHistogram() method there.

Note: this method is only useful if one frame is rendered, but if it is iterated over many frames, this will be slow. You should then better use a direct GPU operation, for example by using a shader.
Last edited by lrq3000 on Sun Nov 04, 2012 1:47 pm, edited 1 time in total.
lrq3000
 
Posts: 16
Joined: Fri Oct 19, 2012 1:46 pm

Postby lrq3000 » Sun Nov 04, 2012 1:46 pm

Update: another way to get a screenshot of a rendered image, but directly in-memory instead of writing it to a file. You need to use PNMImage:

Code: Select all
    def renderToPNM(self):
        ### RENDER IMAGE
        # Render the frame
        self.base.graphicsEngine.renderFrame()

        ### FETCHING THE RENDERED IMAGE
        # Prepare the variable that will store the frame image (in a PNMImage class, this is NOT a file format, but a convenient image manipulation class offered by Panda3D)
        image = PNMImage()
        # Set display region to the default
        dr = base.camNode.getDisplayRegion(0)
        # Store the rendered frame into the variable screenshot
        dr.getScreenshot(image)

        return image
lrq3000
 
Posts: 16
Joined: Fri Oct 19, 2012 1:46 pm


Return to Scripting Issues

Who is online

Users browsing this forum: Bing [Bot] and 2 guests