LWJGL Tilerenderer仅渲染一种类型的图块/一种纹理

时间:2019-04-11 19:43:51

标签: java opengl glsl shader lwjgl

我通过遵循我发现的this教程来学习LWJGL和OpenGL,我尽力将代码更改为与Never版本兼容,并且到目前为止还没有问题。但是,现在我的Tilerenderer不会为VBO渲染多种类型的图块/一种纹理,而且自3天以来我一直试图对其进行修复(一开始它什么都没有渲染),但找不到可以解决此问题的任何东西。

我正在使用Java 9.0.4和LWJGL 3.2.1 build 12以及JOML 1.9.13,GLFW,OpenGL和stb。

到目前为止,我尝试更改涉及此问题的整个代码,并为着色器更改不同的变量,但到目前为止似乎没有任何效果。

这里我想过的所有类都可能与问题有关。

主班

import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;

import org.joml.Matrix4f;
import org.joml.Vector3f;
import org.lwjgl.opengl.GL;

public class Main {



    public static void main(String[] args) {

        int speed = 5;

        Window.setCallbacks();

        if (!glfwInit()) {

            throw new IllegalStateException("Failed to init GLFW");

        }

        glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);

        Window window = new Window();
        window.setSize(640, 480);
        window.setFullscreen(false);
        window.createWindow("GAME");

        GL.createCapabilities();

        Camera camera = new Camera(window.getWidth(), window.getHeight());

        glEnable(GL_TEXTURE_2D);

        TileRenderer tiles = new TileRenderer();

        Shader shader = new Shader("shader");

        World world = new World();

        world.setTile(Tile.test_tile, 0, 0);

        double frameCap = 1.0 / 60.0;
        double frameTime     = 0;
        double time = Timer.getTime();
        double unprocessed = 0;

        int frames = 0;

        while(!window.shouldClose()) {

            boolean canRender = false;

            double time2 = Timer.getTime();
            double passed = time2 - time;
            unprocessed+=passed;

            frameTime += passed;

            time = time2;

            while (unprocessed >= frameCap) {

                canRender = true;

                unprocessed-=frameCap;

                if(window.getInput().isMouseButtonDown(0)) {

                    glfwSetWindowShouldClose(window.getWindow(), true);

                }   

                if (window.getInput().isKeyPressed(GLFW_KEY_ESCAPE)) {

                    glfwSetWindowShouldClose(window.getWindow(), true);

                }

                if(window.getInput().isKeyDown(GLFW_KEY_W)) {

                    camera.addPosition(new Vector3f(0, -speed, 0));

                }

                if(window.getInput().isKeyDown(GLFW_KEY_A)) {

                    camera.addPosition(new Vector3f(speed, 0, 0));

                }

                if(window.getInput().isKeyDown(GLFW_KEY_S)) {

                    camera.addPosition(new Vector3f(0, speed, 0));

                }

                if(window.getInput().isKeyDown(GLFW_KEY_D)) {

                    camera.addPosition(new Vector3f(-speed, 0, 0));

                }

                if(window.getInput().isKeyDown(GLFW_KEY_O)) {

                    speed = 5;

                }

                if(window.getInput().isKeyDown(GLFW_KEY_P)) {

                    speed = 25;

                }

                window.update();

                if (frameTime >= 1.0) {

                    frameTime = 0;
                    System.out.println("FPS:" + frames);
                    frames = 0;

                }
            }

            if (canRender) {

                glClear(GL_COLOR_BUFFER_BIT);

                world.render(tiles, shader, camera);

                window.swapBuffers();

                frames++;

            }
        }

        glfwTerminate();

    }
}

世界一流

import org.joml.Matrix4f;
import org.joml.Vector3f;

public class World {

    private byte[] tiles;

    private int width;
    private int height;

    private Matrix4f world;

    public World () {

        width = 16;
        height = 16;

        tiles = new byte [width * height];

        world = new Matrix4f().setTranslation(new Vector3f(0));
        world.scale(32);
    }

    public void render(TileRenderer renderer, Shader shader, Camera camera) {

        for (int x = 0; x < height; x++) {

            for (int y = 0; y < width; y++) {

                renderer.renderTile(tiles[x + y * width], y, -x, shader, world, camera);

            }

        }

    }

    public void setTile (Tile tile, int x, int y) {

        System.err.println(tile.getId());
        tiles[x + y * width] = tile.getId();


    }

}

Tilerenderer类

import java.util.HashMap;

import org.joml.Matrix4f;
import org.joml.Vector3f;

public class TileRenderer {

        private HashMap<String, Texture> tileTextures;

        private Model tileModel;

        public TileRenderer() {
            tileTextures = new HashMap<>();
            float[] vertices = new float[]{
                -1f, 1f, 0, // TOP LEFT 0
                1f, 1f, 0,  // TOP RIGHT 1
                1f, -1f, 0, // BOTTOM RIGHT 2
                -1f, -1f, 0,// BOTTOM LEFT 3
            };

            float[] texture = new float[]{0, 0, 1, 0, 1, 1, 0, 1,};

            int[] indices = new int[]{0, 1, 2, 2, 3, 0};

            tileModel = new Model(vertices, texture, indices);

            for (int i = 0; i < Tile.tiles.length; i++) {
                if (Tile.tiles[i] != null) {
                    if (!tileTextures.containsKey(Tile.tiles[i].getTexture())) {
                        String tex = Tile.tiles[i].getTexture();
                        tileTextures.put(tex, new Texture(tex + ".png"));
                    }
                }
            }
        }

        public void renderTile (byte id, int x, int y, Shader shader, Matrix4f world, Camera camera) { 

            shader.bind();

            if (tileTextures.containsKey(Tile.tiles[id].getTexture())) {

                tileTextures.get(Tile.tiles[id].getTexture()).bind(0);

            }

            Matrix4f tilePos = new Matrix4f().translate(new Vector3f(x*2, y*2, 0));
            Matrix4f target = new Matrix4f();

            camera.getProjection().mul(world, target);
            target.mul(tilePos);

            shader.setUniform("sampler", 0);
            shader.setUniform("projection", target);

            tileModel.render();

        }   
}

Tile类

public class Tile {

    public static Tile tiles[] = new Tile[16];

    public static final Tile testTile = new Tile((byte)0, "Test");
    public static final Tile testTile2 = new Tile((byte)1, "Test2");

    private byte id;
    private String texture;

    public Tile(byte id, String texture) {

        this.id = id;
        this.texture = texture;

        if (tiles[id] != null) {

            throw new IllegalStateException("Tiles at: [" + id + "] is already being used!");

        }

        tiles[id] = this;

    }

    public byte getId () {return id;}
    public String getTexture () {return texture;}

}

Model类

import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL15.*;
import static org.lwjgl.opengl.GL20.*;

import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import org.lwjgl.BufferUtils;

public class Model {

    private int draw_count;
    private int v_id;
    private int t_id;
    private int i_id;

    public Model (float[] vertices, float[] tex_coords, int[] indices) {

        draw_count = indices.length;

        IntBuffer buffer = BufferUtils.createIntBuffer(indices.length);
        buffer.put(indices);
        buffer.flip();

        v_id = glGenBuffers();

        glBindBuffer(GL_ARRAY_BUFFER, v_id);
        glBufferData(GL_ARRAY_BUFFER, createBuffer(vertices), GL_STATIC_DRAW);

        t_id = glGenBuffers();

        glBindBuffer(GL_ARRAY_BUFFER, t_id);
        glBufferData(GL_ARRAY_BUFFER, createBuffer(tex_coords), GL_STATIC_DRAW);

        i_id = glGenBuffers();

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, i_id);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, buffer, GL_STATIC_DRAW);

        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

    }

    public void render() {

        glEnableVertexAttribArray(0);
        glEnableVertexAttribArray(1);

        glBindBuffer(GL_ARRAY_BUFFER, v_id);
        glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);

        glBindBuffer(GL_ARRAY_BUFFER, t_id);
        glVertexAttribPointer(1, 2, GL_FLOAT, false, 0, 0);

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, i_id);
        glDrawElements(GL_TRIANGLES, draw_count, GL_UNSIGNED_INT, 0);

        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

        glDisableVertexAttribArray(0);
        glDisableVertexAttribArray(1);

    }

    private FloatBuffer createBuffer(float[] data) {

        FloatBuffer buffer = BufferUtils.createFloatBuffer(data.length);
        buffer.put(data);
        buffer.flip();
        return buffer;

    }
}

Texture类

import java.nio.ByteBuffer;
import java.nio.IntBuffer;

import static org.lwjgl.stb.STBImage.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL13.*;

import org.lwjgl.BufferUtils;

public class Texture {

    private int id;
    private int width;
    private int heigth;

    public Texture (String filename) {

        IntBuffer width = BufferUtils.createIntBuffer(1);
        IntBuffer heigth = BufferUtils.createIntBuffer(1);
        IntBuffer comp = BufferUtils.createIntBuffer(1);

        ByteBuffer data = stbi_load("./res/" + filename, width, heigth, comp, 4);

        this.width = width.get();
        this.heigth = heigth.get();

        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, this.width, this.heigth, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);   

        stbi_image_free(data);

    }

    public void bind (int sampler) {

        if (sampler >= 0 && sampler <= 31) {

            glActiveTexture(GL_TEXTURE0 + sampler);
            glBindTexture(GL_TEXTURE_2D, sampler);

        }
    }
}

着色器类

import static org.lwjgl.opengl.GL20.*;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.nio.FloatBuffer;

import org.joml.Matrix4f;
import org.lwjgl.BufferUtils;

public class Shader {

    private int program;
    private int vs;
    private int fs;

    public Shader (String filename) {

        program = glCreateProgram();

        vs = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vs, readFile(filename + ".vs"));
        glCompileShader(vs);
        if (glGetShaderi(vs, GL_COMPILE_STATUS) != 1) {

            System.err.println(glGetShaderInfoLog(vs));
            System.exit(1);

        }

        fs = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(fs, readFile(filename + ".fs"));
        glCompileShader(fs);
        if (glGetShaderi(fs, GL_COMPILE_STATUS) != 1) {

            System.err.println(glGetShaderInfoLog(fs));
            System.exit(1);

        }

        glAttachShader(program, vs);
        glAttachShader(program, fs);

        glBindAttribLocation(program, 0, "vertices");
        glBindAttribLocation(program, 1, "textures");

        glLinkProgram(program);
        if (glGetProgrami(program, GL_LINK_STATUS) != 1) {

            System.err.println(glGetProgramInfoLog(program));
            System.exit(1);

        }

        glValidateProgram(program);
        if (glGetProgrami(program, GL_VALIDATE_STATUS) != 1) {

            System.err.println(glGetProgramInfoLog(program));
            System.exit(1);

        }
    }

    public void bind () {

        glUseProgram(program);

    }

    private String readFile (String filename) {

        StringBuilder string = new StringBuilder();

        BufferedReader br;

        try {

            br = new BufferedReader(new FileReader(new File("./shaders/" + filename)));
            String line;

            while((line = br.readLine()) != null) {

                string.append(line);
                string.append("\n");

            }

        } catch (IOException e ) {e.printStackTrace();}

        return string.toString();

    }

    public void setUniform (String name, int value) {

        int location = glGetUniformLocation(program, name);

        if (location != -1) {

            glUniform1i(location, value);

        }
    }

    public void setUniform (String name, Matrix4f value) {

        int location = glGetUniformLocation(program, name);

        FloatBuffer buffer = BufferUtils.createFloatBuffer(16); 

        value.get(buffer);

        if (location != -1) {

            glUniformMatrix4fv(location, false, buffer);

        }
    }
}

片段着色器

#version 120

uniform sampler2D sampler;

varying vec2 tex_coords;

void main () {
    gl_FragColor = texture2D(sampler, tex_coords);
}

顶点着色器

#version 120

attribute vec3 vertices;
attribute vec2 textures;

varying vec2 tex_coords;

uniform mat4 projection;

void main() {
    tex_coords = textures;
    gl_Position = projection*vec4(vertices, 1);
}

到目前为止,我正在创建全部具有相同纹理的16x16瓦片,但应该将其在0、0(左上角)处更改为具有不同的纹理

1 个答案:

答案 0 :(得分:1)

关于纹理在OpenGL中的工作方式存在一个基本的误解。

您必须通过glGenTextures为每个纹理创建一个单独的纹理对象。 (另请参见Java Code Examples for org.lwjgl.opengl.GL11.glTexImage2D())。

int textureObject = glGenTextures();

在加载纹理之前和渲染网格之前必须绑定此纹理对象。纹理绑定到由glActiveTexture设置的活动纹理单元。

int textureUnitIndex = 0; // e.g
glActiveTexture(GL_TEXTURE0 + textureUnitIndex);
glBindTexture(GL_TEXTURE_2D, textureObject);

纹理单元是着色器程序的绑定点。着色器程序中的纹理采样器必须与相同的绑定点关联。这可以通过glUniform1i完成:

GLSL:

uniform sampler2D sampler;

Java:

int location = glGetUniformLocation(program, "sampler");
glUniform1i(location, textureUnitIndex);

旁注:自GLSL 4.2版开始,可以在片段着色器中通过指定binding points-请参见OpenGL Shading Language 4.20 Specification - 4.4.4 Opaque-Uniform Layout Qualifiers; page 60

#version 420

layout (binding = 0) uniform sampler2D sampler;

在您的代码中,永远不会生成纹理对象。因此,默认纹理对象0用于所有纹理。这导致您的代码无法正常工作。

按以下方式更改类Texture以解决该问题:

public class Texture {

    private int textureObject;
    private int width;
    private int heigth;

    public Texture (String filename) {

        IntBuffer width = BufferUtils.createIntBuffer(1);
        IntBuffer heigth = BufferUtils.createIntBuffer(1);
        IntBuffer comp = BufferUtils.createIntBuffer(1);

        ByteBuffer data = stbi_load("./res/" + filename, width, heigth, comp, 4);

        this.width = width.get();
        this.heigth = heigth.get();

        textureObject = glGenTextures(); // generate texture name
        glBindTexture(GL_TEXTURE_2D, textureObject);

        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, this.width, this.heigth, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);   

        stbi_image_free(data);

    }

    public void bind (int sampler) {

        if (sampler >= 0 && sampler <= 31) {

            glActiveTexture(GL_TEXTURE0 + sampler);
            glBindTexture(GL_TEXTURE_2D, textureObject); // bind texture object 
        }
    }
}