Gaussian blur in real time?

Advanced game related topics
Joubarbe
Enthusiast
Enthusiast
Posts: 555
Joined: Wed Sep 18, 2013 11:54 am
Location: France

Gaussian blur in real time?

Post by Joubarbe »

Hey,

I'd like to know if there's a library somewhere that makes it possible to blur a part of a (2D) screen in real time. The goal would be to have a GUI over the game world, and have this game world blurred as a GUI background. I think the best way would be to capture an area of the screen, then turn it into a blurred sprite.

I saw two libraries that blur images, but they are extremely slow (500ms for 3 passes on a 512x512 image). I fear the answer contains the word "shader", and I know nothing about them :)

(the effect is pretty cool in 3D)
User avatar
DK_PETER
Addict
Addict
Posts: 898
Joined: Sat Feb 19, 2011 10:06 am
Location: Denmark
Contact:

Re: Gaussian blur in real time?

Post by DK_PETER »

Most 2D games, unless very old school, use 3D too.
Glow effect can be achieved in PureBasic's Ogre3D using CreateCompositorEffect().
See this one: http://wiki.ogre3d.org/Glow
You need the scripts and a suitable material to go with it.
Do a search for 'compositor' here and that should lead you to relevant and nice examples.
Current configurations:
Ubuntu 20.04/64 bit - Window 10 64 bit
Intel 6800K, GeForce Gtx 1060, 32 gb ram.
Amd Ryzen 9 5950X, GeForce 3070, 128 gb ram.
#NULL
Addict
Addict
Posts: 1440
Joined: Thu Aug 30, 2007 11:54 pm
Location: right here

Re: Gaussian blur in real time?

Post by #NULL »

I don't know how to do it with shaders unfortunately.
I tried with direct DrawingBuffer() access what it could do but I don't know how to make it fast enough (and good). You can change the test case with number keys 1-8. The mouse moves the sprite and it should blur in some way in the center of the screen in case 3,4,6,7,8.
Please disable the debugger.
Here are the times the drawing block takes on my Linux machine. On my windows machine the times are way to long, over a second in some cases, but I do not have a real graphics card in there at the moment, maybe that's why.

Code: Select all

mode 1: empty drawing block                  time: 11ms
mode 2: base                                 time: 11ms
mode 3: 4 direct neighbors                   time: 15ms
mode 4: 8 neighbors with greater distance    time: 16ms
mode 5: test only CopyMemory()               time: 12ms
mode 6: CopyMemory() and more neighbors      time: 110ms
mode 7: CopyMemory() and weighted neighbors  time: 175ms
mode 8: 4 neighbors with multiple distances  time: 25ms
It doesn't blur very much (would be more expensive) and it blurs only 1/4 of a 800x600 screen, so not sure it could ever be done fast enough for a whole large screen, so FWIW :)

Code: Select all

CompilerIf #PB_Compiler_Debugger
  If MessageRequester("debugger?", "the debugger is enabled. run anyway?", #PB_MessageRequester_YesNo) = #PB_MessageRequester_No
    End
  EndIf
CompilerEndIf


EnableExplicit

#ScreenWidth  = 800
#ScreenHeight = 600

InitSprite()
InitKeyboard()
InitMouse()
ElapsedMilliseconds()

Structure Pixel
  r.a
  g.a
  b.a
EndStructure

Define win, quit, mode, mode$
Define spr, i
Define sprFR, FR, FRt, FR$
Define time, timeTmp

win = OpenWindow(#PB_Any, 0, 0, #ScreenWidth, #ScreenHeight, "window")
OpenWindowedScreen(WindowID(win), 0, 0, #ScreenWidth, #ScreenHeight)

UsePNGImageDecoder()
CompilerIf #PB_Compiler_OS = #PB_OS_Windows
  Define spr = LoadSprite(#PB_Any, #PB_Compiler_Home + "examples/3D/Data/PureBasic3DLogo.png") ; windows
CompilerElse
  Define spr = LoadSprite(#PB_Any, #PB_Compiler_Home + "examples/3d/Data/PureBasic3DLogo.png") ; linux
CompilerEndIf

CompilerIf #PB_Compiler_OS = #PB_OS_Windows
  Define font = LoadFont(#PB_Any, "courier new", 10)
CompilerElse
  Define font = LoadFont(#PB_Any, "monospace", 10)
CompilerEndIf

sprFR = CreateSprite(#PB_Any, 400, 60)

mode = 6

Procedure mode3(xStart, yStart, xEnd, yEnd, buffer, pitch, pixelSize)
  Protected x, y
  Protected *pixel1.Pixel
  Protected *pixel2lef.Pixel
  Protected *pixel2rig.Pixel
  Protected *pixel2top.Pixel
  Protected *pixel2bot.Pixel
  
  For y = yStart To yEnd
    For x = xStart To xEnd
      
      *pixel1    = buffer + ((y  ) * pitch) + ((x  ) * pixelSize)
      
      *pixel2lef = buffer + ((y  ) * pitch) + ((x-1) * pixelSize)
      *pixel2rig = buffer + ((y  ) * pitch) + ((x+1) * pixelSize)
      *pixel2top = buffer + ((y-1) * pitch) + ((x  ) * pixelSize)
      *pixel2bot = buffer + ((y+1) * pitch) + ((x  ) * pixelSize)
      
      *pixel1\r = (*pixel1\r + *pixel2lef\r + *pixel2rig\r + *pixel2top\r + *pixel2bot\r) / 5.0
      *pixel1\g = (*pixel1\g + *pixel2lef\g + *pixel2rig\g + *pixel2top\g + *pixel2bot\g) / 5.0
      *pixel1\b = (*pixel1\b + *pixel2lef\b + *pixel2rig\b + *pixel2top\b + *pixel2bot\b) / 5.0
    Next
  Next
EndProcedure

Procedure mode4(xStart, yStart, xEnd, yEnd, buffer, pitch, pixelSize)
  Protected x, y
  Protected *pixel1.Pixel
  Protected *pixel2_1.Pixel
  Protected *pixel2_2.Pixel
  Protected *pixel2_3.Pixel
  Protected *pixel2_4.Pixel
  Protected *pixel2_5.Pixel
  Protected *pixel2_6.Pixel
  Protected *pixel2_7.Pixel
  Protected *pixel2_8.Pixel
  
  For y = yStart To yEnd
    For x = xStart To xEnd
      
      *pixel1   = buffer + ((y  ) * pitch) + ((x  ) * pixelSize)
      
      *pixel2_1 = buffer + ((y-3) * pitch) + ((x+1) * pixelSize)
      *pixel2_2 = buffer + ((y-1) * pitch) + ((x+3) * pixelSize)
      *pixel2_3 = buffer + ((y+1) * pitch) + ((x+3) * pixelSize)
      *pixel2_4 = buffer + ((y+3) * pitch) + ((x+1) * pixelSize)
      *pixel2_5 = buffer + ((y+3) * pitch) + ((x-1) * pixelSize)
      *pixel2_6 = buffer + ((y+1) * pitch) + ((x-3) * pixelSize)
      *pixel2_7 = buffer + ((y-1) * pitch) + ((x-3) * pixelSize)
      *pixel2_8 = buffer + ((y-3) * pitch) + ((x-1) * pixelSize)
      
      *pixel1\r = (*pixel2_1\r + *pixel2_2\r + *pixel2_3\r + *pixel2_4\r + *pixel2_5\r + *pixel2_6\r + *pixel2_7\r + *pixel2_8\r) / 8.0
      *pixel1\g = (*pixel2_1\g + *pixel2_2\g + *pixel2_3\g + *pixel2_4\g + *pixel2_5\g + *pixel2_6\g + *pixel2_7\g + *pixel2_8\g) / 8.0
      *pixel1\b = (*pixel2_1\b + *pixel2_2\b + *pixel2_3\b + *pixel2_4\b + *pixel2_5\b + *pixel2_6\b + *pixel2_7\b + *pixel2_8\b) / 8.0
    Next
  Next
EndProcedure

Procedure mode5(xStart, yStart, xEnd, yEnd, buffer, pitch, pixelSize, buffer2)
  Protected x, y
  
  CopyMemory(buffer, buffer2, OutputHeight() * pitch)
  
EndProcedure

Procedure mode6(xStart, yStart, xEnd, yEnd, buffer, pitch, pixelSize, buffer2)
  Protected x, y, i, k
  Protected r, g, b, cnt
  Protected *pixelTmp.Pixel
  Protected *pixel1.Pixel
  
  CopyMemory(buffer, buffer2, OutputHeight() * pitch)
  
  For y = yStart To yEnd
    For x = xStart To xEnd
      r = 0
      g = 0
      b = 0
      cnt = 0
      For k=-4 To 4
        For i=-4 To 4
          *pixelTmp = buffer2 + ((y+k) * pitch) + ((x+i) * pixelSize)
          r + *pixelTmp\r
          g + *pixelTmp\g
          b + *pixelTmp\b
          cnt + 1
        Next
      Next
      *pixel1 = buffer  + (y * pitch) + (x * pixelSize)
      *pixel1\r = r / cnt
      *pixel1\g = g / cnt
      *pixel1\b = b / cnt
    Next
  Next
  
EndProcedure

Procedure mode7(xStart, yStart, xEnd, yEnd, buffer, pitch, pixelSize, buffer2)
  Protected x, y, i, k
  Protected r.f, g.f, b.f, cnt.f
  Protected *pixelTmp.Pixel
  Protected *pixel1.Pixel
  
  CopyMemory(buffer, buffer2, OutputHeight() * pitch)
  
  #d = 3
  
  Protected initWeights
  Protected Dim weights.f(1, 1) ; use .d ?
  Protected rMax.f
  If Not initWeights
    initWeights = #True
    Dim weights( (2 * #d + 1), (2 * #d + 1) )
    rMax = Sqr((#d * #d) + (#d * #d))
    For k=-#d To #d
      For i=-#d To #d
        weights(#d + k, #d + i) = 1.0 - Sqr((i * i) + (k * k)) / rMax
        Debug "" + i + "   " + k + "   " + weights(#d + k, #d + i)
      Next
    Next
    ;End
  EndIf
  
  ; test weights()
  ; DrawingMode(#PB_2DDrawing_AllChannels)
  ; Box(10-2, 10-2, 90+4, 90+4, $ff006600)
  ; For k=-4 To 4
  ;   For i=-4 To 4
  ;     Circle(10+5+(4+k)*10, 10+5+(4+i)*10, 3, RGBA(255.0 * weights(4+k, 4+i), 0, 0, 255))
  ;   Next
  ; Next
  
  For y = yStart To yEnd
    For x = xStart To xEnd
      r = 0
      g = 0
      b = 0
      cnt = 0
      For k=-#d To #d
        For i=-#d To #d
          *pixelTmp = buffer2 + ((y+k) * pitch) + ((x+i) * pixelSize)
          r + *pixelTmp\r * weights(#d+k, #d+i)
          g + *pixelTmp\g * weights(#d+k, #d+i)
          b + *pixelTmp\b * weights(#d+k, #d+i)
          cnt + weights(#d+k, #d+i)
        Next
      Next
      *pixel1 = buffer  + (y * pitch) + (x * pixelSize)
      *pixel1\r = r / cnt
      *pixel1\g = g / cnt
      *pixel1\b = b / cnt
    Next
  Next
  
EndProcedure

Procedure mode8(xStart, yStart, xEnd, yEnd, buffer, pitch, pixelSize, buffer2)
  Protected x, y
  Protected *pixel1.Pixel
  Protected *pixel2lef.Pixel
  Protected *pixel2rig.Pixel
  Protected *pixel2top.Pixel
  Protected *pixel2bot.Pixel
  
  CopyMemory(buffer, buffer2, OutputHeight() * pitch)
  
  Define n
  For n = 1 To 4
    For y = yStart To yEnd
      For x = xStart To xEnd
        
        *pixel1    = buffer  + ((y  ) * pitch) + ((x  ) * pixelSize)
        
        *pixel2lef = buffer2 + ((y  ) * pitch) + ((x-n) * pixelSize)
        *pixel2rig = buffer2 + ((y  ) * pitch) + ((x+n) * pixelSize)
        *pixel2top = buffer2 + ((y-n) * pitch) + ((x  ) * pixelSize)
        *pixel2bot = buffer2 + ((y+n) * pitch) + ((x  ) * pixelSize)
        
        *pixel1\r = (*pixel2lef\r + *pixel2rig\r + *pixel2top\r + *pixel2bot\r) / 4.0
        *pixel1\g = (*pixel2lef\g + *pixel2rig\g + *pixel2top\g + *pixel2bot\g) / 4.0
        *pixel1\b = (*pixel2lef\b + *pixel2rig\b + *pixel2top\b + *pixel2bot\b) / 4.0
      Next
    Next
  Next
EndProcedure

Repeat
  ExamineMouse()
  ExamineKeyboard()
  
  If KeyboardPushed(#PB_Key_Escape) : quit = #True : EndIf
  If KeyboardPushed(#PB_Key_1) : mode = 1 : EndIf
  If KeyboardPushed(#PB_Key_2) : mode = 2 : EndIf
  If KeyboardPushed(#PB_Key_3) : mode = 3 : EndIf
  If KeyboardPushed(#PB_Key_4) : mode = 4 : EndIf
  If KeyboardPushed(#PB_Key_5) : mode = 5 : EndIf
  If KeyboardPushed(#PB_Key_6) : mode = 6 : EndIf
  If KeyboardPushed(#PB_Key_7) : mode = 7 : EndIf
  If KeyboardPushed(#PB_Key_8) : mode = 8 : EndIf
  
  While WindowEvent()
    Select Event()
      Case #PB_Event_CloseWindow
        quit = #True
    EndSelect
  Wend
  
  FR + 1
  If ElapsedMilliseconds() > FRt
    time / FR
    
    Select mode
      Case 1 : mode$ = "empty drawing block"
      Case 2 : mode$ = "base"
      Case 3 : mode$ = "4 direct neighbors"
      Case 4 : mode$ = "8 neighbors with greater distance"
      Case 5 : mode$ = "test only CopyMemory()"
      Case 6 : mode$ = "CopyMemory() and more neighbors"
      Case 7 : mode$ = "CopyMemory() and weighted neighbors"
      Case 8 : mode$ = "4 neighbors with multiple distances"
    EndSelect
    
    FRt = ElapsedMilliseconds() + 500
    FR$ = "fps:" + Str(FR * 2)
    FR = 0
    StartDrawing(SpriteOutput(sprFR))
      DrawingMode(#PB_2DDrawing_Transparent)
      Box(0, 0, OutputWidth(), OutputHeight(), $0)
      DrawingFont(FontID(font))
      DrawText(0, 0, FR$)
      DrawText(0, 20, "mode: " + mode + " (" + mode$ + ")")
      DrawText(0, 40, "time: " + time + "ms")
    StopDrawing()
  EndIf
  
  ClearScreen($333333)
  
  DisplayTransparentSprite(spr, MouseX(), MouseY())
  
  timeTmp = ElapsedMilliseconds()
  
  If StartDrawing(ScreenOutput())
    
    If mode >= 2
      
      Define buffer      = DrawingBuffer()
      Define pitch       = DrawingBufferPitch()
      Define pixelFormat = DrawingBufferPixelFormat()
      Define pixelSize
;       Debug pixelFormat
;       Debug pixelFormat!#PB_PixelFormat_ReversedY
;       Debug #PB_PixelFormat_24Bits_RGB
;       Debug #PB_PixelFormat_32Bits_BGR
;       End
      If pixelFormat & (#PB_PixelFormat_24Bits_BGR | #PB_PixelFormat_24Bits_RGB)
        pixelSize = 3
      ElseIf pixelFormat & (#PB_PixelFormat_32Bits_BGR | #PB_PixelFormat_32Bits_RGB)
        pixelSize = 4
      Else
        CloseScreen()
        CloseWindow(win)
        MessageRequester("error", "pixel format not supported")
        End
      EndIf
      If pixelSize
        
        Define yStart = #ScreenHeight * 0.25
        Define yEnd   = #ScreenHeight * 0.75
        Define xStart = #ScreenWidth  * 0.25
        Define xEnd   = #ScreenWidth  * 0.75
        
        DrawingMode(#PB_2DDrawing_Outlined)
        Box(xStart - 20, yStart - 20, xEnd - xStart + 40, yEnd - yStart + 40, $ff00ff00)
        
        Define buffer2
        If Not buffer2
          buffer2 = AllocateMemory(pitch * OutputHeight())
        EndIf
        
        If mode >= 3
          
          Select mode
            Case 3 : mode3(xStart, yStart, xEnd, yEnd, buffer, pitch, pixelSize)
            Case 4 : mode4(xStart, yStart, xEnd, yEnd, buffer, pitch, pixelSize)
            Case 5 : mode5(xStart, yStart, xEnd, yEnd, buffer, pitch, pixelSize, buffer2)
            Case 6 : mode6(xStart, yStart, xEnd, yEnd, buffer, pitch, pixelSize, buffer2)
            Case 7 : mode7(xStart, yStart, xEnd, yEnd, buffer, pitch, pixelSize, buffer2)
            Case 8 : mode8(xStart, yStart, xEnd, yEnd, buffer, pitch, pixelSize, buffer2)
          EndSelect
          
        EndIf
      EndIf
    EndIf
    
    StopDrawing()
  EndIf
  
  time = time + (ElapsedMilliseconds() - timeTmp)
  
  DisplaySprite(sprFR, MouseX(), MouseY() + 80)
  
  FlipBuffers()
  
Until quit

User avatar
darius676
Enthusiast
Enthusiast
Posts: 278
Joined: Thu Jan 31, 2019 12:59 am
Contact:

Re: Gaussian blur in real time?

Post by darius676 »

Hello,
you may be able to try the following:
Take the sprite for the background and copy it to a number of SpriteID . For example, 4 sprites.
You change the transparency and output the sprites slightly (randomly) offset and overlapping on the screen.
It is possible that this will achieve the desired effect.




download:
https://www.dropbox.com/s/fjizfurnprr70 ... O.zip?dl=0

Code: Select all

;small and dirty example for "blur"

InitKeyboard()
InitSprite()
InitMouse()
InitSound()
UsePNGImageDecoder()


EnableExplicit




Structure _gfx
  
  id.i
  x.f
  y.f
  t.l
  
EndStructure

Global NewList _gfx._gfx()
Global _gfx_work_id.i=0
Global _key_id.l=0
Global _ok.l=0
Global _win_id.i=0
Global _x.f=0
Global _y.f=0
Global _w.f=1024
Global _h.f=512
Global _win_id.i=0
Global _screen_id.i=0
Global _event.i=0
Global _i.b=0
Global _bx.b=0
Global _by.b=6
;load the gfx:

  
_win_id.i=OpenWindow(#PB_Any,_x.f,_y.f,_w.f,_h.f," *BLUR* ?")
_screen_id.i=OpenWindowedScreen(WindowID(_win_id.i),0,0,_w.f,_h.f)
_gfx_work_id.i=LoadSprite(#PB_Any,GetCurrentDirectory()+"blur.png",#PB_Sprite_AlphaBlending )

If IsSprite(_gfx_work_id.i)
  
  ResetList(_gfx())
  
  For _i.b=0 To 3
    
    
    If AddElement(_gfx())
      CopySprite(_gfx_work_id,_gfx()\id)
      _bx.b+1
      _by.b-1
      _gfx()\x=_bx.b
      _gfx()\y=_by.b
    EndIf
 
  Next
  
  
  
  If ListSize(_gfx())>0
    
    Repeat 
      _event.i=WaitWindowEvent(1)
      
      
      ResetList(_gfx())
      
       
      
      While NextElement(_gfx())
;         _gfx()\x=Random(4)
;         _gfx()\y=Random(-4)
        DisplayTransparentSprite(_gfx()\id,_gfx()\x,_gfx()\y,50)
       
      Wend
      
      FlipBuffers()
      
      
    Until _event.i=#PB_Event_CloseWindow
    
  EndIf
  
  
  
Else
  
  _ok.l=MessageRequester("ERROR","Can not load gfx",#PB_MessageRequester_Ok )
  
  
EndIf

End 
Last edited by darius676 on Mon Oct 14, 2019 8:59 pm, edited 4 times in total.
Joubarbe
Enthusiast
Enthusiast
Posts: 555
Joined: Wed Sep 18, 2013 11:54 am
Location: France

Re: Gaussian blur in real time?

Post by Joubarbe »

Thank you for your answers! Those pieces of code will be useful!
User avatar
Michael Vogel
Addict
Addict
Posts: 2666
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: Gaussian blur in real time?

Post by Michael Vogel »

Not sure if the following routines are from Wilbert or someone else, but they are quite fast - do quick check by loading an image of your preference...

Code: Select all

Procedure Gaussian1D(*PixelBuf32,NumPixels,NextPixelOffset=4)

	!mov ecx, [p.v_NumPixels]
	!sub ecx, 3
	!js gaussian1d_exit
	!mov eax, [p.v_NextPixelOffset]

	CompilerIf #PB_Compiler_Processor=#PB_Processor_x64
		!mov rdx, [p.p_PixelBuf32]
		!movd mm1, [rdx]
		!movd mm4, [rdx + rax]
	CompilerElse
		!mov edx, [p.p_PixelBuf32]
		!movd mm1, [edx]
		!movd mm4, [edx + eax]
	CompilerEndIf
	!punpcklbw mm1, mm1
	!punpcklbw mm4, mm4
	!psrlw mm1, 4
	!psrlw mm4, 4
	!movq mm2, mm1
	!movq mm3, mm1

	; loop
	!gaussian1d_loop0:
	CompilerIf #PB_Compiler_Processor=#PB_Processor_x64
		!movd mm5, [rdx + rax * 2]
	CompilerElse
		!movd mm5, [edx + eax * 2]
	CompilerEndIf
	!punpcklbw mm5, mm5
	!psrlw mm5, 4
	!gaussian1d_loop1:
	!movq mm0, mm2
	!paddw mm0, mm3
	!paddw mm0, mm4
	!psllw mm0, 2
	!paddw mm0, mm1
	!paddw mm0, mm3
	!paddw mm0, mm3
	!paddw mm0, mm5
	!psrlw mm0, 8
	!packuswb mm0, mm0
	CompilerIf #PB_Compiler_Processor=#PB_Processor_x64
		!movd [rdx], mm0
		!add rdx, rax
	CompilerElse
		!movd [edx], mm0
		!add edx, eax
	CompilerEndIf
	!movq mm1, mm2
	!movq mm2, mm3
	!movq mm3, mm4
	!movq mm4, mm5
	!dec ecx
	!jns gaussian1d_loop0
	!cmp ecx, -3
	!jne gaussian1d_loop1
	!gaussian1d_exit:
	!emms

EndProcedure
Procedure Gaussian2D(*PixelBuf32,Width,Height,BufferPitch=0)

	If BufferPitch=0
		BufferPitch=Width<<2
	EndIf

	Protected i.i=0
	While i<Height
		Gaussian1D(*PixelBuf32 + BufferPitch * i, Width)
		i+1
	Wend

	i=0
	While i<Width
		Gaussian1D(*PixelBuf32+i<<2,Height,BufferPitch)
		i+1
	Wend

EndProcedure
Procedure GaussianBlur(Image,Strength=2)

	Protected.i i,w,h,x,y,max_x,max_y

	;If CheckCompilerInformation()


	If StartDrawing(ImageOutput(image))
		w=OutputWidth()
		h=OutputHeight()

		If DrawingBufferPixelFormat() & $60
			For i=0 To Strength
				Gaussian2D(DrawingBuffer(),w,h,DrawingBufferPitch())
			Next

		Else
			max_x=w-1
			max_y=h-1

			Dim Buffer.l(max_y, max_x)
			DrawingMode(#PB_2DDrawing_AllChannels)

			For y=0 To max_y
				For x=0 To max_x
					Buffer(y,x)=Point(x,y)
				Next
			Next

			For i=0 To Strength
				Gaussian2D(@Buffer(),w,h)
			Next

			For y=0 To max_y
				For x=0 To max_x
					Plot(x,y,Buffer(y,x))
				Next
			Next
		EndIf
		StopDrawing()
	EndIf
	;EndIf

EndProcedure

#ScreenWidth  = 400
#ScreenHeight = 400


win = OpenWindow(0, 0, 0, #ScreenWidth, #ScreenHeight, "window")
ImageGadget(0,0,0,#ScreenWidth, #ScreenHeight,0)
UsePNGImageDecoder()
LoadImage(1,"c:\...\....png")
AddWindowTimer(0,0,100)

strength=200
Repeat

	Select WaitWindowEvent()
	Case #PB_Event_CloseWindow
		quit = #True
	EndSelect

	t=ElapsedMilliseconds()
	CopyImage(1,0)
	GaussianBlur(0,strength)
	t=ElapsedMilliseconds()-t

	SetGadgetState(0,ImageID(0))
	SetWindowTitle(0,Str(t)+" @"+Str(strength))

	If strength
		strength-2
	EndIf

Until quit
Joubarbe
Enthusiast
Enthusiast
Posts: 555
Joined: Wed Sep 18, 2013 11:54 am
Location: France

Re: Gaussian blur in real time?

Post by Joubarbe »

Yeah that one I know. Not fast enough in my opinion :)
#NULL
Addict
Addict
Posts: 1440
Joined: Thu Aug 30, 2007 11:54 pm
Location: right here

Re: Gaussian blur in real time?

Post by #NULL »

It can blur an 800x600 screen with 3 passes in ~40ms on my system.

Code: Select all

CompilerIf #PB_Compiler_Debugger
  If MessageRequester("debugger?", "the debugger is enabled. run anyway?", #PB_MessageRequester_YesNo) = #PB_MessageRequester_No
    End
  EndIf
CompilerEndIf


EnableExplicit

#ScreenWidth  = 800
#ScreenHeight = 600
;#ScreenWidth  = 1680
;#ScreenHeight = 1050

InitSprite()
InitKeyboard()
InitMouse()
ElapsedMilliseconds()

Structure Pixel
  r.a
  g.a
  b.a
EndStructure

Define win, quit, mode, mode$
Define spr, i
Define sprFR, FR, FRt, FR$
Define time, timeTmp

win = OpenWindow(#PB_Any, 0, 0, #ScreenWidth, #ScreenHeight, "window")
OpenWindowedScreen(WindowID(win), 0, 0, #ScreenWidth, #ScreenHeight)

UsePNGImageDecoder()
CompilerIf #PB_Compiler_OS = #PB_OS_Windows
  Define spr = LoadSprite(#PB_Any, #PB_Compiler_Home + "examples/3D/Data/PureBasic3DLogo.png") ; windows
CompilerElse
  Define spr = LoadSprite(#PB_Any, #PB_Compiler_Home + "examples/3d/Data/PureBasic3DLogo.png") ; linux
CompilerEndIf

CompilerIf #PB_Compiler_OS = #PB_OS_Windows
  Define font = LoadFont(#PB_Any, "courier new", 10)
CompilerElse
  Define font = LoadFont(#PB_Any, "monospace", 10)
CompilerEndIf

sprFR = CreateSprite(#PB_Any, 400, 60)

mode = 3

Procedure Gaussian1D(*PixelBuf32,NumPixels,NextPixelOffset=4)

   !mov ecx, [p.v_NumPixels]
   !sub ecx, 3
   !js gaussian1d_exit
   !mov eax, [p.v_NextPixelOffset]

   CompilerIf #PB_Compiler_Processor=#PB_Processor_x64
      !mov rdx, [p.p_PixelBuf32]
      !movd mm1, [rdx]
      !movd mm4, [rdx + rax]
   CompilerElse
      !mov edx, [p.p_PixelBuf32]
      !movd mm1, [edx]
      !movd mm4, [edx + eax]
   CompilerEndIf
   !punpcklbw mm1, mm1
   !punpcklbw mm4, mm4
   !psrlw mm1, 4
   !psrlw mm4, 4
   !movq mm2, mm1
   !movq mm3, mm1

   ; loop
   !gaussian1d_loop0:
   CompilerIf #PB_Compiler_Processor=#PB_Processor_x64
      !movd mm5, [rdx + rax * 2]
   CompilerElse
      !movd mm5, [edx + eax * 2]
   CompilerEndIf
   !punpcklbw mm5, mm5
   !psrlw mm5, 4
   !gaussian1d_loop1:
   !movq mm0, mm2
   !paddw mm0, mm3
   !paddw mm0, mm4
   !psllw mm0, 2
   !paddw mm0, mm1
   !paddw mm0, mm3
   !paddw mm0, mm3
   !paddw mm0, mm5
   !psrlw mm0, 8
   !packuswb mm0, mm0
   CompilerIf #PB_Compiler_Processor=#PB_Processor_x64
      !movd [rdx], mm0
      !add rdx, rax
   CompilerElse
      !movd [edx], mm0
      !add edx, eax
   CompilerEndIf
   !movq mm1, mm2
   !movq mm2, mm3
   !movq mm3, mm4
   !movq mm4, mm5
   !dec ecx
   !jns gaussian1d_loop0
   !cmp ecx, -3
   !jne gaussian1d_loop1
   !gaussian1d_exit:
   !emms

EndProcedure
Procedure Gaussian2D(*PixelBuf32,Width,Height,BufferPitch=0)

   If BufferPitch=0
      BufferPitch=Width<<2
   EndIf

   Protected i.i=0
   While i<Height
      Gaussian1D(*PixelBuf32 + BufferPitch * i, Width)
      i+1
   Wend

   i=0
   While i<Width
      Gaussian1D(*PixelBuf32+i<<2,Height,BufferPitch)
      i+1
   Wend

EndProcedure
Procedure GaussianBlur(Image,Strength=2)

   Protected.i i,w,h,x,y,max_x,max_y

   ;If CheckCompilerInformation()


   ;If StartDrawing(ImageOutput(image))
      w=OutputWidth()
      h=OutputHeight()
      
      If DrawingBufferPixelFormat() & $60
         For i=0 To Strength
            Gaussian2D(DrawingBuffer(),w,h,DrawingBufferPitch())
         Next

       Else
         max_x=w-1
         max_y=h-1

         Dim Buffer.l(max_y, max_x)
         DrawingMode(#PB_2DDrawing_AllChannels)

         For y=0 To max_y
            For x=0 To max_x
               Buffer(y,x)=Point(x,y)
            Next
         Next
         
         For i=0 To Strength
            Gaussian2D(@Buffer(),w,h)
         Next

         For y=0 To max_y
            For x=0 To max_x
               Plot(x,y,Buffer(y,x))
            Next
         Next
      EndIf
      ;StopDrawing()
   ;EndIf
   ;EndIf

EndProcedure

Repeat
  ExamineMouse()
  ExamineKeyboard()
 
  If KeyboardPushed(#PB_Key_Escape) : quit = #True : EndIf
  If KeyboardPushed(#PB_Key_1) : mode = 1 : EndIf
  If KeyboardPushed(#PB_Key_2) : mode = 2 : EndIf
  If KeyboardPushed(#PB_Key_3) : mode = 3 : EndIf
 
  While WindowEvent()
    Select Event()
      Case #PB_Event_CloseWindow
        quit = #True
    EndSelect
  Wend
 
  FR + 1
  If ElapsedMilliseconds() > FRt
    time / FR
   
    Select mode
      Case 1 : mode$ = "empty drawing block"
      Case 2 : mode$ = "base"
      Case 3 : mode$ = "asm gauss"
    EndSelect
   
    FRt = ElapsedMilliseconds() + 500
    FR$ = "fps:" + Str(FR * 2)
    FR = 0
    StartDrawing(SpriteOutput(sprFR))
      DrawingMode(#PB_2DDrawing_Transparent)
      Box(0, 0, OutputWidth(), OutputHeight(), $0)
      DrawingFont(FontID(font))
      DrawText(0, 0, FR$)
      DrawText(0, 20, "mode: " + mode + " (" + mode$ + ")")
      DrawText(0, 40, "time: " + time + "ms")
    StopDrawing()
  EndIf
 
  ClearScreen($333333)
 
  DisplayTransparentSprite(spr, MouseX(), MouseY())
 
  timeTmp = ElapsedMilliseconds()
 
  If StartDrawing(ScreenOutput())
   
    If mode >= 2
     
      Define buffer      = DrawingBuffer()
      Define pitch       = DrawingBufferPitch()
      Define pixelFormat = DrawingBufferPixelFormat()
      Define pixelSize
;       Debug pixelFormat
;       Debug pixelFormat!#PB_PixelFormat_ReversedY
;       Debug #PB_PixelFormat_24Bits_RGB
;       Debug #PB_PixelFormat_32Bits_BGR
;       End
      If pixelFormat & (#PB_PixelFormat_24Bits_BGR | #PB_PixelFormat_24Bits_RGB)
        pixelSize = 3
      ElseIf pixelFormat & (#PB_PixelFormat_32Bits_BGR | #PB_PixelFormat_32Bits_RGB)
        pixelSize = 4
      Else
        CloseScreen()
        CloseWindow(win)
        MessageRequester("error", "pixel format not supported")
        End
      EndIf
      If pixelSize
       
        Define yStart = #ScreenHeight * 0.25
        Define yEnd   = #ScreenHeight * 0.75
        Define xStart = #ScreenWidth  * 0.25
        Define xEnd   = #ScreenWidth  * 0.75
       
        DrawingMode(#PB_2DDrawing_Outlined)
        Box(xStart - 20, yStart - 20, xEnd - xStart + 40, yEnd - yStart + 40, $ff00ff00)
       
        Define buffer2
        If Not buffer2
          buffer2 = AllocateMemory(pitch * OutputHeight())
        EndIf
       
        If mode >= 3
         
          Select mode
            Case 3
              Define strength = 3
              GaussianBlur(1, strength)
          EndSelect
         
        EndIf
      EndIf
    EndIf
   
    StopDrawing()
  EndIf
 
  time = time + (ElapsedMilliseconds() - timeTmp)
 
  DisplaySprite(sprFR, MouseX(), MouseY() + 80)
 
  FlipBuffers()
 
Until quit
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Gaussian blur in real time?

Post by wilbert »

Joubarbe wrote:Yeah that one I know. Not fast enough in my opinion :)
How much faster do you need it to be ?
You can also think the other way around. First decide how much time the blur is allowed to take and then see what you can accomplish in that time.
A one directional blur (only horizontal or vertical) would be faster and if you have an example image of how much blur you want, it might also be possible to blur more in one pass so you don't need 3 passes.
Windows (x64)
Raspberry Pi OS (Arm64)
Joubarbe
Enthusiast
Enthusiast
Posts: 555
Joined: Wed Sep 18, 2013 11:54 am
Location: France

Re: Gaussian blur in real time?

Post by Joubarbe »

The look I'd like to obtain is in my first post, the link to Everspace 2. That's pretty blurry. 40ms in a real time game is out of the question, but I'm trying some stuff right now, "pre-blurring" the elements that have a chance to go behind the UI. I don't have a choice anyway, that's either that, or shaders (or give up on this idea).

"blur more in one pass" you said, how so? (I know nothing about ASM)

(and thanks for your code anyway wilbert, it's been useful to me in the past :) )
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Gaussian blur in real time?

Post by wilbert »

Joubarbe wrote:The look I'd like to obtain is in my first post, the link to Everspace 2. That's pretty blurry. 40ms in a real time game is out of the question, but I'm trying some stuff right now, "pre-blurring" the elements that have a chance to go behind the UI. I don't have a choice anyway, that's either that, or shaders (or give up on this idea).
The link you posted seems to freeze the background image when the menu appears.
In that case you only have to blur it once.
Joubarbe wrote:"blur more in one pass" you said, how so? (I know nothing about ASM)
If you take another approach (like a low pass filter), you can make the strength of the blur independent of the running time of the procedure.
It would require different code but you would be able to create a highly blurred image without a lot of extra time.
Windows (x64)
Raspberry Pi OS (Arm64)
Joubarbe
Enthusiast
Enthusiast
Posts: 555
Joined: Wed Sep 18, 2013 11:54 am
Location: France

Re: Gaussian blur in real time?

Post by Joubarbe »

Is there such code in PB somewhere?
User avatar
STARGÅTE
Addict
Addict
Posts: 2067
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: Gaussian blur in real time?

Post by STARGÅTE »

You can try our UB2D - 2D Physically Based Rendering Engine.
In this video you can actually see the blurred scene behind the GUI: https://youtu.be/_luehiIpetU?t=182
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
Joubarbe
Enthusiast
Enthusiast
Posts: 555
Joined: Wed Sep 18, 2013 11:54 am
Location: France

Re: Gaussian blur in real time?

Post by Joubarbe »

Wow I forgot about this one. Seeing that it's still pre-alpha, is it safe to use?
A shame this seems... discontinued? It's by far the most promising 2D engine I've seen on PB.

(32bits only, hmm... No way to have the source code of the blur part I suppose? - really don't feel good using a whole alpha engine for just one library)
(I'm also of the opinion of Artus about Godot. Would have been easier to play with predefined shaders)

EDIT: Could you explain briefly what is the method you used for this blur behind the GUI?
Last edited by Joubarbe on Mon Oct 14, 2019 7:24 am, edited 2 times in total.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Gaussian blur in real time?

Post by wilbert »

Joubarbe wrote:Is there such code in PB somewhere?
I could try to create it if you wish; it might benefit others as well.
I believe I can make it have a strong blur and at the same time be faster as my gaussian blur procedure at its lowest strength.
The only thing is that the code will be longer and a bit more complicated for me to code so it might take a few days to get it right.

You could also check out thenOpenCV examples by JHPJHP.
OpenCV is also capable of blurring.
Windows (x64)
Raspberry Pi OS (Arm64)
Post Reply