翻译:Panda3D Manual/V. Programming with Panda/I. Fog and Lighting

来源:互联网 发布:jenkins 插件源码下载 编辑:程序博客网 时间:2024/05/20 14:23

雾与光照(Fog and Lighting

雾与光照是增强空间立体感和纵深感的两种技术。Panda3D提供了好几种灯光的顶点光照(Vertex lighting)技术。顶点光照对整个模型进行着色,因此模型面片越多,光照效果越好。在灯光作用比较重要的地方,在不影响帧率的前提下最好将模型尽量tessellate(网格化)。

 

本节的内容可能有些错误。请参考讨论页。

基本的雾

创建雾效果非常简单:

myFog = Fog("Fog Name")
myFog.setColor(R,G,B)
myFog.setExpDensity(Float 0 to 1)
render.setFog(myFog)

但要吸引观众的眼球还得做更多的事情。我们已经创建了一个fog节点,并插入到scene graph里,因此,雾有它的位置,就是雾最浓(概念上)的地方。

如果fog对象没有插入scene graph里(例如上面的例子),那么fog的位置将被忽略,它将与摄影机相关联。同样,如果雾是以指数形式给出,fog的位置也将被忽略而与摄影机相关联。

setFog指令创建一个雾属性(fog attribute)对象,同任何一种渲染属性(Render Attribute)一样,雾属性影响它粘附到的那个节点,以及该节点的子节点。只要在你想要出现雾的scene graph子树的根节点上调用setFog,就可以很容易就使场景中的一部分对象(或单独一个模型)受到雾的影响。之后我们可以使用clearFog指令移除雾属性:

render.clearFog()

当产生雾效果时,通常要设定背景颜色来与雾匹配:

base.setBackgroundColor( myFogColor )

雾的细节

雾模式

Panda2种雾模式:Fog.M_exponentialFog.M_linear。可以使用fog.getMode()fog.setMode(Fog.Mode)来转化一个雾对象的模式。一般不必要我们显式地进行模式转化,因为Fog的方法隐含了雾模式转换。

Panda3D里,一个Fog对象是一个节点,可以像其他节点一样连接到scene graph上,有自己的位置、颜色和方向。(必须记得,FogPandaNode的子类,不是一个NodePath)(Fog节点可以按比例缩放吗?)

Fog节点在scene graph里的位置并不能决定哪些对象受雾影响,当雾在线性模式下时,这个位置决定雾的原点和方向。当雾节点在指数模式下时,它的位置和方向是不起作用的。scene graph里的NodePath必须通过nodePath.setFog(fogNode)来激活Fog节点,你对哪些NodePath调用setFog决定了场景的那些部分被雾笼罩:该NodaPath以及它的全体子节点。

线性雾

默认的模式。在该模式下,Fog节点的位置和方向显得很关键。一个线性模式Fog节点必须首先连接到scene graph,然后对某些NodaPath调用setFog(fogNode)来激活雾效果。

在原点设置一个线性雾:

colour = (0.5,0.8,0.8)
linfog = Fog("A linear-mode Fog node")
linfog.setColor(*colour)
linfog.setLinearRange(0,320)
linfog.setLinearFallback(45,160,320)
render.attachNewNode(linfog)
render.setFog(linfog)

在线性模式下,雾的onset距离opaque距离被定义为fog节点Y轴正向的距离。onset距离代表从fog节点到开始起雾的地方的距离,opaque距离代表从fog节点到雾变得完全不透明的距离。通过查阅Fog类的API说明可知,当超出opaque距离时雾就不存在了(而不像我们想象的继续有不透明的浓雾):“雾沿着从onset点到opaque点的方向蔓延。”

这些设置可以通过Fog对象的getLinearOnsetPoint()getLinearOpaquePoint()setLinearOnsetPoint(float x,y,z)setLinearOpaquePoint(Point3D pos)setLinearRange(float onset, float opaque)方法修改。

由于硬件的问题,雾会由于观察角度的改变而消失:

“硬件支持的雾效果通常只是一维的,某一点的雾必须根据从摄影机平面到该点的线性距离来渲染。因此,onset点到opaque点的方向与摄影机的视方向平行时,雾的效果最好。当雾方向与视方向之间的夹角增大时,雾的精度下降,当夹角大到90度时雾完全失效。”

在这种情况下,Fog提供一个方法setLinearFallback(float angle, float onset, float opaque)来决定雾该怎么渲染。angle是雾效果开始产生偏差最小视角(摄影机方向和雾方向的夹角),onsetopaque指定与摄影机关联的onset距离opaque距离,将覆盖Fog节点的onset距离opaque距离。

setLinearFallback(float angle, float onset, float opaque)办法只在特定条件下发挥良好,例如显示一个黑洞深处的雾。因此一般来说,指数模式的雾比默认的线性雾好用得多。

指数雾

在指数雾模式下,你的fog节点在scene graph里的位置和方向以及它的onset距离opaque距离都是忽略不计的(实际上你甚至不必把雾节点放到scene graph里)。相反,雾的渲染根据一个浓度因子(density factor)与摄像机相关。雾跟随摄影机的位置和方向的改变而改变:

onset点到opaque点完全没用,雾效果根据set_exp_density() 指定的值变化,跟雾节点的父节点及它的子节点都没有任何关系。”

fog.setExpDensity(float)方法决定了用于指数雾计算的浓度值。

通过调用NodePathsetFog(Fog)来激活指数雾效果,例如:render.setFog(myFog)

设置这个场景范围的指数雾:

colour = (0.5,0.8,0.8)
expfog = Fog("Scene-wide exponential Fog object")
expfog.setColor(*colour)
expfog.setExpDensity(0.005)
render.setFog(expfog)
base.setBackgroundColor(*colour) #
一般来说,如果场景的背景颜色与雾颜色匹配的话效果非常不错

因为对render调用setFog,因此雾将影响整个场景。也可以只对其他NodePath调用setFog,这样就只影响该NodePath以及它的子节点。

使用clearFog将关闭指数雾效果:

render.clearFog()

 

光照

Panda3D定义了4种灯光:点光、有向光、环境光和聚光灯。每一种灯光效果都有些许不同。

每个灯光都是一个节点,可被附到scene graph的某个位置。所有灯光都带一种颜色,由light.setColor(VBase4(r, g, b, a))指定。默认的颜色是纯白:setColor(VBase4(1, 1, 1, 1))alpha值不起作用。

注意:r/g/b/a的值可以大于1,如果你想要更亮的灯的话!

大多数灯光都有一个位置和/或方向,由基本的scene graph操作如setPos()setHpr()等设定。lookAt()方法在把聚光灯或有向光指向某个对象时非常有用。

注意,和一个真实的、物理的灯泡不一样,Panda灯光对象本身并不直接可见。尽管不能看到一个灯光,你可以看到它对周围物体的照明效果。如果想让灯光可见,一个简单办法就是载入一个简单模型(如一个球体),然后把它作为灯光的一个直接子节点。

一般来说,你可以创建一个灯光,然后像其他节点一样把它加到scene graph里。但是,由于多重继承带来的问题,那时你必须调用upcastToPandaNode()把灯光放进scene graph里,如下:

dlight = DirectionalLight('dlight')
dlnp = render.attachNewNode(dlight.upcastToPandaNode())

如果你忘记使用upcastToPandaNode()Panda肯定会crash。从Panda3D1.1开始就不会再有这个问题,你可以把灯光当成一般的节点来使用。(天!这段也太陈旧了吧,更新的人在干嘛?!我用Panda1.3.2,直接dlnp = render.attachNewNode(dlight)没有问题)

仅仅创建一个灯光然后放到 scene graph,这样是不会有产生什么视觉效果的,灯它不会自己发光。为了开灯,必须首先确定那些对象被照射。为此,使用nodePath.setLight()方法,对该NodePath打开光照,在它之下的全部的节点也将被照亮。

最简单的办法是对scene graph的制高点render开灯,这样全部对象都被照亮:

render.setLight(dlnp)

关闭光照效果只需从render移除灯光:

render.clearLight(dlnp)

你也可以对scene graph中的子节点应用setLight(),这样灯光只会照亮某个或某组对象。

注意,这里涉及2(或更多)种不同的NodePath:一种是灯光自身的NodePath,决定了灯光的位置和/或方向,另一种是你对它调用setLight()NodePath,决定了scene graph里的哪些子集被灯光照亮。这两种NodePath之间不需要任何关联。

点光(Point Lights

点光是一种最容易理解的光:它模拟从一个点向四面八方放出的光,就像一个非常微小的灯泡。点光的位置相当重要,但方向可以忽视:

plight = PointLight('plight')
plight.setColor(VBase4(0.2, 0.2, 0.2, 1))
plnp = render.attachNewNode(plight.upcastToPandaNode())
plnp.setPos(10, 20, 0)
render.setLight(plnp)

有向光(Directional Lights

有向光就是一束来自无穷远的光线,向同一个方向照射,就像太阳光。有向光的位置不重要,重要的是它的方向。默认的有向光沿着Y轴正向,可以使用nodePath.setHpr() nodePath.lookAt()来旋转有向光,使之射向某个方向:

dlight = DirectionalLight('dlight')
dlight.setColor(VBase4(0.8, 0.8, 0.5, 1))
dlnp = render.attachNewNode(dlight.upcastToPandaNode())
dlnp.setHpr(0, -60, 0)
render.setLight(dlnp)

环境光(Ambient Lights

环境光用来照射一个物体阴面,使之看起来不是完全黑暗。环境光均匀地分散在场景的各个角落,它没有位置和方向的概念。

通常在没有其他种类的光时,你不必创建一个环境光,因为仅仅由环境光照射的物体毫无明暗区别(flat shaded),体现不出任何形态细节。一般来说,环境光的颜色是均匀的黑灰色,因此不会盖过场景里的其他光。

alight = AmbientLight('alight')
alight.setColor(VBase4(0.2, 0.2, 0.2, 1))
alnp = render.attachNewNode(alight.upcastToPandaNode())
render.setLight(alnp)

聚光灯(Spotlights

聚光灯是最复杂的一种灯光,它有位置和方向,以及一个视域。事实上,聚光灯包含一个镜头,就像摄影机一样。这个镜头应该是一个透视镜头,用于确定光线可以照射到的区域(光线照射处于镜头视区内的所有物体)。

注意英文单词“spotlight”是一个单词,而其他种类的光都是两个单词。聚光灯类的名字是“Spotlight”,不要拼写成“SpotLight”。

由于聚光灯和其他种类的光继承自的类不同,因此需要用upcastToLensNode()而不是 upcastToPandaNode()

slight = Spotlight('slight')
slight.setColor(VBase4(1, 1, 1, 1))
lens = PerspectiveLens()
slight.setLens(lens)
slnp = render.attachNewNode(slight.upcastToLensNode())
slnp.setPos(10, 20, 0)
slnp.lookAt(myObject)
render.setLight(slnp)

 

光照的例子

这里提供一个光照的示例。一个环境光和2个有向光照亮整个场景,一个绿色的环境光仅仅照射其中一只大熊猫:

import direct.directbase.DirectStart

from pandac.PandaModules import *

 

#在场景中放置2只大熊猫xy

x= loader.loadModel("panda")

x.reparentTo(render)

x.setPos(10,0,-6)

 

y= loader.loadModel("panda")

y.reparentTo(render)

y.setPos(-10,0,-6)

 

#让摄影机对着2只大熊猫

base.trackball.node().setPos(0, 60, 0)

 

#现在来创建灯光,应用到场景中所有物体

 

#创建环境光

ambientLight = AmbientLight( 'ambientLight' )

ambientLight.setColor( Vec4( 0.1, 0.1, 0.1, 1 ) )

ambientLightNP = render.attachNewNode( ambientLight.upcastToPandaNode() )

render.setLight(ambientLightNP)

 

#有向光01

directionalLight = DirectionalLight( "directionalLight" )

directionalLight.setColor( Vec4( 0.8, 0.2, 0.2, 1 ) )

directionalLightNP = render.attachNewNode( directionalLight.upcastToPandaNode() )

#面朝背面,对着摄影机

directionalLightNP.setHpr(180, -20, 0)

render.setLight(directionalLightNP)

 

#有向光02

directionalLight = DirectionalLight( "directionalLight" )

directionalLight.setColor( Vec4( 0.2, 0.2, 0.8, 1 ) )

directionalLightNP = render.attachNewNode( directionalLight.upcastToPandaNode() )

#面朝前面,从摄像机位置出射

directionalLightNP.setHpr(0, -20, 0)

render.setLight(directionalLightNP)

 

#将一个绿光只附到对象x

ambient = AmbientLight('ambient')

ambient.setColor(Vec4(.5,1,.5,1))

ambientNP = x.attachNewNode(ambient.upcastToPandaNode())

 

#如果我们不首先调用setLightOff(),绿光将加到这个物体的所有光照上,由于我们调用了 setLightOff(),首先将别的光关掉,然后仅仅打开绿光

x.setLightOff()

x.setLight(ambientNP)

 

#运行程序

run()

 
原创粉丝点击