Rendern einer Voxelwelt

Design Patterns, Erklärungen zu Algorithmen, Optimierung, Softwarearchitektur
Forumsregeln
Wenn das Problem mit einer Programmiersprache direkt zusammenhängt, bitte HIER posten.
Antworten
focus0941
Beiträge: 31
Registriert: 27.03.2023, 18:28
Benutzertext: hatte mit 3 einen Bart
Echter Name: Vladimir Ilyushko

Rendern einer Voxelwelt

Beitrag von focus0941 »

Ich möchte gerne das Rendern meiner Voxelwelt optimieren. Dazu sollen nur Flächen gerendert werden, die man sehen kann. Allerdings wären dann manche Punkte doppelt. Ich dachte man könnte es mit Indexen lösen, komme aber nicht drauf wie.
Hier ist mein bisheriger Code:

Code: Alles auswählen

package terrain;

import models.CubeModel;
import org.joml.Vector3f;
import org.joml.Vector3i;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class ChunkMesh {
    private List<Vertex> vertices;
    private List<Float> positionList;
    private List<Float> uvList;
    private List<Float> normalList;
    public float[] positions;
    public float[] uvs;
    public float[] normals;
    public Chunk chunk;

    public ChunkMesh(Chunk chunk) {
        this.chunk = chunk;
        vertices = new ArrayList<>();
        positionList = new ArrayList<>();
        uvList = new ArrayList<>();
        normalList = new ArrayList<>();
        buildMesh();
        populateList();
    }

    public void update(Chunk chunk) {
        this.chunk = chunk;
        buildMesh();
        populateList();
    }

    private void buildMesh() {
        HashMap<Vector3i, Voxel> voxelMap = new HashMap<>();

        for (Voxel voxel : chunk.blocks) {
            voxelMap.put(voxel.position, voxel);
        }

        Vector3i tempPos = new Vector3i();

        for (Voxel voxelI : chunk.blocks) {
            Vector3i position = voxelI.position;
            int x = position.x;
            int y = position.y;
            int z = position.z;

            boolean px = voxelMap.containsKey(tempPos.set(x + 1, y, z));
            boolean nx = voxelMap.containsKey(tempPos.set(x - 1, y, z));
            boolean py = voxelMap.containsKey(tempPos.set(x, y + 1, z));
            boolean ny = voxelMap.containsKey(tempPos.set(x, y - 1, z));
            boolean pz = voxelMap.containsKey(tempPos.set(x, y, z + 1));
            boolean nz = voxelMap.containsKey(tempPos.set(x, y, z - 1));

            if (!px) {
                addVoxelFace(CubeModel.facePX, voxelI);
            }

            if (!nx) {
                addVoxelFace(CubeModel.faceNX, voxelI);
            }

            if (!py) {
                addVoxelFace(CubeModel.facePY, voxelI);
            }

            if (!ny) {
                addVoxelFace(CubeModel.faceNY, voxelI);
            }

            if (!pz) {
                addVoxelFace(CubeModel.facePZ, voxelI);
            }

            if (!nz) {
                addVoxelFace(CubeModel.faceNZ, voxelI);
            }
        }
    }

    private void addVoxelFace(Vector3f[] face, Voxel voxel) {
        for (int k = 0; k < 6; k++) {
            vertices.add(new Vertex(new Vector3f(face[k].x + voxel.position.x, face[k].y + voxel.position.y, face[k].z + voxel.position.z), CubeModel.NORMALS[k], CubeModel.UVS[k]));
        }
    }

    private void populateList() {
        for (Vertex vertex : vertices) {
            positionList.add(vertex.positions.x);
            positionList.add(vertex.positions.y);
            positionList.add(vertex.positions.z);
            normalList.add(vertex.normals.x);
            normalList.add(vertex.normals.y);
            normalList.add(vertex.normals.z);
            uvList.add(vertex.uvs.x);
            uvList.add(vertex.uvs.y);
        }

        positions = new float[positionList.size()];
        uvs = new float[uvList.size()];
        normals = new float[normalList.size()];

        for (int i = 0; i < positionList.size(); i++) {
            positions[i] = positionList.get(i);
        }

        for (int i = 0; i < uvList.size(); i++) {
            uvs[i] = uvList.get(i);
        }

        for (int i = 0; i < normalList.size(); i++) {
            normals[i] = normalList.get(i);
        }

        positionList.clear();
        uvList.clear();
        normalList.clear();
    }
}
Benutzeravatar
Schrompf
Moderator
Beiträge: 4732
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: Rendern einer Voxelwelt

Beitrag von Schrompf »

Ich hab mir jetzt nicht die Mühe gemacht, Deinen Code zu verstehen. Daher nur aufgrund des Fließtextes:

Erster Aufschlag: nimm die Dopplungen in Kauf. Dein Problem wird höchstwahrscheinlich nicht das GPU Vertex Processing sein, und nur da hilft gutes Indexing.

Zweiter Aufschlag: Das Einzige, was Du meiner Meinung nach sharen kannst, sind die 4 Vertizes einer Würfel-Seite. Und die kannst Du ja einfach so schreiben: aktuelle VertexAnzahl merken als Basis-Index, vier Vertizes in den VertexBuffer, 6 Indizes in den IndexBuffer (BasisIndex, BasisIndex+1, BasisIndex+2, BasisIndex+1, BasisIndex+3, BasisIndex+2). Fertsch.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
focus0941
Beiträge: 31
Registriert: 27.03.2023, 18:28
Benutzertext: hatte mit 3 einen Bart
Echter Name: Vladimir Ilyushko

Re: Rendern einer Voxelwelt

Beitrag von focus0941 »

Die Vertices sind alle schon als Array für einen Würfel vorgegeben, man muss nur die position in der welt hinzuaddieren (siehe addVoxelFace()).
Die Indices zu vergeben war auch kein Problem, allerdings kommt etwas heraus, was ich mir nicht erklären kann.
Dateianhänge
das ist eigentlich ein würfel
das ist eigentlich ein würfel
Benutzeravatar
Lord Delvin
Establishment
Beiträge: 539
Registriert: 05.07.2003, 11:17

Re: Rendern einer Voxelwelt

Beitrag von Lord Delvin »

Wie sollte es denn aussehen?
XML/JSON/EMF in schnell: OGSS
Keine Lust mehr auf C++? Versuche Tyr: Get & Get started
focus0941
Beiträge: 31
Registriert: 27.03.2023, 18:28
Benutzertext: hatte mit 3 einen Bart
Echter Name: Vladimir Ilyushko

Re: Rendern einer Voxelwelt

Beitrag von focus0941 »

So, aber so sind die Vertices vierfach im Arbeitsspeicher.
Dateianhänge
eigentlich.png
focus0941
Beiträge: 31
Registriert: 27.03.2023, 18:28
Benutzertext: hatte mit 3 einen Bart
Echter Name: Vladimir Ilyushko

Re: Rendern einer Voxelwelt

Beitrag von focus0941 »

ich könnte auch raycasting versuchen, es soll mit svos schneller sein und damit habe ich mehr erfahrung
NytroX
Establishment
Beiträge: 330
Registriert: 03.10.2003, 12:47

Re: Rendern einer Voxelwelt

Beitrag von NytroX »

Versuch doch mal den Fehler einzugrenzen, indem du nur einen Teil renderst.
Im Bild sieht das so aus als wäre das Problem auch mit 2x2 reproduzierbar.
Und dann schau dir das Ganze im Debugger an.

Denk dran, dass du die Vertices im Uhrzeigersinn durchlaufen musst (oder Gegen den Uhrzeigersinn, je nach Einstellung) und schau nochmal ob die Indices dazu passen.
Benutzeravatar
Lord Delvin
Establishment
Beiträge: 539
Registriert: 05.07.2003, 11:17

Re: Rendern einer Voxelwelt

Beitrag von Lord Delvin »

Irgendwie ist auch seltsam, dass die Texturkoordinaten offenbar nicht überall passen.
XML/JSON/EMF in schnell: OGSS
Keine Lust mehr auf C++? Versuche Tyr: Get & Get started
focus0941
Beiträge: 31
Registriert: 27.03.2023, 18:28
Benutzertext: hatte mit 3 einen Bart
Echter Name: Vladimir Ilyushko

Re: Rendern einer Voxelwelt

Beitrag von focus0941 »

Die Texturkoordinaten passen nicht, da ein Vertex bisher nur eine Texturkoordinate haben kann, aber 4 Mal genutzt wird.
joeydee
Establishment
Beiträge: 1025
Registriert: 23.04.2003, 15:29
Kontaktdaten:

Re: Rendern einer Voxelwelt

Beitrag von joeydee »

(Weiß nicht genau ob ich das Problem richtig verstehe)
Ich nehme generell unique Vertices, also [Position, Normale, UV, Farbe, ...] als Einheit. Ja, Positionen doppeln sich dann. Aber z.B. die Normalendaten doppeln sich noch mehr bei einer Voxelwelt, es gibt ja nur 6 verschiedene.
Ob/wie man das sonst mit Vertex- und Indexbuffern (mit Bordmitteln außerhalb von Shaderakrobatik) praktisch beliebig "interleaven" könnte bin ich überfragt, da wären ja theoretisch mehrere Indexbuffer je Drawcall nötig oder so.
Mit Shaderakrobatik, falls die Version es hergibt: nur IDs in den VB packen (posID, normID, uvID, ...), und dann Lookups auf Arrays (alle Positionen, Normalen, UVs, ...) im Shader.

Die Frage ist halt, ob man wirklich auf diese Art an der Datenmenge sparen muss, und ob es unterm Strich überhaupt einen Gewinn bringt.
Antworten