IlluminatedSprite - Beleuchtungseffekte nur mit Sprites

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.
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7031
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

IlluminatedSprite - Beleuchtungseffekte nur mit Sprites

Beitrag von STARGÅTE »

Beitragslink zum Modul-Code
Beitragslink zu den Beispielen

Hallo Leute,

mit diesem kleinen Module (Code im nächsten Post) möchte ich euch zeigen, dass auch mit nativen PureBasic Sprite-Befehlen eine glaubwürdige Beleuchtung von Bildern ganz ohne 3D möglich ist. Alles was ihr dazu braucht ist das Bild als Diffuse Map (also die normale Farbdarstellung) und als Normal Map (also die Darstellung der Oberflächenneigungen).
Mit dem Modul könnt ihr dann beleuchtete Sprites (IlluminatedSprites) erstellen und diese auf dem Bildschirm wie normale Sprites anzeigen, mit dem unterschied, dass diese über in die Szene gesetzte Lichter (SpriteLight) beleuchtete werden können (Beispiele im übernächsten Post):
Bild

Was das Modul macht ist, aus der Diffuse Map und der Normal Map ein Set von verschiedenen Beleuchtungsrichtungen zu erstellen und dieses dann beim Anzeigen entsprechend mischen:
Bild + Bild = Bild
Beim ausführen von DisplayIlluminatedSprite() wird dann aus allen Lichtern eine Beleuchtungsversion gemischt und dargestellt. Mehr Lichter (also viele Lichter) führen also nicht so mehr Sprites die angezeigt werden müssen. Pro IlluminatedSprite werden maximal 30 DisplaySprite Befehle ausgeführt.
Das ist natürlich trotzdem alles andere als effizient und akkurat gegenüber echten Lösungen mit Shadern, das soll hier aber auch gar nicht das Thema sein. Es soll einfach nur ein "Trick" sein und zeigen was mit reinem PureBasic ohne 3D Engine möglich ist. Und für das eine oder andere kleine Spiel vielleicht ein nettes AddOn.
Zuletzt geändert von STARGÅTE am 08.01.2025 14:57, insgesamt 1-mal geändert.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7031
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: IlluminatedSprite - Beleuchtungseffekte nur mit Sprites

Beitrag von STARGÅTE »

Quellcode des Moduls:

Code: Alles auswählen

;- Illuminated Sprites

DeclareModule IlluminatedSprites
	
	Enumeration
		#SpriteLight_Ambient      ; An ambient light source that illuminates the whole sprite angle independent.
		#SpriteLight_Directional  ; A directional light source that illuminates the sprite from the specified direction.
		#SpriteLight_Point        ; A point light source that illuminates the sprite from the specified position and range.
	EndEnumeration
	
	; Procedure for the illuminated sprites:	
	Declare.i CreateIlluminatedSprite(Sprite_DiffuseMap.i, Sprite_NormalMap.i, WeightingZ.f = 1.0)   ; Creates an IlluminatedSprite from a diffuse and normal map.
	Declare.i DisplayIlluminatedSprite(*IlluminatedSprite, X.f, Y.f, Z.f=0.0)                        ; Displays the IlluminatedSprite at the given position.
	Declare.i RotateIlluminatedSprite(*IlluminatedSprite, Angle.f)                                   ; Rotates the IlluminatedSprite to the given angle in degree.
	Declare.i ZoomIlluminatedSprite(*IlluminatedSprite, Factor.f)                                    ; Zooms the IlluminatedSprite by the given factor relative to the original size.
	Declare.i FreeIlluminatedSprite(*IlluminatedSprite)                                              ; Deletes the given IlluminatedSprite
	
	; Procedure for the light sources:	
	Declare.i CreateSpriteLight(Type.i, Color.q, Intensity.f=1.0, X.f=0, Y.f=0, Z.f=0, Range.f=1000.0)  ; Create a SpriteLight with the given color and parameters:
	                                                                                                    ; - #SpriteLight_Ambient: X, Y, Z and Range have no meaning.
	                                                                                                    ; - #SpriteLight_Directional: X, Y, and Z are the direction, Range has no meening.
	                                                                                                    ; - #SpriteLight_Point: X, Y, and Z are the position, Range is the max distance of illumination.
	Declare.i FreeSpriteLight(*SpriteLight)                       ; Deletes the given SpriteLight
	Declare.i SpriteLightIntensity(*SpriteLight, Intensity.f)     ; Changes the intensity of a light source
	Declare.i SpriteLightRange(*SpriteLight, Range.f)             ; Changes the range of a point light source
	Declare.i SpriteLightDirection(*SpriteLight, X.f, Y.f, Z.f)   ; Changes the direction of a directional light source
	Declare.i SpriteLightPosition(*SpriteLight, X.f, Y.f, Z.f=0)  ; Changes the position of a point light source
	
EndDeclareModule


Module IlluminatedSprites
	
	EnableExplicit
	
	#Inverse255 = 1.0/255.0
	#TwoInverse255 = 2.0/255.0
	#SquaredInverse255 = 1.0/(255.0*255.0)
	#LightDirections = 10
	
	Structure Color
		Channel.f[0]
		R.f
		G.f
		B.f
		A.f
	EndStructure
	
	Structure Vector
		X.f
		Y.f
		Z.f
	EndStructure
	
	Structure IlluminatedSprite
		Sprite_Diffuse.i
		Sprite_Color.i
		Rotation.f
		Zoom.f
		Width.i
		Height.i
	EndStructure
	
	Global NewList IlluminatedSprite.IlluminatedSprite()
	
	Structure SpriteLight
		Type.i
		Color.Color
		StructureUnion
			Position.Vector
			Direction.Vector
		EndStructureUnion
		Intensity.f
		Range.f
	EndStructure
	
	Global NewList SpriteLight.SpriteLight()
	
	
	;- Some helper vector math functions
	
	Procedure.f Vector_Length(*Vector.Vector)
		ProcedureReturn Sqr(*Vector\X**Vector\X+*Vector\Y**Vector\Y+*Vector\Z**Vector\Z)
	EndProcedure
	
	Procedure.f Vector_Dot(*Vector1.Vector, *Vector2.Vector)
		ProcedureReturn *Vector1\X * *Vector2\X + *Vector1\Y * *Vector2\Y + *Vector1\Z * *Vector2\Z
	EndProcedure
	
	Procedure.i Vector_Normalize(*Vector.Vector)
		Protected Length.f = Vector_Length(*Vector)
		*Vector\X / Length  :  *Vector\Y / Length  :  *Vector\Z / Length
		ProcedureReturn *Vector
	EndProcedure
	
	Procedure.i Vector_Rotate(*Vector.Vector, Angle.f)
		Protected OldVector.Vector
		OldVector\X = *Vector\X  :  OldVector\Y = *Vector\Y
		*Vector\X = Cos(Angle) * OldVector\X - Sin(Angle) * OldVector\Y
		*Vector\Y = Sin(Angle) * OldVector\X + Cos(Angle) * OldVector\Y
		ProcedureReturn *Vector
	EndProcedure
	
	;- Procedures for the lights
	
	Procedure.i CreateSpriteLight(Type.i, Color.q, Intensity.f=1.0, X.f=0, Y.f=0, Z.f=0, Range.f=1000.0)
		
		Select Type
			Case #SpriteLight_Ambient
				AddElement(SpriteLight())
				SpriteLight()\Type      = #SpriteLight_Ambient
			Case #SpriteLight_Directional
				AddElement(SpriteLight())
				SpriteLight()\Type        = #SpriteLight_Directional
				SpriteLight()\Direction\X = X
				SpriteLight()\Direction\Y = Y
				SpriteLight()\Direction\Z = Z
				Vector_Normalize(SpriteLight()\Direction)
			Case #SpriteLight_Point
				AddElement(SpriteLight())
				SpriteLight()\Type  = #SpriteLight_Point
				SpriteLight()\Position\X = X
				SpriteLight()\Position\Y = Y
				SpriteLight()\Position\Z = Z
			Default
				ProcedureReturn #False
		EndSelect
		
		With SpriteLight()
			\Color\R   = #Inverse255 * Red(Color)
			\Color\G   = #Inverse255 * Green(Color)
			\Color\B   = #Inverse255 * Blue(Color)
			\Range     = Range
			\Intensity = Intensity
		EndWith
				
		ProcedureReturn SpriteLight()
		
	EndProcedure
	
	Procedure   FreeSpriteLight(*SpriteLight.SpriteLight)
		
		ChangeCurrentElement(SpriteLight(), *SpriteLight)
		DeleteElement(SpriteLight())
		
	EndProcedure
	
	Procedure.i SpriteLightPosition(*SpriteLight.SpriteLight, X.f, Y.f, Z.f=0.0)
		
		*SpriteLight\Position\X = X
		*SpriteLight\Position\Y = Y
		*SpriteLight\Position\Z = Z
		
	EndProcedure
	
	Procedure.i SpriteLightDirection(*SpriteLight.SpriteLight, X.f, Y.f, Z.f)
		
		*SpriteLight\Direction\X = X
		*SpriteLight\Direction\Y = Y
		*SpriteLight\Direction\Z = Z
		Vector_Normalize(*SpriteLight\Direction)
		
	EndProcedure
	
	Procedure.i SpriteLightIntensity(*SpriteLight.SpriteLight, Intensity.f)
		
		*SpriteLight\Intensity = Intensity
		
	EndProcedure
	
	Procedure.i SpriteLightRange(*SpriteLight.SpriteLight, Range.f)
		
		*SpriteLight\Range = Range
		
	EndProcedure
	
	
	;- Procedures for the illuminated sprites
	
	Procedure.i CreateIlluminatedSprite(Sprite_DiffuseMap.i, Sprite_NormalMap.i, WeightingZ.f = 1.0)
		
		Protected Dim Color.Color(0, 0), *Color.Color
		Protected Dim Normal.Vector(0, 0), *Normal.Vector
		Protected *IlluminationDirection.Vector
		Protected Sprite_ColorMap.i, Color.l
		Protected X.i, Y.i, Width.i, Height.i
		Protected Dot.f, DotXY.f, DotWeighted.f
		Protected Temp.Vector
		Protected Part.i
	
		If Not IsSprite(Sprite_DiffuseMap) Or Not IsSprite(Sprite_NormalMap)
			ProcedureReturn #False
		EndIf
		
		Width  = SpriteWidth(Sprite_DiffuseMap)
		Height = SpriteHeight(Sprite_NormalMap)
		If Width <> SpriteWidth(Sprite_NormalMap) Or Height <> SpriteHeight(Sprite_NormalMap)
			ProcedureReturn #False
		EndIf
		
		Dim Color(Width-1, Height-1)
		Dim Normal(Width-1, Height-1)
		
		; Import color data from the diffuse map.
		If StartDrawing(SpriteOutput(Sprite_DiffuseMap))
			DrawingMode(#PB_2DDrawing_AllChannels)
			For Y = Height-1 To 0 Step -1
				For X = Width-1 To 0 Step -1
					Color    = Point(X, Y)
					*Color   = Color(X, Y)
					*Color\R = #SquaredInverse255 * Alpha(Color) * Red(Color)
					*Color\G = #SquaredInverse255 * Alpha(Color) * Green(Color)
					*Color\B = #SquaredInverse255 * Alpha(Color) * Blue(Color)
				Next
			Next
			StopDrawing()
		EndIf
		
		; Import normal vector data from the normal map.
		If StartDrawing(SpriteOutput(Sprite_NormalMap))
			DrawingMode(#PB_2DDrawing_AllChannels)
			For Y = Height-1 To 0 Step -1
				For X = Width-1 To 0 Step -1
					Color     = Point(X, Y)
					*Normal   = Normal(X, Y)
					*Normal\X = #TwoInverse255 * Red(Color) - 1.0
					*Normal\Y = -(#TwoInverse255 * Green(Color) - 1.0)
					*Normal\Z = (#TwoInverse255 * Blue(Color) - 1.0) * WeightingZ
					Vector_Normalize(*Normal)
				Next
			Next
			StopDrawing()
		EndIf
		
		; Create the special illuminated Sprite, a grid of three colors,
		;   each having the ambient light version and nine light directions.
		*IlluminationDirection = ?IlluminationDirections
		Sprite_ColorMap = CreateSprite(#PB_Any, #LightDirections*Width, 3*Height)
		If StartDrawing(SpriteOutput(Sprite_ColorMap))
			For Part = 0 To #LightDirections-1
				For Y = Height-1 To 0 Step -1
					For X = Width-1 To 0 Step -1
						*Normal = Normal(X, Y)
						*Color  = Color(X, Y)
						If Part > 0
							Dot = Vector_Dot(*Normal, *IlluminationDirection)
							Temp\X = *Normal\X
							Temp\Y = *Normal\Y
							Vector_Normalize(Temp)
							DotXY = Vector_Dot(Temp, *IlluminationDirection)
							If Part = 1
								DotWeighted = Pow(Cos(ACos(Dot)*1.0),1.01) 
							Else
								DotWeighted = DotXY * Pow(Cos(ACos(Dot)*1.0),2.01)
							EndIf
							If Dot > 0.0 And DotWeighted > 0
								Plot(X+Width*Part, Y+Height*0, RGB(*Color\R*DotWeighted*255, 0, 0))
								Plot(X+Width*Part, Y+Height*1, RGB(0, *Color\G*DotWeighted*255, 0))
								Plot(X+Width*Part, Y+Height*2, RGB(0, 0, *Color\B*DotWeighted*255))
							EndIf
						Else
							Plot(X, Y+Height*0, RGB(*Color\R*255, 0, 0))
							Plot(X, Y+Height*1, RGB(0, *Color\G*255, 0))
							Plot(X, Y+Height*2, RGB(0, 0, *Color\B*255))
						EndIf
					Next
				Next
				*IlluminationDirection + SizeOf(Vector)
			Next
			StopDrawing()
		EndIf
		
		AddElement(IlluminatedSprite())
		With IlluminatedSprite()
			\Sprite_Diffuse   = Sprite_DiffuseMap
			\Sprite_Color     = Sprite_ColorMap
			\Width            = Width
			\Height           = Height
			\Rotation         = 0
			\Zoom             = 1.0
		EndWith
		
		ProcedureReturn IlluminatedSprite()
		
	EndProcedure
	
	Procedure   FreeIlluminatedSprite(*IlluminatedSprite.IlluminatedSprite)
		
		FreeSprite(*IlluminatedSprite\Sprite_Color)
		ChangeCurrentElement(IlluminatedSprite(), *IlluminatedSprite)
		DeleteElement(IlluminatedSprite())
		
	EndProcedure
	
	Procedure.i DisplayColorChannels(*IlluminatedSprite.IlluminatedSprite, *Color.Color, Part.i, X.f, Y.f)
		
		Protected I.i
		
		With *IlluminatedSprite
			For I = 0 To 2
				If *Color\Channel[I] > 0
					ClipSprite(\Sprite_Color, \Width*Part, \Height*I, \Width, \Height)
					RotateSprite(\Sprite_Color, \Rotation, #PB_Absolute)
					ZoomSprite(\Sprite_Color, \Width*\Zoom, \Height*\Zoom)
					DisplayTransparentSprite(\Sprite_Color, X, Y, 255 * *Color\Channel[I])
					;If *Color\Channel[I] > 1 ; One more drawing if the intensity is brighter than 1.0, not used at the moment
					;	DisplayTransparentSprite(\Sprite_Color, X, Y, 255 * (*Color\Channel[I]-1))
					;EndIf
				EndIf
			Next
		EndWith
		
	EndProcedure
	
	Procedure.i RotateIlluminatedSprite(*IlluminatedSprite.IlluminatedSprite, Angle.f)
		
		*IlluminatedSprite\Rotation = Angle
		
	EndProcedure
	
	Procedure.i ZoomIlluminatedSprite(*IlluminatedSprite.IlluminatedSprite, Factor.f)
		
		*IlluminatedSprite\Zoom = Factor
		
	EndProcedure
	
	Procedure.i DisplayIlluminatedSprite(*IlluminatedSprite.IlluminatedSprite, X.f, Y.f, Z.f=0.0)
		
		Protected *IlluminationDirections.Vector
		Protected LightColor.Color
		Protected Part.i, Direction.Vector, Intensity.f, Distance.f, Weighting.f
		Protected View.Vector
		
		With *IlluminatedSprite
			
			RotateSprite(\Sprite_Diffuse, \Rotation, #PB_Absolute)
			ZoomSprite(\Sprite_Diffuse, \Width*\Zoom, \Height*\Zoom)
			DisplayTransparentSprite(\Sprite_Diffuse, X-\Zoom*\Width/2, Y-\Zoom*\Height/2, 255, 0)
			
			SpriteBlendingMode(#PB_Sprite_BlendSourceAlpha, #PB_Sprite_BlendOne)
			*IlluminationDirections = ?IlluminationDirections
			For Part = 0 To #LightDirections-1
				ResetStructure(@LightColor, Color)
				ForEach SpriteLight()
					Intensity = 0.0
					Select SpriteLight()\Type
						Case #SpriteLight_Ambient
							If Part = 0
								Intensity = SpriteLight()\Intensity
							EndIf
						Case #SpriteLight_Directional
							Direction = SpriteLight()\Direction
							Vector_Rotate(Direction, -Radian(\Rotation))
							If Part = 1
								Intensity = Vector_Dot(Direction, *IlluminationDirections) * SpriteLight()\Intensity
							Else
								Intensity = Vector_Dot(Direction, *IlluminationDirections) * SpriteLight()\Intensity * 0.7
							EndIf
						Case #SpriteLight_Point
							Direction\X = SpriteLight()\Position\X - X
							Direction\Y = SpriteLight()\Position\Y - Y
							Direction\Z = SpriteLight()\Position\Z - Z
							Distance = Vector_Length(Direction)
							If Distance < SpriteLight()\Range
								Vector_Normalize(Direction)
								Vector_Rotate(Direction, -Radian(\Rotation))
								If Part = 1
									Intensity = Vector_Dot(Direction, *IlluminationDirections) * SpriteLight()\Intensity * (1 - Distance/SpriteLight()\Range) 
								Else
									Intensity = Vector_Dot(Direction, *IlluminationDirections) * SpriteLight()\Intensity * (1 - Distance/SpriteLight()\Range) * 0.7
								EndIf
							EndIf
					EndSelect
					If Intensity > 0
						LightColor\R + Intensity * SpriteLight()\Color\R
						LightColor\G + Intensity * SpriteLight()\Color\G
						LightColor\B + Intensity * SpriteLight()\Color\B
					EndIf
				Next
				DisplayColorChannels(*IlluminatedSprite, LightColor, Part, X-\Zoom*\Width/2, Y-\Zoom*\Height/2)
				*IlluminationDirections + SizeOf(Vector)
			Next
			SpriteBlendingMode(#PB_Sprite_BlendSourceAlpha, #PB_Sprite_BlendInvertSourceAlpha)
			
		EndWith
		
	EndProcedure
	
	
	DataSection
		IlluminationDirections:
		Data.f  0.0,  0.0, 0.0  ; Ambient
		Data.f  0.0,  0.0, 1.0  ; Front
		Data.f  1.0,  0.0, 0.0  ; Right
		Data.f  0.0,  1.0, 0.0  ; Bottom
		Data.f -1.0,  0.0, 0.0  ; Left
		Data.f  0.0, -1.0, 0.0  ; Top
		Data.f  0.7,  0.7, 0.0  ; Right-Bottom
		Data.f -0.7,  0.7, 0.0  ; Left-Bottom
		Data.f -0.7, -0.7, 0.0  ; Left-Top
		Data.f  0.7, -0.7, 0.0  ; Right-Top
	EndDataSection
	
	
EndModule
Zuletzt geändert von STARGÅTE am 09.01.2025 15:11, insgesamt 1-mal geändert.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7031
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: IlluminatedSprite - Beleuchtungseffekte nur mit Sprites

Beitrag von STARGÅTE »

Beispiele zu dem Modul:

Info: Die jeweiligen Beispiel-Bilder werden vom Code selbst runtergeladen und in Temp abgespeichert.

Bild

Code: Alles auswählen

UseModule IlluminatedSprites

Enumeration
	#Window
	#Font
	#Sprite_Moon_DiffuseMap
	#Sprite_Moon_NormalMap
	#Sprite_LightPoint
EndEnumeration

UsePNGImageDecoder()
UsePNGImageEncoder()

InitSprite()
	
LoadFont(#Font, "Arial", 12, #PB_Font_Bold)

OpenWindow(#Window, 0, 0, 900, 600, "Illuminated Sprites", #PB_Window_ScreenCentered|#PB_Window_MinimizeGadget)
OpenWindowedScreen(WindowID(#Window), 0, 0, WindowWidth(#Window), WindowHeight(#Window))

CreateSprite(#Sprite_LightPoint, 16, 16)
If StartDrawing(SpriteOutput(#Sprite_LightPoint))
	Circle(8,8,8,$FFFFFF)
	StopDrawing()
EndIf

If FileSize(GetTemporaryDirectory()+"moon_diffuse.png") = -1
	ReceiveHTTPFile("https://data.unionbytes.de/illuminatedsprite/moon_diffuse.png", GetTemporaryDirectory()+"moon_diffuse.png")
EndIf
If FileSize(GetTemporaryDirectory()+"moon_normal.png") = -1
	ReceiveHTTPFile("https://data.unionbytes.de/illuminatedsprite/moon_normal.png", GetTemporaryDirectory()+"moon_normal.png")
EndIf
LoadSprite(#Sprite_Moon_DiffuseMap, GetTemporaryDirectory()+"moon_diffuse.png", #PB_Sprite_AlphaBlending)
LoadSprite(#Sprite_Moon_NormalMap, GetTemporaryDirectory()+"moon_normal.png")

; Create an IlluminatedSprite from the two sprites of the moon with DiffuseMap and NormalMap
Define Moon = CreateIlluminatedSprite(#Sprite_Moon_DiffuseMap, #Sprite_Moon_NormalMap)

CreateSpriteLight(#SpriteLight_Ambient, $202020) ;- Base illumination for all illuminated Sprites
CreateSpriteLight(#SpriteLight_Directional, $C00000, 1.0, 1.0, -0.7, 0.0) ;- Directional illumination

Define Light_Red = CreateSpriteLight(#SpriteLight_Point, $0000FF) ;- Point-light illumination
Define Light_Green = CreateSpriteLight(#SpriteLight_Point, $00FF40, 1.0, 0, 0, 0, 500) ;- Point-light illumination with shorter range

Define Time.f

Repeat
	
	Repeat
		
		Select WindowEvent()
			Case #PB_Event_None
				Break
			Case #PB_Event_CloseWindow
				Break 2
		EndSelect
		
	ForEver
	
	Time = ElapsedMilliseconds()/2000
	
	ClearScreen($000000)
	
	SpriteBlendingMode(#PB_Sprite_BlendSourceAlpha, #PB_Sprite_BlendInvertSourceAlpha)
		
	RotateIlluminatedSprite(Moon, Time*20)
	ZoomIlluminatedSprite(Moon, 1.0)
	DisplayIlluminatedSprite(Moon, ScreenWidth()/3, ScreenHeight()/2)
	
	ZoomSprite(#Sprite_Moon_DiffuseMap, 128, 128)
	RotateSprite(#Sprite_Moon_DiffuseMap, 0, #PB_Absolute)
	DisplayTransparentSprite(#Sprite_Moon_DiffuseMap, ScreenWidth()*0.8, ScreenHeight()*0.2)
	
	ZoomSprite(#Sprite_Moon_NormalMap, 128, 128)
	RotateSprite(#Sprite_Moon_NormalMap, 0, #PB_Absolute)
	DisplayTransparentSprite(#Sprite_Moon_NormalMap, ScreenWidth()*0.8, ScreenHeight()*0.6)
	
	If Light_Red
		SpriteLightPosition(Light_Red, (Cos(Time)+1)*ScreenWidth()/2, (Sin(Time*2.4)+1)*ScreenHeight()/2, 50)
		DisplayTransparentSprite(#Sprite_LightPoint, (Cos(Time)+1)*ScreenWidth()/2-8, (Sin(Time*2.4)+1)*ScreenHeight()/2-8, 255, $0000FF)
	EndIf
		
	If Light_Green
		If WindowMouseX(#Window) = -1 And  WindowMouseY(#Window) = -1
			SpriteLightIntensity(Light_Green, 0.0)
		Else
			SpriteLightIntensity(Light_Green, 1.0)
			SpriteLightPosition(Light_Green, WindowMouseX(#Window), WindowMouseY(#Window), 50)
			DisplayTransparentSprite(#Sprite_LightPoint, WindowMouseX(#Window)-8, WindowMouseY(#Window)-8, 255, $00FF00)
		EndIf
	EndIf
	
	If StartDrawing(ScreenOutput())
		DrawingMode(#PB_2DDrawing_Transparent)
		DrawingFont(FontID(#Font))
		DrawText(710, 70, "Sprite: Diffuse map")
		DrawText(710, 300, "Sprite: Normal map")
 		DrawText(230, 70, "IlluminatedSprite")
		StopDrawing()
	EndIf
	
	FlipBuffers()
	
ForEver

End
Bild

Code: Alles auswählen

UseModule IlluminatedSprites

Enumeration
	#Window
	#Font
	#Sprite_Tower_DiffuseMap
	#Sprite_Tower_NormalMap
	#Sprite_LightPoint
EndEnumeration

UsePNGImageDecoder()

InitSprite()

LoadFont(#Font, "Arial", 12, #PB_Font_Bold)

OpenWindow(#Window, 0, 0, 800, 500, "Illuminated Sprites", #PB_Window_ScreenCentered|#PB_Window_MinimizeGadget)
OpenWindowedScreen(WindowID(#Window), 0, 0, WindowWidth(#Window), WindowHeight(#Window))

CreateSprite(#Sprite_LightPoint, 16, 16)
If StartDrawing(SpriteOutput(#Sprite_LightPoint))
	Circle(8,8,8,$FFFFFF)
	StopDrawing()
EndIf

	
If FileSize(GetTemporaryDirectory()+"castle_tower_diffuse.png") = -1
	ReceiveHTTPFile("https://data.unionbytes.de/illuminatedsprite/castle_tower_diffuse.png", GetTemporaryDirectory()+"castle_tower_diffuse.png")
EndIf
If FileSize(GetTemporaryDirectory()+"castle_tower_normal.png") = -1
	ReceiveHTTPFile("https://data.unionbytes.de/illuminatedsprite/castle_tower_normal.png", GetTemporaryDirectory()+"castle_tower_normal.png")
EndIf
LoadSprite(#Sprite_Tower_DiffuseMap, GetTemporaryDirectory()+"castle_tower_diffuse.png", #PB_Sprite_AlphaBlending)
LoadSprite(#Sprite_Tower_NormalMap, GetTemporaryDirectory()+"castle_tower_normal.png")


Define Tower = CreateIlluminatedSprite(#Sprite_Tower_DiffuseMap, #Sprite_Tower_NormalMap)

CreateSpriteLight(#SpriteLight_Ambient, $202020) ;- Base illumination for all illuminated Sprites

Define Light_Orange = CreateSpriteLight(#SpriteLight_Point, $80C0FF, 1.0, 800)	;- Point-light illumination
Define Light_Green   = CreateSpriteLight(#SpriteLight_Point, $40FF20, 1.0, 0, 0, 0, 300)	;- Point-light illumination with shorter range

Define Time.f

Repeat
	
	Repeat
		
		Select WindowEvent()
			Case #PB_Event_None
				Break
			Case #PB_Event_CloseWindow
				Break 2
		EndSelect
		
	ForEver
	
	Time = ElapsedMilliseconds()/2000
	
	ClearScreen($003010)
	
	SpriteBlendingMode(#PB_Sprite_BlendSourceAlpha, #PB_Sprite_BlendInvertSourceAlpha)
	
	If Light_Orange
		SpriteLightPosition(Light_Orange, 400+Sin(Time*2)*150, 300+Cos(Time*2)*100, 20)
		DisplayTransparentSprite(#Sprite_LightPoint, 400+Sin(Time*2)*150-8, 300+Cos(Time*2)*100-8, 255, $80C0FF)
	EndIf
	
	If Light_Green
		If WindowMouseX(#Window) = -1 And  WindowMouseY(#Window) = -1
			SpriteLightIntensity(Light_Green, 0.0)
		Else
			SpriteLightIntensity(Light_Green, 1.0)
			SpriteLightPosition(Light_Green, WindowMouseX(#Window), WindowMouseY(#Window), 20)
			DisplayTransparentSprite(#Sprite_LightPoint, WindowMouseX(#Window)-8, WindowMouseY(#Window)-8, 255, $40FF20)
		EndIf
	EndIf
	
	DisplayIlluminatedSprite(Tower, 400, 300)
	
	DisplayTransparentSprite(#Sprite_Tower_DiffuseMap, 100-SpriteWidth(#Sprite_Tower_DiffuseMap)/2, 300-SpriteHeight(#Sprite_Tower_DiffuseMap)/2)
	DisplayTransparentSprite(#Sprite_Tower_NormalMap, 700-SpriteWidth(#Sprite_Tower_NormalMap)/2, 300-SpriteHeight(#Sprite_Tower_NormalMap)/2)
	
	If StartDrawing(ScreenOutput())
		DrawingMode(#PB_2DDrawing_Transparent)
		DrawingFont(FontID(#Font))
		DrawText(30, 30, "Sprite: Diffuse map")
		DrawText(330, 30, "IlluminatedSprite")
		DrawText(630, 30, "Sprite: Normal map")
		StopDrawing()
	EndIf
	
	FlipBuffers()
	
ForEver

End
Zuletzt geändert von STARGÅTE am 08.01.2025 16:29, insgesamt 1-mal geändert.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Benutzeravatar
Macros
Beiträge: 1361
Registriert: 23.12.2005 15:00
Wohnort: Olching(bei FFB)
Kontaktdaten:

Re: IlluminatedSprite - Beleuchtungseffekte nur mit Sprites

Beitrag von Macros »

Wirklich beeindruckendes Ergebnis und das mit erstaunlich elegantem und kurzem Code!
Auch ein schönes Beispiel für Leute die Fragen: Wofür lerne ich die ganze Mathematik eigentlich?

Mir ist aufgefallen, dass wenn man das Licht mittig über den Mond bewegt der Eindruck entsteht er würde sich zusammenziehen.
Beim Turm tritt der Effekt nicht auf. Woher er kommt kann ich nicht einordnen.

Übrigens: Wer den gleichen Fehler macht wie ich und erst mal überlegt "Wo sind denn die Beispielbilder?"
STARGÅTE hat den Download in die Beispielcodes eingebaut :wink:

Vielen Dank fürs Teilen :D
Bild
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7031
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: IlluminatedSprite - Beleuchtungseffekte nur mit Sprites

Beitrag von STARGÅTE »

Danke für das Feedback Marcos.
Macros hat geschrieben: 08.01.2025 15:56 Mir ist aufgefallen, dass wenn man das Licht mittig über den Mond bewegt der Eindruck entsteht er würde sich zusammenziehen.
Beim Turm tritt der Effekt nicht auf. Woher er kommt kann ich nicht einordnen.
Das ist mir in der Tat auch aufgefallen. Ich würde behaupten, dass das eine optische Täuschung der Kugel-NormalMap ist.
Wenn das Licht genau über dem Mond ist, werden alle Mondstrukturen von oben also "innen" angeleuchtet. Dadurch liegen die grünen aufgehellten Bereiche näher am Mittelpunkt, das Bild wirkt kleiner.
Kommt das Licht von der Seite, sind alle Aufhellungen in die selbe Richtung "verschoben" und es gibt kein Zoom-Effekt.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Antworten