I am trying to get a terrain rendering system I wrote a while ago with LWJGL. This terrain rendering system is quite simple: a quad mesh is split as the camera moves closer to it, each time a new quad is created a FBO is used to render a heightmap using perlin noise implemented in a pixel shader. This texture is then applied to the quad and sampled for normals and elevation. The problem I am having with it is a bit odd... So the camera starts out at a reasonable distance away from the root quad. This causes it to be split into 4 quads, each with their own heightmap. This can be represented as follows:
As the camera moves closer the program acts as expected and the nearest quad (the white one) is split into 4 children quads, again, each with their own heightmap.
Now here is where the problems start to arise... When the camera moves even closer the blue quad that you see above (the larger one) is split into 4 quads as expected. Yet what is strange is that the smallest green quad's heightmap is changed to be the heightmap of the largest red quad shown above...
1 is the quad that has it's heightmap changed when the blue quad is split. #2 refers to the quad that the green quad seems to "take" it's new heightmap from.
This continues to happen with different quads as the camera moves closer. Yet it is only the green quads that exhibit this type of behavior. I suspect this because the green quad is the last quad to have a heightmap rendered and this can be seen as follows:
QuadMesh q1 = q.children.get(0);
QuadMesh q2 = q.children.get(1);
QuadMesh q3 = q.children.get(2);
QuadMesh q4 = q.children.get(3);
Heightmap.getHeightmap(q1);
Heightmap.getHeightmap(q2);
Heightmap.getHeightmap(q3);
//q4 is the green quad
Heightmap.getHeightmap(q4);
The getHeightmap() method looks like so:
public static void getHeightmap(QuadMesh q){
float scale = q.width/1024;
float size = q.width;
int unit = heightmapTextureUnit;
glActiveTexture(GL_TEXTURE0 + unit);
int heightmapTexture = glGenTextures();
glBindTexture(GL_TEXTURE_2D, heightmapTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, 256, 256, 0, GL_RGB, GL_FLOAT, (java.nio.ByteBuffer)null);
int framebuffer = glGenFramebuffers();
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, heightmapTexture, 0);
glViewport(0,0,256,256);
glUseProgram(noiseProgram);
glUniform1i(glGetUniformLocation(noiseProgram, "permSampler2d"), permutationTextureUnit);
glUniform1i(glGetUniformLocation(noiseProgram, "permGradSampler"), gradientTextureUnit);
glUniform3f(glGetUniformLocation(noiseProgram, "meshOffset"), q.meshOffset.x, q.meshOffset.y, q.meshOffset.z);
glUniform1f(glGetUniformLocation(noiseProgram, "scale"), scale);
glUniform1f(glGetUniformLocation(noiseProgram, "size"), size);
glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
drawQuad();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDeleteFramebuffers(framebuffer);
glViewport(0,0,1024,768);
q.HeightmapTexture = heightmapTexture;
q.HeightmapUnit = unit;
heightmapTextureUnit++;
}
To render a quad, the following method is used:
public void render(Matrix4f view, Matrix4f projection){
glUseProgram(p_ID);
float scale = width/1024;
FloatBuffer matBuffer = BufferUtils.createFloatBuffer(16);
this.cubeMatrix.store(matBuffer);
matBuffer.flip();
glUniformMatrix4(glGetUniformLocation(p_ID, "cubeMatrix"), false, matBuffer);
view.store(matBuffer);
matBuffer.flip();
glUniformMatrix4(glGetUniformLocation(p_ID, "V"), false, matBuffer);
projection.store(matBuffer);
matBuffer.flip();
glUniformMatrix4(glGetUniformLocation(p_ID, "P"), false, matBuffer);
glUniform1f(glGetUniformLocation(p_ID, "scale"), scale);
glUniform1f(glGetUniformLocation(p_ID, "size"), width);
glUniform1f(glGetUniformLocation(p_ID, "intensity"), intensity);
glUniform3f(glGetUniformLocation(p_ID, "meshOffset"), this.meshOffset.x, this.meshOffset.y, this.meshOffset.z);
glUniform3f(glGetUniformLocation(p_ID, "color"), color.x, color.y, color.z);
glUniform1i(glGetUniformLocation(p_ID, "heightmap"), HeightmapUnit);
glBindVertexArray(VAO_ID);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO_ID);
if(Main.wireframe)
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
else
glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
glDrawElements(GL_TRIANGLES, QuadData.getIndexLength(), GL_UNSIGNED_INT, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glBindVertexArray(0);
glUseProgram(0);
}
EDIT: So going through the program a little bit more carefully returned a few errors. Apparently when heightmapTextureUnit gets to be 80 and above the error "GL_INVALID_ENUM" is returned when the texture is activated. This is returned when the arguemnt of glActiveTexture() is not within the bound of GL_MAX_COMBINED_TEXTURE_IMAGE_UNIT. Is it impossible for me to have more than 80 textures? Also the error GL_INVALID_OPERATION is returned when I use the glDrawElements() method...but only when there are around 150 quads active. What causes this?