【发布时间】:2023-04-01 15:00:01
【问题描述】:
我目前正在使用 LWJGL 3 并构建一个简单的天空盒。我希望天空盒接收一个 HDR 文件,一个 equirectangular 地图。我可以使用 PNGDecoder 获得一个运行 PNG 的天空盒,但不确定它如何与 HDR 文件一起使用。据我了解,STB(在 c++ 中)允许将 HDR 文件上传到程序中,并且 LWJGL 3 支持 STB。
如何制作支持 STB 和 HDR 文件的 loadTexture 函数?
编辑:我将发布我的进度,以便任何人都可以看到我一直在做什么。
-
我的加载器类包含我所有的 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;
【问题讨论】: