2016-05-28 42 views
6

Sto provando a creare una heightmap colorata per faccia, invece che per vertice. Ad esempio, questo è quello che ho attualmente:Colorare heightmap facce anziché vertici

My terrain, by vertex Ma questo è ciò che voglio: Per face coloring

Ho letto che devo dividere ogni vertice in più vertici, quindi indice di ciascuna separatamente per i triangoli. So anche che il blender ha una funzione come questa per i suoi modelli (vertici divisi, o qualcosa del genere?), Ma non sono sicuro di quale tipo di algoritmo seguirò per questo. Questa sarebbe l'ultima risorsa, perché moltiplicare la quantità di vertici nella mesh per nessun motivo diverso dal colore non sembra efficiente.

Ho anche scoperto qualcosa chiamato bagliore (usando il qualificatore flat sul colore dei pixel negli shader), ma sembra che disegni solo quadrati invece di triangoli. C'è un modo per farlo sfumare triangoli?

Flatshaded

Per avere un riferimento, questo è il mio codice generazione heightmap corrente:

public class HeightMap extends GameModel { 

private static final float START_X = -0.5f; 
private static final float START_Z = -0.5f; 
private static final float REFLECTANCE = .1f; 

public HeightMap(float minY, float maxY, float persistence, int width, int height, float spikeness) { 
    super(createMesh(minY, maxY, persistence, width, height, spikeness), REFLECTANCE); 
} 

protected static Mesh createMesh(final float minY, final float maxY, final float persistence, final int width, 
     final int height, float spikeness) { 
    SimplexNoise noise = new SimplexNoise(128, persistence, 2);// Utils.getRandom().nextInt()); 

    float xStep = Math.abs(START_X * 2)/(width - 1); 
    float zStep = Math.abs(START_Z * 2)/(height - 1); 

    List<Float> positions = new ArrayList<>(); 
    List<Integer> indices = new ArrayList<>(); 

    for (int z = 0; z < height; z++) { 
     for (int x = 0; x < width; x++) { 
      // scale from [-1, 1] to [minY, maxY] 
      float heightY = (float) ((noise.getNoise(x * xStep * spikeness, z * zStep * spikeness) + 1f)/2 
        * (maxY - minY) + minY); 

      positions.add(START_X + x * xStep); 
      positions.add(heightY); 
      positions.add(START_Z + z * zStep); 

      // Create indices 
      if (x < width - 1 && z < height - 1) { 
       int leftTop = z * width + x; 
       int leftBottom = (z + 1) * width + x; 
       int rightBottom = (z + 1) * width + x + 1; 
       int rightTop = z * width + x + 1; 

       indices.add(leftTop); 
       indices.add(leftBottom); 
       indices.add(rightTop); 

       indices.add(rightTop); 
       indices.add(leftBottom); 
       indices.add(rightBottom); 
      } 
     } 
    } 

    float[] verticesArr = Utils.listToArray(positions); 
    Color c = new Color(147, 105, 59); 
    float[] colorArr = new float[positions.size()]; 
    for (int i = 0; i < colorArr.length; i += 3) { 
     float brightness = (Utils.getRandom().nextFloat() - 0.5f) * 0.5f; 
     colorArr[i] = (float) c.getRed()/255f + brightness; 
     colorArr[i + 1] = (float) c.getGreen()/255f + brightness; 
     colorArr[i + 2] = (float) c.getBlue()/255f + brightness; 
    } 
    int[] indicesArr = indices.stream().mapToInt((i) -> i).toArray(); 

    float[] normalArr = calcNormals(verticesArr, width, height); 

    return new Mesh(verticesArr, colorArr, normalArr, indicesArr); 
} 

private static float[] calcNormals(float[] posArr, int width, int height) { 
    Vector3f v0 = new Vector3f(); 
    Vector3f v1 = new Vector3f(); 
    Vector3f v2 = new Vector3f(); 
    Vector3f v3 = new Vector3f(); 
    Vector3f v4 = new Vector3f(); 
    Vector3f v12 = new Vector3f(); 
    Vector3f v23 = new Vector3f(); 
    Vector3f v34 = new Vector3f(); 
    Vector3f v41 = new Vector3f(); 
    List<Float> normals = new ArrayList<>(); 
    Vector3f normal = new Vector3f(); 
    for (int row = 0; row < height; row++) { 
     for (int col = 0; col < width; col++) { 
      if (row > 0 && row < height - 1 && col > 0 && col < width - 1) { 
       int i0 = row * width * 3 + col * 3; 
       v0.x = posArr[i0]; 
       v0.y = posArr[i0 + 1]; 
       v0.z = posArr[i0 + 2]; 

       int i1 = row * width * 3 + (col - 1) * 3; 
       v1.x = posArr[i1]; 
       v1.y = posArr[i1 + 1]; 
       v1.z = posArr[i1 + 2]; 
       v1 = v1.sub(v0); 

       int i2 = (row + 1) * width * 3 + col * 3; 
       v2.x = posArr[i2]; 
       v2.y = posArr[i2 + 1]; 
       v2.z = posArr[i2 + 2]; 
       v2 = v2.sub(v0); 

       int i3 = (row) * width * 3 + (col + 1) * 3; 
       v3.x = posArr[i3]; 
       v3.y = posArr[i3 + 1]; 
       v3.z = posArr[i3 + 2]; 
       v3 = v3.sub(v0); 

       int i4 = (row - 1) * width * 3 + col * 3; 
       v4.x = posArr[i4]; 
       v4.y = posArr[i4 + 1]; 
       v4.z = posArr[i4 + 2]; 
       v4 = v4.sub(v0); 

       v1.cross(v2, v12); 
       v12.normalize(); 

       v2.cross(v3, v23); 
       v23.normalize(); 

       v3.cross(v4, v34); 
       v34.normalize(); 

       v4.cross(v1, v41); 
       v41.normalize(); 

       normal = v12.add(v23).add(v34).add(v41); 
       normal.normalize(); 
      } else { 
       normal.x = 0; 
       normal.y = 1; 
       normal.z = 0; 
      } 
      normal.normalize(); 
      normals.add(normal.x); 
      normals.add(normal.y); 
      normals.add(normal.z); 
     } 
    } 
    return Utils.listToArray(normals); 
} 

} 

Modifica

Ho provato a fare un paio di cose. Ho provato a riordinare gli indici con ombreggiature piatte, ma questo non mi dava l'aspetto che volevo. Ho provato ad usare un uniform vec3 colors e indicizzarlo con gl_VertexID o gl_InstanceID (non sono del tutto sicuro della differenza), ma non ho potuto ottenere la compilazione degli array. Here è il repository Github, a proposito.

risposta

6

flat gli input shader di frammenti qualificati riceveranno lo stesso valore per la stessa primitiva. Nel tuo caso, un triangolo.

Ovviamente, un triangolo è composto da 3 vertici. E se i vertex shader emettono 3 valori diversi, in che modo il framment shader sa quale valore ottenere?

Questo si riduce a ciò che viene chiamato "provoking vertex". Quando si esegue il rendering, si specifica una particolare primitiva da utilizzare nella chiamata glDraw* (GL_TRIANGLE_STRIP, GL_TRIANGLES, ecc.). Questi tipi primitivi genereranno un certo numero di primitive di base (es .: triangolo singolo), in base al numero di vertici che hai fornito.

Quando viene generata una primitiva di base, uno dei vertici in tale base primitiva è detto "vertice di provocazione". Sono i dati del vertice che vengono utilizzati per tutti i parametri flat.

La ragione per cui stai vedendo quello che stai vedendo è perché i due triangoli adiacenti hanno appena usato lo stesso vertice provocatorio. La tua maglia è liscia, quindi due triangoli adiacenti condividono 2 vertici. La tua generazione di mesh sta appena generando una mesh in modo tale che il vertice provocante per ogni triangolo sia condiviso tra loro. Ciò significa che i due triangoli avranno lo stesso valore flat.

È necessario regolare l'elenco di indici o modificare in altro modo la generazione di mesh in modo che ciò non avvenga. O puoi semplicemente dividere la tua mesh in triangoli individuali; questo è probabilmente molto più facile.

+1

Hai qualche idea di come dividere la mesh in triangoli singoli? – Kyranstar

+1

@Kyranstar usa lo shader della geometria e per ogni 4 vertici ('QUAD') o ogni nuovo 2 vertice (' QUAD_STRIP') emette 2x 3 Vertici (2x 'TRIANGLE') o riscrive la routine di rendering mesh per usare direttamente le primitive' TRIANGLE'. Dovresti anche calcolare la normale per la primitiva emessa per sovrascrivere le normali uniformi e duplicate a causa di ciò che questa risposta descrive ... – Spektre

+0

@Spektre Geometry Shaders in OpenGL si dice che sia molto lento e usare uno per creare triangoli con quad è incredibilmente inefficiente . – RecursiveExceptionException

0

Come risorsa finale, ho appena duplicato i vertici e sembra funzionare.Non sono stato in grado di delineare il profilo per vedere se fa un grande calo di prestazioni. Sarei aperto a qualsiasi altro suggerimento!

for (int z = 0; z < height; z++) { 
     for (int x = 0; x < width; x++) { 
      // scale from [-1, 1] to [minY, maxY] 
      float heightY = (float) ((noise.getNoise(x * xStep * spikeness, z * zStep * spikeness) + 1f)/2 
        * (maxY - minY) + minY); 

      positions.add(START_X + x * xStep); 
      positions.add(heightY); 
      positions.add(START_Z + z * zStep); 
      positions.add(START_X + x * xStep); 
      positions.add(heightY); 
      positions.add(START_Z + z * zStep); 
     } 
    } 
    for (int z = 0; z < height - 1; z++) { 
     for (int x = 0; x < width - 1; x++) { 
      int leftTop = z * width + x; 
      int leftBottom = (z + 1) * width + x; 
      int rightBottom = (z + 1) * width + x + 1; 
      int rightTop = z * width + x + 1; 

      indices.add(2 * leftTop); 
      indices.add(2 * leftBottom); 
      indices.add(2 * rightTop); 

      indices.add(2 * rightTop + 1); 
      indices.add(2 * leftBottom + 1); 
      indices.add(2 * rightBottom + 1); 
     } 
    }