Interpolation per Palette (Grafikspielerei)

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Kekskiller
Beiträge: 752
Registriert: 14.09.2004 21:39
Kontaktdaten:

Interpolation per Palette (Grafikspielerei)

Beitrag von Kekskiller »

Basierend auf diesen Thread habe ich r7mk4 einen Interpolationsalghorythmus entworfen, der auf "Palettenbasis" arbeitet. Der Funktion kann also ein Array mit indexierten Farben übergeben werden und es interpretiert den Index als interpolierbaren Wert (beginnend ab 0).

Das ganze läuft mit über 60 Frames auf 640x480-Bildschirm auf beim 3.0 Ghz Pentium 4 flüsig. Ab 800x600 gehts in Richung 32 Frames, dennoch bietet sich auch die "Autostretch-Variante" an, mit der ein WindowedScreen verdoppelt wird und dadurch der fehlende Detailgrad ausgeglichen wird.

Schauts euch einfach mal an.
Hab oben einen Frame-Begrenzer eingebaut, damit der Effekt auch sichtbar wird ;) ...

Läuft nur auf 8bit, 24bit u. 32bit (war zu faul 16 bit auch noch zu zerhacken...)
8bit verwendet direkt die Screen-Palette.

Code: Alles auswählen

; Interpolation auf Palettenbasis
; (c) Max 'Kekskiller' Beutling

#IMAGE_SIZE_DIVIDER = 1 ;weder teiler für die bildhöhe -> je höher desto schneller, aber auch desto geblurrter...
#MAX_FRAMES = 17 ;anzahl der maximalen frames

;------------------------------------------------------------------------------------------------------------------------------


; fenster öffnen...
InitSprite()
OpenWindow(0, 0,0, 640,480, "interpolation test", #PB_Window_ScreenCentered|#PB_Window_TitleBar|#PB_Window_SystemMenu)
OpenWindowedScreen(WindowID(0), 0,0, WindowWidth(0)/#IMAGE_SIZE_DIVIDER,WindowHeight(0)/#IMAGE_SIZE_DIVIDER, 1,0,0)
AddKeyboardShortcut(0, #PB_Shortcut_F1, 0)
AddKeyboardShortcut(0, #PB_Shortcut_F2, 1)


; enstellungen...
Global field_w.w, field_h.w, block_width.w, block_height.w
field_w = 64
field_h = 48
block_width = WindowWidth(0) / #IMAGE_SIZE_DIVIDER / field_w
block_height = WindowHeight(0) / #IMAGE_SIZE_DIVIDER / field_h


; vorbereiten...
Global Dim field.l(field_w-1,field_h-1)


; einzelne farben -> unabhängig von der anzahl der farben hat die funktion weiterhin konstante geschwindigkeit. so kann man theoretisch ganze
                    ;farbkombinationen vorberechnen, um sie später schneller einzuzeichnen!
Global gradient_color_count.c, gradient_presets.c
gradient_presets = 4
Global Dim gradient_color_count(gradient_presets)
Global Dim gradient_colors.l(gradient_presets-1, 20) ;temporär mal zwanzig, is ja nur nen test...

; nette farben!
gradient_color_count(0) = 9
gradient_colors(0, 0) = $00ff00
gradient_colors(0, 1) = $00ffab
gradient_colors(0, 2) = $00ffff
gradient_colors(0, 3) = $00abff
gradient_colors(0, 4) = $0000ff
gradient_colors(0, 5) = $ab00ff
gradient_colors(0, 6) = $ff00ff
gradient_colors(0, 7) = $ff00ab
gradient_colors(0, 8) = $ff0000

; wetterkarte (oder so in diese richtung!)
gradient_color_count(1) = 5
gradient_colors(1, 0) = $00ff00
gradient_colors(1, 1) = $00ffab
gradient_colors(1, 2) = $00ffff
gradient_colors(1, 3) = $00abff
gradient_colors(1, 4) = $0000ff

; plasmastream
gradient_color_count(2) = 7
gradient_colors(2, 0) = $ff0000
gradient_colors(2, 1) = $ff5500
gradient_colors(2, 2) = $ffaa00
gradient_colors(2, 3) = $ffee00
gradient_colors(2, 4) = $ffff00
gradient_colors(2, 5) = $ffabab
gradient_colors(2, 6) = $ff00ff

; schwarz, grau, weiß
gradient_color_count(3) = 3
gradient_colors(3, 0) = $000000
gradient_colors(3, 1) = $898989
gradient_colors(3, 2) = $ffffff

Global current_preset.c
current_preset = 0

;farbveränderungsmodi
Global current_mode.c, modes.c
current_mode = 1
modes = 3


;------------------------------------------------------------------------------------------------------------------------------

; strukturen
Structure CHAR
  c.c
EndStructure

Structure RGB
  r.c
  g.c
  b.c
EndStructure

Structure RGBA
  r.c
  g.c
  b.c
  a.c
EndStructure

Structure interpol_pixel
  StructureUnion
    c.CHAR
    rgb.RGB
    rgba.RGBA
  EndStructureUnion
EndStructure


; adressen/größen u. procs fürs rendern
Global *_interpol_buffer.l
Global _interpol_pitch.l
Global _interpol_bytes.c
Global _interpol_format.c

Procedure GetInterpolationBuffer() ; <- sollte vor jeder renderschleife aufgerufen werden, am besten direkt nach jedem startdrawing..
  *_interpol_buffer = DrawingBuffer()
  _interpol_pitch = DrawingBufferPitch()
  _interpol_format = DrawingBufferPixelFormat()
  Select _interpol_format
    Case #PB_PixelFormat_8Bits      : _interpol_bytes = 1
    Case #PB_PixelFormat_15Bits     : _interpol_bytes = 2
    Case #PB_PixelFormat_16Bits     : _interpol_bytes = 2
    Case #PB_PixelFormat_24Bits_RGB : _interpol_bytes = 3
    Case #PB_PixelFormat_24Bits_BGR : _interpol_bytes = 3
    Case #PB_PixelFormat_32Bits_RGB : _interpol_bytes = 4
    Case #PB_PixelFormat_32Bits_BGR : _interpol_bytes = 4 
  EndSelect
EndProcedure


; ~~ Die Prozedur ~~
; Parameter:
; x.w          = x-position
; y.w          = y-position
; w.w          = breite
; h.w          = höhe
; *colors.RGBA = pointer auf array mit farbwerten, kann gan popeliges array mit long-werten sein
;                hauptsache die index-nummern der farben gehen nicht über die anzahl der element hinaus!
; col1.c       = index der oberen, linken farbe
; col2.c       = index der oberen, rechten farbe
; col3.c       = index der unteren, rechten farbe
; col4.c       = index der unteren, linken farbe
Procedure DrawInterpolatedBox(x.w,y.w, w.w,h.w, *colors.RGBA, col1.c, col2.c, col3.c, col4.c)
  
  Protected zx.l, zy.l
  
  ; brauchen hier nur die index-übergangswerte berechnen -> ist halt nur 1 wert fürn index
  Protected g1to2.f, g3to4.f, g4to1.f
  g1to2 = (col2 - col1) / w
  g3to4 = (col3 - col4) / w
  g4to1 = (col4 - col1) / h
  
  ; für schreiben der werte
  Protected *drawpos.interpol_pixel, linepush.l ; der "zeilenvorschub" für eine pixelzeile, sprich die anzahl pixel zum nächsten zeilenanfang
  *drawpos = *_interpol_buffer + y * _interpol_pitch + x * _interpol_bytes ; pointer auf 1. pixel setzen
  linepush = _interpol_pitch - _interpol_bytes * w ; zeilenvorschub errechnen (pixelzeilenlänge - breite einer pixelzeile des interpolationsrechteckes)
  
  Protected *destcol.RGBA
  Protected destcol_index_float.f
  Protected a1.f, a2.f
  Protected index_step.f
  Protected destcol_index.w ;ACHTUNG: beschissenerweise MUSS das ein Wordwert sein. Aufgrund der Instabilität von Float kommen teilweise extrem kleine
                            ;negative Zahlen raus, die durch diese Rechnung als 255-Zahl interpretiert werden, WENN man ein char As Zahl nimmt.
                            ;Ein Word muss daher her!
  
  For zy = 0 To h-1
    
    ; startfarbe setzen
    destcol_index_float = col1 + g4to1 * zy
    
    ; alpha-anteile von oberem u. unterem farbübergang errechnen
    a1 = zy / h
    a2 = 1.0 - a1
    
    ;übergangswert vorberechnen (wird fortlaufend addiert)
    index_step = g1to2 * a2 + g3to4 * a1
        
    For zx = 0 To w-1
      
      ; farbe erhöhen
      destcol_index_float + index_step
      
      ; index aufrunden
      ; (Die Floatumwandlung frisst ziemlich viel Geschwindigkeit! Leider ist sie nötig, damit die Darstellung auch korrekt ist...)
      destcol_index = Int(destcol_index_float)
      If destcol_index_float - destcol_index > 0.5
        destcol_index + 1
      EndIf
      
      ; je nach pixeltiefe unterschiedlich formatiert einzeichnen (bisher nur 8, 24 und 32!)
      If _interpol_format = #PB_PixelFormat_8Bits
        *drawpos\c\c = destcol_index
      ElseIf _interpol_format = #PB_PixelFormat_24Bits_RGB
        *destcol = *colors + destcol_index * SizeOf(RGBA)
        *drawpos\rgb\r = *destcol\r
        *drawpos\rgb\g = *destcol\g
        *drawpos\rgb\b = *destcol\b
      ElseIf _interpol_format = #PB_PixelFormat_24Bits_BGR
        *destcol = *colors + destcol_index * SizeOf(RGBA)
        *drawpos\rgb\b = *destcol\r
        *drawpos\rgb\g = *destcol\g
        *drawpos\rgb\r = *destcol\b
      ElseIf _interpol_format = #PB_PixelFormat_32Bits_RGB
        *destcol = *colors + destcol_index * SizeOf(RGBA)
        *drawpos\rgba\r = *destcol\r
        *drawpos\rgba\g = *destcol\g
        *drawpos\rgba\b = *destcol\b
      ElseIf _interpol_format = #PB_PixelFormat_32Bits_BGR
        *destcol = *colors + destcol_index * SizeOf(RGBA)
        *drawpos\rgba\b = *destcol\r
        *drawpos\rgba\g = *destcol\g
        *drawpos\rgba\r = *destcol\b
      EndIf
      
      ; auf nächsten pixel zeigen
      *drawpos + _interpol_bytes
      
    Next
    
    ; zeilenvorschub auf bildschirm
    *drawpos + linepush
    
  Next
  
EndProcedure

;------------------------------------------------------------------------------------------------------------------------------

; paar funktionen...

Procedure Randomize()
  
  Protected zx.l, zy.l
  
  For zy = 0 To field_h-1
    For zx = 0 To field_w-1
      Select current_mode
        Case 0
          field(zx,zy) = Random(gradient_color_count(current_preset)-1)
        Case 1
          field(zx,zy) + 1
          If field(zx,zy) > gradient_color_count(current_preset)-1
            field(zx,zy) = Random(gradient_color_count(current_preset)-1)
          EndIf
        Case 2
          field(zx,zy) - 1
          If field(zx,zy) < 0
            field(zx,zy) = Random(gradient_color_count(current_preset)-1)
          EndIf
      EndSelect
    Next
  Next
  
EndProcedure

Procedure DrawArray()
  
  Protected zx.l, zy.l
  
  For zy = 0 To field_h-2
    For zx = 0 To field_w-2
      DrawInterpolatedBox(zx*block_width,zy*block_height, block_width,block_height, @gradient_colors(current_preset, 0), field(zx,zy), field(zx+1,zy), field(zx+1,zy+1), field(zx,zy+1))
    Next
  Next
  
EndProcedure

;------------------------------------------------------------------------------------------------------------------------------

; hauptschleife...

Global time.l, fps.l, delay.l, framedelay.l
framedelay.l = 1000 / #MAX_FRAMES

MessageRequester("Info", "F1 = toggel color presets" + Chr(10) + "F2 = toggle color change mode", #MB_OK)

Repeat
  
  Select WindowEvent()
    Case #PB_Event_CloseWindow
      End
    Case #PB_Event_Menu
      Select EventMenu()
        Case 0
          current_preset + 1
          If current_preset >= gradient_presets
            current_preset = 0
          EndIf
        Case 1
          current_mode + 1
          If current_mode >= modes
            current_mode = 0
          EndIf
      EndSelect
  EndSelect
  
  Randomize()
  
  ClearScreen($111111)
  StartDrawing(ScreenOutput())
    
    time = ElapsedMilliseconds()
    
    GetInterpolationBuffer()
    DrawArray()
    
    delay = ElapsedMilliseconds() - time
    If delay = 0
      SetWindowTitle(0, "FPS: 60+")
    Else
      fps = 1000 / delay
      SetWindowTitle(0, "FPS: " + Str(fps))
    EndIf
    
  StopDrawing()
  FlipBuffers()
  
  ;nötige verzögerung berechnen
  If delay < framedelay
    Delay(framedelay - delay)
  EndIf
  
ForEver
Weitere Optimierungen wären z.B. nicht direkt auf den Screen, sondern auf ein Sprite im Hauptspeicher zu schreiben...

Btw: Spielt mal mit den Werten u. Einstellungen rum. Da ergeben sich teilweise wunderhübsche Animationen :allright: ...
Benutzeravatar
Fluid Byte
Beiträge: 3110
Registriert: 27.09.2006 22:06
Wohnort: Berlin, Mitte

Beitrag von Fluid Byte »

Das erste was mir einfällt ist Commodore64! Bild
Windows 10 Pro, 64-Bit / Outtakes | Derek
Kekskiller
Beiträge: 752
Registriert: 14.09.2004 21:39
Kontaktdaten:

Beitrag von Kekskiller »

Hast recht, erinnert ein wenig an die ganzen Plasma-Demos.
Hab mal nen kleinen, kranken Effekt ausprobiert...

Code: Alles auswählen

; Interpolation von Partikeleffekten
; (c) Max 'Kekskiller' Beutling

#IMAGE_SIZE_DIVIDER = 1
#MAX_FRAMES = 60

;------------------------------------------------------------------------------------------------------------------------------

InitSprite()
OpenWindow(0, 0,0, 640,480, "interpolation test", #PB_Window_ScreenCentered|#PB_Window_TitleBar|#PB_Window_SystemMenu)
OpenWindowedScreen(WindowID(0), 0,0, WindowWidth(0)/#IMAGE_SIZE_DIVIDER,WindowHeight(0)/#IMAGE_SIZE_DIVIDER, 1,0,0)

Global field_w.w, field_h.w, block_width.w, block_height.w
field_w = 64
field_h = 48
block_width = WindowWidth(0) / #IMAGE_SIZE_DIVIDER / field_w
block_height = WindowHeight(0) / #IMAGE_SIZE_DIVIDER / field_h

Global Dim field.l(field_w-1,field_h-1)
Global Dim gradient_colors.l(9)
gradient_colors(8) = $00ff00
gradient_colors(7) = $00ffab
gradient_colors(6) = $00ffff
gradient_colors(5) = $00abff
gradient_colors(4) = $0000ff
gradient_colors(3) = $ab00ff
gradient_colors(2) = $ff00ff
gradient_colors(1) = $ff00ab
gradient_colors(0) = $ff0000

;------------------------------------------------------------------------------------------------------------------------------

Structure CHAR
  c.c
EndStructure

Structure RGB
  r.c
  g.c
  b.c
EndStructure

Structure RGBA
  r.c
  g.c
  b.c
  a.c
EndStructure

Structure interpol_pixel
  StructureUnion
    c.CHAR
    rgb.RGB
    rgba.RGBA
  EndStructureUnion
EndStructure

Global *_interpol_buffer.l
Global _interpol_pitch.l
Global _interpol_bytes.c
Global _interpol_format.c

Procedure GetInterpolationBuffer()
  *_interpol_buffer = DrawingBuffer()
  _interpol_pitch = DrawingBufferPitch()
  _interpol_format = DrawingBufferPixelFormat()
  Select _interpol_format
    Case #PB_PixelFormat_8Bits      : _interpol_bytes = 1
    Case #PB_PixelFormat_15Bits     : _interpol_bytes = 2
    Case #PB_PixelFormat_16Bits     : _interpol_bytes = 2
    Case #PB_PixelFormat_24Bits_RGB : _interpol_bytes = 3
    Case #PB_PixelFormat_24Bits_BGR : _interpol_bytes = 3
    Case #PB_PixelFormat_32Bits_RGB : _interpol_bytes = 4
    Case #PB_PixelFormat_32Bits_BGR : _interpol_bytes = 4 
  EndSelect
EndProcedure

Procedure DrawInterpolatedBox(x.w,y.w, w.w,h.w, *colors.RGBA, col1.c, col2.c, col3.c, col4.c)
  
  Protected zx.l, zy.l
  
  ; brauchen hier nur die index-übergangswerte berechnen -> ist halt nur 1 wert fürn index
  Protected g1to2.f, g3to4.f, g4to1.f
  g1to2 = (col2 - col1) / w
  g3to4 = (col3 - col4) / w
  g4to1 = (col4 - col1) / h
  
  ; für schreiben der werte
  Protected *drawpos.interpol_pixel, linepush.l ; der "zeilenvorschub" für eine pixelzeile, sprich die anzahl pixel zum nächsten zeilenanfang
  *drawpos = *_interpol_buffer + y * _interpol_pitch + x * _interpol_bytes ; pointer auf 1. pixel setzen
  linepush = _interpol_pitch - _interpol_bytes * w ; zeilenvorschub errechnen (pixelzeilenlänge - breite einer pixelzeile des interpolationsrechteckes)
  
  Protected *destcol.RGBA
  Protected destcol_index_float.f
  Protected a1.f, a2.f
  Protected index_step.f
  Protected destcol_index.w ;ACHTUNG: beschissenerweise MUSS das ein Wordwert sein. Aufgrund der Instabilität von Float kommen teilweise extrem kleine
                            ;negative Zahlen raus, die durch diese Rechnung als 255-Zahl interpretiert werden, WENN man ein char As Zahl nimmt.
                            ;Ein Word muss daher her!
  
  For zy = 0 To h-1
    
    ; startfarbe setzen
    destcol_index_float = col1 + g4to1 * zy
    
    ; alpha-anteile von oberem u. unterem farbübergang errechnen
    a1 = zy / h
    a2 = 1.0 - a1
    
    ;übergangswert vorberechnen (wird fortlaufend addiert)
    index_step = g1to2 * a2 + g3to4 * a1
        
    For zx = 0 To w-1
      
      ; farbe erhöhen
      destcol_index_float + index_step
      
      ; index aufrunden
      ; (Die Floatumwandlung frisst ziemlich viel Geschwindigkeit! Leider ist sie nötig, damit die Darstellung auch korrekt ist...)
      destcol_index = Int(destcol_index_float)
      If destcol_index_float - destcol_index > 0.5
        destcol_index + 1
      EndIf
      
      ; je nach pixeltiefe unterschiedlich formatiert einzeichnen (bisher nur 8, 24 und 32!)
      If _interpol_format = #PB_PixelFormat_8Bits
        *drawpos\c\c = destcol_index
      ElseIf _interpol_format = #PB_PixelFormat_24Bits_RGB
        *destcol = *colors + destcol_index * SizeOf(RGBA)
        *drawpos\rgb\r = *destcol\r
        *drawpos\rgb\g = *destcol\g
        *drawpos\rgb\b = *destcol\b
      ElseIf _interpol_format = #PB_PixelFormat_24Bits_BGR
        *destcol = *colors + destcol_index * SizeOf(RGBA)
        *drawpos\rgb\b = *destcol\r
        *drawpos\rgb\g = *destcol\g
        *drawpos\rgb\r = *destcol\b
      ElseIf _interpol_format = #PB_PixelFormat_32Bits_RGB
        *destcol = *colors + destcol_index * SizeOf(RGBA)
        *drawpos\rgba\r = *destcol\r
        *drawpos\rgba\g = *destcol\g
        *drawpos\rgba\b = *destcol\b
      ElseIf _interpol_format = #PB_PixelFormat_32Bits_BGR
        *destcol = *colors + destcol_index * SizeOf(RGBA)
        *drawpos\rgba\b = *destcol\r
        *drawpos\rgba\g = *destcol\g
        *drawpos\rgba\r = *destcol\b
      EndIf
      
      ; auf nächsten pixel zeigen
      *drawpos + _interpol_bytes
      
    Next
    
    ; zeilenvorschub auf bildschirm
    *drawpos + linepush
    
  Next
  
EndProcedure

;------------------------------------------------------------------------------------------------------------------------------

Structure particle
  x.w
  y.w
  issparcle.c
  lifetime.c
  deadtime.c
  addx.b
  addy.b
  fally.b
EndStructure

Global NewList p.particle()

Procedure Randomize()
  
  Protected zx.l, zy.l, delme.c
  
  ; farben verringern
  For zy = 0 To field_h-1
    For zx = 0 To field_w-1
      If field(zx,zy) > 0
        field(zx,zy) - 1
      EndIf
    Next
  Next
  
  ; partikel updaten
  ForEach p()
    ;explosionspartikel
    
    If p()\issparcle = #True
      
      p()\lifetime + 1
      If p()\lifetime > p()\deadtime Or p()\x < 0 Or p()\y < 0 Or p()\x >= field_w Or p()\y >= field_h
        delme = #True
      EndIf
      
      If delme = #False
        
        If field(p()\x,p()\y) < 3
          field(p()\x,p()\y) = 6
        EndIf
        
        p()\x + p()\addx
        p()\y + p()\addy
        
        gradient_colors(Random(8)) = Random($898989)
        
      EndIf
      
    Else
      ;flugpartikel
      
      p()\lifetime + 1
      If p()\lifetime > p()\deadtime Or p()\x < 0 Or p()\y < 0 Or p()\x >= field_w Or p()\y >= field_h
        delme = #True
      EndIf
      
      If delme = #False
        
        field(p()\x,p()\y) = 8
        
        p()\x + p()\addx
        p()\y + p()\addy
        p()\y + p()\fally
        p()\fally + 1
        
        p()\lifetime + 1
        If p()\lifetime > p()\deadtime Or p()\x < 0 Or p()\y < 0 Or p()\x >= field_w Or p()\y >= field_h
          
          *root.particle = @p()
          
          For zy = -1 To 1
            For zx = -1 To 1
              AddElement(p())
              p()\x = *root\x
              p()\y = *root\y
              p()\addx = zx
              p()\addy = zy
              p()\deadtime = 4 + Random(8)
              p()\issparcle = #True
              gradient_colors(Random(8)) = Random($ffffff)
            Next
          Next
          
          ChangeCurrentElement(p(), *root)
          delme = #True
          
        EndIf
        
      EndIf
      
    EndIf
    
    If delme = #True
      DeleteElement(p())
      delme = #False
    EndIf
    
  Next
    
EndProcedure

Procedure DrawArray()
  
  Protected zx.l, zy.l
  
  For zy = 0 To field_h-2
    For zx = 0 To field_w-2
      DrawInterpolatedBox(zx*block_width,zy*block_height, block_width,block_height, @gradient_colors(0), field(zx,zy), field(zx+1,zy), field(zx+1,zy+1), field(zx,zy+1))
    Next
  Next
  
EndProcedure

;------------------------------------------------------------------------------------------------------------------------------

Global time.l, fps.l, delay.l, framedelay.l
framedelay.l = 1000 / #MAX_FRAMES

Repeat
  
  Select WindowEvent()
    Case #PB_Event_CloseWindow
      End
  EndSelect
  
  Randomize()
  
  ClearScreen($111111)
  StartDrawing(ScreenOutput())
    
    time = ElapsedMilliseconds()
    
    GetInterpolationBuffer()
    DrawArray()
    
    delay = ElapsedMilliseconds() - time
    If delay = 0
      SetWindowTitle(0, "FPS: 60+")
    Else
      fps = 1000 / delay
      SetWindowTitle(0, "FPS: " + Str(fps))
    EndIf
    
    ;If Random(1) = 0
      AddElement(p())
      p()\x = Random(field_w-1)
      p()\y = Random(field_h-1)
      p()\addx = 1-Random(2)
      p()\addy = 2-Random(2)
      p()\deadtime = Random(field_h/2-1)
    ;EndIf
    
  StopDrawing()
  FlipBuffers()
  
  ;nötige verzögerung berechnen
  If delay < framedelay
    Delay(framedelay - delay)
  EndIf
  
ForEver
Antworten