LibGDX 网格高度图法线和灯光

2024-01-30

我正在尝试让网格法线和灯光在 LibGDX 项目中工作。

我已经有了从高度图纹理像素生成的纹理网格。

问题是我无法正确点亮法线。另外,我不能 100% 确定我在 TerrainChunk 类中正确设置了法线顶点。

这是主类代码:

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类代码:

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);
        }
    }

}

我看到的是,当我移动相机时,当我在地形上方时,灯光无法正确显示。 当我在地形下时,它们会显示得更多,尽管我认为这是错误的。

pics:

  1. below: https://i.stack.imgur.com/ahlKR.png https://i.stack.imgur.com/ahlKR.png

  2. above: https://i.stack.imgur.com/D3rjY.png https://i.stack.imgur.com/D3rjY.png


通过使用 MeshPartBuilder / GL_LINES 调试和绘制所有正常位置解决了该问题。

我发现法线指向地形内部。改变法线方向是解决方案。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

LibGDX 网格高度图法线和灯光 的相关文章

  • 读取文件并获取 key=value 而不使用 java.util.Properties

    我正在构建一个 RMI 游戏 客户端将加载一个包含一些键和值的文件 这些键和值将用于多个不同的对象 它是一个保存游戏文件 但我不能为此使用 java util Properties 它符合规范 我必须读取整个文件并忽略注释行和与某些类不相关
  • 如何用Java写入OS系统日志?

    Mac OS 有一个名为 Console 的应用程序 其中包含记录的消息 错误和故障 我相信 Windows 中的等效项是事件查看器 我想 Linux 上也有一个 但我不知道它是什么 也不知道它在哪里 是否可以像这样从 Java 输出获取消
  • Java - 从配置文件加密/解密用户名和密码

    我们正忙于为客户开发 Java Web 服务 有两种可能的选择 将加密的用户名 密码存储在Web服务客户端上 从配置中读取 文件在客户端 解密并发送 将加密的用户名 密码存储在 Web 服务器上 从配置中读取 Web 服务器上的文件 解密并
  • 重构——套接字中的良好实践——简单的服务器-客户端 Swing 应用程序

    我使用单例和观察者模式编写了一个带有 Swing 接口的简单服务器 客户端程序 每个客户端都连接到服务器并可以发送消息 服务器将其收到的消息转发给其余的客户端 客户端使用 GUI 允许它们随时连接和断开与服务器的连接 该程序运行得很好 因为
  • 如何防止在 CXF Web 服务客户端中生成 JAXBElement

    我正在尝试使用 CXF 创建一个 Web 服务客户端来使用 WCF Web 服务 当我使用 wsdl2java 时 它生成具有 JAXBElement 类型而不是 String 的对象 我读到有关使用 jaxb bindings xml 文
  • JBoss AS 5 中的共享库应该放在哪里?

    我是 Jboss 新手 但我有多个 Web 应用程序 每个应用程序都使用 spring hibernate 和其他开源库和 portlet 所以基本上现在每个 war 文件都包含这些 jar 文件 如何将这些 jar 移动到一个公共位置 以
  • org.postgresql.util.PSQLException:协议错误。会话设置失败

    我知道这些类型的问题已经存在 但提供的解决方案对我不起作用 在我的应用程序中 没有版本不匹配的黑白驱动程序和 PostgreSQL 服务器 我还没有找到任何其他解决方案 我正在使用 PostgreSQL 服务器 9 4 和 postgres
  • JAX-WS:有状态 WS 在独立进程中失败

    我在 Tomcat 上部署了一个有状态的 Web 服务 它由工厂服务和主要 API 服务组成 并且工作得很好 工厂服务将 W3CEndpointReference 返回到主 API 实例 客户端使用会话 现在 我尝试将相同的服务作为独立应用
  • 动画图像视图

    目前我正在开发一款游戏 这是我的游戏的详细信息 用户应选择正确的图像对象 我希望图像从左到右加速 当他们到达终点时 他们应该再次出现在活动中 这是我正在处理的屏幕截图 我有 5 个图像视图 它们应该会加速 您有此类动画的示例代码吗 非常感谢
  • 如何在Gradle中支持多种语言(Java和Scala)的多个项目?

    我正在尝试将过时的 Ant 构建转换为 Gradle 该项目包含约50个Java子项目和10个Scala子项目 Java 项目仅包含 Java Scala 项目仅包含 Scala 每个项目都是由 Java 和 Scala 构建的 这大大减慢
  • 中间件 API 的最佳实践是什么? [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我们正在开发一个中间件 SDK 采用 C 和 Java 语言 供游戏开发人员 动画软件开发人员 阿凡达开
  • Java 中 static 关键字如何工作?

    我正在阅读Java教程 http docs oracle com javase tutorial index html从一开始我就有一个问题static字段或变量上的关键字 作为Java said here http docs oracle
  • 在多模块项目中访问绑定适配器

    我有一个多模块项目 其中应用程序模块包含我的绑定适配器 而我的功能模块取决于我的应用程序模块 因为它是动态功能模块 应用程序 包含绑定适配器 gt 动态功能模块 存在布局的地方 我在所有模块中启用了数据绑定和 kapt 我无法成功构建应用程
  • NoSuchMethodError:将 Firebase 与应用程序引擎应用程序集成时

    我试图将 firebase 实时数据库与谷歌应用程序引擎应用程序集成 我在调用时收到此错误 gt DatabaseReference ref FirebaseDatabase gt getInstance gt getReference t
  • 如何列出所有可用的 LookAndFeel 主题?

    如何列出所有可用的 LookAndFeel 主题 我想在 JComboBox 中显示以供用户选择 这真的很简单 public static UIManager LookAndFeelInfo getInstalledLookAndFeels
  • 为什么 RMI 注册表忽略 java.rmi.server.codebase 属性

    我正在运行 java RMI 的 Hello World 示例 1 我在空文件夹中运行注册表 motta motta laptop tmp rmiregistry 2 我启动 HTTP 服务器以在运行时检索类 下载文件夹包含客户端 服务器的
  • 使用 Cucumber Scenario Outline 处理 Excel 电子表格

    如果可能的话 我试图找到一种更优雅的方法来处理从与 Excel 电子表格行 第 n 个 相关的 Cucumber Scenario Outline 中调用第 n 个数字 目前 我正在使用迭代编号来定义要从中提取数据的 Excel 电子表格的
  • Java:基于 Web 的应用程序中的单例类实例

    我在 Web Application 中有这个 Singleton 类 public class MyDAO private static MyDAO instance private MyDAO public static MyDAO g
  • 在多线程环境中,Collections.sort 方法有时会抛出 ConcurrentModificationException。列表没有进行结构性修改

    package CollectionsTS import java util ArrayList import java util Collections import java util HashSet import java util
  • java中的回调是什么[重复]

    这个问题在这里已经有答案了 可能的重复 什么是回调函数 https stackoverflow com questions 824234 what is a callback function 我已经阅读了回调的维基百科定义 但我仍然没有明

随机推荐