Code : Tout sélectionner
; ============================================================
; BLACK HOLE SIMULATION - INTERPOLATED CIRCULAR LENS
; ============================================================
; - Black hole moving to the right
; - Stars scrolling to the left
; - Circular gravitational lens
; - Gradual interpolation at entry
; - Progressive attraction toward the center
; - Maximum deformation near the horizon
; - Gradual interpolation at exit
; - Radial + tangential deformation
; - Dynamic light flow
; - Perfectly black horizon
; ============================================================
EnableExplicit
; ============================================================
; CONFIGURATION
; ============================================================
#WIDTH = 1000
#HEIGHT = 700
#STARS = 700
#BLACK_HOLE_X = 180
#BLACK_HOLE_Y = 350
#HORIZON_X = 90
#HORIZON_Y = 55
#DISK_INNER = 95
#DISK_OUTER = 240
#BLACK_HOLE_SPEED = 65.0
#BACKGROUND_SPEED = 28.0
#LENS_RADIUS = 300.0
#LENS_STRENGTH = 1.35
#ATTRACTION_STRENGTH = 0.42
#TANGENTIAL_STRENGTH = 28.0
; ============================================================
; STAR STRUCTURE
; ============================================================
Structure Star
x.f
y.f
size.f
brightness.f
EndStructure
; ============================================================
; GLOBAL VARIABLES
; ============================================================
Global Dim Stars.Star(#STARS - 1)
Global Running = #True
Global Time.f = 0.0
Global HoleX.f = #BLACK_HOLE_X
Global HoleY.f = #BLACK_HOLE_Y
Global BackgroundOffset.f = 0.0
Define i
; ============================================================
; MINIMUM FUNCTION
; ============================================================
Procedure.i Min(a.i, b.i)
If a < b
ProcedureReturn a
EndIf
ProcedureReturn b
EndProcedure
; ============================================================
; MAXIMUM FUNCTION
; ============================================================
Procedure.i Max(a.i, b.i)
If a > b
ProcedureReturn a
EndIf
ProcedureReturn b
EndProcedure
; ============================================================
; FLOAT MINIMUM
; ============================================================
Procedure.f MinF(a.f, b.f)
If a < b
ProcedureReturn a
EndIf
ProcedureReturn b
EndProcedure
; ============================================================
; FLOAT MAXIMUM
; ============================================================
Procedure.f MaxF(a.f, b.f)
If a > b
ProcedureReturn a
EndIf
ProcedureReturn b
EndProcedure
; ============================================================
; SMOOTHSTEP INTERPOLATION
; ============================================================
Procedure.f SmoothStep(value.f)
Protected v.f
v = MinF(1.0, MaxF(0.0, value))
ProcedureReturn v * v * (3.0 - 2.0 * v)
EndProcedure
; ============================================================
; STAR INITIALIZATION
; ============================================================
RandomSeed(12345)
For i = 0 To #STARS - 1
Stars(i)\x = Random(#WIDTH - 1)
Stars(i)\y = Random(#HEIGHT - 1)
Stars(i)\size = 1.0 + Random(2)
Stars(i)\brightness = 0.35 + Random(65) / 100.0
Next
; ============================================================
; WINDOW CREATION
; ============================================================
If OpenWindow(0, 0, 0, #WIDTH/DesktopResolutionX(), #HEIGHT/DesktopResolutionY(), "Black Hole - Dynamic Gravitational Lens", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
CanvasGadget(0, 0, 0, #WIDTH, #HEIGHT)
Else
MessageRequester("Error", "Unable to create the window.")
End
EndIf
; ============================================================
; STARS + GRAVITATIONAL LENS
; ============================================================
Procedure DrawStars()
Protected i
Protected c
Protected starX.f
Protected starY.f
Protected dx.f
Protected dy.f
Protected distance.f
Protected lensStart.f
Protected lens.f
Protected attraction.f
Protected attractionCore.f
Protected exitInterpolation.f
Protected angle.f
Protected distortedAngle.f
Protected radial.f
Protected radialWarp.f
Protected tangentialWarp.f
Protected tangentialAngle.f
Protected cycle.f
Protected cycle2.f
Protected glass.f
Protected warpedX.f
Protected warpedY.f
; ----------------------------------------------------------
; STAR LOOP
; ----------------------------------------------------------
For i = 0 To #STARS - 1
; --------------------------------------------------------
; BACKGROUND SCROLLING TO THE LEFT
; --------------------------------------------------------
starX = Stars(i)\x - BackgroundOffset
If starX < -20
starX = starX + #WIDTH
EndIf
starY = Stars(i)\y
; --------------------------------------------------------
; DISTANCE FROM THE BLACK HOLE
; --------------------------------------------------------
dx = starX - HoleX
dy = starY - HoleY
distance = Sqr(dx * dx + dy * dy)
; --------------------------------------------------------
; ORIGINAL POSITION
; --------------------------------------------------------
warpedX = starX
warpedY = starY
; ========================================================
; GRAVITATIONAL LENS
; ========================================================
If distance < #LENS_RADIUS And distance > #HORIZON_X
; ------------------------------------------------------
; STAR ANGLE
; ------------------------------------------------------
angle = ATan2(dy, dx)
; ------------------------------------------------------
; ENTRY PROGRESSION
; ------------------------------------------------------
;
; 0.0 = outside the lens
; 1.0 = horizon
;
; ------------------------------------------------------
lensStart = (#LENS_RADIUS - distance) / (#LENS_RADIUS - #HORIZON_X)
lensStart = MinF(1.0, MaxF(0.0, lensStart))
; ------------------------------------------------------
; SMOOTH ENTRY INTERPOLATION
; ------------------------------------------------------
lens = SmoothStep(lensStart)
; ------------------------------------------------------
; PROGRESSIVE ACCENTUATION
; ------------------------------------------------------
lens = Pow(lens, 1.65)
; ======================================================
; ATTRACTION TOWARD THE CENTER
; ======================================================
;
; The attraction starts weak and progressively increases
; as the star approaches the horizon.
;
; ======================================================
attraction = lens * lens
; ------------------------------------------------------
; STRENGTHENING NEAR THE HORIZON
; ------------------------------------------------------
If distance < #HORIZON_X * 2.5
attractionCore = (#HORIZON_X * 2.5 - distance) / (#HORIZON_X * 1.5)
attractionCore = MinF(1.0, MaxF(0.0, attractionCore))
attractionCore = SmoothStep(attractionCore)
attraction = attraction * 0.65 + attractionCore * 0.35
EndIf
; ======================================================
; EXIT INTERPOLATION
; ======================================================
;
; When the star leaves the area near the black hole,
; the deformation is gradually released.
;
; ======================================================
exitInterpolation = distance / #LENS_RADIUS
exitInterpolation = MinF(1.0, MaxF(0.0, exitInterpolation))
exitInterpolation = SmoothStep(exitInterpolation)
; ------------------------------------------------------
; FINAL LENS PROFILE
; ------------------------------------------------------
lens = lens * (1.0 - exitInterpolation * 0.28)
; ------------------------------------------------------
; FINAL ATTRACTION
; ------------------------------------------------------
attraction = attraction * (1.0 - exitInterpolation * 0.35)
attraction = attraction * #ATTRACTION_STRENGTH
; ======================================================
; ANGULAR DEFORMATION
; ======================================================
cycle = Sin(angle * 2.0 + Time * 0.35)
cycle = cycle * lens
cycle = cycle * #LENS_STRENGTH
; ------------------------------------------------------
; SECOND WAVE
; ------------------------------------------------------
cycle2 = Sin(angle * 5.0 - Time * 0.55 + distance * 0.018)
cycle2 = cycle2 * lens * 0.28
; ------------------------------------------------------
; FINAL ANGLE
; ------------------------------------------------------
distortedAngle = angle + cycle + cycle2
; ======================================================
; RADIAL DEFORMATION
; ======================================================
radialWarp = lens * lens * 125.0
radial = distance + radialWarp
; ======================================================
; ATTRACTION TOWARD THE CENTER
; ======================================================
radial = radial * (1.0 - attraction)
; ------------------------------------------------------
; HORIZON PROTECTION
; ------------------------------------------------------
If radial < #HORIZON_X + 1.0
radial = #HORIZON_X + 1.0
EndIf
; ------------------------------------------------------
; DEFORMED RADIAL POSITION
; ------------------------------------------------------
warpedX = HoleX + Cos(distortedAngle) * radial
warpedY = HoleY + Sin(distortedAngle) * radial
; ======================================================
; TANGENTIAL DEFORMATION
; ======================================================
tangentialAngle = distortedAngle + 1.5707963
glass = Sin(angle * 4.0 - Time * 0.75 + distance * 0.025)
glass = glass * lens
tangentialWarp = glass * #TANGENTIAL_STRENGTH
warpedX = warpedX + Cos(tangentialAngle) * tangentialWarp
warpedY = warpedY + Sin(tangentialAngle) * tangentialWarp
; ======================================================
; SECOND TANGENTIAL DEFORMATION
; ======================================================
glass = Sin(angle * 7.0 + Time * 0.42 + distance * 0.035)
glass = glass * lens * lens
warpedX = warpedX + Cos(tangentialAngle) * glass * 16.0
warpedY = warpedY + Sin(tangentialAngle) * glass * 16.0
; ======================================================
; RELEASE ZONE AFTER THE HORIZON
; ======================================================
;
; This zone gradually releases the attraction
; as the star emerges.
;
; ======================================================
If distance > #HORIZON_X * 1.2 And distance < #HORIZON_X * 3.0
exitInterpolation = (distance - #HORIZON_X * 1.2) / (#HORIZON_X * 1.8)
exitInterpolation = MinF(1.0, MaxF(0.0, exitInterpolation))
exitInterpolation = SmoothStep(exitInterpolation)
; ------------------------------------------------------
; RADIAL RELEASE
; ------------------------------------------------------
radialWarp = (1.0 - exitInterpolation) * lens * 18.0
warpedX = warpedX + Cos(distortedAngle) * radialWarp
warpedY = warpedY + Sin(distortedAngle) * radialWarp
; ------------------------------------------------------
; TANGENTIAL RELEASE
; ------------------------------------------------------
glass = (1.0 - exitInterpolation) * lens
warpedX = warpedX + Cos(tangentialAngle) * glass * 10.0
warpedY = warpedY + Sin(tangentialAngle) * glass * 10.0
EndIf
; ======================================================
; EXTREME DEFORMATION AROUND THE HORIZON
; ======================================================
If distance < #HORIZON_X * 2.0
lensStart = (#HORIZON_X * 2.0 - distance) / #HORIZON_X
lensStart = MinF(1.0, MaxF(0.0, lensStart))
lensStart = SmoothStep(lensStart)
radialWarp = lensStart * lensStart * 24.0
warpedX = warpedX + Cos(distortedAngle) * radialWarp
warpedY = warpedY + Sin(distortedAngle) * radialWarp
EndIf
EndIf
; ========================================================
; OCCULTATION BY THE HORIZON
; ========================================================
If distance > #HORIZON_X
; ------------------------------------------------------
; NORMAL BRIGHTNESS
; ------------------------------------------------------
c = Int(255 * Stars(i)\brightness)
; ------------------------------------------------------
; BRIGHTNESS INSIDE THE LENS
; ------------------------------------------------------
If distance < #LENS_RADIUS
lensStart = (#LENS_RADIUS - distance) / (#LENS_RADIUS - #HORIZON_X)
lensStart = MinF(1.0, MaxF(0.0, lensStart))
lens = SmoothStep(lensStart)
c = Int(c * (1.0 + lens * 0.55))
c = Min(255, Max(0, c))
EndIf
; ------------------------------------------------------
; DRAW THE STAR
; ------------------------------------------------------
If warpedX > -20 And warpedX < #WIDTH + 20 And warpedY > -20 And warpedY < #HEIGHT + 20
Circle(Int(warpedX), Int(warpedY), Int(Stars(i)\size), RGB(c, c, c))
EndIf
EndIf
Next
EndProcedure
; ============================================================
; DYNAMIC ACCRETION DISK
; ============================================================
Procedure DrawAccretionDisk()
Protected x
Protected y
Protected dx.f
Protected dy.f
Protected distance.f
Protected normalized.f
Protected angle.f
Protected rotation.f
Protected wave.f
Protected plasma.f
Protected brightness.f
Protected red
Protected green
Protected blue
Protected noise.f
; ----------------------------------------------------------
; PLASMA ROTATION
; ----------------------------------------------------------
rotation = Time * 0.75
; ----------------------------------------------------------
; DISK LOOP
; ----------------------------------------------------------
For y = Int(HoleY) - #DISK_OUTER To Int(HoleY) + #DISK_OUTER
For x = Int(HoleX) - #DISK_OUTER To Int(HoleX) + #DISK_OUTER
dx = x - HoleX
dy = y - HoleY
; ------------------------------------------------------
; ELLIPTICAL DISTANCE
; ------------------------------------------------------
distance = Sqr(dx * dx + (dy * 2.5) * (dy * 2.5))
If distance > #DISK_INNER And distance < #DISK_OUTER
; ----------------------------------------------------
; ANGLE
; ----------------------------------------------------
angle = ATan2(dy, dx)
angle = angle + rotation
; ----------------------------------------------------
; RADIAL POSITION
; ----------------------------------------------------
normalized = (distance - #DISK_INNER) / (#DISK_OUTER - #DISK_INNER)
; ----------------------------------------------------
; FIRST PLASMA LAYER
; ----------------------------------------------------
plasma = Sin(angle * 8.0 + Time * 3.0)
plasma = (plasma + 1.0) * 0.5
; ----------------------------------------------------
; SECOND LAYER
; ----------------------------------------------------
wave = Sin(angle * 17.0 - Time * 5.0 + distance * 0.035)
wave = (wave + 1.0) * 0.5
; ----------------------------------------------------
; MIX
; ----------------------------------------------------
plasma = plasma * 0.65 + wave * 0.35
; ----------------------------------------------------
; RADIAL BRIGHTNESS
; ----------------------------------------------------
brightness = 1.0 - normalized
brightness = Pow(brightness, 1.8)
; ----------------------------------------------------
; PLASMA MOTION
; ----------------------------------------------------
brightness = brightness * (0.65 + plasma * 0.55)
; ----------------------------------------------------
; PULSATION
; ----------------------------------------------------
brightness = brightness * (0.88 + Sin(Time * 2.0) * 0.12)
; ----------------------------------------------------
; TURBULENCE
; ----------------------------------------------------
noise = Random(100) / 100.0
brightness = brightness * (0.90 + noise * 0.10)
; ----------------------------------------------------
; COLOR
; ----------------------------------------------------
red = 255
green = Int(35 + 220 * brightness)
blue = Int(4 + 45 * brightness)
; ----------------------------------------------------
; INTENSITY
; ----------------------------------------------------
red = Int(red * brightness)
green = Int(green * brightness)
blue = Int(blue * brightness)
; ----------------------------------------------------
; CLAMPING
; ----------------------------------------------------
red = Min(255, Max(0, red))
green = Min(255, Max(0, green))
blue = Min(255, Max(0, blue))
; ----------------------------------------------------
; DRAW
; ----------------------------------------------------
If brightness > 0.015
Plot(x, y, RGB(red, green, blue))
EndIf
EndIf
Next
Next
EndProcedure
; ============================================================
; DYNAMIC LIGHT FLOW
; ============================================================
Procedure DrawLensGlow()
Protected i
Protected j
Protected angle.f
Protected radius.f
Protected wave.f
Protected wave2.f
Protected intensity.f
Protected x.f
Protected y.f
Protected distortion.f
Protected red
Protected green
Protected blue
; ----------------------------------------------------------
; LIGHT RING
; ----------------------------------------------------------
For i = 0 To 260
angle = (i / 260.0) * 6.2831853
angle = angle + Time * (0.18 + Sin(i * 0.37) * 0.025)
For j = 0 To 9
radius = #HORIZON_X + 4.0 + j * 7.0
; ----------------------------------------------------
; MAIN WAVE
; ----------------------------------------------------
wave = Sin(angle * 7.0 + Time * 2.4 + radius * 0.055)
; ----------------------------------------------------
; SECOND WAVE
; ----------------------------------------------------
wave2 = Sin(angle * 13.0 - Time * 3.6 + radius * 0.09)
; ----------------------------------------------------
; INTENSITY
; ----------------------------------------------------
intensity = (wave + 1.0) * 0.5
intensity = intensity * 0.65 + ((wave2 + 1.0) * 0.5) * 0.35
; ----------------------------------------------------
; LIGHT FLOW DEFORMATION
; ----------------------------------------------------
distortion = Sin(angle * 3.0 + Time * 0.45) * 7.0
x = HoleX + Cos(angle) * (radius + distortion * intensity)
y = HoleY + Sin(angle) * (radius + distortion * intensity) * 0.48
; ----------------------------------------------------
; RADIAL ATTENUATION
; ----------------------------------------------------
intensity = intensity * (1.0 - j / 13.0)
; ----------------------------------------------------
; PULSATION
; ----------------------------------------------------
intensity = intensity * (0.65 + Sin(Time * 2.0 + i * 0.1) * 0.35)
; ----------------------------------------------------
; COLOR
; ----------------------------------------------------
red = 255
green = Int(45 + intensity * 210)
blue = Int(3 + intensity * 35)
; ----------------------------------------------------
; DRAW
; ----------------------------------------------------
If intensity > 0.32
Plot(Int(x), Int(y), RGB(red, green, blue))
EndIf
Next
Next
EndProcedure
; ============================================================
; BLACK HOLE HORIZON
; ============================================================
Procedure DrawBlackHole()
; ----------------------------------------------------------
; DARK HALO
; ----------------------------------------------------------
Circle(Int(HoleX), Int(HoleY), #HORIZON_X + 4, RGB(12, 2, 0))
; ----------------------------------------------------------
; HORIZON
; ----------------------------------------------------------
Circle(Int(HoleX), Int(HoleY), #HORIZON_X, RGB(0, 0, 0))
; ----------------------------------------------------------
; PERFECTLY BLACK CENTER
; ----------------------------------------------------------
Circle(Int(HoleX), Int(HoleY), #HORIZON_X - 8, RGB(0, 0, 0))
EndProcedure
; ============================================================
; SCENE
; ============================================================
Procedure DrawScene()
StartDrawing(CanvasOutput(0))
; ----------------------------------------------------------
; BACKGROUND
; ----------------------------------------------------------
Box(0, 0, #WIDTH, #HEIGHT, RGB(1, 1, 5))
; ----------------------------------------------------------
; STARS + LENS
; ----------------------------------------------------------
DrawStars()
; ----------------------------------------------------------
; ACCRETION DISK
; ----------------------------------------------------------
DrawAccretionDisk()
; ----------------------------------------------------------
; LIGHT FLOW
; ----------------------------------------------------------
DrawLensGlow()
; ----------------------------------------------------------
; BLACK HOLE
; ----------------------------------------------------------
DrawBlackHole()
StopDrawing()
EndProcedure
; ============================================================
; MAIN LOOP
; ============================================================
Define LastTime = ElapsedMilliseconds()
Define CurrentTime
Define Delta.f
Repeat
; ----------------------------------------------------------
; EVENT HANDLING
; ----------------------------------------------------------
Select WindowEvent()
Case #PB_Event_CloseWindow
Running = #False
EndSelect
; ----------------------------------------------------------
; TIME CALCULATION
; ----------------------------------------------------------
CurrentTime = ElapsedMilliseconds()
Delta = (CurrentTime - LastTime) / 1000.0
LastTime = CurrentTime
; ----------------------------------------------------------
; ANIMATION
; ----------------------------------------------------------
Time = Time + Delta
; ========================================================
; MOVE BLACK HOLE TO THE RIGHT
; ========================================================
HoleX = HoleX + #BLACK_HOLE_SPEED * Delta
If HoleX > #WIDTH + #HORIZON_X
HoleX = -#HORIZON_X
EndIf
HoleY = #BLACK_HOLE_Y
; ========================================================
; MOVE STARS TO THE LEFT
; ========================================================
BackgroundOffset = BackgroundOffset + #BACKGROUND_SPEED * Delta
If BackgroundOffset > #WIDTH
BackgroundOffset = BackgroundOffset - #WIDTH
EndIf
; ========================================================
; RENDER
; ========================================================
DrawScene()
; ========================================================
; APPROXIMATE 60 FPS LIMIT
; ========================================================
Delay(8)
Until Running = #False