How to create a surface mirror / surface distortion effect?

Started by Grisu, October 04, 2020, 11:47:00

Previous topic - Next topic

Grisu

Hi everyone,

I'm tinkering around with another spectrum analyser. I uploaded a temp zip file, so you can check it out.




'---------------------------------------
' Mirror Spectrum Test by Grisu
'---------------------------------------
    SuperStrict
     
    Framework MaxGui.Drivers
    Import BRL.GLMax2D
    Import BRL.StandardIO
    Import BRL.Pixmap
    Import BRL.EventQueue
    Import BRL.Timer
    Import BRL.Retro
    Import BRL.Max2D
    Import BRL.Pngloader
    Import BRL.Jpgloader
Import Bah.FMOD   ' Brucey's Mega Warpper

    Const MAINWINDOW_W:Int = 278
    Const MAINWINDOW_H:Int = 154
    Const WIN_MENU_FLAG:Int = 0
     
    Global SpectrumTimer:TTimer=CreateTimer(24) ' Canvas refresh update timer
     
' FMOD EX -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------       
Global playing:Int
Global stationsound:TFMODSound
Global system:TFMODSystem = New TFMODSystem.Create() ' <- FModEX Init!
Global stationchannel:TFMODChannel

Global result:Int=system.Init(32, FMOD_INIT_NORMAL)
If result <> FMOD_OK Then
End
End
EndIf

    Global station_url:String
    Global Spec_BackgroundIMG:TImage

    Global Spectrum:Float[64]

SeedRnd MilliSecs()
Local seed:Float=RndSeed()

    Global i:Int=Rand(0,2) ' Select one station from pool and logo

    Select i
    Case 0
    station_url:String = "http://hd.stream.frequence3.net/frequence3-256.mp3"  ' F3
'     Spec_BackgroundIMG:TImage = LoadImage("station_ico0.png", MIPMAPPEDIMAGE)
    Case 1
    station_url:String = "http://sc-80s.1.fm:8097"  ' 1-FM
'     Spec_BackgroundIMG:TImage = LoadImage("station_ico1.png", MIPMAPPEDIMAGE)
    Case 2
    station_url:String = "http://streamhq.top100station.com/top100station-high.mp3"  ' Top 100
'     Spec_BackgroundIMG:TImage = LoadImage("station_ico2.png", MIPMAPPEDIMAGE)
    End Select

    Print "Connecting to "+station_url+" ..."
    stationsound:TFMODSound = system.CreateStreamURL(station_url, FMOD_DEFAULT | FMOD_CREATESTREAM)
    Delay(300)

    Print "Start Streaming..."
    stationchannel = system.PlaySound(FMOD_CHANNEL_FREE,stationsound,False) ' start streaming
    stationchannel.SetVolume(0.35)

' Main Window -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------       
   
    Global flags:Int=WINDOW_TITLEBAR | WINDOW_CLIENTCOORDS | WINDOW_CENTER | WIN_MENU_FLAG | WINDOW_ACCEPTFILES | WINDOW_RESIZABLE
    Global window:TGadget=CreateWindow("Mirror Test #07",0,0,MAINWINDOW_W,MAINWINDOW_H+70,Null, flags)

    Global Button1:TGadget=CreateButton("",2,MAINWINDOW_H+70-40,MAINWINDOW_W-3,20,window,BUTTON_OK)
    Global Button2:TGadget=CreateButton("Exit APP",2,MAINWINDOW_H+70-20,MAINWINDOW_W-3,20,window,BUTTON_OK)
     
    Global RadioStationCanvas:TGadget = CreateCanvas(2, 2, 274, 87, window, PANEL_ACTIVE)
   
    ' size of each plate
    Const PlateSize_W:Int=4
    Const PlateSize_H:Int=4
    Const PlateSpace:Int=2

    Global Plate_Grid_CenterX_tmp:Int=Plate_Grid_CenterX
    Global Plate_colorset:Int=Rand(0,8) 'set a custom color skin 1-5

    Const Plate_Grid_CenterX  :Int=137
    Const Plate_Grid_CenterX_L:Int=137-PlateSize_W-PlateSpace
    Const Plate_Grid_CenterX_R:Int=137+PlateSize_W+PlateSpace
    Const Plate_Grid_CenterY  :Int=44

'    Global MirrorBg_IMG       :TImage ' gradient image for the mirror surface

    Mirror_Init()       

' Main Loop -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------       
     
    While WaitEvent()
        Select EventID()
            Case EVENT_WINDOWCLOSE
                End
            Case EVENT_GADGETACTION
                Select EventSource()

                    Case BUTTON1
                         Mirror_Init()       

                    Case BUTTON2
                        End

                End Select
     
            Case EVENT_TIMERTICK
     
                Select EventSource()
                    Case SpectrumTimer ' Update Canvas

                        SetGraphics CanvasGraphics( RadioStationCanvas )
                        Cls     

                        ' Get Spectrum data and draw left & right
        If stationchannel.GetSpectrum(spectrum , 0 , FMOD_DSP_FFT_WINDOW_TRIANGLE) = FMOD_OK Then DrawSpectrumL_Mirror(spectrum)
                If stationchannel.GetSpectrum(spectrum , 1 , FMOD_DSP_FFT_WINDOW_TRIANGLE) = FMOD_OK Then DrawSpectrumR_Mirror(spectrum)   

                        ' Create Mirror Surface
    Draw_Plate_Reflection()

                       Flip 0
                End Select
        End Select
    Wend
    End

' End Nain Loop---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


' Mirror Grid  ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Function Set_Plate_Color(Plate_colorset:Int,Plate_col:Int)
' Set up a custom color palette for variation
    Select Plate_colorset
    Case 1
   
    Select Plate_col ' Purple set

       Case 1
        SetColor 55,30,105 'Darkest Purple
       Case 2
        SetColor 40,95,150 'Darker Purple
       Case 3
        SetColor 140,0,175 'Dark Purple
       Case 4
        SetColor 207,0,255 'Mid Purple
       Case 5
        SetColor 220,87,250 'Light Purple
       Case 6
        SetColor 230,130,242 'Lighter Purple
       Case 7
        SetColor 255,85,155 'Lighter Purple redish

     End Select ' Current Plate_col

    Case 2

    Select Plate_col ' Red / Orange set

       Case 1
        SetColor 150,0,60    'Darker Red
       Case 2
        SetColor 170,0,70    'Dark Red
       Case 3
        SetColor 222,2,90    'Dark Redish
       Case 4
        SetColor 238,44,35   'Dark Orange
       Case 5
        SetColor 255,42,42   'Mid Orange
       Case 6
        SetColor 255,105,5   'Orange
       Case 7
        SetColor 248,148,80   'Lighter Orange

     End Select ' Current Plate_col

    Case 3

    Select Plate_col ' Yellow / Brown set

       Case 1
        SetColor 130,55,0  'Darkest Brown
       Case 2
        SetColor 180,77,0  'Dark Brown
       Case 3
        SetColor 255,115,15  'Orange
       Case 4
        SetColor 251,251,30  'Bright Yellow
       Case 5
        SetColor 254,211,46  'Yellow
       Case 6
        SetColor 234,225,107 'Light Yellow
       Case 7
        SetColor 255,254,177 'Lightest Yellow

     End Select ' Current Plate_col

    Case 4

    Select Plate_col ' Greenish set

       Case 1
        SetColor 0,100,0     'Darkest Green
       Case 2
        SetColor 5,150,5     'Dark Green
       Case 3
        SetColor 2,199,5     'Light Green
       Case 4
        SetColor 104,255,170  'Green
       Case 5
        SetColor 35,210,5    'Olive Green
       Case 6
        SetColor 100,255,6   'Light Olive Green
       Case 7
        SetColor 175,255,105 'Lightest Olive Green

     End Select ' Current Plate_col

    Case 5

    Select Plate_col ' Blue / Cyan set

       Case 1
        SetColor 0,0,110     'Dark Blue
       Case 2
        SetColor 5,5,200     'Ocean
       Case 3
        SetColor 5,114,225   'Sky Blue 
       Case 4
        SetColor 3,182,148   'Light Sky Blue 
       Case 5
        SetColor 77,227,247  'Light Sky Blue 
       Case 6
        SetColor 90,230,255  'Lightest Sky Blue 
       Case 7
        SetColor 200,255,250 'White like Blue

     End Select ' Current Plate_col

    Case 6

    Select Plate_col ' Retro RGB set

       Case 1
        SetColor 0,250,0     'Green
       Case 2
        SetColor 145,255,0   'Green
       Case 3
        SetColor 255,255,0   'Yellow
       Case 4
        SetColor 255,255,0   'Yellow
       Case 5
        SetColor 250,170,0    'Orange
       Case 6
        SetColor 255,0,0     'Red
       Case 7
        SetColor 255,0,0     'Red

     End Select ' Current Plate_col

    Case 7

    Select Plate_col ' Retro RGB set

       Case 1
        SetColor 135,25,255     'Purple light
       Case 2
        SetColor 255,20,220     'Purple
       Case 3
        SetColor 30,20,255      'Blue
       Case 4
        SetColor 30,20,255      'Blue
       Case 5
        SetColor 20,135,255     'Blue Light
       Case 6
        SetColor 20,135,255     'Blue Light
       Case 7
        SetColor 15,240,250     'Cyan

     End Select ' Current Plate_col

    Case 8

    Select Plate_col ' Xmas Chicken

       Case 1
        SetColor 50,55,25       'Green2
       Case 2
        SetColor 90,105,30      'Green1
       Case 3
        SetColor 115,15,20      'Red2
       Case 4
        SetColor 240,50,50      'Red
       Case 5
        SetColor 240,200,100
       Case 6
        SetColor 170,110,35    ' Brownish
       Case 7
        SetColor 170,110,35    ' Brownish

     End Select ' Current Plate_col

     End Select ' Plate_colorset

End Function

Function Mirror_Init()
' Init all

' Just circle through all coloursets when button is pressed
Local i:Int
Plate_colorset=Plate_colorset+1
If Plate_colorset > 8 Then Plate_colorset=1
If Plate_colorset < 1 Then Plate_colorset=8

Rem     
Repeat
   i:Int=Rand(1,7) ' Force change each time function is called.
Until i <> Plate_colorset
'    Plate_colorset:Int=i
End Rem

    SetGadgetText(Button1, "Change Colourset [0"+Plate_colorset+"/08]")
    Print "Colourset: "+Plate_colorset

End Function

Rem
Function RotatePixmap:TPixmap(Source:TPixmap,Rotation:Int)
Local Pixmap:TPixmap

Select Rotation

Case 90
Pixmap = TPixmap.Create(Source.Height,Source.Width,Source.Format)
For Local x:Int = 0 Until Source.Width
For Local y:Int = 0 Until Source.Height
WritePixel(Pixmap,Source.Height-y-1,x,ReadPixel(Source,x,y))
Next
Next

Case 180
Pixmap = TPixmap.Create(Source.Width,Source.Height,Source.Format)
For Local x:Int = 0 Until Source.Width
For Local y:Int = 0 Until SOurce.Height
WritePixel(Pixmap,Source.Width-x-1,Source.Height-y-1,ReadPixel(Source,x,y))
Next
Next

Case 270
Pixmap = TPixmap.Create(Source.Height,Source.Width,Source.Format)
For Local x:Int = 0 Until Source.Width
For Local y:Int = 0 Until Source.Height
WritePixel(Pixmap,y,Source.Width-x-1,ReadPixel(Source,x,y))
Next
Next

Default
Return Source.Copy()

End Select
Return Pixmap

End Function
End Rem

Function Draw_Plate_Reflection()
' Create a fake Mirror surface below horizon line

Local Plate_Mirror_Pixmap:TPixmap=CreatePixmap(274,42,PF_RGBA8888) ' ~ half the canvas height

SetColor 255,255,255     
Plate_Mirror_Pixmap=GrabPixmap(0,0,274,42)
DrawPixmap YFlipPixmap(Plate_Mirror_Pixmap),0,Plate_Grid_CenterY+1

SetBlend ALPHABLEND
SetAlpha  0.5

' Draw Mirror Gradient
For Local k:Int=1 To 42
Local g:Int=85-k*3
  SetColor g,g,g
  DrawRect 1,Plate_Grid_CenterY-1+k,272,1
Next

Rem
'SetColor 75,75,75
'DrawRect 1,Plate_Grid_CenterY,272,42
End Rem

SetBlend SOLIDBLEND
SetAlpha  1.0

'Draw center grid lines as orientation
'  SetColor 205,205,205     
'  DrawLine 1,Plate_Grid_CenterY,272,Plate_Grid_CenterY ' Horizontal line fixed
'  DrawLine 137,Plate_Grid_CenterY,137,85 ' Vertical line (cemter) fixed

End Function

Function Draw_Plates_Up_L(Plate_Grid_CenterX:Int, Plate_Hmax:Int)
     For Local k:Int=1 To Plate_Hmax
       Set_Plate_Color(Plate_colorset,k)
       DrawRect Plate_Grid_CenterX-(PlateSpace+PlateSize_W), Plate_Grid_Centery-k*(PlateSpace+PlateSize_H),PlateSize_H,PlateSize_W
     Next   
End Function

Function Draw_Plates_Up_R(Plate_Grid_CenterX:Int, Plate_Hmax:Int)
     For Local j:Int=1 To Plate_Hmax
       Set_Plate_Color(Plate_colorset,j)
       DrawRect Plate_Grid_CenterX+(PlateSpace+PlateSize_W), Plate_Grid_Centery-j*(PlateSpace+PlateSize_H),PlateSize_H,PlateSize_W
     Next   
End Function

Function DrawSpectrumL_Mirror(_spectrum:Float[])
Local icol:Int=1
Local height_L:Int
        Plate_Grid_CenterX_tmp:Int=Plate_Grid_CenterX_L

For Local f_val:Float = EachIn _spectrum
If icol < 17 Then ' ' max number of columns
            height_L = Int(f_val*70)       
'           height_L=7 'Max it out every time for testing
'           If height_L <>0 Then Print height_L
            If height_L > 7 Then height_L=7 
        Plate_Grid_CenterX_tmp=Plate_Grid_CenterX_L-icol*(PlateSpace+PlateSize_W) ' 1 step left
Draw_Plates_Up_R(Plate_Grid_CenterX_tmp+(PlateSpace/2), height_L)
            icol:Int=icol+1
           Else
            Exit
           EndIf
Next

End Function

Function DrawSpectrumR_Mirror(_spectrum:Float[])
Local icol:Int=1
Local height_R:Int
        Plate_Grid_CenterX_tmp:Int=Plate_Grid_CenterX_R
       
For Local f_val:Float = EachIn _spectrum
If icol < 17 Then ' max number of columns
            height_R = Int(f_val*70)       
'           height_R=7
'           If height_R <> 0 Then Print height_R
            If height_R > 7 Then height_R=7
Draw_Plates_Up_L(Plate_Grid_CenterX_tmp+(PlateSpace/2), height_R)
        Plate_Grid_CenterX_tmp=Plate_Grid_CenterX_R+icol*(PlateSpace+PlateSize_W) ' 1 step left
            icol:Int=icol+1
           Else
            Exit
           EndIf
Next
End Function
' End Mirror Grid  ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Download (full bmx code, exe, dll and bah/fmod.mod):
http://www.mediafire.com/file/p5vbyotgpsrq8na/PRP_Mirror.zip/file

In the version 07, I created a simple fake surface mirror.

- I'm wondering, if the results could be improved somehow? Would be great to make it more realistic / visually interesting for a user. Adding an angle effect somehow?
- I'd love to give several different options for the surface as well. Does anyone have some water animation / distortion code that might fit here (water surface effect...).

I'm only working on a small canvas (274x87?. So it should be doable without performance issues.

Thanks in advance
Grisu
Pocket Radio Player     Cardwar

_PJ_

Hi,
Since it's so small, I suspect it might be quick enough to grab the "real" portion and duplicate to another image buffer. You can then FLIP this (VERTICAL flip over X axis) to generate the mirrored version.

It seems you've done this already :)
Plate_Mirror_Pixmap=GrabPixmap(0,0,274,42)
DrawPixmap YFlipPixmap(Plate_Mirror_Pixmap),0,Plate_Grid_CenterY+1


Not sure what you specifically mean with 'angle' but you can then use the shearing functions to slant this image to the sides.

As for "water effects" - again, it's such a small image, that it should be very quick to:
instead of grab the entire block image, instead, take a sample of each horizontal line of pixels and put these into a bank or list or something (not be overkill to  to use image buffer 1 x Y res images ).

You then draw each of these at a slight horizontal offset calculated by a sinusoidal function (scaled up, since -1 to 1 will not be very appealing) and limited by millsecod timing or something to keep it smooth.


Grisu

I'm trying to create a more natural mirror effect like this from Neon Abyss:


I draw the fake mirror line by line (y) and displace it a bit (x value):


Compared to my example above it looks lame...  :'(




Pocket Radio Player     Cardwar

Derron

Choose a part in your example/reference and watch how it animates ... the pixels move in a sine wave (or similar)

In addition they have "wave heights" (see the head - it is appearing "split" in some frames - as the wave hills/tops and valleys/downs cover each other).

But to make it look "more convicing" you might just need to have something "more" to mirror than your equalizer bars.
So  you might have a full grid of "gray" rects where no "colored rect" is. And ALL rects are mirrored - so that the mirror effect is easier to see than with 10 little rects being distorted for an "ocean effect".

Instead of "gray bars" you can do whatever you like. Eg you could have stars in the background, a moon which is "moving" (circulating) ... maybe even have a dusk/dawn change and a sun which replaces the moon etc.
(I have such stuff in my game TVTower ... so you might scrap through my sourcecodes - on github - to uhm... "collect ideas" on how to transition between the colors etc).

Edit: I made a short video of the day night cycle in the game (it also has moon phases and short nights during winter etc ... so ...check the code if you want). Excuse some font errors, am currently rewriting stuff.

[youtube width=800]https://youtu.be/s1QjwRvzHsc[/youtube]

The gradient which you see is a simple gradient-overlay, so no "for loop" or so.

bye
Ron

Grisu

Thanks for your input. Yeah, its even more difficult to work on such a small screen space.

For the full grid idea... does this look any better?

Pocket Radio Player     Cardwar

Derron

Shouldn't the reflexive surface "wobble" (water...)?

Bye
Ron

Grisu

Yeah, but I haven't found a way to make it look good on the eyes.  :-X
Pocket Radio Player     Cardwar

col

To show the effects as water waves maybe you could try add some wave crests?
https://github.com/davecamp

"When you observe the world through social media, you lose your faith in it."

Grisu

Pocket Radio Player     Cardwar