Panda
渲染过程
Panda的渲染过程由4个类相互作用构成,它们分别是:GraphicsPipe、GraphicsEngine、GraphicsStateGaurdian和GraphicsOutput。本章将详细讲解这些类的作用。
注意,这些接口只针对高级用户。如果只是一个打开窗口进行基本的3D渲染的简单程序,就用不着这些接口,因为当你在程序开头import direct.directbase.DirectStart时,系统将自动调用正确的函数来打开一个默认窗口。
图形管线(The Graphics Pipe
)
GraphicsPipe类是诸如OpenGL、DirectX等3D API的Panda3D接口。为了使用某种API来渲染一个窗口,你必须提供一个针对该API的GraphicsPipe。
一般来讲,当你import DirectStart时系统自动为你创建了一个默认的图形管线,可以由base.pipe访问。对于大多数应用,你没必要再创建新的图形管线。
Config.prc文件中用2个变量决定程序可使用哪个或哪些图形管线:
load-display:该变量指定图形管线的第一选择,给出最先尝试使用的GraphicsPipe类型名,如pandagl或pandadx8。如果因为某些原因不能创建该类型的GraphicsPipe,比如由于缺乏驱动,Panda3D将转到下一个变量:
aux-display:该变量可以多次出现,列出所有可能的GraphicsPipe实现。如果Panda3D不能打开由load-display指定的管线类型,它将转入aux-display提供的管线列表,按顺序一个一个地尝试直到成功打开某个图形管线为止。
注 意,以上2个变量指定的名字,如pandagl,实际上是指定了一个Windows DLL或Unix Shared-library文件。Panda3D将先在这个名字的前面加上“lib”,并在其后面加上“.dll”或“.so”(取决于操作系统),然 后导入(import)这个库。即“load-display pandagl”真正的含义是导入“libpandagl.dll”文件。panda提供了各种不同的用于显示的DLL,当成功导入时,它们将在 panda里注册,对运行它们的GraphicsPipe进行支持。
你可以增加图形管线,例如提供在OpenGL和DirectX之间转换的游戏内部接口。最简单的办法是调用base.makeAllPipes(),然后让base.pipeList列出某环境下所有有效的GraphicsPipe。
对base.pipeList列出的每一种GraphicsPipes都可以调用以下的接口:
pipe.isValid()
|
如果该管线可以用来渲染,返回True,否则返回False。
|
pipe.getDisplayWidth()
|
返回桌面的宽度,或者一个offscreen-only GraphicsPipe最大的缓冲(buffer)宽度。
|
pipe.getDisplayHeight()
|
返回桌面的高度,或者一个offscreen-only GraphicsPipe最大的缓冲(buffer)高度。
|
pipe.getInterfaceName()
|
返回该GraphicsPipe运行的API,如“OpenGL”或“DirectX8
”。
|
pipe.getType()
|
为每一种管线返回一个不同的TypeHandle对象。
|
图形引擎(The Graphics Engine
)
图形引擎是渲染过程的心脏。最终负责每一帧图像绘制(drawing)和剔除(culling)的就是GraphicsEngine类。
一般来讲,我们不需要创建GraphicsEngine,Panda3D在程序一启动就为我们创建好了。这个默认的GraphicsEngine存储在base.graphicsEngine里。
注意,下面的接口只能由高级用户来使用。如果你想创建一个新窗口或一个offscreen的渲染缓冲(buffer),只需使用base.openWindow()或window.makeTextureBuffer()接口就行,它们将自动为你处理所有的细节问题。
但是,如果你想详细了解Panda怎样管理窗口和缓冲,或者上面的方法不能满足你的某些特殊需求,那么就请继续阅读吧。
渲染一帧(Rendering a frame
)
每一帧图像的渲染都少不了一个关键的接口:
base.graphicsEngine.renderFrame()
这个方法使全体打开的GraphicsWindows和GraphicsBuffers渲染当前帧的内容。
为了让Panda3D渲染任何场景,该方法必须每一帧调用一次。一般来讲,这个由“igloop”task自动完成,该task在启动Panda时创建。
使用GraphicsEngine
创建窗口和缓冲
为了在Panda3D里渲染,你需要一个
GraphicsStateGuardian,以及一个
GraphicsWindow (渲染到窗口)或者一个
GraphicsBuffer(渲染offscreen)。我们不能直接创建或销毁这些对象,必须使用GraphicsEngine的接口来创建它们。
在创建它们之前,还必须提供一个GraphicsPipe,指出你要使用的图形API(OpenGL或DirectX)。你的Config.prc文件里默认的GraphicsPipe已经在启动时被创建好了,可以通过base.pipe来访问。
有 了GraphicsPipe和GraphicsEngine,你就可以创建一个GraphicsStateGuardian对象。在图形API上,该对象 相当于一个单一的图形context,例如一个单一的OpenGL context。(context拥有全部OpenGL对象或全部DirectX对象,如显示列表、顶点缓冲和纹理对象)在创建 GraphicsWindow之前至少需要有一个GraphicsStateGuardian:
myGsg=base.graphicsEngine.makeGsg(base.pipe)
一旦有了GraphicsStateGuardian,你就可以用它来创建onscreen的GraphicsWindow或offscreen的GraphicsBuffer。
base.graphicsEngine.makeWindow(gsg, name, sort)
base.graphicsEngine.makeBuffer(gsg, name, sort, xSize, ySize, wantTexture)
gsg指的就是那个GraphicsStateGuardian,name是你分配给窗口/缓冲的名字,sort是一个整数,决定窗口/缓冲的渲染顺序。
缓冲参数xSize和ySize确定缓冲的宽高尺寸,如果以后想从这个缓冲取回一个纹理,wantTexture参数应该设为True。
你 也可以使用graphicsEngine.makeParasite(host,name,sort,xSize,ySize), host是一个GraphicsOutput对象。它创建一个缓冲但并不为其分配空间,而是渲染到host的帧缓冲上(framebuffer)。实际上 它把wantTexture设为True,因此你以后可以从它那取回一个纹理。
更多详情请参考“GraphicsOutput类”和“
图形缓冲与窗口”部分。
myWindow=base.graphicsEngine.makeWindow(myGsg, "Hello World", 0)
myBuffer=base.graphicsEngine.makeBuffer(myGsg, "Hi World", 0, 800,600, True)
myParasite=base.graphicsEngine.makeBuffer(myBuffer,"Im a leech", 0, 800, 600)
注意,如果你想让缓冲可见,在你的
配置文件里加上“
show-buffers true
”,使缓冲像窗口一样显示,在
debug
时特别有用。
共享图形上下文(Sharing graphics contexts
)
可 以在多个不同的GraphicsWindow和/或 GraphicsBuffer之间共享同一个GraphicsStateGuardian。这样一来,图形context将被渲染到每个窗口,一次一个窗 口。当不同窗口渲染很多相同的物体时特别有用,因为各窗口可以共享相同的纹理对象和顶点缓冲。
两个窗口也可以各自使用不同的 GraphicsStateGuardian。如果在各窗口都渲染某个纹理,该纹理将被载入显存2次,一个context一次,势必造成了浪费。但在某些 情况下必须这么做,例如你有2块显卡,想让它们同时渲染。(注意,Panda对双显卡并行渲染的支持目前还未完成,只是设计了API预备日后实现)
关闭窗口(Closing windows
)
removeWindow(window)关闭某个窗口或缓冲,removeAllWindows()关闭所有窗口:
base.graphicsEngine.removeWindow(myWindow)
base.graphicsEngine.removeAllWindows()
补充内容(More about GraphicsEngine
)
下面列出
GraphicsEngine
类其他一些有用的功能:
getNumWindows()
|
返回该GraphicsEngine对象管理的窗口和缓冲数。
|
isEmpty()
|
如果该GraphicsEngine不管理任何窗口或缓冲,返回True。
|
更多功能请查阅GraphicsEngine和GraphicsStateGuardian类的API文档。
GraphicsOutput
类
封 装在GraphicsBuffer和GraphicsWindow类中的缓冲和窗口在Panda中几乎是等价的。事实上,GraphicsEngine类 的大部分操作都是针对GraphicsOutput对象,并且返回GraphicsOutput对象,而GraphicsBuffer和 GraphicsWindow都是从GraphicsOutput类继承得来。因此,我们将首先讨论GraphicsOutput对象的属性。
首先要注意的重要一点是这些对象都不能直接构造,如:
myOutput=GraphicsOutput()
myWindow=GraphicsWindow()
myBuffer=GraphicsBuffer()
都是无效的。如何创建请参考GraphicsEngine部分。此外,由于GraphicsOutput是一个抽象类,我们将在示例代码里使用GraphicsWindow对象。
全 部GraphicsOutput对象都有getGsg()、getPipe()和getName()方法,分别返回它们的 GraphicsStateGuardian、GraphicsPipe和name,你也可以由getXSize()和getYSize()得到窗口或缓 冲的宽、高:
from pandac.PandaModules import GraphicsWindow
#假设我们已经创建了一个 myWindow窗口
myWindowGsg=myWindow.getGsg()
myWindowPipe=myWindow.getPipe()
myWindowName=myWindow.getName()
myWindowWidth=myWindow.getXSize()
myWindowLength=myWindow.getYSize()
使 用saveScreenShot(fileName) 可以从任何GraphicsOutput那里保存一个截屏,fileName为图片的名字(图片格式由fileName的扩展名指定)。截屏成功返回 True,失败返回False。图片保存在当前运行的脚本同一路径下:
from pandac.PandaModules import Filename
myWindow.saveScreenShot(Filename('hello.bmp'))
该 功能自然而然将用于渲染到纹理。我们将从场景拷贝开始介绍。如果你想把GraphicsOutput对象的内容拷贝成一个纹理,必须首先调用 setupCopyTexture(),接着用getTexture()得到纹理。然后你就可以将这个从内存载入的纹理应用到一个NodePath。由于 指针的贡献,当GraphicsOutput的内容改变时纹理将自动更新。如果不想让纹理自动更新你应该使用detachTexture()。但因为渲染 的第一帧通常为空白,最佳方式是在一个do-later task或event里使用detachTexture():
myWindow.setupCopyTexture()
myTexture=myWindow.getTexture()
#假设已经设置了一个myModel模型
myModel.setTexture(myTexture)
#如果不想让纹理自动更新
def stopUpdating():
global myWindow
myWindow.detachTexture()
taskMgr.doMethodLater(1,stopUpdating,'stops updating')
虽 然这样挺有用,但你可能要渲染一个全新的场景到GraphicsOutput然后在屏幕上显示(例如,你的场景里有个电视机要现场直播)。首先要创建一个 GraphicsOutput保留场景。通过调用makeTextureBuffer实现,我们专门为渲染场景准备了一个GraphicsOutput, 然后通过getTexture()取回:
makeTextureBuffer(name, xSize, ySize)
参数name、xSize和ySize与
makeWindow
和
makeBuffer
方法中的意义一样。
接下来为新场景创建一个新的摄影机,使用:
base.makeCamera(win, sort=0, scene=None, displayRegion=(0,1,0,1), aspectRatio=None, camName='cam')
下面列出各个参数的含义:
win
|
建立摄影机的那个GraphicsOutput对象
|
sort
|
摄影机的类别值,决定了在同一窗口里DisplayRegion的绘制顺序。详情请参考API。
|
scene
|
由于其他函数的反对,这个参数不起作用。
|
displayRegion
|
新的GraphicsOutput 覆盖的区域,格式为(左边起点,右终点,下起点,上终点)。(0,0) 代表屏幕的左下角,(1,1)代表屏幕右上角。因此, (0,1,0,1) 代表整个屏幕区域。参数值必须在0到1之间。
|
aspectRatio
|
GraphicsOutput的纵横比。如果留空,makeCamera将使用默认窗口纵横比。
|
camName
|
在scene graph中该摄影机的节点的名字。
|
摄 影机渲染scene graph里任何连接到它的祖先上的对象。因此如果你想得到一个真正独立的场景,必须重新建立一个新的scene graph。创建一个空(dummy)Nodepath,把新摄影机作为它的子节点,然后可以把你的场景渲染到GraphicsOuptut上。
但 是,你对原摄影机NodePath做的状态改变不会再影响新摄影机,新摄影机也不会有原摄影机 NodePath上标准的鼠标控制。另一种办法是,使用Camera类方法setScene(scenePath),这里的scenePath是你想绘制 的scene graph的最顶端。这样做保留了标准的摄影机控制状态:
#我只使用一个GraphicsBuffer ,因为你想让用户看见这个过程
myBuffer=myWindow.makeTextureBuffer("Another Scene", 800,600)
#必须给NodePath构造器传递一个字符串,否则把它当成父节点时将会删掉子节点
myNewScene=NodePath("myRender")
myNewCamera=base.makeCamera(myBuffer)
myNewCamera.reparentTo(myNewScene)
#或者myNewCamera.node().setScene(myNewScene)
#现在你可以得到新场景的任何纹理(仍然是自动更新的)
myTexture=myBuffer.getTexture()
图形缓冲与窗口(Graphics Buffers and Windows
)
本节将详细讲述具体的缓冲和窗口函数。
GraphicsBuffer
与ParasiteBuffer
off-screen渲染时使用这2种缓冲。如果想从它们那获取纹理必须在创建时传递True参数。否则,它们在功能上与一个GraphicsOutput毫无差别。
GraphicsBuffer
和
ParasiteBuffer
唯一区别在后者没有自己的帧缓冲空间。创建
ParasiteBuffer
时从图形引擎调用
makeParasite()
:
makeParasite(host, name,sort, xSize, ySize)
name、sort、xSize和ySize参数与
makeWindow
和
makeBuffer
中含义相同。新的参数
host
为将被使用内存空间作为缓冲的
GraphicsOutput
对象。寄主渲染到宿主
host
的内存空间。为了节省空间,makeTextureBuffer函数有时返回一个
ParasiteBuffer
,对不支持
offscreen
渲染的
API
同样有用。
调用getTexture()自动建立
ParasiteBuffer
对象,因为当
host
绘制自己时,缓冲的内容很明确。
GraphicsWindows
跟GraphicsBuffer不同,GraphicsWindow对象拥有比GraphicsOutput更多的功能。
最基本的就是hasKeyboard()和hasPointer()函数,分别判断窗口是否拥有键盘或光标的焦点。在没有获得控制权的情况下调用键盘或光标函数将产生错误。
使 用getNumInputDevices()获得该窗口的输入设备数。在没有joystick等其他设备的情况下,一般只有一种输入设备。如果你使用的 API支持“键/鼠”,你可以使用movePointer(device, x,y)把鼠标移动到窗口的某个位置,device为控制鼠标的设备名(绝大多数为“键/鼠”),x和y表示你要移动到的屏幕坐标。函数成功返回 True,否则返回False。
还可以用isFullscreen()或isClosed()查询窗口是否全屏或是否关闭。值得注意的是,调用makeWindow后窗口不会自动打开,调用closeWindow后也不会自动关闭。
为了获得窗口的全部属性,我们可以使用getProperties()函数,它返回一个WindowProperties对象,其中包含指定窗口的全部信息。详情请参考WindowProperties类API文档。
如果你想改变窗口属性,使用getRequestedProperties()并应用适当的WindowProperties函数。
例如要想全屏运行Panda3D:
wp = WindowProperties()
wp.setFullscreen(True)
base.win.requestProperties(wp)
另一种办法,在导入direct.directbase.DirectStart之前修改全屏配置变量:
from pandac.PandaModules import loadPrcFileData
loadPrcFileData("", """fullscreen 1
win-size 1024 768""")
from direct.showbase.DirectObject import DirectObject # for event handling
import direct.directbase.DirectStart
import sys
class World(DirectObject):
def __init__(self):
self.accept("escape",sys.exit)
w= World()
run()
某些修改属性的请求是行不通的或无效的,你可以调用getRejectedProperties(),它返回一个WindowProperties对象,包含所有不能修改的属性。
当 用户改变窗口属性时,窗口也发送事件。调用getWindowEvent()可获得这个事件的名字。初始状态下,所有窗口改变时都发送相同的事件。如果你 想对某个窗口设置事件,请使用setWindowEvent(name),参数name为窗口改变时你想发送的事件的名字。
更多内容请参考GraphicsWindow的API文档。
多重渲染(Multi-Pass Rendering
)
有时需要在一帧画面里对同一个场景进行多次绘制,每一次绘制的结果都不同。这就是所谓的多重渲染。
实现多重渲染最简单的办法是使用“GraphicsOutput类”那一节最后提到的办法,我们可以这样做:
1)建立一个GraphicsBuffer对象
2)为它创建一个摄影机
3)将摄影机放进场景中
然 而,这种办法的前提是你得有2个独立的scene graph。如果用该办法来渲染同一个scene graph,得到只是从不同摄影机观察到的场景而已。为了真正让场景拥有不同的RenderStates(例如一个有光照,另一个没有),必须修改每个摄 影机的渲染方式。
每个摄影机节点都有一个名为setInitialState(state)的函数,它使场景里的物体都以同样的RenderState渲染,也就是说摄影机放进场景后,属性仍然可以被修改/覆盖。
#用默认摄影机渲染的所有物体都使用myNodePath这个节点的RenderState
base.cam.setInitialState(myNodePath.getState())
你 可能想进一步场景里每个节点的RenderState进行控制。使用Camera类的setTagStateKey(key) 和setTagState(value, state)方法可以达到这一目的。对你想特殊对待的NodePath调用setTag(key, value)(参考“普通状态改变”一节)。这样,当摄影机发现有贴有key标签的NodePath时,它就会给节点分配与value关联的 RenderState:
#假设我们有2个CgShaderAttrib 类的实例toonShadingAttrib和blurShadingAttrib
#以及一个NodePath为myCamera 的摄影机
base.cam.node().setTagStateKey("Toon Shading")
base.cam.node().setTagState("True", RenderState.make(toonShadingAttrib))
myCamera.node().setTagStateKey("Blur Shading")
myCamera.node().setTagState("True", RenderState.make(blurShadingAttrib))
#这使得默认摄影机以卡通形式渲染myNodePath 和它的子节点
myNodePath.setTag("Toon Shading", "True")
....
#如果你想让myNodePath被myCamera渲染成模糊形式
#只需添加一个标签就行
myNodePath.setTag("Blur Shading", "True")
渲染到纹理(Render to Texture
)
基础
在Panda3D中,渲染到纹理由3个基本步骤构成:
l 创建一个隐藏窗口(GraphicsBuffer类)
l 渲染到这个隐藏窗口
l 将隐藏窗口的内容转化成一个纹理
我 说把窗口内容“转化”成一个纹理,并不是指“拷贝”。有许多转化方法比拷贝更快。举个例子,如果OpenGL的实现支持ARB_pbuffers扩展,那 么我们就可以用wglBindTexImageARB来完成转化。Panda用户不必操心转化如何进行,唯一要记住的就是Panda会使用最快的工具来把 窗口内容转化为纹理。
更概括地说,虽然渲染到纹理一般由隐藏窗口(GraphicsBuffer类)来完成,但其实也可以用可见窗 口(GraphicsWindow类)。任何窗口,无论隐藏或可见,都可以转化成纹理。这一点非常有用——例如,你把主窗口(main window)的内容转化成一个纹理,在渲染下一帧时使用,在不用累积缓冲(accumulation buffer)的条件下达到类似累积缓冲(accumulation-buffer-like)的效果。
简单API
:makeTextureBuffer
下面这一小段程序创建一个隐藏的窗口,和一个渲染到该窗口的摄影机以及摄影机的scene graph:
mybuffer = base.win.makeTextureBuffer("My Buffer", 512, 512)
mytexture = mybuffer.getTexture()
mybuffer.setSort(-100)
mycamera = base.makeCamera(mybuffer)
myscene = NodePath("My Scene")
mycamera.node().setScene(myscene)
makeTextureBuffer 是实现渲染到纹理最简单的接口。它创建一个新的隐藏窗口(通常为一个GraphicsBuffer)和一个用于渲染的纹理,并将纹理和隐藏窗口联系在一 起。调用时使用(512, 512)指定隐藏窗口和纹理的大小。当然,必须是2的乘方。getTexture方法取回纹理,每一帧都渲染到该纹理。
setSort方法设置窗口的排序,它决定Panda渲染各个窗口的顺序。主窗口的排序为0。把mybuffer的排序设定为一个负数可以确保mybuffer首先被渲染。这样,就保证了当渲染主窗口时,mytexture已经准备好可以使用了。
那个新的隐藏窗口不会自动连接到scene graph。在上例中,我们创建了一个根节点为myscene的单独的scene graph,以及一个观察该scene graph的摄影机,并把摄影机关联到mybuffer。
makeTextureBuffer 函数通常创建一个GraphicsBuffer(隐藏窗口),但如果显卡的功能有限,无法创建offscreen窗口时, makeTextureBuffer将创建一个parasiteBuffer来替代。寄生缓冲(parasite buffer)是在低端显卡上模拟GraphicsBuffer的一种技巧。它的诀窍在于:既然不能先渲染到一个offscreen窗口再把数据转化成纹 理,panda就干脆渲染到主窗口然后把数据拷贝到纹理上。该技巧的局限性是不证自明的,第一,它对主窗口的内容断章取义,这个缺点还不是很严重,因为主 窗口的每一帧总是清除后从零开始渲染的。第二,如果主窗口比要得到的纹理小的话就该技巧会失败。既然这2个问题在实际中都不常见, makeTextureBuffer在不能创建GraphicsBuffer是还是会用寄生缓冲来代替。
在debugging模式 下,makeTextureBuffer将创建一个可见窗口(GraphicsWindow类)而不是一个隐藏窗口(GraphicsBuffer类)。 在你的panda配置文件里设置布尔变量“show-buffers #t”就可以进入debugging模式。
高级API
:addRenderTexture
使用简单API很方便,但有些事情它无能为力。例如,它不能:
l 拷贝主窗口到一个纹理。
l 拷贝Z缓冲(Z-buffer)到一个深度纹理(depth texture)。
l 拷贝窗口到一个纹理,但不是每一帧。
l 限制或强制使用寄生缓冲。
如果要进行此类操作,你需要使用底层的API。
(本节作者Josh还未写完)
如何控制渲染顺序(How to Control Render Order
)
How to Control Render Order
在大多数简单场景中,你只要把几何体放入scene graph里,然后由Panda来决定这些物体的渲染顺序。一般来讲,Panda安排得挺好了,但某些情况下我们有必要对渲染顺序进行干预和控制。
为此,你需要了解渲染顺序的含义。在典型的OpenGL或DirectX型Z缓冲系统下,图元传送到显卡的顺序理论上是无关紧要的,但在实践中谁在谁之前渲染有很重要的理由。
首 先,状态(state)排序是重要的优化手段之一。它把所有拥有简单状态(纹理、颜色等等)的物体挑选出来同时渲染,以此减少在一帧内显卡渲染状态改变的 次数。这种优化对高端显卡尤其关键,它们只有在没有状态改变的前提下才能达到广告中宣称的多边形吞吐率。对于许多高级显卡,每一次状态改变都将造成全部寄 存器的刷新和强制管线重新启动。
其次,一些显卡有其他的优化需求,先绘制近处的物体也许对它们更有利,因为对于一些距离遥远模糊不清的像素,Z缓冲算法可以有效地绕过某些高级的着色特性从而缩短渲染时间。从最近物体到最远物体,或以“从前往后”顺序绘制时,这种显卡绘图效率最高。
最 后一点,除了上面所说的2中优化手段,渲染透明物体时也需要某种排序(只有少数显卡提供对透明支持,所以我们忽略它们的存在)。透明和半透明物体一般这样 绘制:将它们半透明的部分和已经渲染到帧缓冲的内容进行混合(blending),即在绘制遮挡物的半透明部分之前,必须先渲染位于它后面的所有物体。这 也暗示着所有半透明物体必须从最远到最近进行绘制,或称为 “从后往前”顺序,而且不透明物体应在任何透明物体之前被渲染。
Panda使用bins机制来实现这些有时会产生冲突的排序。
Cull Bins
CullBinManager是一个全局对象,它维护场景中全部的cull bin及其属性。初始时有5种默认的bin,它们的渲染顺序如下:
Bin名字
|
序号
|
类型
|
background
|
10
|
BT_fixed
|
opaque
|
20
|
BT_state_sorted
|
transparent
|
30
|
BT_back_to_front
|
fixed
|
40
|
BT_fixed
|
unsorted
|
50
|
BT_unsorted
|
渲 染每一帧时,Panda都要遍历scene graph,把每个几何体分配到CullBinManager定义的某个bin里。(上面的表只列出默认的bin,可以根据需要增加bin,使用 CullBinManager::add_bin()方法,或者Config.prc文件中的“cull-bin”变量)
你也可以使用NodePath::set_bin()接口把某个节点或某些节点分配到一个bin。set_bin()要求2个参数,bin的名字和一个整数序号。序号参数只有bin类型为BT_fixed时才有意义,其他情况下通常被忽略。
如果一个节点没有明确地分配到某个bin,Panda将根据它是否允许透明把它分配到“opaque”或“transparent”bin。(注意,反方向不成立:我们把一个对象分配到“transparent”bin并不会自动打开该对象的透明属性)
当遍历了整个场景,所有对象都分配了bin后,接下来Panda将根据序号来渲染各个bin,同一个bin内部将更根据类型来排序。
如果你想把排在后面的物体提到前面来渲染,请对那个模型使用下面的代码:
model.setBin("fixed", 40)
model.setDepthTest(False)
model.setDepthWrite(False)
下面列出bin类型的解释:
BT_fixed
以用户指定的固定顺序来渲染bin中全部对象,序号来自NodePath::set_bin()方法的第二个参数,从小到大渲染。
BT_state_sorted
将状态相近的对象集中到一起渲染,力求减少场景的状态转换。
BT_back_to_front
根据每个几何体包围盒的中心到摄影机平面的直线距离排序,由远及近绘制。即在Panda默认的Z轴向上右手坐标系中,正Y值比较大的物体先被绘制。
BT_front_to_back
与back_to_front恰恰相反,近处的物体首先被绘制。
BT_unsorted
对象以它们在scene graph里出现的顺序绘制,深度优先的遍历是自顶向下然后从左到右进行。