【问题标题】:using HDR maps in LWJGL 3在 LWJGL 3 中使用 HDR 贴图
【发布时间】:2023-04-01 15:00:01
【问题描述】:

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

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

编辑:我将发布我的进度,以便任何人都可以看到我一直在做什么。

  1. 我的加载器类包含我所有的 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; }
    

我从这个演示 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 纹理和加载器的错误,所以我假设它接受它。大部分都有效。

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

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

所以,我似乎无法找出问题所在。我会再试一试,看看我可以改进什么。任何帮助将不胜感激。

编辑:

所以我尝试重新设计它。图像变量已更改为浮点缓冲区,现在接受带有 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;

【问题讨论】:

    标签: java opengl lwjgl


    【解决方案1】:

    您应该使用stbi_loadf_from_memory() 而不是stbi_load_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

    【讨论】:

    • 感谢您的回复!到目前为止,我将 Bytebuffer 图像更改为 FloatBuffer。并将 image = stbi_load_from_memory 更改为 stbi_loadf_from_memory。我的错误检查仍然保持不变,w、h 和 comp 变量的 IntBuffers 是相同的。找不到 GL_RGB16F,我确定我有当前版本的库。
    • 另外,我有一个示例似乎可以很好地加载 HDR 图像,但不需要 stbi_loadf 或适当的浮点即可工作。我假设它是因为只有在您想最大限度地使用 HDR 图像时才需要它。
    • (1) GL_RGB16F 应该可用,参见legacy.lwjgl.org/javadoc/org/lwjgl/opengl/GL30.html#GL_RGB16F,(2) HDR,与高动态范围一样,通常需要浮点值来存储更大范围的值。
    猜你喜欢
    • 2020-03-04
    • 1970-01-01
    • 2012-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-19
    • 1970-01-01
    相关资源
    最近更新 更多