How can I do this blend effect?

BlitzMax Forums/BlitzMax Programming/How can I do this blend effect?

Czar Flavius(Posted 2011) [#1]
THIS IS DRIVING ME AROUND THE BEND

I have these two terrains





And this mask



I want a result like this



However, even where the mask is totally black the grass is contaminated somewhat by the sand image. I did it by experimenting and got this

SetBlend ALPHABLEND
DrawImage grass, 0, 0
SetBlend LIGHTBLEND
DrawImage mask, 0, 0
SetBlend SHADEBLEND
DrawImage sand, 0, 0


How can I do this properly using normal Max2d without using grab image?

I just want to draw two images on top of each other displaying either one or the other or a mixture of the two according to a monochrome mask. WHY CAN'T I DO THIS! Thanks.

Last edited 2011

Last edited 2011


Kryzon(Posted 2011) [#2]
Have you tried making that mask texture fully black or fully white in the RGB channels, and then using that gradient as its alpha channel?
Then draw the mask with ALPHABLEND as well (so you just need a single call for both the grass and the mask), and then test either LIGHT or SHADE blends for the sand.

You shouldn't use color-blending like you are now because the middle of the resulting composite texture is a bit desaturated from the blending with gray. If you use alpha blending with a flat-colored mask texture it should preserve the smooth result and the coloring too (even though the mask texture is flatly colored, its alpha channel gives the smooth transition).


Czar Flavius(Posted 2011) [#3]
I've tried that but it doesn't work. The mask has its gradient in the alpha channels. It only draws the sand at the end. Could you post code like from my one above to explain how I should do this? Thanks.


Gabriel(Posted 2011) [#4]
This is basically texture splatting in 2D, isn't it? I don't think you can do it in normal Max2D. It would require blend modes which Max doesn't expose. Or does Max2D now expose more blend modes? Are you able to set the source and destination blend modes separately?


Czar Flavius(Posted 2011) [#5]
I have no idea. I can't even figure out how to draw rectangles in GIMP at the moment, let alone alpha transparency. I think that is why my mask wasn't working.


Czar Flavius(Posted 2011) [#6]
Even with the mask in the alpha channel, when I draw the sand, it is still going over the pure grass region and making it appear too yellow.


Czar Flavius(Posted 2011) [#7]
Can I use GRAPHICS_ALPHABUFFER to do this?

Draw the first image solidly, draw the alpha mask to the alpha buffer, and then draw the second image in alpha blend?


Gabriel(Posted 2011) [#8]
Well that's what I had in mind, yes. But you would definitely need full control over the source and destination blend modes. I'm sure I've seen some kind of addon which extended the blend modes available, but I can't find it offhand.


Czar Flavius(Posted 2011) [#9]
I'VE DONE IT I'VE DONE IT I'VE DONE IT I'VE DONE IT I'VE DONE IT I'VE DONE IT I'VE DONE IT I'VE DONE IT I'VE DONE IT I'VE DONE IT I'VE DONE IT I'VE DONE IT I'VE DONE IT I'VE DONE IT I'VE DONE IT I'VE DONE IT I'VE DONE IT I'VE DONE IT I'VE DONE IT

I've had to make some changes to max2d.

First of all you will need to enable GRAPHICS_ALPHABUFFER. Then you will need to draw the alpha mask so that its alpha values are input directly onto the buffer. I called this direct blend. The other blend methods don't write to alpha.

Then I made blend functions to write the image according to the destination alpha, which are the values from the mask. There are two versions of this blend. The second version inverts the alpha.

I made this change to driver.bmx
'modes for SetBlend
Const MASKBLEND=1
Const SOLIDBLEND=2
Const ALPHABLEND=3
Const LIGHTBLEND=4
Const SHADEBLEND=5
Const DIRECTBLEND=6
Const MASKONBLEND=7
Const MASKOFFBLEND=8


I made this change to glmax2d.bmx
	Method SetBlend( blend )
		If blend=state_blend Return
		state_blend=blend
		Select blend
		Case MASKBLEND
			glDisable GL_BLEND
			glEnable GL_ALPHA_TEST
			glAlphaFunc GL_GEQUAL,.5
		Case SOLIDBLEND
			glDisable GL_BLEND
			glDisable GL_ALPHA_TEST
		Case ALPHABLEND
			glEnable GL_BLEND
			glBlendFunc GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA
			glDisable GL_ALPHA_TEST
		Case LIGHTBLEND
			glEnable GL_BLEND
			glBlendFunc GL_SRC_ALPHA,GL_ONE
			glDisable GL_ALPHA_TEST
		Case SHADEBLEND
			glEnable GL_BLEND
			glBlendFunc GL_DST_COLOR,GL_ZERO
			glDisable GL_ALPHA_TEST
		Case DIRECTBLEND
			glEnable GL_BLEND
			glBlendFunc GL_ONE,GL_ZERO
			glDisable GL_ALPHA_TEST
		Case MASKONBLEND
			glEnable GL_BLEND
			glBlendFunc GL_DST_ALPHA,GL_ONE_MINUS_DST_ALPHA
			glDisable GL_ALPHA_TEST
		Case MASKOFFBLEND
			glEnable GL_BLEND
			glBlendFunc GL_ONE_MINUS_DST_ALPHA,GL_DST_ALPHA
			glDisable GL_ALPHA_TEST
		Default
			glDisable GL_BLEND
			glDisable GL_ALPHA_TEST
		End Select
	End Method


Here is my user code

Strict
Const folder:String = "C:\SVN\trunk\Omega Directive\Temp\"

MainF()

Function MainF()
	SetGraphicsDriver(GLMax2DDriver(), GRAPHICS_BACKBUFFER | GRAPHICS_ALPHABUFFER)
	
	Graphics 512, 512
	
	Local grass:TImage = LoadImage(folder + "Grass1.png", 0)
	Local sand:TImage = LoadImage(folder + "Sand1.png", 0)
	Local mask:TImage = LoadImage(folder + "Mask5.png", 0)
	
	While Not (KeyDown(KEY_ESCAPE) Or AppTerminate())
		Cls
		
		SetBlend DIRECTBLEND
		DrawImage mask, 0, 0
		
		SetBlend MASKONBLEND
		DrawImage grass, 0, 0
	
		SetBlend MASKOFFBLEND
		DrawImage sand, 0, 0
			
		Flip
		Delay 1
	Wend
End Function

End


Here is the result!



Thanks to all that helped, including a number of old threads I read.

Final part - how to add this for DirectX? The function for DX9 is this one, but I haven't looked up the parameters to change for this one.

Method SetBlend( blend )
		If blend=_active_blend Return
		Select blend
		Case SOLIDBLEND
			_d3dDev.SetRenderState D3DRS_ALPHATESTENABLE,False
			_d3dDev.SetRenderState D3DRS_ALPHABLENDENABLE,False
		Case MASKBLEND
			_d3dDev.SetRenderState D3DRS_ALPHATESTENABLE,True
			_d3dDev.SetRenderState D3DRS_ALPHABLENDENABLE,False
		Case ALPHABLEND
			_d3dDev.SetRenderState D3DRS_ALPHATESTENABLE,False
			_d3dDev.SetRenderState D3DRS_ALPHABLENDENABLE,True
			_d3dDev.SetRenderState D3DRS_SRCBLEND,D3DBLEND_SRCALPHA
			_d3dDev.SetRenderState D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA
		Case LIGHTBLEND
			_d3dDev.SetRenderState D3DRS_ALPHATESTENABLE,False
			_d3dDev.SetRenderState D3DRS_ALPHABLENDENABLE,True
			_d3dDev.SetRenderState D3DRS_SRCBLEND,D3DBLEND_SRCALPHA
			_d3dDev.SetRenderState D3DRS_DESTBLEND,D3DBLEND_ONE
		Case SHADEBLEND		
			_d3dDev.SetRenderState D3DRS_ALPHATESTENABLE,False
			_d3dDev.SetRenderState D3DRS_ALPHABLENDENABLE,True
			_d3dDev.SetRenderState D3DRS_SRCBLEND,D3DBLEND_ZERO
			_d3dDev.SetRenderState D3DRS_DESTBLEND,D3DBLEND_SRCCOLOR
		End Select
		_active_blend=blend
	End Method



AdamRedwoods(Posted 2011) [#10]
Nice job!
Although I can't really figure out why this would work-- unless the alpha buffer is filled first with $ff, which makes a little sense.

It also gives strange artifacts when the color is either $ff or $00, but that could just be the images I'm using.


Czar Flavius(Posted 2011) [#11]
Hi Adam,

How does it work with the images I provide? This game will be publically distributed so I need to iron out any bugs. Please upload the images and mask you are using and I will test it my end.

It doesn't matter what is in the alpha buffer as the direct blend mode will totally replace the value with that from the mask. Images you don't want to blend with a mask use one of the existing blend modes which ignore the alpha buffer anyway (I think, I've not tried integrated)

I think I made some changes to the mask. The mask needs to be pure black with the transition in the alpha channel. Here is the mask I used. Hopefully its alpha channel won't get botched in the transfer.



Last edited 2011


Gabriel(Posted 2011) [#12]
It's been a while since I worked with DX9 blend modes, but I think :

Case MASKONBLEND
   glEnable GL_BLEND
   glBlendFunc GL_DST_ALPHA,GL_ONE_MINUS_DST_ALPHA
   glDisable GL_ALPHA_TEST
Case MASKOFFBLEND
   glEnable GL_BLEND
   glBlendFunc GL_ONE_MINUS_DST_ALPHA,GL_DST_ALPHA
   glDisable GL_ALPHA_TEST


would translate to

Case MASKONBLEND
   _d3dDev.SetRenderState D3DRS_ALPHABLENDENABLE,True
   _d3dDev.SetRenderState D3DRS_ALPHATESTENABLE,False
   _d3dDev.SetRenderState D3DRS_SRCBLEND,D3DBLEND_DESTALPHA
   _d3dDev.SetRenderState D3DRS_DESTBLEND,D3DBLEND_INVDESTALPHA
Case MASKOFFBLEND
   _d3dDev.SetRenderState D3DRS_ALPHABLENDENABLE,True
   _d3dDev.SetRenderState D3DRS_ALPHATESTENABLE,False
   _d3dDev.SetRenderState D3DRS_SRCBLEND,D3DBLEND_INVDESTALPHA
   _d3dDev.SetRenderState D3DRS_DESTBLEND,D3DBLEND_DESTALPHA


I'm not 100% on how BlitzMax spells those constants, so you may have to replace DEST with DST. With DX9, they're supposed to be DEST but BlitzMax might have them as DST to provide consistency with OpenGL.


AdamRedwoods(Posted 2011) [#13]
@Czar: works fine with your images.


Zeke(Posted 2012) [#14]
Hi, this can be made without any extra blend modes, and this works with all drivers (dx7/9,opengl)

http://www.byrathon.com/omat/blend.zip

the trick is using "alpha only" mask image. make mask image and convert that to PF_A8 format.

Local pix:TPixmap=LoadPixmap("filename.png") 'Load mask image
SavePixmapPNG(ConvertPixmap(pix,PF_A8),"alpha_mask.png") 'convert and save mask image to PF_A8 (alpha only image)


then you can draw images using only 2 blend modes:
SuperStrict
'Blend effect

SetGraphicsDriver D3D7Max2DDriver()
'SetGraphicsDriver D3D9Max2DDriver()
'SetGraphicsDriver GLMax2DDriver()

Graphics 800,600

Global grass:TImage=LoadImage("grass.jpg")
Global sand:TImage=LoadImage("sand.jpg")
Global mask:TImage=LoadImage("alpha_mask.png") 'PF_A8 format

While Not (KeyHit(KEY_ESCAPE) Or AppTerminate())
	Cls

	SetBlend ALPHABLEND
	DrawImage grass,0,0

	DrawImage mask,0,0

	SetBlend ShadeBlend
	DrawImage sand,0,0

	Flip
	Delay 1
Wend

[EDIT] and if you want to use different color when drawing sand, you must set that color before drawing mask. and set color back to white when drawing sand.

example:
SuperStrict
'Blend effect

SetGraphicsDriver D3D7Max2DDriver()
SetGraphicsDriver D3D9Max2DDriver()
'SetGraphicsDriver GLMax2DDriver()

Graphics 800,600

Global grass:TImage=LoadImage("grass.jpg")
Global sand:TImage=LoadImage("sand.jpg")
Global mask:TImage=LoadImage("alpha_mask.png") 'PF_A8 format

While Not (KeyHit(KEY_ESCAPE) Or AppTerminate())
	Cls

	SetColor 255,255,255 'Grass drawing color (white)
	SetBlend ALPHABLEND
	DrawImage grass,0,0
	
	SetColor 0,255,0 'Sand drawing color (hreen)
	DrawImage mask,0,0
 
	SetColor 255,255,255 'set WHITE color here
	SetBlend ShadeBlend
	DrawImage sand,0,0

	Flip
	Delay 1
Wend


Last edited 2012