Web Analytics
Animacija Sunce–Zemlja–Mesec u p5.js

Sistem Sunce–Zemlja–Mesec

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 .

Ključni pojmovi

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.

Animacija3D: Rotacija Zemlje oko Sunca

Animacija ne radi? Učitajte stranicu ponovo(refresh)

← scroll horizontal →

Objašnjenje koda: animacija Sunce–Zemlja–Mesec

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():

  • Učitavamo slike pozivom loadImage(prefix + "Textures/…").

setup():

  • Određujemo dimenzije platna funkcijom determineSize(), a zatim kreiramo createCanvas(widthS, heightS, WEBGL).
  • Inicijalizujemo objekte Planeta za Sunce, Zemlju i Mesec.
  • Spremljeni WebGL kontekst (canvasDrawing.GL) služi za čišćenje dubine pre crtanja 2D pozadine.

draw():

  • Podešavamo 2D pozadinu uz image(spaceIm,…) i potom čistimo Z-buffer.
  • Postavljamo osnovno osvetljenje (ambientLight(255)) i omogućavamo orbitControl za rotaciju kamere mišem.
  • Hijerarhijski poziv 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.

zemlja_sunce_mesec_animacija.js


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();
  }
}

index.html


<!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>