在LWJGL 3中使用HDR地图

时间:2017-12-22 07:38:45

标签: java opengl lwjgl

我目前正在使用LWJGL 3并构建一个简单的天空盒。我希望天空盒能够拍摄一个HDR文件,一个equirectangular地图。我可以使用带PNGDecoder的PNG运行天空盒,但不确定它如何与HDR文件一起使用。根据我的理解,STB(在c ++中)允许将HDR文件上传到程序中,LWJGL 3支持STB。

我如何制作支持STB和HDR文件的loadTexture函数?

编辑:我要发布我的进展,以便任何人都可以看到我一直在做的事情。

  1. 我的loader类包含我所有的loadTexture方法,我使用的是存储纹理ID的int方法,目前方法如下:

    public int loadCubeMap(String textureFile) throws IOException {
    
     int texID = glGenTextures();
     glBindTexture(GL_TEXTURE_2D, texID);
    
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    
     ByteBuffer imageBuffer;
     IntBuffer w = BufferUtils.createIntBuffer(1);
     IntBuffer h = BufferUtils.createIntBuffer(1);
     IntBuffer comp = BufferUtils.createIntBuffer(1);
     ByteBuffer image;
    
     imageBuffer = IOUtil.ioResourceToByteBuffer(textureFile, 8 * 1024);
    
     if (!stbi_info_from_memory(imageBuffer, w, h, comp))
         throw new IOException("Failed to read image information: " + stbi_failure_reason());
    
     image = stbi_load_from_memory(imageBuffer, w, h, comp, 3);
    
     if (image == null)
         throw new IOException("Failed to load image: " + stbi_failure_reason());
     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, w.get(0), h.get(0), 0, GL_RGB, GL_UNSIGNED_BYTE, image);
    
     stbi_image_free(image);
    
     return texID; }
    
  2. 我从这个演示lwjgl3-demos here获得了一个使用HDR纹理和使用STBImage java绑定的环境贴图示例。该方法还在我的utils包中使用了一个名为IOUtil的类,它来自示例并在示例中工作。 (我也尝试过LearnOpengl教程中的HDR文件,该教程适用于该示例,但不适用于我自己的代码);

    我有一个天空盒着色器和一个天空盒渲染器,它们似乎都运行良好。天空盒渲染器的编写如下:

    公共类SkyboxRenderer {

    private static final float SIZE = 500f;
    
    private static final float[] VERTICES = {        
        -SIZE,  SIZE, -SIZE,
        -SIZE, -SIZE, -SIZE,
        SIZE, -SIZE, -SIZE,
         SIZE, -SIZE, -SIZE,
         SIZE,  SIZE, -SIZE,
        -SIZE,  SIZE, -SIZE,
    
        -SIZE, -SIZE,  SIZE,
        -SIZE, -SIZE, -SIZE,
        -SIZE,  SIZE, -SIZE,
        -SIZE,  SIZE, -SIZE,
        -SIZE,  SIZE,  SIZE,
        -SIZE, -SIZE,  SIZE,
    
         SIZE, -SIZE, -SIZE,
         SIZE, -SIZE,  SIZE,
         SIZE,  SIZE,  SIZE,
         SIZE,  SIZE,  SIZE,
         SIZE,  SIZE, -SIZE,
         SIZE, -SIZE, -SIZE,
    
        -SIZE, -SIZE,  SIZE,
        -SIZE,  SIZE,  SIZE,
         SIZE,  SIZE,  SIZE,
         SIZE,  SIZE,  SIZE,
         SIZE, -SIZE,  SIZE,
        -SIZE, -SIZE,  SIZE,
    
        -SIZE,  SIZE, -SIZE,
         SIZE,  SIZE, -SIZE,
         SIZE,  SIZE,  SIZE,
         SIZE,  SIZE,  SIZE,
        -SIZE,  SIZE,  SIZE,
        -SIZE,  SIZE, -SIZE,
    
        -SIZE, -SIZE, -SIZE,
        -SIZE, -SIZE,  SIZE,
         SIZE, -SIZE, -SIZE,
         SIZE, -SIZE, -SIZE,
        -SIZE, -SIZE,  SIZE,
         SIZE, -SIZE,  SIZE
    };
    
    private RawModel cube;
    private int skyboxTexture;
    private SkyboxShader shader;
    
    public SkyboxRenderer(Loader loader, Matrix4f projectionMatrix) throws IOException {
        cube = loader.loadToVAO(VERTICES, 3);
        skyboxTexture = loader.loadCubeMap("res/newport_loft.hdr");
        shader = new SkyboxShader();
        shader.start();
        shader.loadProjectionMatrix(projectionMatrix);
        shader.connectTextureUnits();
        shader.stop();
        }
    
    public void render(Camera camera) {
        shader.start();
        shader.loadViewMatrix(camera);
        GL30.glBindVertexArray(cube.getVaoID());
        GL20.glEnableVertexAttribArray(0);
    
        GL11.glBindTexture(GL13.GL_TEXTURE_CUBE_MAP, skyboxTexture);
        GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, cube.getVertexCount());
        GL20.glDisableVertexAttribArray(0);
        GL30.glBindVertexArray(0);
        shader.stop();
        }
    }
    

    我正在使用来自learnOpengl的PBR教程的顶点和片段着色器。

    顶点着色器:

    #version 330 core 
    
    layout (location = 0) in vec3 aPos;
    
    out vec3 WorldPos;
    
    uniform mat4 projection; 
    uniform mat4 view;
    
    void main() {
        WorldPos = aPos;  
        gl_Position =  projection * view * vec4(WorldPos, 1.0); }
    

    片段着色器:

    #version 330 core
    out vec4 FragColor;
    in vec3 WorldPos;
    
    uniform sampler2D equirectangularMap;
    
    const vec2 invAtan = vec2(0.1591, 0.3183);
    vec2 SampleSphericalMap(vec3 v)
    {
        vec2 uv = vec2(atan(v.z, v.x), asin(v.y));
        uv *= invAtan;
        uv += 0.5;
        return uv;
    }
    
    void main()
    {       
        vec2 uv = SampleSphericalMap(normalize(WorldPos));
        vec3 color = texture(equirectangularMap, uv).rgb;
    
        FragColor = vec4(color, 1.0);
    }
    

    在Thinmatrix教程的帮助下,着色器代码可以正常工作。

    公共类SkyboxShader扩展了ShaderProgram {

    private static final String VERTEX_FILE = "src/skybox/cubemap.vs";
    private static final String FRAGMENT_FILE = "src/skybox/cubemap.fs";
    
    private int location_projectionMatrix;
    private int location_viewMatrix;
    
    private int location_equirectangularMap;
    
    public SkyboxShader() {
        super(VERTEX_FILE, FRAGMENT_FILE);
    }
    
    public void loadProjectionMatrix(Matrix4f matrix){
        super.loadMatrix(location_projectionMatrix, matrix);
    }
    
    public void loadViewMatrix(Camera camera){
        Matrix4f matrix = Maths.createViewMatrix(camera);
        super.loadMatrix(location_viewMatrix, matrix);
    }
    
    @Override
    protected void getAllUniformLocations() {
        location_projectionMatrix = super.getUniformLocation("projection");
        location_viewMatrix = super.getUniformLocation("view");
        location_equirectangularMap = super.getUniformLocation("equirectangularMap");
    }
    
    @Override
    protected void bindAttributes() {
        super.bindAttribute(0, "aPos");
    }
    
    public void connectTextureUnits() {
        super.loadInt(location_equirectangularMap, 0);
    } }
    

    我在天空盒渲染器中初始化loadCubeMap函数以及文件名,然后在主渲染器类中初始化天空盒渲染器。

    当我运行它时,我没有得到关于HDR纹理和加载器的错误,所以我认为它接受它。大多数都有效。

    here

    我得到一个盒子和一个纹理,但纹理绑定到错误的纹理。它绑定了我在地形中使用的反照率地面纹理,我认为这是当事物没有正确绑定时的默认条件,只是猜测。

    编辑:我刚刚意识到HDR地图用于球体,我正在渲染一个立方体。 (LOL)

    所以,我似乎无法弄清楚问题。我会再给它一个旋转,看看我能改进什么。任何帮助将不胜感激。

    编辑:

    所以我尝试过改造它。图像变量已更改为浮动缓冲区,现在接受带有stbi_loadf_from_memory的图像。仍然困惑但并不认为HDR地图会让人感到困惑。

    public int loadCubeMap(String textureFile) throws IOException {
    
             int texID = glGenTextures();
             //glActiveTexture(GL11.GL_TEXTURE);
             glBindTexture(GL_TEXTURE_2D, texID);
    
             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    
             ByteBuffer imageBuffer;
             IntBuffer w = BufferUtils.createIntBuffer(1);
             IntBuffer h = BufferUtils.createIntBuffer(1);
             IntBuffer comp = BufferUtils.createIntBuffer(1);
             FloatBuffer image;
    
             imageBuffer = IOUtil.ioResourceToByteBuffer(textureFile, 8 * 1024);
    
             if (!stbi_info_from_memory(imageBuffer, w, h, comp))
                 throw new IOException("Failed to read image information: " + stbi_failure_reason());
    
             image = stbi_loadf_from_memory(imageBuffer, w, h, comp, 3);
    
    
    
             if (image == null)
                 throw new IOException("Failed to load image: " + stbi_failure_reason());
             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16, w.get(0), h.get(0), 0, GL_RGB16, GL_FLOAT, image);
    
             stbi_image_free(image);
    
             return texID;
    

1 个答案:

答案 0 :(得分:0)

而不是stbi_load_from_memory()您应该使用stbi_loadf_from_memory(),这是读取HDR文件的函数。 STB还包含stbi_is_hdr_from_memory()等功能,可以确保缓冲区包含HDR图像。

请注意,stbi_load_from_memory()会返回包含纹理的FloatBuffer

此外,您应该修改对glTexImage2D()的调用:

  • GL_UNSIGNED_BYTE替换为GL_FLOAT,因为缓冲区现在每个组件都包含float,并且
  • 选择适当的浮点内部格式,例如GL_RGB16F以替换GL_RGB8