Ich versuchte es.
Spritzer (Federn)
Wie in diesem Tutorial erwähnt, ist die Wasseroberfläche wie ein Draht: Wenn Sie an einem Punkt des Drahtes ziehen, werden die Punkte neben diesem Punkt ebenfalls nach unten gezogen. Alle Punkte werden auch auf eine Grundlinie zurückgeführt.
Es sind im Grunde viele vertikale Federn nebeneinander, die sich auch gegenseitig anziehen.
Ich habe das in Lua mit LÖVE skizziert und folgendes bekommen:
Sieht plausibel aus. Oh Hooke , du hübsches Genie.
Wenn Sie damit spielen möchten, ist hier ein JavaScript-Port von Phil ! Mein Code ist am Ende dieser Antwort.
Hintergrundwellen (gestapelte Sinus)
Natürliche Hintergrundwellen sehen für mich aus wie ein Bündel von Sinuswellen (mit unterschiedlichen Amplituden, Phasen und Wellenlängen), die alle zusammen addiert werden. So sah das aus, als ich es schrieb:
Die Interferenzmuster sehen ziemlich plausibel aus.
Jetzt alle zusammen
Dann ist es ziemlich einfach, die Splash-Wellen und die Hintergrundwellen zusammenzufassen:
Wenn Spritzer auftreten, können Sie kleine graue Kreise sehen, die anzeigen, wo sich die ursprüngliche Hintergrundwelle befinden würde.
Es sieht dem Video, das Sie verlinkt haben , sehr ähnlich , daher würde ich dies als ein erfolgreiches Experiment betrachten.
Hier ist meine main.lua
(die einzige Datei). Ich denke es ist ziemlich lesbar.
-- Resolution of simulation
NUM_POINTS = 50
-- Width of simulation
WIDTH = 400
-- Spring constant for forces applied by adjacent points
SPRING_CONSTANT = 0.005
-- Sprint constant for force applied to baseline
SPRING_CONSTANT_BASELINE = 0.005
-- Vertical draw offset of simulation
Y_OFFSET = 300
-- Damping to apply to speed changes
DAMPING = 0.98
-- Number of iterations of point-influences-point to do on wave per step
-- (this makes the waves animate faster)
ITERATIONS = 5
-- Make points to go on the wave
function makeWavePoints(numPoints)
local t = {}
for n = 1,numPoints do
-- This represents a point on the wave
local newPoint = {
x = n / numPoints * WIDTH,
y = Y_OFFSET,
spd = {y=0}, -- speed with vertical component zero
mass = 1
}
t[n] = newPoint
end
return t
end
-- A phase difference to apply to each sine
offset = 0
NUM_BACKGROUND_WAVES = 7
BACKGROUND_WAVE_MAX_HEIGHT = 5
BACKGROUND_WAVE_COMPRESSION = 1/5
-- Amounts by which a particular sine is offset
sineOffsets = {}
-- Amounts by which a particular sine is amplified
sineAmplitudes = {}
-- Amounts by which a particular sine is stretched
sineStretches = {}
-- Amounts by which a particular sine's offset is multiplied
offsetStretches = {}
-- Set each sine's values to a reasonable random value
for i=1,NUM_BACKGROUND_WAVES do
table.insert(sineOffsets, -1 + 2*math.random())
table.insert(sineAmplitudes, math.random()*BACKGROUND_WAVE_MAX_HEIGHT)
table.insert(sineStretches, math.random()*BACKGROUND_WAVE_COMPRESSION)
table.insert(offsetStretches, math.random()*BACKGROUND_WAVE_COMPRESSION)
end
-- This function sums together the sines generated above,
-- given an input value x
function overlapSines(x)
local result = 0
for i=1,NUM_BACKGROUND_WAVES do
result = result
+ sineOffsets[i]
+ sineAmplitudes[i] * math.sin(
x * sineStretches[i] + offset * offsetStretches[i])
end
return result
end
wavePoints = makeWavePoints(NUM_POINTS)
-- Update the positions of each wave point
function updateWavePoints(points, dt)
for i=1,ITERATIONS do
for n,p in ipairs(points) do
-- force to apply to this point
local force = 0
-- forces caused by the point immediately to the left or the right
local forceFromLeft, forceFromRight
if n == 1 then -- wrap to left-to-right
local dy = points[# points].y - p.y
forceFromLeft = SPRING_CONSTANT * dy
else -- normally
local dy = points[n-1].y - p.y
forceFromLeft = SPRING_CONSTANT * dy
end
if n == # points then -- wrap to right-to-left
local dy = points[1].y - p.y
forceFromRight = SPRING_CONSTANT * dy
else -- normally
local dy = points[n+1].y - p.y
forceFromRight = SPRING_CONSTANT * dy
end
-- Also apply force toward the baseline
local dy = Y_OFFSET - p.y
forceToBaseline = SPRING_CONSTANT_BASELINE * dy
-- Sum up forces
force = force + forceFromLeft
force = force + forceFromRight
force = force + forceToBaseline
-- Calculate acceleration
local acceleration = force / p.mass
-- Apply acceleration (with damping)
p.spd.y = DAMPING * p.spd.y + acceleration
-- Apply speed
p.y = p.y + p.spd.y
end
end
end
-- Callback when updating
function love.update(dt)
if love.keyboard.isDown"k" then
offset = offset + 1
end
-- On click: Pick nearest point to mouse position
if love.mouse.isDown("l") then
local mouseX, mouseY = love.mouse.getPosition()
local closestPoint = nil
local closestDistance = nil
for _,p in ipairs(wavePoints) do
local distance = math.abs(mouseX-p.x)
if closestDistance == nil then
closestPoint = p
closestDistance = distance
else
if distance <= closestDistance then
closestPoint = p
closestDistance = distance
end
end
end
closestPoint.y = love.mouse.getY()
end
-- Update positions of points
updateWavePoints(wavePoints, dt)
end
local circle = love.graphics.circle
local line = love.graphics.line
local color = love.graphics.setColor
love.graphics.setBackgroundColor(0xff,0xff,0xff)
-- Callback for drawing
function love.draw(dt)
-- Draw baseline
color(0xff,0x33,0x33)
line(0, Y_OFFSET, WIDTH, Y_OFFSET)
-- Draw "drop line" from cursor
local mouseX, mouseY = love.mouse.getPosition()
line(mouseX, 0, mouseX, Y_OFFSET)
-- Draw click indicator
if love.mouse.isDown"l" then
love.graphics.circle("line", mouseX, mouseY, 20)
end
-- Draw overlap wave animation indicator
if love.keyboard.isDown "k" then
love.graphics.print("Overlap waves PLAY", 10, Y_OFFSET+50)
else
love.graphics.print("Overlap waves PAUSED", 10, Y_OFFSET+50)
end
-- Draw points and line
for n,p in ipairs(wavePoints) do
-- Draw little grey circles for overlap waves
color(0xaa,0xaa,0xbb)
circle("line", p.x, Y_OFFSET + overlapSines(p.x), 2)
-- Draw blue circles for final wave
color(0x00,0x33,0xbb)
circle("line", p.x, p.y + overlapSines(p.x), 4)
-- Draw lines between circles
if n == 1 then
else
local leftPoint = wavePoints[n-1]
line(leftPoint.x, leftPoint.y + overlapSines(leftPoint.x), p.x, p.y + overlapSines(p.x))
end
end
end
Für die Lösung (mathematisch gesehen können Sie das Problem mit der Lösung von Differentialgleichungen lösen, aber ich bin mir sicher, dass sie es nicht so machen) der Erzeugung von Wellen haben Sie 3 Möglichkeiten (abhängig davon, wie detailliert es werden soll):
Lösung 1
Ganz einfach, wir berechnen für jede Welle den (absoluten) Abstand von jedem Punkt der Oberfläche zur Quelle und berechnen die 'Höhe' mit der Formel
1.0f/(dist*dist) * sin(dist*FactorA + Phase)
wo
Beachten Sie, dass wir beliebig viele Begriffe addieren können (Überlagerungsprinzip).
Profi
Contra
Lösung 2
Profi
Contra
Lösung 3
Jetzt bin ich auf eine harte Wand gestoßen, das ist die komplizierteste Lösung.
Ich habe dieses nicht implementiert, aber es ist möglich, diese Monster zu lösen.
Hier finden Sie eine Präsentation über die Mathematik, es ist nicht einfach und es gibt auch Differentialgleichungen für verschiedene Arten von Wellen.
Hier ist eine nicht vollständige Liste mit einigen Differentialgleichungen zur Lösung speziellerer Fälle (Solitons, Peakons, ...)
Profi
Contra
Lösung 4
Etwas komplizierter als Lösung 1, aber nicht so kompliziert wie Lösung 3.
Wir verwenden vorberechnete Texturen und mischen sie zusammen. Danach verwenden wir ein Verschiebungs-Mapping (eigentlich eine Methode für 2D-Wellen, aber das Prinzip kann auch für 1D-Wellen funktionieren).
Das Spiel sturmovik hat diesen Ansatz verwendet, aber ich finde den Link zum Artikel darüber nicht.
Profi
Contra
quelle
Um konstante Wellen hinzuzufügen, fügen Sie ein paar Sinuswellen hinzu, nachdem Sie die Dynamik berechnet haben. Der Einfachheit halber würde ich diese Verschiebung nur grafisch darstellen und nicht die Dynamik selbst beeinflussen lassen, aber Sie könnten beide Alternativen ausprobieren und sehen, welche am besten funktioniert.
Um das "Splashhole" zu verkleinern, würde ich vorschlagen, die Methode Splash (int index, float speed) so zu ändern, dass sie nicht nur den Index, sondern auch einige der nahen Scheitelpunkte direkt beeinflusst, um den Effekt zu verteilen, aber immer noch die gleiche " Energie". Die Anzahl der betroffenen Scheitelpunkte kann davon abhängen, wie breit Ihr Objekt ist. Wahrscheinlich müssen Sie den Effekt viel optimieren, bevor Sie ein perfektes Ergebnis erzielen.
Um die tieferen Teile des Wassers zu texturieren, können Sie entweder wie im Artikel beschrieben vorgehen und den tieferen Teil "blauer" machen, oder Sie können zwischen zwei Texturen interpolieren, abhängig von der Wassertiefe.
quelle