Schattenwurf bei Rechtecken [GDI+]

  • Moin,

    Habe mir überlegt mal einen kleinen Test für den Schattenwurf abzuhalten was Rechtecke betrifft. (Soll später in ASM ablaufen und teiltransparente Grafiken unterstützen, sodass man 2D Grafiken benutzen kann die je nach Lichtquelle einen Schatten haben. Die Sache mit dem GdipFillPolygon ist erstmal provisorisch).

    Leider habe ich gemerkt, dass das von mir verwendete Verfahren relativ Fehlerbehaftet und Langsam ist.
    Man kann mit ASM natürlich viel rausholen, aber der bessere Algorithmus schlägt den schnelleren Computer.

    Verfahren

    - Ermittlung der 3 von der Lichtquelle am weitesten entfernten Eckpunkte
    - Berechnung der 3 Gradengleichungen mit z = 0 = P + a * Vz (für z = 0 trifft die Gerade den Boden. Damit ist der Multiplikator a bekannt)
    - Berechnung der 3 Punkte am Boden
    - Zusammenstellung von aPoints mit den 3 Eckpunkten und 3 Bodenpunkten
    - Umsortierung (dieser Schritt dürfte garnicht vorkommen, aber ich weiß nicht wie man aPoints klüger zusammenstellt, sodass es direkt passt)
    - GdipFillPolygon (dient erstmal nur als Platzhalter)


    Daher bitte ich darum mir mitzuteilen, falls jemand schonmal etwas ähnliches gebaut hat, was mehr Performance bietet. (Leicht herauszufinden indem man die anzahl Rechtecke erhöht und schaut wann es ruckelt).

    Skript
    [autoit]


    #include <GDIPlus.au3>
    #include <Array.au3>

    [/autoit] [autoit][/autoit] [autoit]

    Opt('GuiOnEventMode', 1)
    Opt('MustDeclareVars', 1)

    [/autoit] [autoit][/autoit] [autoit]

    Global Const $BLACKNESS = 66, $WHITENESS = 16711778
    Global Const $iW = @DesktopWidth/4, $iH = @DesktopWidth/4, $iD = ($iW^2+$iH^2)^0.5
    Global Const $iAnzahl = 8
    Global Const $_ID_Rect = 1

    [/autoit] [autoit][/autoit] [autoit]

    _GDIPlus_Startup()

    [/autoit] [autoit][/autoit] [autoit]

    Global $aLight[3] = [$iW/2, $iH/2, $iD]
    Global $aArea[1]

    [/autoit] [autoit][/autoit] [autoit]

    ; 2D Obj
    ; [0] - Objekttyp
    ; [1] - x
    ; [2] - y
    ; [3] - w
    ; [4] - h
    ; [5] - vX
    ; [6] - vY
    ; [7] - Gravitationsmultiplikator ( 0 - 1 )
    ; [8] - Höhe über dem Boden

    [/autoit] [autoit][/autoit] [autoit]

    Global $hGUI = GUICreate('Schatten Test', $iW, $iH)
    Global $hDC = _WinAPI_GetDC($hGUI)
    Global $hIMG_Front = _Image_Create($iW, $iH)
    Global $hDC_Buf_Front = DllStructGetData($hIMG_Front, 1, 1)
    Global $hGFX_Buf_Front = _GDIPlus_GraphicsCreateFromHDC($hDC_Buf_Front)
    Global $hIMG_Back = _Image_Create($iW, $iH)
    Global $hDC_Buf_Back = DllStructGetData($hIMG_Back, 1, 1)
    Global $hGFX_Buf_Back = _GDIPlus_GraphicsCreateFromHDC($hDC_Buf_Back)
    Global $hBRU_Obj = _GDIPlus_BrushCreateSolid(0xFFA0A0A0)
    Global $hBRU_Sch = _GDIPlus_BrushCreateSolid(0x30000000)
    Global $hDLL_MSIMG32 = DllOpen('MSIMG32.DLL')
    Global $vBlendFunc = __Image_GetBlendFunction()
    _GDIPlus_GraphicsSetSmoothingMode($hGFX_Buf_Back, 4)
    _GDIPlus_GraphicsSetSmoothingMode($hGFX_Buf_Front, 4)

    [/autoit] [autoit][/autoit] [autoit]

    GUISetOnEvent(-3, 'EVENT', $hGUI)
    GUIRegisterMsg(0xF, 'WM_PAINT')
    OnAutoItExitRegister('_X')
    GUISetState(@SW_SHOW, $hGUI)

    [/autoit] [autoit][/autoit] [autoit]

    _Main()

    [/autoit] [autoit][/autoit] [autoit]

    Func _Main()

    [/autoit] [autoit][/autoit] [autoit]

    For $i = 1 To $iAnzahl Step 1
    _Area_AddRect($aArea, _Rect_Create(($i = 1) * 999))
    Next

    [/autoit] [autoit][/autoit] [autoit]

    While Sleep(50)
    _WinAPI_BitBlt($hDC_Buf_Front, 0, 0, $iW, $iH, 0, 0, 0, $BLACKNESS)
    _WinAPI_BitBlt($hDC_Buf_Back, 0, 0, $iW, $iH, 0, 0, 0, $WHITENESS)
    _Area_Render($hGFX_Buf_Front, $hGFX_Buf_Back, $aArea)
    WM_PAINT()
    WEnd

    [/autoit] [autoit][/autoit] [autoit]

    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _Area_Render($hGFX_Front, $hGFX_Back, ByRef $aArea)

    [/autoit] [autoit][/autoit] [autoit]

    Local $aObj, $aPoints, $tPoints

    [/autoit] [autoit][/autoit] [autoit]

    For $i = 0 To UBound($aArea) - 1 Step 1
    $aObj = $aArea[$i]
    DllCall($ghGDIPDll, "int", "GdipFillRectangle", "handle", $hGFX_Front, "handle", $hBRU_Obj, "float", $aObj[1], "float", $aObj[2], "float", $aObj[3], "float", $aObj[4])
    $aPoints = _CalcShadowRect($aLight, $aObj)
    $tPoints = DllStructCreate("float[" & $aPoints[0][0] * 2 & "]")

    [/autoit] [autoit][/autoit] [autoit]

    For $iI = 1 To $aPoints[0][0]
    DllStructSetData($tPoints, 1, $aPoints[$iI][0], (($iI - 1) * 2) + 1)
    DllStructSetData($tPoints, 1, $aPoints[$iI][1], (($iI - 1) * 2) + 2)
    Next

    [/autoit] [autoit][/autoit] [autoit]

    DllCall($ghGDIPDll, "int", "GdipFillPolygon", "handle", $hGFX_Buf_Back, "handle", $hBRU_Sch, "struct*", $tPoints, "int", $aPoints[0][0], "int", "FillModeAlternate")

    [/autoit] [autoit][/autoit] [autoit]

    $aObj[1] += $aObj[5]
    $aObj[2] += $aObj[6]
    If $aObj[1] < 0 Then
    $aObj[1] = -$aObj[1]
    $aObj[5] = -$aObj[5]
    ElseIf ($aObj[1] + $aObj[3]) > $iW Then
    $aObj[1] -= ($aObj[1] + $aObj[3]) - $iW
    $aObj[5] = -$aObj[5]
    EndIf
    If $aObj[2] < 0 Then
    $aObj[2] = -$aObj[2]
    $aObj[6] = -$aObj[6]
    ElseIf ($aObj[2] + $aObj[4]) > $iH Then
    $aObj[2] -= ($aObj[2] + $aObj[4]) - $iH
    $aObj[6] = -$aObj[6]
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    $aArea[$i] = $aObj
    Next

    [/autoit] [autoit][/autoit] [autoit]

    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _CalcShadowRect($aLight, $aRect)
    Local $aDist[4], $aVect[4][3], $xTmp

    [/autoit] [autoit][/autoit] [autoit]

    $aVect[0][0] = $aRect[1] - $aLight[0]
    $aVect[0][1] = $aRect[2] - $aLight[1]
    $aVect[1][0] = $aRect[1] + $aRect[3] - $aLight[0]
    $aVect[1][1] = $aRect[2] - $aLight[1]
    $aVect[2][0] = $aRect[1] - $aLight[0]
    $aVect[2][1] = $aRect[2] + $aRect[4] - $aLight[1]
    $aVect[3][0] = $aRect[1] + $aRect[3] - $aLight[0]
    $aVect[3][1] = $aRect[2] + $aRect[4] - $aLight[1]
    $aVect[0][2] = $aRect[8] - $aLight[2]
    $aVect[1][2] = $aVect[0][2]
    $aVect[2][2] = $aVect[0][2]
    $aVect[3][2] = $aVect[0][2]

    [/autoit] [autoit][/autoit] [autoit]

    For $i = 0 To 3 Step 1
    $aDist[$i] = ($aVect[$i][0]^2 + $aVect[$i][1]^2)^0.5
    Next

    [/autoit] [autoit][/autoit] [autoit]

    While ($aDist[0] > $aDist[1]) Or ($aDist[1] > $aDist[2]) Or ($aDist[2] > $aDist[3])
    For $i = 0 To 2 Step 1
    If $aDist[$i] > $aDist[$i + 1] Then
    $xTmp = $aDist[$i]
    $aDist[$i] = $aDist[$i + 1]
    $aDist[$i + 1] = $xTmp
    $xTmp = $aVect[$i][0]
    $aVect[$i][0] = $aVect[$i+1][0]
    $aVect[$i+1][0] = $xTmp
    $xTmp = $aVect[$i][1]
    $aVect[$i][1] = $aVect[$i+1][1]
    $aVect[$i+1][1] = $xTmp
    EndIf
    Next
    WEnd

    [/autoit] [autoit][/autoit] [autoit]

    ;~ [0][0] - Number of vertices
    ;~ [1][0] - Vertice 1 X position
    ;~ [1][1] - Vertice 1 Y position
    ;~ [2][0] - Vertice 2 X position
    ;~ [2][1] - Vertice 2 Y position
    ;~ [n][0] - Vertice n X position
    ;~ [n][1] - Vertice n Y position

    [/autoit] [autoit][/autoit] [autoit]

    Local $aRet[7][2]
    $aRet[0][0] = 6

    [/autoit] [autoit][/autoit] [autoit]

    For $i = 1 To 3 Step 1
    $aRet[$i][0] = $aLight[0] + $aVect[$i][0]
    $aRet[$i][1] = $aLight[1] + $aVect[$i][1]
    Next

    [/autoit] [autoit][/autoit] [autoit]

    ;~ For $i = 4 To 6 Step 1
    ;~ $aRet[$i][0] = $aLight[0] + $aVect[$i-3][0] + 10
    ;~ $aRet[$i][1] = $aLight[1] + $aVect[$i-3][1] + 10
    ;~ Next

    [/autoit] [autoit][/autoit] [autoit]

    ;~ X = lightX + a * VectX
    ;~ Y = lightY + a * VectY
    ;~ 0 = lightZ + a * VectZ

    [/autoit] [autoit][/autoit] [autoit]

    ;~ a = lightZ/VectZ

    [/autoit] [autoit][/autoit] [autoit]

    For $i = 4 To 6 Step 1
    $aRet[$i][0] = $aLight[0] - $aVect[$i-3][0] * $aLight[2] / $aVect[$i-3][2]
    $aRet[$i][1] = $aLight[1] - $aVect[$i-3][1] * $aLight[2] / $aVect[$i-3][2]
    Next

    [/autoit] [autoit][/autoit] [autoit]

    ;~ _ArrayDisplay($aRet)
    ;~ _ArrayDisplay($aRet)
    For $x = 1 To 5 Step 1
    ;~ _ArrayDisplay($aRet)
    For $i = $x+1 To 6 Step 1
    If ($aRet[$x][0] = $aRet[$i][0]) Or ($aRet[$x][1] = $aRet[$i][1]) Then
    ;~ ConsoleWrite($i & @CRLF)
    $xTmp = $aRet[$i][0]
    $aRet[$i][0] = $aRet[$x][0]
    $aRet[$x][0] = $xTmp
    $xTmp = $aRet[$i][1]
    $aRet[$i][1] = $aRet[$x][1]
    $aRet[$x][1] = $xTmp
    ExitLoop
    EndIf
    Next
    Next

    [/autoit] [autoit][/autoit] [autoit]

    Local $iRichtung = 0

    [/autoit] [autoit][/autoit] [autoit]

    If ($aRet[4][0] - $aRet[1][0]) < 0 Then $iRichtung += 1
    If ($aRet[4][1] - $aRet[1][1]) < 0 Then $iRichtung += 2

    [/autoit] [autoit][/autoit] [autoit]

    ;~ Switch $iRichtung
    ;~ Case 0
    ;~ Case 1
    ;~ Case 2
    ;~ Case 3
    _Swap($aRet[3][0], $aRet[5][0])
    _Swap($aRet[3][1], $aRet[5][1])
    _Swap($aRet[5][0], $aRet[6][0])
    _Swap($aRet[5][1], $aRet[6][1])
    ;~ EndSwitch

    [/autoit] [autoit][/autoit] [autoit]

    ;~ _GDIPlus_GraphicsDrawString($hGFX_Buf_Front, 'richtung: ' & $iRichtung, $aRet[1][0]+10, $aRet[1][1]+10)

    [/autoit] [autoit][/autoit] [autoit]

    ;~ _ArrayDisplay($aRet)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ;~ For $i = 1 To 6 Step 1
    ;~ _GDIPlus_GraphicsFillEllipse($hGFX_Buf_Back, $aRet[$i][0] - 3, $aRet[$i][1] - 3, 6, 6, $hBRU_Obj)
    ;~ _GDIPlus_GraphicsDrawString($hGFX_Buf_Back, $i, $aRet[$i][0] - 3, $aRet[$i][1] - 3)
    ;~ Next

    [/autoit] [autoit][/autoit] [autoit]

    ;~ _ArrayDisplay($aRet)

    [/autoit] [autoit][/autoit] [autoit]

    Return $aRet

    [/autoit] [autoit][/autoit] [autoit]

    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _Swap(ByRef $a, ByRef $b)
    Local $c = $a
    $a = $b
    $b = $c
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func EVENT()
    Switch @GUI_CtrlId
    Case -3
    Exit
    EndSwitch
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func WM_PAINT()
    DllCall($hDLL_MSIMG32, 'int', 'AlphaBlend', 'ptr', $hDC_Buf_Back, 'int', 0, 'int', 0, 'int', $iW, 'int', $iH, 'ptr', $hDC_Buf_Front, 'int', 0, 'int', 0, 'int', $iW, 'int', $iH, 'dword', $vBlendFunc)
    _WinAPI_BitBlt($hDC, 0, 0, $iW, $iH, $hDC_Buf_Back, 0, 0, 0xCC0020)
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _X()
    _GDIPlus_GraphicsDispose($hGFX_Buf_Back)
    _GDIPlus_GraphicsDispose($hGFX_Buf_Front)
    _Image_Delete($hIMG_Back)
    _Image_Delete($hIMG_Front)
    _WinAPI_ReleaseDC($hGUI, $hDC)
    _GDIPlus_BrushDispose($hBRU_Obj)
    _GDIPlus_BrushDispose($hBRU_Sch)
    _GDIPlus_Shutdown()
    DllClose($hDLL_MSIMG32)
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _Rect_Create($x = 0, $y = 0, $w = 0, $h = 0, $vX = 0, $vY = 0, $fGrav = 1, $nHoehe = 0)
    If $x + $y + $w + $h + $vX + $vY + $fGrav = 1 Then
    $w = Random($iW/16/4, $iW/16)
    $h = Random($iH/16/4, $iH/16)
    $x = Random($w/2, $iW - $w/2)
    $y = Random($h/2, $iH - $h/2)

    [/autoit] [autoit][/autoit] [autoit]

    ;~ $x = Random($w/2, $iW - $w/2)
    ;~ $y = Random($h/2, $iH - $h/2)
    $vX = _AbsRandom(0.5, 1) * 5
    $vY = (5 - Abs($vX)) * _RandomSign()
    $fGrav = Random(0, 1)
    $nHoehe = Random($iD/18, $iD/14)
    EndIf
    If $x = 999 Then
    $w = $iD/32
    $h = $iD/32
    $x = $iW/2 - $w*2 + 0.00000031
    $y = $iH/2 - $h*2 + 0.000000314
    $vX = 5 + 0.000000001
    $vY = 0 + 0.000000001
    $fGrav = Random(0, 1)
    $nHoehe = $iD*0.9
    EndIf
    Local $a[9] = [$_ID_Rect, $x, $y, $w, $h, $vX, $vY, $fGrav, $nHoehe]
    Return $a
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _AbsRandom($nMin, $nMax)
    Return _RandomSign() * Random($nMin, $nMax)
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _RandomSign()
    Switch Random(0, 1, 1)
    Case 0
    Return 1
    Case 1
    Return -1
    EndSwitch
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _Area_AddRect(ByRef $aArea, $aRect)
    Local $u = UBound($aArea)
    If ($u = 1) And Not IsArray($aArea[0]) Then
    $aArea[0] = $aRect
    Else
    ReDim $aArea[$u + 1]
    $aArea[$u] = $aRect
    EndIf
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _Image_Create($iW, $iH)
    Local $Ptr, $hDC, $hBmp, $tBMI, $qDIB, $vStruct
    $hDC = _WinAPI_CreateCompatibleDC(0)
    $tBMI = DllStructCreate($tagBITMAPINFO)
    DllStructSetData($tBMI, "Size", DllStructGetSize($tBMI) - 4)
    DllStructSetData($tBMI, "Width", $iW)
    DllStructSetData($tBMI, "Height", -$iH)
    DllStructSetData($tBMI, "Planes", 1)
    DllStructSetData($tBMI, "BitCount", 32)
    $qDIB = DllCall('GDI32.dll', 'ptr', 'CreateDIBSection', 'hwnd', 0, 'ptr', DllStructGetPtr($tBMI), 'uint', 0, 'ptr*', 0, 'ptr', 0, 'uint', 0)
    $hBmp = $qDIB[0]
    $Ptr = $qDIB[4]
    _WinAPI_SelectObject($hDC, $hBmp)
    $vStruct = DllStructCreate('int[5]')
    DllStructSetData($vStruct, 1, $hDC, 1)
    DllStructSetData($vStruct, 1, $iW, 2)
    DllStructSetData($vStruct, 1, $iH, 3)
    DllStructSetData($vStruct, 1, $Ptr, 4)
    DllStructSetData($vStruct, 1, $hBmp, 5)
    Return $vStruct
    EndFunc ;==>_Image_Create

    [/autoit] [autoit][/autoit] [autoit]

    Func _Image_Delete(ByRef $vStruct)
    _WinAPI_DeleteObject(DllStructGetData($vStruct, 1, 5))
    _WinAPI_DeleteDC(DllStructGetData($vStruct, 1, 1))
    $vStruct = 0
    EndFunc ;==>_Image_Delete

    [/autoit] [autoit][/autoit] [autoit]

    Func __Image_GetBlendFunction($iAlpha = 255)
    Local $vStruct = DllStructCreate($tagBLENDFUNCTION)
    DllStructSetData($vStruct, 1, 0) ; 0 = AC_SRC_OVER
    DllStructSetData($vStruct, 2, 0) ; 0 "Must be Zero"
    DllStructSetData($vStruct, 3, $iAlpha) ; Alpha fürs ganze Bild
    DllStructSetData($vStruct, 4, 1) ; 1 = Bild enthält einen Alphakanal
    Local $vData = DllStructCreate('dword', DllStructGetPtr($vStruct))
    Local $iData = DllStructGetData($vData, 1)
    Return $iData
    EndFunc ;==>__Image_GetBlendFunction

    [/autoit]

    PS: Die "Lichtquelle" ist in der Mitte.

    Edit: Boah, nach dem durchlesen merke ich, dass Deutsch am Morgen nicht meine Stärke ist :D

    lg
    Mars

  • Dein Computer ist langsam und braucht dadurch mehr Zeit um jede einzelne Frame zu berechnen. Bei mir läuft es mit einer Wartezeit von 50 Millisekunden zwischen jedem Schleifendurchgang flüssig, weil mein PC wesentlich schneller ist und nur einen Bruchteil der Zeit benötigt um die Frame zu erzeugen. Ich habe also mehr Frames pro Sekunde als du. Wenn du nun die Wartezeit verringerst, verringerst du die Gesamtzeit zwischen jeder Frame und gleichst den Unterschied somit aus.

    Um so etwas zu vermeiden, verwende ich bei aufwändigeren GDI+ Animationen meist eine Schleife, deren Wartezeit sich an die Leistung des Computers anpasst. Da gibt es das Problem nicht.

  • Der Trick soll ja sein die ganze Sache (möglichst stark) zu beschleunigen. (hat mit dem Sleep nix zu tun).

    Meine neue Idee ist es nur noch einen Vektor zu berechnen (statt 3) und die anderen Koordinaten per Strahlensatz daraus zu erhalten.
    Dadurch könnte! sich auch das Sortierungsproblem lösen. Müsste ca. 1.5x so schnell sein als die aktuelle Lösung.

    Bin für weitere Vorschläge natürlich offen. So ein Faktor 10 wäre schon nicht schlecht :P

    Im Endeffekt sollen damit Pseudo3D Effekte (Voxel) erzielt werden. Dann ist ein Pixel nicht mehr 1x1, sondern 1x1x1.
    Dann kann man z.B. eine Grafik mit 16x16x16 Px erstellen und einen fast realistischen Schattenwurf hinbekommen ohne "echtes" 3D Rendering (also Schichtweise 2D).
    Das Ganze noch mit unterschiedlichen Lichtquellen unterschiedlicher Farbe und Intensität, und los gehts :D

    Edit: Damit man sich das Vorstellen kann, hier ein Beispiel.
    - Rechtecke sind jetzt Pyramiden mit einer Steigung von 45 Grad (Unterteilt in Schichten die jeweils einen rechteckigen Schatten haben)
    - Die Pyramiden haben am Boden die Höhe 0 und (je nach Größe) eine Maximalhöhe von 10
    - Die Lichtquelle hängt bei 20 an der Decke
    - Unperformantestes Skript das ich jemals gepostet habe :D (zum Glück nur ein Beispiel...)

    Beispiel
    [autoit]


    #include <GDIPlus.au3>
    #include <Array.au3>

    [/autoit] [autoit][/autoit] [autoit]

    Opt('GuiOnEventMode', 1)
    Opt('MustDeclareVars', 1)

    [/autoit] [autoit][/autoit] [autoit]

    Global Const $BLACKNESS = 66, $WHITENESS = 16711778
    Global Const $iW = @DesktopWidth/4, $iH = @DesktopWidth/4, $iD = ($iW^2+$iH^2)^0.5
    Global Const $iAnzahl = 5
    Global Const $_ID_Rect = 1

    [/autoit] [autoit][/autoit] [autoit]

    _GDIPlus_Startup()

    [/autoit] [autoit][/autoit] [autoit]

    Global $aLight[3] = [$iW/2, $iH/2, 20]
    Global $aArea[1]

    [/autoit] [autoit][/autoit] [autoit]

    ; 2D Obj
    ; [0] - Objekttyp
    ; [1] - x
    ; [2] - y
    ; [3] - w
    ; [4] - h
    ; [5] - vX
    ; [6] - vY
    ; [7] - Gravitationsmultiplikator ( 0 - 1 )
    ; [8] - Höhe über dem Boden

    [/autoit] [autoit][/autoit] [autoit]

    Global $hGUI = GUICreate('Schatten Test', $iW, $iH)
    Global $hDC = _WinAPI_GetDC($hGUI)
    Global $hIMG_Front = _Image_Create($iW, $iH)
    Global $hDC_Buf_Front = DllStructGetData($hIMG_Front, 1, 1)
    Global $hGFX_Buf_Front = _GDIPlus_GraphicsCreateFromHDC($hDC_Buf_Front)
    Global $hIMG_Back = _Image_Create($iW, $iH)
    Global $hDC_Buf_Back = DllStructGetData($hIMG_Back, 1, 1)
    Global $hGFX_Buf_Back = _GDIPlus_GraphicsCreateFromHDC($hDC_Buf_Back)
    Global $hBRU_Obj = _GDIPlus_BrushCreateSolid(0xFF000000)
    Global $hBRU_Sch = _GDIPlus_BrushCreateSolid(0x10000000)
    Global $hDLL_MSIMG32 = DllOpen('MSIMG32.DLL')
    Global $vBlendFunc = __Image_GetBlendFunction()
    _GDIPlus_GraphicsSetSmoothingMode($hGFX_Buf_Back, 4)
    _GDIPlus_GraphicsSetSmoothingMode($hGFX_Buf_Front, 4)

    [/autoit] [autoit][/autoit] [autoit]

    GUISetOnEvent(-3, 'EVENT', $hGUI)
    GUIRegisterMsg(0xF, 'WM_PAINT')
    OnAutoItExitRegister('_X')
    GUISetState(@SW_SHOW, $hGUI)

    [/autoit] [autoit][/autoit] [autoit]

    _Main()

    [/autoit] [autoit][/autoit] [autoit]

    Func _Main()

    [/autoit] [autoit][/autoit] [autoit]

    For $i = 1 To $iAnzahl Step 1
    _Area_AddRect($aArea, _Rect_Create())
    Next

    [/autoit] [autoit][/autoit] [autoit]

    While Sleep(10)
    _WinAPI_BitBlt($hDC_Buf_Front, 0, 0, $iW, $iH, 0, 0, 0, $WHITENESS)
    _WinAPI_BitBlt($hDC_Buf_Back, 0, 0, $iW, $iH, 0, 0, 0, $BLACKNESS)
    _Area_Render($hGFX_Buf_Front, $hGFX_Buf_Back, $aArea)
    WM_PAINT()
    WEnd

    [/autoit] [autoit][/autoit] [autoit]

    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _Area_Render($hGFX_Front, $hGFX_Back, ByRef $aArea)

    [/autoit] [autoit][/autoit] [autoit]

    Local $aObj, $aPoints, $tPoints, $aKlon

    [/autoit] [autoit][/autoit] [autoit]

    For $i = 0 To UBound($aArea) - 1 Step 1
    $aObj = $aArea[$i]

    [/autoit] [autoit][/autoit] [autoit]

    DllCall($ghGDIPDll, "int", "GdipFillRectangle", "handle", $hGFX_Front, "handle", $hBRU_Obj, "float", $aObj[1], "float", $aObj[2], "float", $aObj[3], "float", $aObj[4])

    [/autoit] [autoit][/autoit] [autoit]

    For $n = 0.25 To 10 Step 0.25
    $aKlon = $aObj
    $aKlon[1] += $n
    $aKlon[2] += $n
    $aKlon[3] -= $n * 2
    $aKlon[4] -= $n * 2
    $aKlon[8] += $n

    [/autoit] [autoit][/autoit] [autoit]

    If ($aKlon[3] < 2) Or ($aKlon[4] < 2) Then ExitLoop

    [/autoit] [autoit][/autoit] [autoit]

    $aPoints = _CalcShadowRect($aLight, $aKlon)

    [/autoit] [autoit][/autoit] [autoit]

    _GDIPlus_BrushSetSolidColor($hBRU_Sch, $aPoints[4])

    [/autoit] [autoit][/autoit] [autoit]

    DllCall($ghGDIPDll, "int", "GdipFillRectangle", "handle", $hGFX_Front, _
    "handle", $hBRU_Sch, "float", $aPoints[0], "float", $aPoints[1], "float", $aPoints[2], "float", $aPoints[3])

    [/autoit] [autoit][/autoit] [autoit]

    Next

    [/autoit] [autoit][/autoit] [autoit]

    $aObj[1] += $aObj[5]
    $aObj[2] += $aObj[6]
    If $aObj[1] < 0 Then
    $aObj[1] = -$aObj[1]
    $aObj[5] = -$aObj[5]
    ElseIf ($aObj[1] + $aObj[3]) > $iW Then
    $aObj[1] -= ($aObj[1] + $aObj[3]) - $iW
    $aObj[5] = -$aObj[5]
    EndIf
    If $aObj[2] < 0 Then
    $aObj[2] = -$aObj[2]
    $aObj[6] = -$aObj[6]
    ElseIf ($aObj[2] + $aObj[4]) > $iH Then
    $aObj[2] -= ($aObj[2] + $aObj[4]) - $iH
    $aObj[6] = -$aObj[6]
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    $aArea[$i] = $aObj
    Next

    [/autoit] [autoit][/autoit] [autoit]

    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _CalcShadowRect($aLight, $aRect)

    [/autoit] [autoit][/autoit] [autoit]

    Local $xVect, $yVect, $zVect
    $xVect = $aRect[1] - $aLight[0]
    $yVect = $aRect[2] - $aLight[1]
    $zVect = $aRect[8] - $aLight[2]

    [/autoit] [autoit][/autoit] [autoit]

    Local $aMulti = - $aLight[2] / $zVect
    Local $aRet[5] = [$aLight[0] + $aMulti * $xVect, $aLight[1] + $aMulti * $yVect, $aRect[3] * $aMulti, $aRect[4] * $aMulti, '0x' & Hex(Int(40 / $aLight[2] * ($aLight[2] - $aRect[8])), 2) &'000000']

    [/autoit] [autoit][/autoit] [autoit]

    Return $aRet

    [/autoit] [autoit][/autoit] [autoit]

    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _Swap(ByRef $a, ByRef $b)
    Local $c = $a
    $a = $b
    $b = $c
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func EVENT()
    Switch @GUI_CtrlId
    Case -3
    Exit
    EndSwitch
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func WM_PAINT()
    DllCall($hDLL_MSIMG32, 'int', 'AlphaBlend', 'ptr', $hDC_Buf_Front, 'int', 0, 'int', 0, 'int', $iW, 'int', $iH, 'ptr', $hDC_Buf_Back, 'int', 0, 'int', 0, 'int', $iW, 'int', $iH, 'dword', $vBlendFunc)
    _WinAPI_BitBlt($hDC, 0, 0, $iW, $iH, $hDC_Buf_Front, 0, 0, 0xCC0020)
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _X()
    _GDIPlus_GraphicsDispose($hGFX_Buf_Back)
    _GDIPlus_GraphicsDispose($hGFX_Buf_Front)
    _Image_Delete($hIMG_Back)
    _Image_Delete($hIMG_Front)
    _WinAPI_ReleaseDC($hGUI, $hDC)
    _GDIPlus_BrushDispose($hBRU_Obj)
    _GDIPlus_BrushDispose($hBRU_Sch)
    _GDIPlus_Shutdown()
    DllClose($hDLL_MSIMG32)
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _Rect_Create($x = 0, $y = 0, $w = 0, $h = 0, $vX = 0, $vY = 0, $fGrav = 1, $nHoehe = 0)
    If $x + $y + $w + $h + $vX + $vY + $fGrav = 1 Then
    $w = Random($iW/16/2, $iW/16)
    $h = Random($iH/16/2, $iH/16)
    $x = Random($w/2, $iW - $w/2)
    $y = Random($h/2, $iH - $h/2)
    $vX = _AbsRandom(0.5, 1) * 5
    $vY = (5 - Abs($vX)) * _RandomSign()
    $fGrav = Random(0, 1)
    $nHoehe = 0
    EndIf
    Local $a[9] = [$_ID_Rect, $x, $y, $w, $h, $vX, $vY, $fGrav, $nHoehe]
    Return $a
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _AbsRandom($nMin, $nMax)
    Return _RandomSign() * Random($nMin, $nMax)
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _RandomSign()
    Switch Random(0, 1, 1)
    Case 0
    Return 1
    Case 1
    Return -1
    EndSwitch
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _Area_AddRect(ByRef $aArea, $aRect)
    Local $u = UBound($aArea)
    If ($u = 1) And Not IsArray($aArea[0]) Then
    $aArea[0] = $aRect
    Else
    ReDim $aArea[$u + 1]
    $aArea[$u] = $aRect
    EndIf
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _Image_Create($iW, $iH)
    Local $Ptr, $hDC, $hBmp, $tBMI, $qDIB, $vStruct
    $hDC = _WinAPI_CreateCompatibleDC(0)
    $tBMI = DllStructCreate($tagBITMAPINFO)
    DllStructSetData($tBMI, "Size", DllStructGetSize($tBMI) - 4)
    DllStructSetData($tBMI, "Width", $iW)
    DllStructSetData($tBMI, "Height", -$iH)
    DllStructSetData($tBMI, "Planes", 1)
    DllStructSetData($tBMI, "BitCount", 32)
    $qDIB = DllCall('GDI32.dll', 'ptr', 'CreateDIBSection', 'hwnd', 0, 'ptr', DllStructGetPtr($tBMI), 'uint', 0, 'ptr*', 0, 'ptr', 0, 'uint', 0)
    $hBmp = $qDIB[0]
    $Ptr = $qDIB[4]
    _WinAPI_SelectObject($hDC, $hBmp)
    $vStruct = DllStructCreate('int[5]')
    DllStructSetData($vStruct, 1, $hDC, 1)
    DllStructSetData($vStruct, 1, $iW, 2)
    DllStructSetData($vStruct, 1, $iH, 3)
    DllStructSetData($vStruct, 1, $Ptr, 4)
    DllStructSetData($vStruct, 1, $hBmp, 5)
    Return $vStruct
    EndFunc ;==>_Image_Create

    [/autoit] [autoit][/autoit] [autoit]

    Func _Image_Delete(ByRef $vStruct)
    _WinAPI_DeleteObject(DllStructGetData($vStruct, 1, 5))
    _WinAPI_DeleteDC(DllStructGetData($vStruct, 1, 1))
    $vStruct = 0
    EndFunc ;==>_Image_Delete

    [/autoit] [autoit][/autoit] [autoit]

    Func __Image_GetBlendFunction($iAlpha = 255)
    Local $vStruct = DllStructCreate($tagBLENDFUNCTION)
    DllStructSetData($vStruct, 1, 0) ; 0 = AC_SRC_OVER
    DllStructSetData($vStruct, 2, 0) ; 0 "Must be Zero"
    DllStructSetData($vStruct, 3, $iAlpha) ; Alpha fürs ganze Bild
    DllStructSetData($vStruct, 4, 1) ; 1 = Bild enthält einen Alphakanal
    Local $vData = DllStructCreate('dword', DllStructGetPtr($vStruct))
    Local $iData = DllStructGetData($vData, 1)
    Return $iData
    EndFunc ;==>__Image_GetBlendFunction

    [/autoit]

    lg

  • Hi,
    andere Idee:
    "Grafik" abhängig vom Standpunkt der Lichtquelle rechteckig um Faktor x vergrößern. Abhängig von der Lichtquelle heisst, wenn das Licht von links oben kommt, muss nur der rechte untere Teil der Grafik vergrössert werden.
    Die Vergrösserung in Grautöne umwandeln (ggf. von innen nach aussen mit niedriger Intensität, bspw mit schon bestehendem Graustufenverlauf) ) und mit den ursprünglichen Pixeln des "Hintergrundes" verknüpfen.
    Dieses Verfahren ergäbe auch einen Kernschatten, und semi- oder auch transparente Grafiken würden korrekte Schatten werfen.

    Zitat

    Im Endeffekt sollen damit Pseudo3D Effekte (Voxel) erzielt werden. Dann ist ein Pixel nicht mehr 1x1, sondern 1x1x1.
    Dann kann man z.B. eine Grafik mit 16x16x16 Px erstellen und einen fast realistischen Schattenwurf hinbekommen ohne "echtes" 3D Rendering (also Schichtweise 2D).

    Made my day^^
    Wenn du die 3. Dimension hinzufügst, hast du bereits 3D und dann sind sämtliche anderen Berechnungen als die von "Ray´s" nur Augenwischerei für eine weitere 3D-Darstellung.
    Der Vorteil von 2D ist ja, 1/3 der Berechnungen per se einzusparen und somit schon 50% schneller zu sein!
    Du machst in deinen Beispielscripten nichts weiteres, als die 3D-Berechnungsmethoden auf 2D-Grafiken anzuwenden (Raytracing). Das ist imho gut und ausreichend schnell umgesetzt, die Frage ist, ob ein "realistischer" 2D-Schatten nicht einfacher erstellt werden kann.

    Das Problem ist ja, für jede 2D-Grafik, die einen "realistischen" Schatten werfen soll, eine "realistische" Höhe (Z-Achse) angeben zu müssen, und so aus 2D nun 3D mit allen nötigen Berechnungen zu machen.

  • Also nochmal zusammengefasst, ob ich es verstanden habe:
    - Berechnung des Vergrößerungsfaktors
    - Berechnung des oberen linken Punktes des Schattens als Rechteck
    - Ermittlung der zu zeichnenden Fläche (zusammengesetzt aus 2 Rechtecken wie ein L)
    - Per Alphamap (Graustufenbild für Transparenz) ein schwarzes Äquivalent der Grafik in L Form zeichnen
    - ggf Umschärfe anwenden

    Vorteile:
    - nur 1 Vektor nötig (zur Ermittlung des Faktors und des oberen linken Punktes
    - Rechtecke sind einfach umzusetzen (auch in ASM schön ;)), nur 2 Rechtecke nötig
    - Alphamap vorberechnet, also keine umständliche Echtzeit Helligkeitsberechnung, auch schön in ASM
    - Da nur kleine Grafiken genutzt werden können die Skalierungen der Alphamap gepuffert werden

    Nachteile:
    - ohne echtzeit Verzerrung der Alphamap + schwarzer Grafik kein "langer" Schattenwurf möglich (wäre aber auch so nicht einfacher gewesen)

    Nochmal allgemein: (Vermutung)
    Man braucht für jede Lichtquelle einen eigenen Puffer. Anschließend verknüpft man alle Puffer mit dem logischen Oder um das Gesamtbild zu erhalten. (gehe ich in der Annahme richtig ?) So dürfte auch farbigen Lichtquellen Rechnung getragen werden.

  • Zitat

    Nachteile:
    - ohne echtzeit Verzerrung der Alphamap + schwarzer Grafik kein "langer" Schattenwurf möglich (wäre aber auch so nicht einfacher gewesen)

    der lange Schattenwurf, so wie in deinem Script, ist ein "schöner" effekt, aber idR nicht Praxisrelevant.

    Zitat

    - Berechnung des Vergrößerungsfaktors

    hmmm, ich überlege gerade, ob man überhaupt vergrössern muss!
    Mal angenommen, man möchte ein Quadrat "beschatten", Licht kommt von oben links. Kritisch ist dann der Bereich der rechten oberen und linken unteren Ecke. Wenn man dort ein wenig schummelt, also dort auf den Kernschatten verzichtet, dann reicht es, die Orginalgröße des Quadrates beizubehalten und den "Schatten nur nach rechts unten zu verschieben.

  • Zitat

    dann reicht es, die Orginalgröße des Quadrates beizubehalten und den "Schatten nur nach rechts unten zu verschieben.

    Normalerweise schreibe ich Deutsch, aber "das is ja ma sau low" :D
    Das hat dann mit einem "Schatten" nicht mehr viel gemein, würde das ganze natürlich unendlich schnell machen, da nur 1 Punkt berechnet werden muss und der ASM sich schon freut die Bitmap zu kopieren :thumbup:

    Das gilt aber nur für den Idealfall einer "unendlich" weit entfernten Lichtquelle (also paralleles auftreffen der Strahlen). Für eine Lampe auf dem Spielfeld die vllt nur 5 Meter hoch ist, macht das relativ wenig Sinn. Besser als nix ist es allemal. Und ich glaube ich bau das auch erstmal so in die 2D Engine ein. Verbessern kann man später ja immernoch.