简体   繁体   English

在LWJGL 3中使用HDR映射

[英]using HDR maps in LWJGL 3

I'm currently using LWJGL 3 and building a simple skybox. 我目前正在使用LWJGL 3并构建一个简单的skybox。 I want the skybox to take in a HDR file, an equirectangular map. 我希望Skybox接收HDR文件,即等角图。 I can get a skybox running with a PNG with PNGDecoder but unsure how it would work with an HDR file. 我可以使用PNGDecoder让Skybox与PNG一起运行,但不确定如何与HDR文件配合使用。 From what I understand, STB (as it is in c++) allows for the HDR file to be uploaded in the program, and LWJGL 3 has STB support. 据我了解,STB(在c ++中)允许将HDR文件上传到程序中,并且LWJGL 3具有STB支持。

How would I go about making a loadTexture function that supports STB and an HDR file? 我将如何制作支持STB和HDR文件的loadTexture函数?

EDIT: I'm going to post my progress to so anyone can see what I've been working on. 编辑:我将要发布的进度,以便任何人都可以看到我正在从事的工作。

  1. My loader class holds all my loadTexture methods and I'm using an int method that stores the texture ID, currently the method looks like this: 我的加载程序类包含我所有的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; } 

Which I got from this demo lwjgl3-demos here with an example of an environment map using an HDR texture and using the STBImage java bindings. 这是我从这个演示lwjgl3,演示了在这里使用了HDR纹理,并使用STBImage Java绑定的环境地图的一个例子。 The method also uses a class in my utils package called IOUtil, which is from the example and works in the sample. 该方法还在我的utils包中使用了一个名为IOUtil的类,该类来自示例,可在示例中使用。 (I also tried the HDR file from the learnOpengl tutorials which works fine with the example, but not with my own code); (我还尝试了LearnOpengl教程中的HDR文件,该文件在示例中工作正常,但不适用于我自己的代码);

I have a skybox shader and a skybox renderer which all seem to work well. 我有一个Skybox着色器和一个Skybox渲染器,它们似乎都可以正常工作。 The skybox renderer is written as below: 天空盒渲染器的编写方式如下:

public class SkyboxRenderer { 公共类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();
    }
}

I am using a vertex and fragment shader which came from learnOpengl's PBR tutorial. 我正在使用来自learningOpengl的PBR教程的顶点和片段着色器。

Vertex shader: 顶点着色器:

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

Fragment shader: 片段着色器:

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

The shader code works as well as it does, from the help of Thinmatrix's tutorials. 在Thinmatrix的教程的帮助下,着色器代码可以正常工作。

public class SkyboxShader extends ShaderProgram{ 公共类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);
} }

I initialize the loadCubeMap function in the skybox renderer along with the file name, then initialize the skybox renderer in the master renderer class. 我在天空盒渲染器中初始化loadCubeMap函数以及文件名,然后在主渲染器类中初始化天空盒渲染器。

By the time I run it, I get no errors about the HDR texture and the loader, so I assume its accepting it. 在运行它时,我对HDR纹理和加载程序没有任何错误,因此我假定它已接受它。 Most of it works. 大部分有效。

这里

I get a box and a texture, but the texture is binding to the wrong texture. 我得到一个盒子和一个纹理,但是纹理绑定到错误的纹理。 Its binding the albedo ground texture that I use in my terrain, which I suppose is the default condition when things are not binding correctly, just guessing. 它绑定了我在地形中使用的反照率地面纹理,我猜这是事物未正确绑定(只是猜测)时的默认条件。

Edit: I just realized that the HDR map is used for a sphere, and I'm rendering a cube. 编辑:我刚刚意识到HDR贴图用于一个球体,并且正在渲染一个立方体。 (lol) (大声笑)

So there, I can't seem to figure out the problem. 所以在那儿,我似乎无法解决问题。 I will give it another whirl and see what I can improve on. 我会再旋转一下,看看我可以改进什么。 Any help would be much appreciated. 任何帮助将非常感激。

Edit: 编辑:

So I've tried reworking it. 因此,我尝试对其进行重做。 The image variable was changed to a Float Buffer and now accepts image with stbi_loadf_from_memory. image变量已更改为Float Buffer,现在接受带有stbi_loadf_from_memory的图像。 Still a but confused, didn't think HDR maps would be this confusing. 仍然是一个但很困惑的人,并不认为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;

Instead of stbi_load_from_memory() You should use stbi_loadf_from_memory() , which is the function that reads HDR files. 您应该使用stbi_loadf_from_memory()而不是stbi_load_from_memory()来读取HDR文件。 STB also contains functions such as stbi_is_hdr_from_memory() that allows you to ensure that the buffer contains an HDR image. STB还包含诸如stbi_is_hdr_from_memory()函数,可让您确保缓冲区包含HDR图像。

Note that stbi_load_from_memory() returns a FloatBuffer containing the texture. 请注意, stbi_load_from_memory()返回包含纹理的FloatBuffer

In addition, you should modify the call to glTexImage2D() : 另外,您应该修改对glTexImage2D()的调用:

  • replace GL_UNSIGNED_BYTE with GL_FLOAT , as the buffer now contains a float for each component, and GL_UNSIGNED_BYTE替换为GL_FLOAT ,因为缓冲区现在包含每个组件的float ,并且
  • pick an appropriate floating point internal format, such as GL_RGB16F to replace GL_RGB8 . 选择一个适当的浮点内部格式,例如GL_RGB16F来替换GL_RGB8

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM