Draw image without texture?

BlitzMax Forums/BlitzMax Programming/Draw image without texture?

sswift(Posted 2006) [#1]
Here's the problem:

I want to make sprites glow white. This is useful for making things glow, or flash, or for fading a screen to white instead of black.

Add blending a sprite on top of itself is well and good, but it's not suitable for all cases, and doesn't produce a glow which is as visible.

What I need is some way to draw an image using the SetColor only, with the alpha map still defining the transparent regions.

I'm not particularly interested in any method which requires me to create a new pixmap and draw to it. I'm looking for a hardware based solution so that it will work fast even for fullscreen images, or when you have 50 sprites on the screen using it.

Also, it needs to work both in OpenGL and DirectX.

It's easy enough to render a sprite as black just by setting the color to black because the texture is multiplied with the color, but getting it to render white is not that easy unfortunately.


TartanTangerine (was Indiepath)(Posted 2006) [#2]
Use lightblend? That's how I make the stuff glow in cBubble.


Grey Alien(Posted 2006) [#3]
yeah I went through the same thoughts, but in the end, drawing it again with lightblend seemed to be OK actually.


TartanTangerine (was Indiepath)(Posted 2006) [#4]
Or use something like this which is based on some of sswifts original blur code : http://www.blitzbasic.com/Community/posts.php?topic=56306#626286


sswift(Posted 2006) [#5]
Sigh.


Add blending a sprite on top of itself is well and good, but it's not suitable for all cases, and doesn't produce a glow which is as visible.



I don't WANT add blend. I already have add blend implemented! I've been using it for other stuff.

Add blend is not SUITABLE for some effects. I want a certain look. I want objects to glow until they reach bright white then dissapear. The only way to do that is if I can blit a sprite which is white and the same shape over the original.

There's two ways to do that. One is with a hardware method, and the other is by creating a new image from the original which is pure white. But I don't want to have to do that. It's clunky and wastes video memory.

I'm also not interested in a fuzzy blur glow that requires a DLL. I'm coding for cross platform compatability. And even if it didn't need a DLL I still wouldn't want a fuzzy blur for this effect. :-)


TartanTangerine (was Indiepath)(Posted 2006) [#6]
Atually sswift your original post said:
Alpha blending a sprite on top of itself is well and good, but it's not suitable for all cases, and doesn't produce a glow which is as visible.
Not our fault you messed up.
I'm also not interested in a fuzzy blur glow that requires a DLL. I'm coding for cross platform compatability. And even if it didn't need a DLL I still wouldn't want a fuzzy blur for this effect. :-)
I don't recall suggesting this? My code is crossplatform - it's not a dll.

Sorry to have bothered you!


Michael Reitzenstein(Posted 2006) [#7]
Not his fault he messed up, but painfully obvious what he meant.


ImaginaryHuman(Posted 2006) [#8]
You're going to need a different blend mode.

I don't know what the DX code would be.

But in GL, look at:

glBlendFunc()

Basically, it defines how much of the source value is used and how much of the dest is used. It's what gets called when you do SetBlend in Max2D.

You might be interested in adding the image to itself, which doubles its brightness ... e.g. glBlendFunc(GL_ONE,GL_SRC_COLOR)

Or you could use saturation glBlendFunc(GL_ONE,GL_SRC_ALPHA_SATURATE)

See here for more: http://www.mevis.de/opengl/glBlendFunc.html

I don't think there's any other way you can make your shapes brighter, unless you draw with glBlendFunc(GL_SRC_ALPHA,GL_SRC_ALPHA_SATURATE) which uses the alpha channel to copy over to RGB, which might be an alternative to light blend.


sswift(Posted 2006) [#9]
I didn't mess up. My post NEVER said alpha blend. I don't think I even went back and edited the post! *


I'm also not interested in a fuzzy blur glow that requires a DLL.




I don't recall suggesting this? My code is crossplatform - it's not a dll.



Well you did suggest something based on my blur code. As for the DLL bit, my mistake. I saw "render to texture module" and I translated that to mean DLL.

Still, it is completely different than what I requested. I was pretty specific about what I needed to do. :-)

Angel:
That page really sucks. :-) I can hardly understand it!

But copying the alpha channel to the RGB channel would at least get me the fade to white. If I could tint that alpha by the color set with SetColor, that would give me exactly what I need actually. I think. Hm... Well it might get me close enough anyway. I'm not precisely sure if it would look right in areas that are semi-transparent, but I'm guessing it would either look right, or close enough.

I need a solution that will work in both OpenGL and Direct3D though.


* Disclaimer: I could have messed up. But I don't remember it. Therefore it didn't happen.


ImaginaryHuman(Posted 2006) [#10]
I'm just giving a GL suggestion because I'm on a Mac and have no idea or interest in DX, sorry about that.

The page from the GL manual is technical and does take a bit of understanding, but it's doable.

What I need to know from you is, are you drawing your sprites just with `on/off` masking, or are you wanting them to be alpha blended as well?

I need to know if you want to alphablend at the same time as making it glow white.


Grey Alien(Posted 2006) [#11]
I knew what you meant sswift, I was just saying it looked ok for my purposes, but I'm following this thread with interest for a different approach.


sswift(Posted 2006) [#12]
"What I need to know from you is, are you drawing your sprites just with `on/off` masking, or are you wanting them to be alpha blended as well?"

They need to be alpha blended, and they need to be affected by either SetColor or SetAlpha.

Basically what I want is to be able to take a normal looking sprite, and then using no additional images, be able to bring the brightness of it up all the way to pure white, just as if one were sliding the brightness slider in photoshop.

To do this, instead of add blending the aprite over itself, you add blend a white version or a solid color version of the sprite over itself, where that color is affected by SetColor, so you can start it off dark, so it doesn't affect the sprite at all, and bring it up until the sprite on the screem is completely obscured by the whiteness or the solid color.


BlackSp1der(Posted 2006) [#13]
an image shape function.
drawing only the shape of your image with your setcolor.

or a bright function for images :P
is that possible?

SetBright(value)


Grey Alien(Posted 2006) [#14]
Well either convert the alpha channel to a mask in your paint program for all images, or write some pixmap manipulation code to do it for chosen images when they are loaded. I don't see any other way.


sswift(Posted 2006) [#15]
You give up too easily! :-)


Grey Alien(Posted 2006) [#16]
lol. OK, I *spose* there could be a fancy DX/OpenGL command that you'll need to setup/call. But I really don't think there is a quick way to do this in BMax as it stands...I'd like to be wrong :-)


ImaginaryHuman(Posted 2006) [#17]
I've been trying to figure this out over lunch. You're basically trying to almost do two alphablends at the same time. You can't do that with normal hardware setup. To create alphablending you have to perform a scaling of the source RGB colors, combined with a scaling of the existing destination colors. This doesn't leave any options for doing any other additional blending at the same time. You'd have to move to at least 2 passes.

However, if you are willing to have a second copy of each sprite, which is an all-white-pixel copy of the sprite, but with the same alpha-channel information as the original, then you can use a combination of LIGHTBLEND and SetColor to brighten your separately-rendered RGB sprite.

So ....

1. Make a proper full color sprite with alpha channel.
2. Turn all pixels to pure white and keep the alpha channel - this is the second image. You can do this easily by copying the first sprite to a new pixmap and then going through and writing $FF,$FF,$FF to the RGB components, but keeping the alpha.
3. Draw the first image in AlphaBlend as normal, or whatever general appearance you want.
4. SetColor to anything between $FF,$FF,$FF and $0,$0,$0 to decide how much white will be applied.
5. SetBlend LightBlend and draw the all-white sprite over the original. Depending on the SetColor it will draw that amount of white. The less white it draws the more transparently it draws the white, so at $10,$10,$10 you see a little bit of lightening, at $80,$80,$80 you see all pixel increased by $80. Then at $FF,$FF,$FF you definitely will flood out all pixels to white.

I think this will work but you do need two copies of the sprite. Lightblend also alphablends at the same time, right? SetColor only fades colors DOWN, which is why you have to fade down from white - hence you need to start out with originally white pixels.

One thing to bare in mind is this is not a `brightness` function - it doesn't scale the RGB values by an equal percentage, it's an ADD function that tops out at white - so basically the same as photoshops `lighten` rather than `brightness. But it works and will be the same on DX and GL.

If you want to not have a second copy of the sprite, things are going to get much more complicated, if not impossible. I can't think of any way to do it in one pass because you already use up the blending operations available by doing an alphablend.


ImaginaryHuman(Posted 2006) [#18]
Just out of curiosity try this before you draw your colored sprite by itself (no white sprite):

glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA,GL_ONE)

Normally when you alphablend, you have a blend factor, e,g 0.5 is 50% transparency. Usually to do alphablending you'll scale the source pixel value by this, and then you'll scale the destination pixel by 1.0-thatvalue. This is because the result of both multiplications are then adedd together and you want the whole result to always = 1.0. Ie SourceBlendedValue+DestBlendedValue=1.0.

However, if you set the destination factor to be GL_ONE, that means the destination is not scale down at all. So it will be Source(0.5)+Dest(1.0)=1.5 ... this is then clamped to 1.0, so when the source pixel and the destination pixel added together is >1.0 it will turn into white.

The only problem with this is that the alphablending is interfered with in a strange way. I think you might only be able to get it to look like it is blending from 0.0 to 0.5 ie it might only let you have up to 50% transparency. I'm not sure. Just try it and see how it looks at different transparency levels.

So..

SetBlend AlphaBlend
SetColor $FF,$FF,$FF
glEnable(GL_BLEND) 'note this may interfere with max2d state
glBlendFunc(GL_SRC_ALPHA,GL_ONE)
DrawImage

You might find that changing the SetColor has an unexpected affect as well.


sswift(Posted 2006) [#19]
You'd have to move to at least 2 passes.


When did I say I wanted to do it in 1 pass? :-) I asked how to render a sprite as a solid color, because I wanted to do it with two passes: 1. Draw sprite normally. 2. Draw sprite with solid color instead of texture.

So ....


What you've described is exactly what I already thought of months ago, and exactly what I don't want to do. I'm trying to find a generalized way to do the second pass WITHOUT making another copy of the image. That wastes video ram and makes yet another image I need to blit to the screen to buffer it before drawing to avoid stuttering, and which I need to create, track, and free.


Lightblend also alphablends at the same time, right?



Actually, you had that step wrong. You don't lightblend, you use SetAlpha and alphablend. If you use lightblend then you can never get the image to match the solid color unless the solid color is white. But if you use alpahblend and change the alpha, then you get the effect of applying a translucent colored sheet over the sprite and a fully opaque one when you push it up all the way, which is exactly the effect I'm looking for.

But not if it requires creating new images to do it. And I know it doesn't. Not in hardware anyway. If it is impossible in BlitzMax, it is only because we don't have the commands to do it.

I was hoping someone who knows more about OpenGL and DirectX would know of a way to set the blend mode from BlitzMax like a custom SetBlend which is more avanced.


One thing to bare in mind is this is not a `brightness` function - it doesn't scale the RGB values by an equal percentage, it's an ADD function that tops out at white - so basically the same as photoshops `lighten` rather than `brightness. But it works and will be the same on DX and GL.


Actually, the effect I'm going for is that of a second layer with a solid color and variable opacity, set to normal blend. Forget what I said aobut brightness, it would have only applied to white anyway.


tonyg(Posted 2006) [#20]
Not entirely sure what I'm doing but this might do what you want in DX...
Graphics 800,600
image:TImage=LoadImage("max.png")
n:Float=0.4
    SetBlend alphablend

While Not KeyHit(key_escape)
  Cls
    SetAlpha 1.0
    Setmodnormal()
	DrawImage image,100,100
	settowhite()
	SetAlpha n
    DrawImage image,100,100
    n:+0.01
    If n>1.0 n=0.1
  Flip
Wend
Function setmod2x()
		PrimaryDevice.device.SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_MODULATE2X)
End Function
Function setmodnormal()

				PrimaryDevice.device.SetTextureStageState 0,D3DTSS_COLOROP,D3DTOP_MODULATE
				PrimaryDevice.device.SetTextureStageState 0,D3DTSS_ALPHAOP,D3DTOP_MODULATE

End Function 
Function settowhite()
	PrimaryDevice.device.SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_SELECTARG2 );
	PrimaryDevice.device.SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
End Function



ImaginaryHuman(Posted 2006) [#21]
SetBlend is the same as using glBlendFunc() which I already described the only way you might get some kind of lighting effect with it - GL_SRC_ALPHA_SATURATE and GL_ONE. The only way to make colors increase past their original values is either to use lightblend or to set up the blending so that one or both of the source/dest pixel values is not scaled down (GL_ONE), so the total is more than 1.0.

Otherwise, are you using the stencil buffer at all in your game? Would you be willing to use it to achieve this effect?

If so..

1. Set up the stencil buffer so that when you draw your RGB sprite, any pixels that get drawn sets the stencil buffer to a reference value (e.g. $FF)

2. Draw the RGB sprite with alpha blending as normal - the stencil buffer gets populated with masked pixels.

3. Switch on the stencil test and set it up so that it only allows pixels to be drawn if that pixel in the stencil is the reference value. If possible, also set it so that it also wipes the stencil buffer after using it.

4. SetColor to the maximum color you want the sprite to fade to, e.g. white.

5. SetBlend so that alphablending is switched on and SetAlpha value to whatever level you want.

6. DrawRect over the sprite - so only the stencilled pixels are affected.


Otherwise, good luck finding a way to do this. (maybe you can make use of the destination alpha channel in 32-bit modes)


ImaginaryHuman(Posted 2006) [#22]
Otherwise, try using:

http://www.mevis.de/opengl/glTexEnv.html

Or you basically need to add another source of influence to the pipeline - either some form of multitexturing, or maybe try playing with lighting - perhaps ambient light changes on a per-sprite basis would allow it to brighten to white.


BlackSp1der(Posted 2006) [#23]
tonyg, your code is useful to make sprite flashing effect, thanks. :)
anyone have a GL mode?


sswift(Posted 2006) [#24]
TonyG:
That is exactly the sort of effect I want! It even does the color tint if you put a SetColor() in the SetToWhite function! Thanks!

Now I just need the OpenGL equivalent, and to make sure that when you reset things back to normal they're being reset properly. :-)

Do you have a reference you used for that that I could take a look at? I'd like to see what the different commands and modes do.

Oh and where do I find the PrimaryDevice stuff in Max's source? This is what I was hoping you could do... Access DirectX from in Max like you can OpenGL to influence the output.


ImaginaryHuman(Posted 2006) [#25]
What does the modulation look like? Post a screenshot?


sswift(Posted 2006) [#26]
Modulate 2X is the same thing you get if you add blend a sprite over itself. Modulate 4x is like add blending three copies of a sprite over itself.

It increases the brightness 2x or 4x respectively, but as it only has the colors of the object to work with it can never make a dark green pixel anything other than a bright green pixel. It cannot push a saturated color pixel to white.

My sprite system already does 2X with the two pass method, as that method allows you to vary the brightness by varying the SetColor for the add pass. In my system if you set the color of a sprite over 255 then this second pass kicks in.

In modern 3D games, Modulate 2x is often used to apply the lightmaps to the textures...

Um... actually, my description is a bit off there come to think of it.

Anyway to continue my thought... Modulate 2x is used because it not only allows you to darken the textures in shadow areas, but also allows you to brighten them in very light areas, which looks much better than only being able to darken them. Though you could double the brightness of all your textures before doing a regular multiply pass, the results are ugly because you get flat spots which were too bright in the texture and got clamped to white, but have now had their brightness reduced, and the detail is still gone. Also it can kill the color saturation depending on how you do the brightening.

So yeah, I misspoke there. With the modulate modes what actually happens is any pixel that is less than 127 will darken the underlying image, and any pixel 128 or over will brighten it, up to 2x or 4x. Actually I'm not certain that in 4x the magic color is still 127, but I'm pretty sure it is.

So yeah, it's not exactly like add blending a second pass over the object. It's good for lightmaps though.


BlackSp1der(Posted 2006) [#27]
hehe, I don't know OpenGL, but playing with some code I got the OpenGL version. and work with setcolor. :)

Maybe somebody can check the code.


Graphics 800,600,0

Global image:TImage=LoadImage("bmax160_2.png")
Local n:Float

SetBlend AlphaBlend
SetColor 255,255,255
	
While Not KeyHit(key_escape)
  Cls
	SetAlpha 1
	DrawImage image,100,100
	SetAlpha n
	SetShapeColor(128,255,255)
	DrawImage image,100,100
	SetNormal()
	Flip
    n=(n+0.01) Mod 1
Wend


Global red:Int,green:Int,blue:Int
Function SetShapeColor(r:Int,g:Int,b:Int)
	GetColor red,green,blue
	SetColor r,g,b
	GlTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE)
	GlTexEnvf(GL_TEXTURE_ENV,GL_COMBINE_RGB,GL_REPLACE)
	GlTexEnvf(GL_TEXTURE_ENV,GL_SOURCE0_RGB,GL_PREVIOUS)
	GLTexEnvf(GL_TEXTURE_ENV,GL_OPERAND0_RGB,GL_SRC_COLOR)
EndFunction

Function SetNormal()
	SetColor red,green,blue
	GLTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE, 0);
EndFunction




sswift(Posted 2006) [#28]
Black:
I just tested your code. I don't know what you're doing there because I don't recognize those constants from the OGL 1.1 docs I was looking at, but your code doesn't work. If you set the color to 0, 255, 255, a red sphere will turn dark green, when it should turn bright cyan like the DirectX version above does.

But guess what? I just solved the problem myself!

These functions should work with all drivers on all platforms:



What the above code is doing is this:

SetColor*(1-Texture) + (Texture*TextureColor)

So if texture is 0.25, and SetColor is 1 and TextureColor is 1 (they must be the same or you get weird effects like reversed colors) then you get:

1 * 0.75 + 0.25 * 1 = 1

So that means if you put in white for both colors, you get white back out. The same for any other color, because (1-Texture) + Texture will always equal 1.


Ps:

Grey Alien:
In yo face!

Told you you gave up too easily. :-)


BlackSp1der(Posted 2006) [#29]
ehhh, my code works fine in my MAC. hmm. anyway your final code works fine in MAC too.


tonyg(Posted 2006) [#30]
Do you have a reference you used for that that I could take a look at? I'd like to see what the different commands and modes do.

Oh and where do I find the PrimaryDevice stuff in Max's source? This is what I was hoping you could do... Access DirectX from in Max like you can OpenGL to influence the output


I use the DX7 SDK .chm file to check for available commands but 'Special Effects Game Programming with DirectX' by Mason McCuskey. It's DX8 which is why you need to ensure the commands are also DX7.
'2D in Direct3D' by Ernest Pazera is pretty good as is his 'Isometric Game Programming with DirectX 7.0'
Really useful stuff for rendering is the mfctex utility thaty comes with DX SDK as well as D3DMulTex and OGLMulTex. These utilities allow you to enter renderstage commands plus texture input and show the result.
Not all cards have multipass rendering so you might need multiple single passes.
PrimaryDevice is declared in D3D7Graphics and holds the DX7 device instance. From the you have access to it's fields which include the Direct3D device, frontbuffer and backbuffer etc.
Hope it helps


Grey Alien(Posted 2006) [#31]
sswift: ah, nice. My sit back a watch approach has reaped rich dividends, back at ya! Actually I said:

I *spose* there could be a fancy DX/OpenGL command that you'll need to setup/call. But I really don't think there is a quick way to do this in BMax as it stands
So I was right :-) Although I admit that the resulting code is compact.


sswift(Posted 2006) [#32]

My sit back a watch approach has reaped rich dividends



Ha ha. :-)



So I was right :-)



Wrong! You said that after you'd given up!


Grey Alien(Posted 2006) [#33]
who says I'd given up? I never even started ;-)


WarpZone(Posted 2006) [#34]
Thanks for posting your solution, sswift! :) I was looking for something like this, myself.

And hey, try calling SetBlend LIGHTBLEND just before you call the second drawimage! :) Now THAT's flashing!