U ovoj 3D animaciji modeliramo Sunce, Zemlju i Mesec kao jednostavan hijerarhijski sistem:
Zemlja se okreće oko Sunca, a Mesec oko Zemlje. Svako nebesko telo rotira oko svog
„nosioca“ sopstvenom uglovnom brzinom. Za prikaz koristimo p5.js u WEBGL
režimu,
sa teksturama planeta koje daju realističan izgled i prikaz orbita.
Fizika u pozadini je kinematička: u svakom frejmu povećavamo ugao pomoću izraza
Δθ = ω·Δt
, a zatim crtamo svaku sferu u polarnim koordinatama
(r·cos θ, r·sin θ)
u odnosu na telo oko kog se okreće. Osvetljenje i prikaz putanja
pomažu u razumevanju kretanja u prostoru.
Detalje o matematici i programiranju ove simulacije upotrebom jave i processing-a možete pronaći na našem vodiču: SvetProgramiranja: Java i simulacije u fizici .
ω
pri svakom osvežavanju.(x, y, z)
.Ispod se nalazi interaktivni 3D prikaz. Povucite mišem da rotirate kameru i koristite skrol točkić da biste zumirali. Prikazane orbite pomažu u vizuelizaciji kretanja svakog tela.
Animacija ne radi? Učitajte stranicu ponovo(refresh)
← scroll horizontal →
U ovoj 3D simulaciji koristimo p5.js u WEBGL
režimu.
Model je hijerarhijski: Zemlja orbitira oko Sunca, a Mesec oko Zemlje.
Svako telo ima svoju sopstvenu i orbitalnu uglovnu brzinu, a teksture i
rasveta poboljšavaju realizam.
Globalne promenljive:
sunIm, earthIm, moonIm, spaceIm
— teksture za planete i pozadinu.sun, earth, moon
— objekti klase Planeta
sa parametrima radius, distance, parent, speedRotation, speedOwnRotation i emission.glContext
— WebGL kontekst za čišćenje Z-buffera nakon crtanja pozadine.preload():
loadImage(prefix + "Textures/…")
.setup():
determineSize()
, a zatim kreiramo createCanvas(widthS, heightS, WEBGL)
.Planeta
za Sunce, Zemlju i Mesec.canvasDrawing.GL
) služi za čišćenje dubine pre crtanja 2D pozadine.draw():
image(spaceIm,…)
i potom čistimo Z-buffer.ambientLight(255)
) i omogućavamo orbitControl za rotaciju kamere mišem.sun.orbit()
i sun.show()
rekursivno animira i crta sve planete.Klasa Planeta
:
orbit()
— inkrementira uglove angle
i angleOwn
, pa rekurzivno poziva orbit za decu.show()
— crta orbitu kao krug, rotira u odnosu na ugao i pozicionira sferu sa teksturom ili materijalom.
var canvasDrawing; // gl platno za crtanje 3D scene
let container, containerMain; // HTML elementi za smeštaj canvasa
var isMobile; // da li je uređaj mobilni (orientacija != undefined)
var widthS;
var heightS;
let sun; // glavni objekat - Sunce
let prevMouse; // prethodna pozicija miša (prilikom pritiska)
let zoom = 1; // faktor zumiranja scene
let drag; // vektor za pomeranje svetla
let sunIm, earthIm, moonIm, spaceIm; // teksture za planete i pozadinu
let wEarth = 0.005, wEarth1 = 0.08, wMoon = -0.2; // brzine rotacije (orbitna i oko ose)
let z = 0; // visinska komponenta svetla
let dLight = 200; // udaljenost izvora svetla od centra
var rSun, rEarth, rMoon; // poluprečnici tela
let prefix = "/js/p5js/SunceOrbita/"; // putanja do foldera sa teksturama
let glContext; // WebGL kontekst za upravljanje z-buferom
function setup() {
isMobile = window.orientation > -1; // proveri mobilni uređaj
determineSize(); // izračunaj dimenzije canvasa
drag = createVector(0, 0); // inicijalizuj vektor za svetlosni drag
canvasDrawing = createCanvas(widthS, heightS, WEBGL); // kreiraj WebGL canvas
container = document.getElementById('canvasForHTML');
canvasDrawing.parent(container); // ubači canvas u kontejner
glContext = canvasDrawing.GL; // preuzmi WebGL kontekst
// izračunaj poluprečnike tela na osnovu širine ekrana
rSun = widthS / 20;
rEarth = rSun / 4;
rMoon = rEarth / 3;
// kreiraj instance planeta i postavi hijerarhiju
sun = new Planeta(rSun, 0, null, sunIm, 0, 0, color(255));
earth = new Planeta(rEarth, widthS / 3, sun, earthIm, wEarth, wEarth1);
moon = new Planeta(rMoon, rEarth + rMoon + 25, earth, moonIm, wMoon, 0);
smooth(16); // anti-aliasing za glatke ivice
}
function preload() {
// učitaj sve teksture pre starta scene
sunIm = loadImage(prefix + "Textures/sun1.jpg");
earthIm = loadImage(prefix + "Textures/earth1.jpg");
moonIm = loadImage(prefix + "Textures/moon.jpg");
spaceIm = loadImage(prefix + "Textures/space.jpg");
}
function determineSize() {
// prilagodi dimenzije canvasa na osnovu rezolucije i orijentacije
if (isMobile) {
if (displayWidth > 1200) {
heightS = 2 * displayHeight / 3;
widthS = 2 * displayWidth * 3;
} else if (displayWidth < displayHeight) {
widthS = displayWidth;
heightS = 2 * widthS / 3;
} else {
widthS = 2 * displayWidth / 3;
heightS = 2 * widthS / 3;
}
} else {
// za desktop koristimo full-width
widthS = displayWidth;
heightS = 2 * widthS / 3;
}
// ograničenje maksimalne širine radi održavanja proporcija
if (widthS > 940) {
widthS = 940;
heightS = 627;
}
}
function windowResized() {
determineSize(); // ponovo preračunaj dimenzije
resizeCanvas(widthS, heightS, false);
}
function draw() {
// postavi pozadinu svemira i očisti Z-bufer pre 3D crtanja
spaceIm.resize(widthS, heightS);
imageMode(CENTER);
background(0,0,0,0);
push();
camera(0,0,-300,0,0,0,0,-1,0);
image(spaceIm,0,0,1.5*widthS,1.5*heightS);
glContext.clear(glContext.DEPTH_BUFFER_BIT);
pop();
noStroke();
ambientLight(255); // globalna ambijentalna svetlosna komponenta
orbitControl(); // interaktivno upravljanje kamerom mišem/tačom
rotateX(10*PI/24);
rotateY(PI/64);
// ažuriraj položaje svih tela pre iscrtavanja
sun.orbit();
sun.show();
}
function mousePressed() {
// pamti poziciju miša pri kliku za kasnija svetlosna podešavanja
prevMouse = createVector(mouseX, mouseY);
// pomeranje svetla u z-osi strelicama
if (key == "ArrowRight") { z += 1; }
else if (key == "ArrowLeft") { z -= 1; }
}
function mouseWheel(event) {
// zumiranje scene točkićem miša
zoom += event.delta * 0.0005;
}
class Planeta {
constructor(radius, distance, parent, texture, speedRotation, speedOwnRotation, emission) {
// postavi osnovne parametre tela
this.radius = radius;
this.distance = distance;
this.parent = parent;
this.texture = texture;
this.emission = emission;
// nasumične početne rotacije za dinamičniji efekat
this.angle = random(2*PI);
this.angleOwn = random(2*PI);
this.speedRotation = speedRotation;
this.speedOwnRotation = speedOwnRotation;
this.children = [];
// dodaj sebe u listu dece roditelja (hijerarhija sistema tela)
if (parent) { parent.children.push(this); }
}
orbit() {
// pomeri ugao orbite i rotacije oko ose
this.angle += this.speedRotation;
this.angleOwn += this.speedOwnRotation;
// rekurzivno ažuriraj decu
for (let planet of this.children) {
planet.orbit();
}
}
show() {
// sačuvaj trenutni matriks transformacije
push();
// 1) iscrtaj orbitu kao krug u ravni xy
push();
strokeWeight(0.5);
stroke(color(200,200,100));
noFill();
ellipse(0,0,this.distance*2);
pop();
// 2) ako telo emituje svetlo (sunce), postavi pointLight izvore
if (this.emission) {
fill(this.emission);
scale(100);
// šest pointLight izvora oko centra za realističniji efekat
pointLight(this.emission, drag.x+dLight, drag.y, z);
pointLight(this.emission, drag.x-dLight, drag.y, z);
pointLight(this.emission, drag.x, drag.y+dLight, z);
pointLight(this.emission, drag.x, drag.y-dLight, z);
pointLight(this.emission, drag.x, drag.y, z+dLight);
pointLight(this.emission, drag.x, drag.y, z-dLight);
scale(0.01);
}
// 3) rotiraj i pozicioniraj telo u odnosu na roditelja
rotate(-this.angle);
translate(this.distance, 0);
rotate(-this.angleOwn);
// 4) nanesi teksturu ili boju i izlistaj sferu
if (this.emission) {
texture(this.texture);
sphere(this.radius);
ambientLight(this.emission);
} else {
if (this.texture != null) {
texture(this.texture);
} else {
ambientMaterial(255);
}
sphere(this.radius);
}
// 5) rekurzivno iscrtaj sve satelite (decу) ovog tela
for (let planet of this.children) {
planet.show();
}
// vrati prethodni matriks transformacije
pop();
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="libraries/p5.min.js"></script>
<script src="zemlja_sunce_mesec_animacija.js"></script>
</head>
<body>
<div id="canvasForHTML" class="canvasForHTML"></div>
<p>Animacija ne radi? Osvežite stranicu. </p>
</body>
</html>