Mesh from file, OrthoCamera, PerspectiveCamera Szécsi László 3D Grafikus Rendszerek 3. labor
OrthoCamera.js const OrthoCamera = function() { this.position = new Vec2(0.5, 0); this.rotation = 0; this.windowSize = new Vec2(2, 2); this.viewProjMatrix = new Mat4(); this.updateViewProjMatrix(); };
OrthoCamera.js OrthoCamera.prototype.updateViewProjMatrix = function(){ this.viewProjMatrix.set(). scale(0.5). scale(this.windowSize). rotate(this.rotation). translate(this.position). invert(); };
OrthoCamera.js – aspect ratio OrthoCamera.prototype.setAspectRatio = function(ar) { this.windowSize.x = this.windowSize.y * ar; this.updateViewProjMatrix(); };
Színtérmenedzsment vegyen fel a színtérbe egy kamerát resize esemény -> setAspectRatio
Geometria JSON-ból TriangleMeshGeometry.js a QuadGeometry.js mintájára a konstruktor kapjon egy mesh-leíró objektumot (ahogy az a JSON fileból jön) azért nem a filenevet adjuk át, mert egy fileban több mesh lehet vertices tulajdonság: 3n db koordináta folyotonos tömbben normals tulajdonság: 3n db érték folyotonos tömbben texturecoords tulajdonság: 2n db értéket tartalmazó folyotonos tömbök tömbje (de tipikusan csak egy textúrakoordináta-készlet van) faces tulajdonság: 3 indexet tartalmazó tömböcskék tömbje egy folytonos tömböt lehet belőle gyártani így: [].concat.apply([], jsonObject.faces)
Index buffer az indexek száma nem fix a konstruktorban el kell tárolni tulajdonságként rajzoláskor fel lehet használni
MultiMesh.js - betöltés const MultiMesh = function( gl, jsonModelFileUrl, materials) { this.meshes = []; const request = new XMLHttpRequest(); request.open("GET", jsonModelFileUrl); const theMultiMesh = this; request.onreadystatechange = function () { // next slide }; request.send();
MultiMesh.js – betöltés után if (request.readyState == 4) { const meshesJson = JSON.parse(request.responseText).meshes; for (let i = 0; i < meshesJson.length; i++) { theMultiMesh.meshes.push( new Mesh( new IndexedTrianglesGeometry( gl, meshesJson[i]), materials[i]) ); }
MultiMesh.js – rajzolás MultiMesh.prototype.draw = function(gl){ for (let i = 0; i < this.meshes.length; i++) { this.meshes[i].draw(gl); } };
Skálázni, forgatni kell
3D kamera PerspectiveCamera object view, proj mátrixok beállítása egérrel és WASD billentyűkkel mozgatható
Kamera paraméterek const PerspectiveCamera = function() { this.position = new Vec3(0.0, 0.0, 0.0); this.ahead = new Vec3(0.0, 0.0, -1.0); this.right = new Vec3(1.0, 0.0, 0.0); this.up = new Vec3(0.0, 1.0, 0.0); this.yaw = 0.0; this.pitch = 0.0; this.fov = 1.0; this.aspect = 1.0; this.nearPlane = 0.1; this.farPlane = 1000.0;
Tagváltozók mozgatáshoz this.speed = 0.5; this.isDragging = false; this.mouseDelta = new Vec2(0.0, 0.0);
Mátrixok this.viewMatrix = new Mat4(); this.projMatrix = new Mat4(); this.rayDirMatrix = new Mat4(); this.viewProjMatrix = new Mat4(); this.updateViewMatrix(); this.updateProjMatrix(); this.updateRayDirMatrix(); };
Világ felfelé iránya PerspectiveCamera.worldUp = new Vec3(0, 1, 0);
View mátrix számítása PerspectiveCamera.prototype.updateViewMatrix = function(){ this.viewMatrix.set( this.right.x , this.right.y , this.right.z , 0, this.up.x , this.up.y , this.up.z , 0, -this.ahead.x , -this.ahead.y , -this.ahead.z , 0, 0 , 0 , 0 , 1).translate(this.position).invert(); this.viewProjMatrix.set(this.viewMatrix).mul(this.projMatrix); };
Proj mátrix számítása PerspectiveCamera.prototype.updateProjMatrix = function() { var yScale = 1.0 / Math.tan(this.fov * 0.5); var xScale = yScale / this.aspect; var f = this.farPlane; var n = this.nearPlane; this.projMatrix.set( xScale , 0 , 0 , 0, 0 , yScale , 0 , 0, 0 , 0 , (n+f)/(n-f) , -1, 0 , 0 , 2*n*f/(n-f) , 0); this.viewProjMatrix.set(this.viewMatrix). mul(this.projMatrix); };
RayDir mátrix számítása extra RayDir mátrix számítása PerspectiveCamera.prototype.updateRayDirMatrix = function(){ // önállóan megoldandó feladat // de ráér // az env mapping háttérhez kell csak };
Mozgatás: yaw, pitch drag PerspectiveCamera.prototype.move = function(dt, keysPressed) { if(this.isDragging){ this.yaw -= this.mouseDelta.x * 0.002; this.pitch -= this.mouseDelta.y * 0.002; if(this.pitch > 3.14/2.0) { this.pitch = 3.14/2.0; } if(this.pitch < -3.14/2.0) { this.pitch = -3.14/2.0; this.mouseDelta = new Vec2(0.0, 0.0);
Mozgatás: főirányok a szögekből this.ahead = new Vec3( -Math.sin(this.yaw)*Math.cos(this.pitch), Math.sin(this.pitch), -Math.cos(this.yaw)*Math.cos(this.pitch) ); this.right.setVectorProduct( this.ahead, PerspectiveCamera.worldUp ); this.right.normalize(); this.up.setVectorProduct(this.right, this.ahead); }
Mozgatás gombokkal if(keysPressed.W) { this.position.addScaled(this.speed * dt, this.ahead); } if(keysPressed.S) { this.position.addScaled(-this.speed * dt, this.ahead); if(keysPressed.D) { this.position.addScaled(this.speed * dt, this.right); if(keysPressed.A) { this.position.addScaled(-this.speed * dt, this.right); if(keysPressed.E) { this.position.addScaled(this.speed * dt, PerspectiveCamera.worldUp); if(keysPressed.Q) { this.position.addScaled(-this.speed * dt, PerspectiveCamera.worldUp);
Mátrixok frissítése this.updateViewMatrix(); this.updateRayDirMatrix(); };
Eseménykezelők – feladat: meg is kell hívni őket! PerspectiveCamera.prototype.mouseDown = function() { this.isDragging = true; this.mouseDelta.set(); }; PerspectiveCamera.prototype.mouseMove = function(event) { this.mouseDelta.x += event.movementX; this.mouseDelta.y += event.movementY; event.preventDefault(); PerspectiveCamera.prototype.mouseUp = function() { this.isDragging = false;
Képméretarány állítása – feladat: ezt is meg kell hívni! // ar: canvas.clientWidth / canvas.clientHeight PerspectiveCamera.prototype.setAspectRatio = function(ar) { this.aspect = ar; this.updateProjMatrix(); };
Rakjuk össze! ortho kamera lecserélése mélységteszt bekapcsolása eseményfigyelők bekötése GameObject jó lehet még de figyeljünk rá, hogy a modelViewProjMatrix beállítása rajzoláshoz a PerspectiveCamera tulajdonságai alapján történjen forgatás még csak 2D
3D transzformáló vertex shader kell neki a modelViewProjMatrix továbbra is de kell egy modelMatrix uniform is nincs benne a kameratrafó modellből világkoordintákba transzformál új varying kimenet: worldPos és még kell egy modelMatrixInverse uniform a normálvektorok transzformálásához ezt most bal oldalról szorozzuk, mert az inverz transzponálttal kell a normálvektort transzformálni új varying kimenet: worldNormal
Fragment shader a 3D trafó ellenőrzéséhez rajzoljuk ki a worldNormal-t színként
Várt eredmény
Animáció játékobjektumok különböző move metódusokkal leszármaztatás? extra Animáció játékobjektumok különböző move metódusokkal leszármaztatás? lehetséges, de elég merev mozgatási logika függvényben, és adjuk ezt a függvényt értékül a megfelelő játékobjektumok move tulajdonságának! paraméterek: t - abszolút idő képletanimációhoz dt - időlépcső fizikai animációhoz keysPressed - vezérléshez gameObjects - interakcióhoz
Fizikai animáció move metódus mellett kapjon még az objektum extra Fizikai animáció move metódus mellett kapjon még az objektum lendületet (vagy sebességet) perdületet (vagy szögsebességet) tömeget, tehetetlenségi nyomatékot akár lehet is egy segédfüggvény ami ezeket mind rárakja egy objektumra gömbnyomásra erő, forgatónyomaték erő modellezési koordinátákban: transzformáljuk világba move-ban Euler integrálás "légellenállás" lendület, perdület exponenciálisan csökken az idővel
Csatolt objektum keringjen a mozgatott körül GameObject kiegészítés extra Csatolt objektum keringjen a mozgatott körül move: sima körmozgás origó körül GameObject kiegészítés updateModelMatrix a szülőét is frissítsük a teljes modellmátrix a saját pozíció/orientációból jövő és a szülőé szorzata előbb transzformálunk a szülő terébe, majd onnan tovább
Ütközés-detektálás repkedő aszteroidák extra Ütközés-detektálás repkedő aszteroidák ütközésdetektálás bennfoglaló körökkel honnan tudjuk, hogy mi a másik objektum? duck typing pl. ha van hitByAsteriod metódusa (pl. az űrhajónak), akkor ő reagálni tud aszteroidára a metódus aztán ellenőrzi, összeérnek-e valami hatást érvényesít az objektumon ugyanígy hitByShip is lehet (pl. az aszteroidának), abban meg az aszteroidára kifejtett hatást kezelhetjük
Ütközés-válasz ha ütközünk, történjen valami eltűnik az aszteroida extra Ütközés-válasz ha ütközünk, történjen valami eltűnik az aszteroida move visszatérés true/false, tömbből kirúgjuk ha false visszapattanunk róla ütközésválasz: sebességek ütközési normállal párhuzamos részének megfordítása * restitúciós tényező robbanás animációs fázis követése a GameObject-ben (vagy leszármazottban) közös uniform a materialban rajzolás előtt beállítani a GameObject.prototype.draw-ban
3. HÁZI FELADAT: biliárd ütközőgeometria: egy gömb per objektum, plusz 4 fal render-geometria betöltött gömb modell, textúrázott megjelenítéssel lehessen lőni space lenyomva + valamennyi idő eltelt a múlkori lövés óta: új golyó, kamera-előre-irányú kezdősebességgel orientáció: 3D forgatás mátrixszal, mindig csúszás nélkül gördül (sebességből minden frameben számolható a szögsebesség) ütközésdetektálás (2D-ben elég) minden gömböt minden gömbbel, plusz keret ütközésválasz (2D-ben elég) restitúciós tényező: 1.7 energiaveszteség: 30% per másodperc