alpha

I was under the impression that if one had two identical alpha’d sprites on the screen (same pos,scale) each with an alpha of .5 they would look like a single sprite with an alpha of 1…This does not appear to be the case, and I was wondering if there was a way to make it so both sprites are there but only looks like one…eventually I want to be able to fade text this way, by putting text on the texture/in front of the sprites.

An example:

from pandac.PandaModules import *
import direct.directbase.DirectStart

def createSprite():
	vdata = GeomVertexData('', GeomVertexFormat.getV3t2(), Geom.UHStatic)
	vertex = GeomVertexWriter(vdata, 'vertex')
	uv = GeomVertexWriter(vdata, 'texcoord')
	prim = GeomTriangles(Geom.UHStatic)
	
	vertex.addData3f(0, 0, 1)
	vertex.addData3f(0, 0, 0)
	vertex.addData3f(1, 0, 0)
	vertex.addData3f(1, 0, 1)
	uv.addData2f(0,0)
	uv.addData2f(0,1)
	uv.addData2f(1,1)
	uv.addData2f(1,0)
	
	prim.addVertices(3,2,0)
	prim.addVertices(1,0,2)
	prim.closePrimitive()
	
	geom = Geom(vdata)
	geom.addPrimitive(prim)
	nodepath = NodePath(GeomNode('gnode'))
	nodepath.node().addGeom(geom)
	nodepath.setTransparency(1)

	return nodepath

base.setBackgroundColor(0,0,0,0)
#c1 and c2 are overlayed...together should have an alpha of 1???   (upper right)
c1 = createSprite()
c1.setScale(.5,1,.5)
c1.setAlphaScale(.5)
c1.reparentTo(render2d)

c2 = createSprite()
c2.setScale(.5,1,.5)
c2.setAlphaScale(.5)
c2.reparentTo(render2d)

#alpha of 1       (upper left)
c3 = createSprite()
c3.setScale(.5,1,.5)
c3.setPos(-.51,0,0)
c3.reparentTo(render2d)

#single sprite alpha .5 (bottom left)
c4 = createSprite()
c4.setScale(.5,1,.5)
c4.setPos(-.51,0,-.51)
c4.setAlphaScale(.5)
c4.reparentTo(render2d)

run()

I suspect that a better way might be to look for a way to alter the alpha value of a given piece of geometry (being new to Panda, I’m not sure offhand of what means to do that there may be in Panda. Hopefully one of the more experienced members will know of such facilities). This should, I would imagine, probably give you smoother results at a higher frame rate.

If you’re not interested in the reason (if I’m correct) for your two sprites not adding to an effective alpha of 1.0, I suggest stopping now. This gets a little lengthy, examples included. :stuck_out_tongue_winking_eye:

This is, if I’m not much mistaken, as a result of the calculation that produces the effect of transparency.

Again, if I’m not much mistaken, the calculation for “normal” alpha-blending is this:

Given a background pixel colour “back_colour”, a sprite pixel colour “sprite_colour”, and a sprite alpha value “alpha” in the range from 0 to 1, with 1 being fully opaque, we have

pixel_value = sprite_colour*alpha + back_colour*(1 - alpha)

If two sprites are drawn over each other, I believe that one will be drawn first, and then the result used as the “background” for the drawing of the second.

So, for example, take a screen on which black is 0, 1 white, and 0.5 a grey exactly halfway between black and white. The screen is initially pure black (0).

On this we will draw two sprites, both pure white (1), with an alpha of 0.5, to be drawn over each other.

We will look at a single pixel, which is within the bounds of both sprites.

When the first sprite is drawn, the resulting colour where it is drawn is:

pixel_value = sprite_colour*alpha + back_colour*(1 - alpha)
which in this case is
pixel_value = 1.0*0.5 + 0.0*(1.0 - 0.5)
                 = 0.5 + 0.0 = 0.5

So we end up with a 50% grey.

When the second sprite is drawn, the result is this:

pixel_value = sprite_colour*alpha + back_colour*(1 - alpha)
pixel_value = 1.0*0.5 + 0.5*(1.0 - 0.5)
                 = 0.5 + 0.25 = 0.75

Hence, instead of 1.0 (pure white), we actually end up with 0.75 (three-quarters white).

As we add more and more sprites, the result gets closer and closer to 1.0, until it does eventually get close enough to probably not be particularly noticeable.

With higher alpha values, I believe that this will involve fewer “layers”.

Thanks, Thaumaturge, I wondered if something like that was happening. Now my question becomes more of how do I take sprite A fully showing and transition to sprite B (initially invisible) while keeping the pixels contained within the sprites overlap with an “alpha” of 1 (without showing the background color)?

After experimenting a bit, this seems to work…

I am fading from sprite A to sprite B. I set sprite B to render in front of sprite A, and then increase its alpha (leaving A at alpha=1). When B reaches 1, in the example then I fade B back to A and switch them, but you could then remove A from the scene graph.

Is this the best way to do it?

from pandac.PandaModules import *
import direct.directbase.DirectStart

def createSprite():
	vdata = GeomVertexData('', GeomVertexFormat.getV3t2(), Geom.UHStatic)
	vertex = GeomVertexWriter(vdata, 'vertex')
	uv = GeomVertexWriter(vdata, 'texcoord')
	prim = GeomTriangles(Geom.UHStatic)
	
	vertex.addData3f(0, 0, 1)
	vertex.addData3f(0, 0, 0)
	vertex.addData3f(1, 0, 0)
	vertex.addData3f(1, 0, 1)
	uv.addData2f(0,0)
	uv.addData2f(0,1)
	uv.addData2f(1,1)
	uv.addData2f(1,0)

	prim.addVertices(3,2,0)
	prim.addVertices(1,0,2)
	prim.closePrimitive()
	
	geom = Geom(vdata)
	geom.addPrimitive(prim)
	nodepath = NodePath(GeomNode('gnode'))
	nodepath.node().addGeom(geom)
	nodepath.setTransparency(1)

	return nodepath

base.setBackgroundColor(0,0,0,0)
#c1 and c2 are overlayed...
#process is set c2 (at 0) to a bin greater than c1, then increase c2 alpha until 1, then remove c1 abruptly (doesn't matter)
c1 = createSprite()
c1.setScale(.5,1,.5)
c1.setColorScale(1,0,0,1)
c1.setBin('fixed',0)
c1.setDepthWrite(0)
c1.setDepthTest(0)
c1.reparentTo(render2d)

c2 = createSprite()
c2.setScale(.5,1,.5)
c2.setColorScale(0,1,0,0)
c2.setBin('fixed',1)
c2.setDepthWrite(0)
c2.setDepthTest(0)
c2.reparentTo(render2d)

changing = c2
alpha = 0

#alpha of 1       (upper left)
c3 = createSprite()
c3.setScale(.5,1,.5)
c3.setPos(-.51,0,0)
c3.reparentTo(render2d)

def update(task):
	global changing,alpha,c1,c2
	alpha += task.dt*40
	if alpha > 1: alpha = 1

	changing.setAlphaScale(alpha)
	
	if alpha==1:
		changing.setBin('fixed',0)
		if changing == c1:
			changing = c2
		else:
			changing = c1
		changing.setBin('fixed',1)
		alpha = 0
		changing.setAlphaScale(alpha)

taskMgr.add(update,'update')

run()

another question: if I set a nodepath’s bin to ‘fixed’,1 and another to ‘fixed’,0 and there mutul parent to ‘fixed’,40, does panda somehow combine them or are the nodepaths at 1 and 0 respectively?

I’m not in a position to tell you whether there is better way or not–the “best” way depends on lots of things I have no way of knowing–but this is a fine solution. Note that the FadeLODNode uses a similar technique internally for switching between different LOD’s of a model.

The nodepaths are at 1 and 0.

David