Multitexture alpha trick for geoms with no color column

We noticed some interesting behavior in Panda when trying to fade out some models with a colorScale interval. If the vertex alpha is changed to anything other than ‘1.0’ the geom will be remunged at run-time.

Simply setting a NodePath’s color or colorScale would trigger this behavior. This remunging of the geometry occurs every frame where the alpha value changes, not just once. This can allow a fade to cause the whole system to chug a little. So we’ve developed a work-around that applies the transparency at the texture level rather than the vertex level, thus bypassing the need to do the remunging.

You will need a white texture (1x256) with an alpha gradient from 0 to 255. You’ll also need to be able to support another level of multitexture over whatever you currently have on your model. Or, in the case where your top level texture stage is not doing anything with the alpha channel, you can adapt this method to your situation.

from direct.directbase.DirectStart import *
from direct.interval.IntervalGlobal import Sequence,LerpFunc
from pandac.PandaModules import *

# model to fade
fadeModel = loader.loadModel('models/misc/smiley')
fadeModel.setTransparency(1)
fadeModel.reparentTo(render)

# our alpha gradient texture 
transTex = loader.loadTexture('transSlider.tif')
transTex.setWrapU(Texture.WMClamp)

# our texture stage.  By default it's set to modulate.
# we give it a high sort value since it needs to be 
#   'above' the rest.
ts = TextureStage('alpha')
ts.setSort(1000)

# apply the texture
fadeModel.setTexture(ts,transTex)
fadeModel.setTexScale(ts,Vec2(0))

# wrapper for LerpFunc
def setAlpha(alpha):
    # use the desired alpha value as the TexOffset
    fadeModel.setTexOffset(ts,Vec2(alpha))

# show code
fadeSeq = Sequence(LerpFunc(setAlpha,
                            duration = 2,
                            fromData = 0.25,
                            toData=1.0,
                            blendType = 'easeInOut'),
                   LerpFunc(setAlpha,
                            duration = 2,
                            fromData = 1.0,
                            toData=0.25,
                            blendType = 'easeInOut'),
                   )
fadeSeq.loop()

base.setBackgroundColor(Vec4(0.55,0.55,1,1))
base.mouseInterfaceNode.setY(5)
base.mouseInterfaceNode.setH(180)

run()

Whenever a polygon is rendered with this texture stage, it will look to see what its alpha value should be. Since we’ve set the scale to 0, it doesn’t matter what the UV coordinate is, it will only use the offset value.

One other small gotcha is that some cards don’t support texture UV clamping. If this happens to be the case, you can limit your alpha values to the range (0.5,0.95).

There will be a hit for using multitexturing here, but if your geometry is complex enough, you’ll notice an overall performance gain.

Was wondering about this not too long ago… thanks for posting… will be extremely helpful later on

This is brilliant! I love this solution so much, I’m stealing it and building it into the low-level Panda implementation of setColorScale().

It always bugged me that setColorScale() had to munge the vertices in certain cases. Not all cases: when alpha is not involved in the scale, or when an overall setColor() has been applied (with or without alpha), Panda can scale the color by playing tricks with lighting. But when you use setColorScale() to change the alpha value and you don’t have a setColor() in effect, I couldn’t find any way to effect the change without re-issuing the vertices.

Till now. Thanks for the brilliant idea!

Moving this code into low-level Panda will have a few advantages over doing it from the high level. For one thing, the interface will be simpler for the high-level programmer. But more importantly, other low-level constructs, like FadeLODNode and GeomParticleRenderer, will also automatically be able to take advantage of this trick. And, Panda can automatically determine whether there are any available texture stages left on your graphics card, and if not, it can fall back to the vertex munging approach, so your high-level code doesn’t have to worry about this detail.

David