Licht und Schatten in 2D? Ich bekomms nicht hin.

Übersicht BlitzMax, BlitzMax NG Beginners-Corner

Neue Antwort erstellen

M0rgenstern

Betreff: Licht und Schatten in 2D? Ich bekomms nicht hin.

BeitragMi, Aug 11, 2010 19:59
Antworten mit Zitat
Benutzer-Profile anzeigen
Hey Leute.
Ich hab mir die letzten Tage mal überlegt, wie man Lichter auf einer 2D Tilemap umsetzen könnte.
Bin bisher leider noch nicht zu einem anständigen Ergebnis gekommen.

Folgende Ansätze hatte ich:

1) Mit Lightblend ein halbtransparenten Kreis über entsprechende Stellen zu zeichnen -> Sah bescheuert aus.
2) Einfach prüfen, welche Tiles in dem Einzugsbereich des Lichts sind und diese heller machen -> Gab kantige Kreise.
3) Direkt auf die Pixmap zugreifen und die Entfernung von Pixeln zu Lichtquelle prüfen. -> Erscheint mir immernoch als beste Lösung.
Aber: Ich bekomm das nicht hin. So wie ich es mache, ist meine ganze Tilemap grau.

Ich hab hier mal den Code, aber wie gesagt, graue Tilemap:

BlitzMax: [AUSKLAPPEN]
Function CalcLight()
For Local Light:TLight = EachIn TLight.tlAllLights
For Local ix:Int = 0 Until iMapwidth
For Local iy:Int = 0 Until iMapwidth
If (Pythagoras(Light.ixPos, Light.iyPos, ix * iTILESIZE, iy * iTILESIZE) <= Light.irange) Then
Local pTileMask:TPixmap = LockImage(titileset, iGameMap[iX, iY])
Local argb:Int, dest_argb:Int, a:Int, r:Int, g:Int, b:Int
For Local maskx:Int = 0 Until pTileMask.width
For Local masky:Int = 0 Until pTileMask.height
If Pythagoras(Light.ixPos, Light.iyPos, maskx + ix * iTILESIZE, masky + iy * iTILESIZE) <= Light.irange Then
argb = ReadPixel(pTileMask, maskx, masky)
r = (argb Shr 16) & $FF
g = (argb Shr 8) & $FF
b = (argb) & $FF
a = (argb Shr 24) & $FF
r = r + (250 - r)
g = g + (250 - g)
b = b + (250 - b)
WritePixel(pTileMask, maskx, masky, $00000000 | (a Shl 24) | (r Shl 16) | (g Shl 8) | b)
EndIf
Next
Next
End If
Next
Next
Next
End Function


Ich hab mir gedacht, ich verrechne die Position des Pixels auf dem Bild mit der Position des zugehörigen Tiles auf der Map um die Pixelposition auf der Map zu bekommen, aber irgendwas mach ich falsch.
Vor allem: Warum wird das grau?

Gibt es da einen allgemein besseren Ansatz? (Ich will später halt Wände mit eingeziehen. Lichter müssen nicht mal dynamisch sein. Momentan ist es geplant das vorrendern zu lassen).

Lg, M0rgenstern

ChaosCoder

BeitragMi, Aug 11, 2010 20:39
Antworten mit Zitat
Benutzer-Profile anzeigen
Also das mit dem grau kommt wahrscheinlich daher:
Code: [AUSKLAPPEN]
r = r + (250 - r)
g = g + (250 - g)
b = b + (250 - b)
Egal wie groß r,g und b sind, für jedes kommt 250 raus.

Warum jetzt die ganze Tilemap gefüllt wird? Keine Ahnung, entweder deine Pythagoras-Funktion liefert falsche Werte oder deine Light.irange ist zu groß?! Mach einfach mal Debug ausgaben, wo du mit derselben Zeile prüfst, ob deine Mausposition im Radius liegt, also sowas:
Code: [AUSKLAPPEN]
If Pythagoras(Light.ixPos, Light.iyPos, MouseX(), MouseY()) <= Light.irange Then DrawText "Drin!",10,10


Liebe Grüße
Projekte: Geolaria | aNemy
Webseite: chaosspace.de

Xeres

Moderator

BeitragMi, Aug 11, 2010 20:42
Antworten mit Zitat
Benutzer-Profile anzeigen
Rechne RGB in HSV um, Stelle die Helligkeit ein, und rechne in RGB zurück. Bin sicher die passenden Funktionen im Codearchiv gesehen zu haben.
Win10 Prof.(x64)/Ubuntu 16.04|CPU 4x3Ghz (Intel i5-4590S)|RAM 8 GB|GeForce GTX 960
Wie man Fragen richtig stellt || "Es geht nicht" || Video-Tutorial: Sinus & Cosinus
T
HERE IS NO FAIR. THERE IS NO JUSTICE. THERE IS JUST ME. (Death, Discworld)

M0rgenstern

BeitragMi, Aug 11, 2010 21:36
Antworten mit Zitat
Benutzer-Profile anzeigen
Meine Pyhtagoras Funktion funktioniert.
Das hab ich anderswo schon genutzt.
Ich glaube, dass ich das so nicht berechnen kann:

BlitzMax: [AUSKLAPPEN]
maskx + ix * iTILESIZE, masky + iy * iTILESIZE

Oder?
Ich möchte damit die Position eines Pixels auf dem Bildschirm haben, abhängig von den Tiles.
Oder stimmt das?

@ Xeres: Guck ich gleich mal nach, vielen Dank.

Lg, M0rgenstern

ChaosCoder

BeitragMi, Aug 11, 2010 22:58
Antworten mit Zitat
Benutzer-Profile anzeigen
Also meiner Meinung nach ist die Zeile richtig.

Was steht denn da in den Variablen so drin? Also in:
iTILESIZE,iMapwidth,iMapheight,Light.irange,pTileMask.width, etc.?

BTW: Da steht "For Local iy:Int = 0 Until iMapwidth".
Projekte: Geolaria | aNemy
Webseite: chaosspace.de

M0rgenstern

BeitragMi, Aug 11, 2010 23:06
Antworten mit Zitat
Benutzer-Profile anzeigen
Also:
ITILESIZE = 32
imapwidth = 30 <- Breite der Tilemap
imapheight = 30 <- Höhe der Tilemap
Light.ilightrange = rand(150,200) <- Reichweiter der Lichtquelle in Pixel.
pTileMask.width bzw height stehen die Daten der Pixmap zu dem Tile an einer besitmmten Stelle in der Map.

Oh, vielen Dank. Seh ich jetzt erst^^

Lg, M0rgenstern

ARG!!!
Ich glaube, ich hab das Problem gefunden:
Wenn ich die Pixmap des Bildes verändere, dann wird sie für das komplette Bild übernommen. Dauerhaft. Und da die komplette Map aus diesem Bild besteht, ändert sich die komplette Map.
Kann man das nicht irgendwie umgehen?

Lg, M0rgenstern

ChaosCoder

BeitragMi, Aug 11, 2010 23:32
Antworten mit Zitat
Benutzer-Profile anzeigen
Du musst für jede Zelle ein eigenes Bild speichern.

Im Moment machst du das mit einem Bild und frames, das wird aber nicht klappen. Jede Zelle sollte ein TImage zeigen. Am Anfang zeigen alle auf das gleiche Bild, doch wenn du dann die Funktion aufrufst um die Schatten zu berechnen, kopierst du den relevanten Frame der Originalbildes mittels TPixmap.copy() und veränderst diesen Inhalt. So wird es klappen.

Viel Spaß noch!
Projekte: Geolaria | aNemy
Webseite: chaosspace.de

M0rgenstern

BeitragSa, Aug 14, 2010 16:40
Antworten mit Zitat
Benutzer-Profile anzeigen
Das heißt ich brauche für jedes Tile ein eigenes Bild?
Wie soll das denn bitte gehen? Also, wie soll ich einem Tile ein Bild zuordnen, und vor allem: Wie soll das speichertechnisch gelöst werden?

BTW: Ich hab keine Funktion gefunden um die Farbwerte umzurechnen.

Lg, M0rgenstern

DaysShadow

BeitragSa, Aug 14, 2010 16:54
Antworten mit Zitat
Benutzer-Profile anzeigen
BlitzMax: [AUSKLAPPEN]
Type TColorSpaceHSV

Global Hue:Float, Saturation:Float, Value:Float 'Values to calculate with
Global Huei:Int, Saturationi:Int, Valuei:Int 'Values as int(H) or percentage(S and V)

Function ConvertRGBtoHSV(r:Int, g:Int, b:Int)

Local RGB_r:Float = Float(r) / 255.0
Local RGB_g:Float = Float(g) / 255.0
Local RGB_b:Float = Float(b) / 255.0

Local RGB_max:Float = RGB_r
Local RGB_min:Float = RGB_r

'Calculate Max(r, g, b)

If RGB_g > RGB_max RGB_max = RGB_g

If RGB_b > RGB_max RGB_max = RGB_b

'Calculate Min(r, g, b)

If RGB_g < RGB_min RGB_min = RGB_g

If RGB_b < RGB_min RGB_min = RGB_b

'Calculate Hue

If RGB_max = RGB_min
Hue = 0
ElseIf RGB_max = RGB_r
Hue = 60 * (0 + ( (RGB_g - RGB_b) / (RGB_max - RGB_min) ) )
ElseIf RGB_max = RGB_g
Hue = 60 * (2 + ( (RGB_b - RGB_r) / (RGB_max - RGB_min) ) )
ElseIf RGB_max = RGB_b
Hue = 60 * (4 + ( (RGB_r - RGB_g) / (RGB_max - RGB_min) ) )
EndIf

If Hue < 0 Hue:+ 360

'Calculate Saturation

If RGB_max = 0
Saturation = 0
Else
Saturation = (RGB_max - RGB_min) / RGB_max
EndIf

'Calculate Value

Value = RGB_max

'Convert Calculationvalues to Int

Huei = Int(Hue)
Saturationi = Int(Saturation * 100)
Valuei = Int(Value * 100)

EndFunction

Function ConvertHSVtoRGB(r:Int Var, g:Int Var, b:Int Var)

'Check exception S = 0

If Saturation = 0
r = Int(Value * 255.0); g = Int(Value * 255.0); b = Int(Value * 255.0)
Return
EndIf

'Calculate hi and f

Local hi:Int = Hue / 60
Local f:Float = (Hue / 60) - hi
Local p:Float, q:Float, t:Float

'Calculate p, q and t

p = Value * (1 - Saturation)
q = Value * (1 - (Saturation * f) )
t = Value * ( 1 - (Saturation * (1 - f) ) )

'Select hi and calculate r, g, b according to hi

Select hi

Case 0, 6

r = Int(Value * 255.0); g = Int(t * 255.0); b = Int(p * 255.0)

Case 1

r = Int(q * 255.0); g = Int(Value * 255.0); b = Int(p * 255.0)

Case 2

r = Int(p * 255.0); g = Int(Value * 255.0); b = Int(t * 255.0)

Case 3

r = Int(p * 255.0); g = Int(q * 255.0); b = Int(Value * 255.0)

Case 4

r = Int(t * 255.0); g = Int(p * 255.0); b = Int(Value * 255.0)

Case 5

r = Int(Value * 255.0); g = Int(p * 255.0); b = Int(q * 255.0)

EndSelect

EndFunction

Function SetHSV(h:Float, s:Float, v:Float)

Hue = h
Huei = Int(h)
Saturation = s
Saturationi = Int(s * 100)
Value = v
Valuei = Int(v * 100)

EndFunction

End Type


Beispiel:
BlitzMax: [AUSKLAPPEN]
SuperStrict

TColorSpaceHSV.ConvertRGBToHSV( 127, 90, 12 )

Print TColorSpaceHSV.Huei
Print TColorSpaceHSV.Saturationi
Print TColorSpaceHSV.Valuei

Global r:Int, g:Int, b:Int

TColorSpaceHSV.ConvertHSVToRGB( r, g, b )

Print r
Print g
Print b


Hatte ich noch auf Lager, musste nur kurz aufbereitet werden.
Ich hatte im Codearchiv nur den HSL Teil veröffentlicht: https://www.blitzforum.de/foru...hlight=hsl
Vielleicht kannst du damit ja auch was anfangen.
Blessed is the mind too small for doubt

Xeres

Moderator

BeitragSa, Aug 14, 2010 17:00
Antworten mit Zitat
Benutzer-Profile anzeigen
RGB zu HSV von Markus2, von Nilium... such dir was passendes aus.

Was das speichern angeht; Das kommt wohl darauf an, was du vor hast und wie groß die Tilemaps sind. Du könntest ja nur den Lichtbereich in ein Bild zeichnen und dieses mit Light- oder shadeblend über die Tilemap rendern lassen.
Win10 Prof.(x64)/Ubuntu 16.04|CPU 4x3Ghz (Intel i5-4590S)|RAM 8 GB|GeForce GTX 960
Wie man Fragen richtig stellt || "Es geht nicht" || Video-Tutorial: Sinus & Cosinus
T
HERE IS NO FAIR. THERE IS NO JUSTICE. THERE IS JUST ME. (Death, Discworld)

M0rgenstern

BeitragSa, Aug 14, 2010 17:13
Antworten mit Zitat
Benutzer-Profile anzeigen
Erst mal danke an DaysShadow für den Code. Wollte grade anfangen mir sone Funktion selbst zu schriben (wikipedia ser dank^^).
und auch danke an Xeres für die Links^^

Zitat:
Du könntest ja nur den Lichtbereich in ein Bild zeichnen und dieses mit Light- oder shadeblend über die Tilemap rendern lassen.


Wie genau meinst du das?
Das hab ich schon versucht, also einfach einen hellen Kreis mit lightblend an einer STelle über die Map zu legen, aber das sieht sch.... aus.
Außerdem könnte ich so keine Wände mit einbeziehen. Es geht halt drum, dass ich das ganze auf Basis der Mapdaten berechnen möchte. Also Wände sollen das Licht nicht durchlassen. Welche Tiles das sind weiß ich.
Wenn ich aber nur einzelne Tiles übeprüfe, dann wird das licht "eckig" (also wenn ich alle Tiles im Lichtradius beleuchte mit color 255,255,255).
Das ganze müsste ich also auf die einzelnen Pixel der Tiles umrechnen und prüfen welcher Pixel noch im einzugsbereich liegt.
Dafür müsste ich dann aber jedem Tile ein Bild zuordnen um eben dieses mit Writepixel zu bearbeiten. Oder hab ich da was falsch verstanden?
Wahrscheinlich geh ich das ganze wieder viel zu kompliziert an.

Lg, M0rgenstern

ComNik

BeitragSa, Aug 14, 2010 17:44
Antworten mit Zitat
Benutzer-Profile anzeigen
Also ich kenne deinen weissen Kreis nicht, aber dein Kreis Bild muss so aussehen:

user posted image

Nicht einfach weiss, sondern ein Farbverlauf von weiss nach Schwarz an den Rändern.

Den zeichnest du. Bei mir sah es damals mit ganz normalem Alphablend ganz gut aus, leider kannte ich da Lightblend noch nicht, wenn ich den Code wiederfinde probier ichs aus...


Schatten sind ein wenig komplizierter, du musst alle Kanten des Schatten Werfenden Objektes durchgehen,
dir immer die vorherige merken, und dann vergleichen ob sie in gegensätzliche Richtungen zeigen bzw einen Winkel Unterschied von > 180 haben. Das lässt sich leicht mit ein bisschen Vektorgeometrie ausrechnen (Senkrechte zur Kante bilden, Skalaprodukt(senkrechte Kante, vorherige Kante) > 90). Wenn ja dann kannst du den Punkt zwischen den Beiden als einen Eckpunkt des Schattens speichern. Wenn du alles durch bist, hast du alle Kantenpunkte des Schattens. Für jeden Kantenpunkt rechnest du jetzt den Entfernungsvektor Punkt - Lichtquelle aus und verlängerst den um einen beliebigen Wert (zum Beispiel die Länge des Distanzvektors, oder einfach Global Sonnenstand:Float oder wasweisich...). Nun musst du (Werbung: z.B mit Avas Gfx Engine Wink) zwischen den berechneten und den vorhin herausgefundenen Punkten (bei 2 Kantenpunkten hast du jetzt 4 Punkte) ein dunkles Polygon zeichnen. Das geht sogar auch nativ mit Max2D, da du ja nichts texturieren musst.

Um den Rechenaufwand zu verkleinern könntest du Kantenpunkte vor der Mainloop berechnen und speichern.

Das wäre die grundlegende Vorgehensweise meiner Meinung nach. Weiche Schattenkanten und Selbstbeleuchtung musst du da noch einbauen.

Bei vielen Teiles würde es btw Sinn machen nur die Kantenpunkte der Kanten-Tiles zu berechnen...

lg
ComNik
WIP: Vorx.Engine

Xeres

Moderator

BeitragSa, Aug 14, 2010 18:42
Antworten mit Zitat
Benutzer-Profile anzeigen
M0rgenstern hat Folgendes geschrieben:
Das hab ich schon versucht, also einfach einen hellen Kreis mit lightblend an einer STelle über die Map zu legen, aber das sieht sch.... aus.
Du erstellst ein Bild in der größe, in den der Lichtschein sichtbar sein soll, in das zeichnest du die Pixel anstatt auf die Tiles und zeichnest es, wenn es fertig, ist drüber.
Win10 Prof.(x64)/Ubuntu 16.04|CPU 4x3Ghz (Intel i5-4590S)|RAM 8 GB|GeForce GTX 960
Wie man Fragen richtig stellt || "Es geht nicht" || Video-Tutorial: Sinus & Cosinus
T
HERE IS NO FAIR. THERE IS NO JUSTICE. THERE IS JUST ME. (Death, Discworld)

M0rgenstern

BeitragSa, Aug 14, 2010 19:08
Antworten mit Zitat
Benutzer-Profile anzeigen
Hm.
Also, das heißt, einfach so ein Bild wie das was ComNik gezeigt hat und in das soll ich dann die Pixel der Tiles reinschreiben, und das Bild dann an entsprechender Stelle malen?
Also, auf das Bild mit Writepixel zeichnen? Das heißt, ich muss die Pixelinformationen des Bildes mit denen der Tiles addieren?

Sorry, aber ich hab mit dem Pixelwirrwar manchmal meine Probleme^^...

@ComNik:
Vielen Dank, aber so gut kenn ich mich dann doch in Vektorrechnung nicht aus.

Lg, M0rgenstern

Xeres

Moderator

BeitragSa, Aug 14, 2010 19:14
Antworten mit Zitat
Benutzer-Profile anzeigen
Du erstellst ein neues, leeres Bild in einer passenden größe, um den kompletten Lichtschein ein zu fangen. In dieses Bild zeichnest du die Pixel heller, die beschienen werden, dann hast du in etwa ComNiks bild, nur mit Beachtung der Wände / Schattenwurf.
Aber: Hab's selber noch nicht gemacht, keine Ahnung wie das schlussendlich aussieht.
Mach erst ein Test auf freier Fläche, wenn's gut aussieht kümmer' dich um die Beachtung von Wänden!
Win10 Prof.(x64)/Ubuntu 16.04|CPU 4x3Ghz (Intel i5-4590S)|RAM 8 GB|GeForce GTX 960
Wie man Fragen richtig stellt || "Es geht nicht" || Video-Tutorial: Sinus & Cosinus
T
HERE IS NO FAIR. THERE IS NO JUSTICE. THERE IS JUST ME. (Death, Discworld)

M0rgenstern

BeitragSa, Aug 14, 2010 19:16
Antworten mit Zitat
Benutzer-Profile anzeigen
Achsooooo...
Ja gut.
Jetzt weiß ich was du meinst.
Werd ich soäter mal testen. Hört sich ganz gut an.
Vielen Dank.

Lg, M0rgenstern

Neue Antwort erstellen


Übersicht BlitzMax, BlitzMax NG Beginners-Corner

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group