• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

question-closed Rysunek nie pojawia się na canvasie

Cloud VPS
+1 głos
397 wizyt
pytanie zadane 11 sierpnia 2022 w JavaScript przez Jackoza Początkujący (410 p.)
zamknięte 12 sierpnia 2022 przez Jackoza

Od jakiegoś czasu zajmuję się grafiką 3d z użyciem canvasa 2d w javascripcie. Na początek chcę tylko narysować kostkę w perspektywie. Mam już gotowe funkcje, które wyliczają perspektywę. (sprawdzałem ich działanie w konsoli i działają) Problem niestety jest taki, że nie pojawiają mi się ściany tej kostki. Co ciekawe, gdy próbuje je narysować ręcznie w konsoli to wszystko działa.

To jest fragment kodu w którym może występować problem:

let cube_size = 100;
let cube = new Object3D(
    [
        new Vertex(-1*cube_size, -1*cube_size, 1*cube_size),
        new Vertex(-1*cube_size,  1*cube_size, 1*cube_size),
        new Vertex( 1*cube_size,  1*cube_size, 1*cube_size),
        new Vertex( 1*cube_size, -1*cube_size, 1*cube_size),
        new Vertex(-1*cube_size, -1*cube_size, -1*cube_size),
        new Vertex(-1*cube_size,  1*cube_size, -1*cube_size),
        new Vertex( 1*cube_size,  1*cube_size, -1*cube_size),
        new Vertex( 1*cube_size, -1*cube_size, -1*cube_size),
    ],
    [
        // FRONT
        [0,1,2],
        [0,3,2],
    ]
);

let loop = () => {
    requestAnimationFrame(loop);

    ctx.fillStyle = "#000";
    ctx.fillRect(0,0,canvas.width,canvas.height);
    project(cube).forEach(face => {
        ctx.beginPath();
        ctx.moveTo(face.face[0].x,face.face[0].y);
        ctx.moveTo(face.face[1].x,face.face[1].y);
        ctx.moveTo(face.face[2].x,face.face[2].y);
        ctx.closePath();
        ctx.fillStyle = face.color;
        ctx.fill();
    });
}; loop();

Obiekt cube jest odpowiedzialny za model kostki. Funkcja project oblicza wielokąty (zwraca tablicę), które pokażą się po wyrenderowaniu kostki. Potem "przeglądam" te wielokąty metodą forEach. Każdy wielokąt jest w postaci obiektu:

{ face: [tablica z punktami (każdy punkt to obiekt {x:x,y:y})], color: kolor wielokąta }

i na koniec te wielokąty (tak naprawdę trójkąty) rysuję na canvasie. Niestety właśnie one się nie pojawiają.

Nie mam pojęcia skąd to się bierze.

Byłbym wdzięczny za wszelką pomoc.

komentarz zamknięcia: problem rozwiązany
komentarz 11 sierpnia 2022 przez ScriptyChris Mędrzec (190,190 p.)
    requestAnimationFrame(loop);

Czy to Ci nie tworzy pętli nieskończonej? Usuń to wywołanie i sprawdź czy ruszy.

Co zwraca funkcja project - tablicę? Czy forEach się wykonuje? Są jakieś błędy w konsoli?

komentarz 11 sierpnia 2022 przez Jackoza Początkujący (410 p.)
forEach się wykonuje bo jak wsadzam tam console.log() to się normalnie wyświetla w konsoli,

tak funkcja project zwraca tablicę.

Ta pętla ma być tak, bo ona odświeża klatki. Jak zakomentowałem requestAnimationFrame to i tak było jak było.
komentarz 11 sierpnia 2022 przez ScriptyChris Mędrzec (190,190 p.)

A gdzie jest definiowana zmienna ctx? Skoro pętla się wykonuje i konsola nie rzuca błędami, to nie wiem o co chodzi; przydało by się to przetestować - możesz wrzucić na CodePen albo JSFiddle.

komentarz 11 sierpnia 2022 przez Jackoza Początkujący (410 p.)
w osobnym pliku
komentarz 11 sierpnia 2022 przez Jackoza Początkujący (410 p.)

to jest plik index.html

<!DOCTYPE html>
<html lang="pl">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width">
    <title>Canvas Game 3D</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        body {
            background-color: #222;
            display: flex;
            align-items: center;
            justify-content: center;
            min-height: 100vh;
        }

        #canvas {
            box-shadow: inset 0px 0px 10px 0px rgba(255, 255, 255, 1),
                0px 0px 10px 0px rgba(255, 255, 255, 1);
            border-radius: 10px;
            padding: 10px;
        }
    </style>
</head>
<body>
    <canvas id="canvas" width="800" height="600"></canvas>
    <script src="canvas.js"></script>
    <script src="3d.js"></script>
    <script src="game.js"></script>
</body>
</html>

Potem skrypty:

canvas.js

const canvas = document.querySelector("#canvas");
const ctx = canvas.getContext("2d");

3d.js (wszystkie obliczenia 3d)

function Vertex(x,y,z) {
    this.x = parseFloat(x);
    this.y = parseFloat(y);
    this.z = parseFloat(z);
}

Vertex.prototype.translate = function(vector) {
    this.x += vector.x;
    this.y += vector.y;
    this.z += vector.z;
    return this;
}

function Vertex2D(x,y) {
    this.x = parseFloat(x);
    this.y = parseFloat(y);
}

function Object3D(vertices, faces) {
    this.faces = [];

    faces.forEach(face => {
        let face_vertices = [];
        face.forEach(i => face_vertices.push(vertices[i]));
        this.faces.push(face_vertices);
    });
}

Object3D.prototype.translate = function(vector) {
    this.faces.forEach(face => {
        face.forEach(vertex => {
            vertex.x += vector.x;
            vertex.y += vector.y;
            vertex.z += vector.z;
        });
    });
    return this;
}

Object3D.prototype.rotate = function(rotation, center) {
    this.faces.forEach(face => {
        face.forEach(vertex => {
            vertex.y = vertex.y*Math.cos(rotation.x) - vertex.z*Math.sin(rotation.x);
            vertex.z = vertex.y*Math.sin(rotation.x) + vertex.z*Math.cos(rotation.x);

            vertex.x = vertex.z*Math.sin(rotation.y) - vertex.x*Math.cos(rotation.y);
            vertex.z = vertex.y*Math.cos(rotation.y) + vertex.x*Math.sin(rotation.y);

            vertex.x = vertex.x*Math.cos(rotation.z) - vertex.y*Math.sin(rotation.z);
            vertex.y = vertex.x*Math.sin(rotation.z) + vertex.y*Math.cos(rotation.z);
        });
    });
    this.translate(new Vertex(center.x,center.y,center.z));
    return this;
}

function project(object, callback) {
    let result = [];
    let dx = canvas.width/2, dy = canvas.height/2, focal_length = 800;

    object.faces.forEach(face => {
        let color = "#fff";

        let projected_face = [];
        face.forEach(vertex => {
            let projected_vertex = new Vertex2D(0,0);

            projected_vertex.x = vertex.x * (focal_length / (vertex.z + focal_length)) + dx;
            projected_vertex.y = vertex.y * (focal_length / (vertex.z + focal_length)) + dy;

            projected_face.push(projected_vertex);
        });
        result.push({face:projected_face, color:color});
    });

    return result;
}

game.js (główny plik ze skryptem)

let cube_size = 100;
let cube = new Object3D(
    [
        new Vertex(-1*cube_size, -1*cube_size, 1*cube_size),
        new Vertex(-1*cube_size,  1*cube_size, 1*cube_size),
        new Vertex( 1*cube_size,  1*cube_size, 1*cube_size),
        new Vertex( 1*cube_size, -1*cube_size, 1*cube_size),
        new Vertex(-1*cube_size, -1*cube_size, -1*cube_size),
        new Vertex(-1*cube_size,  1*cube_size, -1*cube_size),
        new Vertex( 1*cube_size,  1*cube_size, -1*cube_size),
        new Vertex( 1*cube_size, -1*cube_size, -1*cube_size),
    ],
    [
        // FRONT
        [0,1,2],
        [0,3,2],
    ]
);

let loop = () => {
    // requestAnimationFrame(loop);

    ctx.fillStyle = "#000";
    ctx.fillRect(0,0,canvas.width,canvas.height);
    ctx.fillStyle = "#fff";
    project(cube).forEach(face => {
        console.log("xd");
        ctx.beginPath();
        ctx.moveTo(face.face[0].x,face.face[0].y);
        ctx.moveTo(face.face[1].x,face.face[1].y);
        ctx.moveTo(face.face[2].x,face.face[2].y);
        ctx.closePath();
        // ctx.fillStyle = face.color;
        ctx.fill();
    });
}; loop();

 

komentarz 11 sierpnia 2022 przez Jackoza Początkujący (410 p.)
3
komentarz 11 sierpnia 2022 przez TOWaD Mądrala (6,480 p.)

@Jackoza, dla mnie, laika to moveto 3x (zamiast lineto) wygląda dziwnie i fill za close

   ctx.beginPath();
        ctx.moveTo(face.face[0].x,face.face[0].y);
        ctx.moveTo(face.face[1].x,face.face[1].y);
        ctx.moveTo(face.face[2].x,face.face[2].y);
        ctx.closePath();
        // ctx.fillStyle = face.color;
        ctx.fill();

 

1
komentarz 12 sierpnia 2022 przez Jackoza Początkujący (410 p.)
masz rację, dzięki @TOWaD
komentarz 13 sierpnia 2022 przez TOWaD Mądrala (6,480 p.)

z kolejnością to nie do końca :(, ale chyba że właśnie zależy Ci na nie zamkniętych krawędziach.

Podobne pytania

0 głosów
1 odpowiedź 225 wizyt
pytanie zadane 23 marca 2017 w JavaScript przez Muhin Gaduła (4,120 p.)
0 głosów
1 odpowiedź 573 wizyt
pytanie zadane 26 października 2016 w JavaScript przez Szymon Lisowiec Mądrala (7,150 p.)
0 głosów
1 odpowiedź 243 wizyt

93,456 zapytań

142,452 odpowiedzi

322,722 komentarzy

62,837 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj

Kursy INF.02 i INF.03
...