2013-12-02 19 views
8

Ich versuche, Mesh-Normalen und Lichter in LibGDX-Projekt zu arbeiten.LibGDX Mesh Heightmap Normalen und Lichter

Ich habe bereits texturierte Mesh generiert aus Heightmap Texture Pixel.

Das Problem ist, kann ich nicht Normalen richtig beleuchtet erhalten. Ich bin auch nicht 100% sicher, dass ich normale Scheitelpunkte korrekt in der TerrainChunk-Klasse eingerichtet habe.

Heres der Hauptklassencode:

package com.me.terrain; 

import com.badlogic.gdx.Game; 
import com.badlogic.gdx.Gdx; 
import com.badlogic.gdx.files.FileHandle; 
import com.badlogic.gdx.graphics.Color; 
import com.badlogic.gdx.graphics.GL20; 
import com.badlogic.gdx.graphics.Mesh; 
import com.badlogic.gdx.graphics.PerspectiveCamera; 
import com.badlogic.gdx.graphics.Pixmap; 
import com.badlogic.gdx.graphics.Texture; 
import com.badlogic.gdx.graphics.VertexAttribute; 
import com.badlogic.gdx.graphics.VertexAttributes.Usage; 
import com.badlogic.gdx.graphics.g3d.utils.CameraInputController; 
import com.badlogic.gdx.graphics.glutils.ShaderProgram; 
import com.badlogic.gdx.math.Matrix3; 
import com.badlogic.gdx.math.Matrix4; 
import com.badlogic.gdx.math.Vector3; 

public class Terra extends Game { 

private PerspectiveCamera camera; 
private CameraInputController camController; 

private TerrainChunk chunk; 
private Mesh mesh; 

private ShaderProgram shader; 
private Texture terrainTexture; 

private final Matrix3 normalMatrix = new Matrix3(); 

private static final float[] lightPosition = { 5, 35, 5 }; 
private static final float[] ambientColor = { 0.2f, 0.2f, 0.2f, 1.0f }; 
private static final float[] diffuseColor = { 0.5f, 0.5f, 0.5f, 1.0f }; 
private static final float[] specularColor = { 0.7f, 0.7f, 0.7f, 1.0f }; 

private static final float[] fogColor = { 0.2f, 0.1f, 0.6f, 1.0f }; 

private Matrix4 model = new Matrix4(); 
private Matrix4 modelView = new Matrix4(); 

private final String vertexShader = 
     "attribute vec4 a_position; \n" + 
     "attribute vec3 a_normal; \n" + 
     "attribute vec2 a_texCoord; \n" + 
     "attribute vec4 a_color; \n" + 

     "uniform mat4 u_MVPMatrix; \n" + 
     "uniform mat3 u_normalMatrix; \n" + 

     "uniform vec3 u_lightPosition; \n" + 

     "varying float intensity; \n" + 
     "varying vec2 texCoords; \n" + 
     "varying vec4 v_color; \n" + 

     "void main() { \n" + 
     " vec3 normal = normalize(u_normalMatrix * a_normal); \n" + 
     " vec3 light = normalize(u_lightPosition); \n" + 
     " intensity = max(dot(normal, light) , 0.0); \n" + 

     " v_color = a_color; \n" + 
     " texCoords = a_texCoord; \n" + 

     " gl_Position = u_MVPMatrix * a_position; \n" + 
     "}"; 

private final String fragmentShader = 
     "#ifdef GL_ES \n" + 
     "precision mediump float; \n" + 
     "#endif \n" + 

     "uniform vec4 u_ambientColor; \n" + 
     "uniform vec4 u_diffuseColor; \n" + 
     "uniform vec4 u_specularColor; \n" + 

     "uniform sampler2D u_texture; \n" + 
     "varying vec2 texCoords; \n" + 
     "varying vec4 v_color; \n" + 

     "varying float intensity; \n" + 

     "void main() { \n" + 
     " gl_FragColor = v_color * intensity * texture2D(u_texture, texCoords); \n" + 
     "}"; 

@Override 
public void create() { 

    // Terrain texture size is 128x128 
    terrainTexture = new Texture(Gdx.files.internal("data/concrete2.png")); 

    // Height map (black/white) texture size is 32x32 
    String heightMapFile = "data/heightmap.png"; 


    // position, normal, color, texture 
    int vertexSize = 3 + 3 + 1 + 2; 

    chunk = new TerrainChunk(32, 32, vertexSize, heightMapFile); 



    mesh = new Mesh(true, chunk.vertices.length/3, chunk.indices.length, 
      new VertexAttribute(Usage.Position, 3, ShaderProgram.POSITION_ATTRIBUTE), 
      new VertexAttribute(Usage.Normal, 3, ShaderProgram.NORMAL_ATTRIBUTE), 
      new VertexAttribute(Usage.ColorPacked, 4, ShaderProgram.COLOR_ATTRIBUTE), 
      new VertexAttribute(Usage.TextureCoordinates, 2, ShaderProgram.TEXCOORD_ATTRIBUTE)); 

    mesh.setVertices(chunk.vertices); 
    mesh.setIndices(chunk.indices); 



    camera = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); 
    camera.position.set(5, 50, 5); 
    camera.direction.set(3, 0, 0).sub(camera.position).nor(); 
    camera.near = 0.005f; 
    camera.far = 300; 
    camera.update(); 

    camController = new CameraInputController(camera); 
    Gdx.input.setInputProcessor(camController); 

    ShaderProgram.pedantic = false; 

    shader = new ShaderProgram(vertexShader, fragmentShader); 

} 

@Override 
public void render() { 

    Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); 
    Gdx.gl.glEnable(GL20.GL_DEPTH_TEST); 
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT); 

    camController.update(); 
    camera.update(); 


    // This is wrong? 
    model.setToRotation(new Vector3(0, 1, 0), 45f); 
    modelView.set(camera.view).mul(model); 


    terrainTexture.bind(); 

    shader.begin(); 

    shader.setUniformMatrix("u_MVPMatrix", camera.combined); 
    shader.setUniformMatrix("u_normalMatrix", normalMatrix.set(modelView).inv().transpose()); 

    shader.setUniform3fv("u_lightPosition", lightPosition, 0, 3); 
    shader.setUniform4fv("u_ambientColor", ambientColor, 0, 4); 
    shader.setUniform4fv("u_diffuseColor", diffuseColor, 0, 4); 
    shader.setUniform4fv("u_specularColor", specularColor, 0, 4); 

    shader.setUniformi("u_texture", 0); 

    mesh.render(shader, GL20.GL_TRIANGLES); 

    shader.end(); 

} 
} 

TerrainChunk Klassencode:

final static class TerrainChunk { 

    public final float[] heightMap; 
    public final short width; 
    public final short height; 
    public final float[] vertices; 
    public final short[] indices; 

    public final int vertexSize; 
    private final int positionSize = 3; 

    public TerrainChunk(int width, int height, int vertexSize, String heightMapTexture) { 

     if ((width + 1) * (height + 1) > Short.MAX_VALUE) { 
      throw new IllegalArgumentException(   
        "Chunk size too big, (width + 1)*(height+1) must be <= 32767"); 
     } 

     this.heightMap = new float[(width + 1) * (height + 1)]; 
     this.width = (short) width; 
     this.height = (short) height; 
     this.vertices = new float[heightMap.length * vertexSize]; 
     this.indices = new short[width * height * 6]; 
     this.vertexSize = vertexSize; 

     buildHeightmap(heightMapTexture); 

     buildIndices(); 
     buildVertices(); 

     calcNormals(indices, vertices); 

    } 

    public void buildHeightmap(String pathToHeightMap) { 

     FileHandle handle = Gdx.files.internal(pathToHeightMap); 
     Pixmap heightmapImage = new Pixmap(handle); 
     Color color = new Color(); 
     int idh = 0; 

     for (int x = 0; x < this.width + 1; x++) { 
      for (int y = 0; y < this.height + 1; y++) { 
       Color.rgba8888ToColor(color, heightmapImage.getPixel(x, y)); 
       this.heightMap[idh++] = color.r; 
      } 
     } 
    } 

    public void buildVertices() { 
     int heightPitch = height + 1; 
     int widthPitch = width + 1; 

     int idx = 0; 
     int hIdx = 0; 
     int strength = 10; // multiplier for height map 

     float scale = 4f; 

     for (int z = 0; z < heightPitch; z++) { 
      for (int x = 0; x < widthPitch; x++) { 

       // POSITION 
       vertices[idx++] = scale * x; 
       vertices[idx++] = heightMap[hIdx++] * strength; 
       vertices[idx++] = scale * z; 

       // NORMAL, skip these for now 
       idx += 3; 

       // COLOR 
       vertices[idx++] = Color.WHITE.toFloatBits(); 

       // TEXTURE 
       vertices[idx++] = (x/(float) width); 
       vertices[idx++] = (z/(float) height); 

      } 
     } 
    } 

    private void buildIndices() { 
     int idx = 0; 
     short pitch = (short) (width + 1); 
     short i1 = 0; 
     short i2 = 1; 
     short i3 = (short) (1 + pitch); 
     short i4 = pitch; 

     short row = 0; 

     for (int z = 0; z < height; z++) { 
      for (int x = 0; x < width; x++) { 
       indices[idx++] = i1; 
       indices[idx++] = i2; 
       indices[idx++] = i3; 

       indices[idx++] = i3; 
       indices[idx++] = i4; 
       indices[idx++] = i1; 

       i1++; 
       i2++; 
       i3++; 
       i4++; 
      } 

      row += pitch; 
      i1 = row; 
      i2 = (short) (row + 1); 
      i3 = (short) (i2 + pitch); 
      i4 = (short) (row + pitch); 
     } 
    } 

    // Gets the index of the first float of a normal for a specific vertex 
    private int getNormalStart(int vertIndex) { 
     return vertIndex * vertexSize + positionSize; 
    } 

    // Gets the index of the first float of a specific vertex 
    private int getPositionStart(int vertIndex) { 
     return vertIndex * vertexSize; 
    } 

    // Adds the provided value to the normal 
    private void addNormal(int vertIndex, float[] verts, float x, float y, float z) { 

     int i = getNormalStart(vertIndex); 

     verts[i] += x; 
     verts[i + 1] += y; 
     verts[i + 2] += z; 
    } 

    /* 
    * Normalizes normals 
    */ 
    private void normalizeNormal(int vertIndex, float[] verts) { 

     int i = getNormalStart(vertIndex); 

     float x = verts[i]; 
     float y = verts[i + 1]; 
     float z = verts[i + 2]; 

     float num2 = ((x * x) + (y * y)) + (z * z); 
     float num = 1f/(float) Math.sqrt(num2); 
     x *= num; 
     y *= num; 
     z *= num; 

     verts[i] = x; 
     verts[i + 1] = y; 
     verts[i + 2] = z; 
    } 

    /* 
    * Calculates the normals 
    */ 
    private void calcNormals(short[] indices, float[] verts) { 

     for (int i = 0; i < indices.length; i += 3) { 
      int i1 = getPositionStart(indices[i]); 
      int i2 = getPositionStart(indices[i + 1]); 
      int i3 = getPositionStart(indices[i + 2]); 

      // p1 
      float x1 = verts[i1]; 
      float y1 = verts[i1 + 1]; 
      float z1 = verts[i1 + 2]; 

      // p2 
      float x2 = verts[i2]; 
      float y2 = verts[i2 + 1]; 
      float z2 = verts[i2 + 2]; 

      // p3 
      float x3 = verts[i3]; 
      float y3 = verts[i3 + 1]; 
      float z3 = verts[i3 + 2]; 

      // u = p3 - p1 
      float ux = x3 - x1; 
      float uy = y3 - y1; 
      float uz = z3 - z1; 

      // v = p2 - p1 
      float vx = x2 - x1; 
      float vy = y2 - y1; 
      float vz = z2 - z1; 

      // n = cross(v, u) 
      float nx = (vy * uz) - (vz * uy); 
      float ny = (vz * ux) - (vx * uz); 
      float nz = (vx * uy) - (vy * ux); 

      // normalize(n) 
      float num2 = ((nx * nx) + (ny * ny)) + (nz * nz); 
      float num = 1f/(float) Math.sqrt(num2); 
      nx *= num; 
      ny *= num; 
      nz *= num; 

      addNormal(indices[i], verts, nx, ny, nz); 
      addNormal(indices[i + 1], verts, nx, ny, nz); 
      addNormal(indices[i + 2], verts, nx, ny, nz); 
     } 

     for (int i = 0; i < (verts.length/vertexSize); i++) { 
      normalizeNormal(i, verts); 
     } 
    } 

} 

Was Im Sehen ist, wenn ich die Kamera bewegen sich die Lichter richtig nicht zeigen, wenn ich oben bin Terrain. Sie zeigen mehr, wenn ich unter dem Gelände bin, obwohl ich auch dann falsch denke.

Bilder:

  1. unter: http://i.imgur.com/TocCLfA.png

  2. oben: http://i.imgur.com/fwGhGDT.png

Antwort

4

das Problem durch das Debuggen gelöst und alle normalen Positionen Zeichnung mit MeshPartBuilder/GL_LINES.

Ich fand heraus, dass die Normalen in das Gelände zeigten. Die normale Richtung zu ändern war die Lösung.

+1

Haben Sie daran gedacht, dies als Teil der Bibliothek libgdx zu veröffentlichen? es ist ein großer Beitrag – scape