Strange Bug in Projected-Grid Rendering

Started by
6 comments, last by multifractal 7 years, 7 months ago

Hello.

I have been attempting to implement a projected grid to test out some water related code I have written. In order to do this I implemented the projected grid created by Eric Bruneton in his ocean water/lighting paper (http://www-ljk.imag.fr/Publications/Basilic/com.lmc.publi.PUBLI_Article@125fe8a8322_1ac3379/article.pdf). I have essentially completed the implementation aside for one odd bug that remains unsolved. Whenever the pitch of the camera increases to above some angle, dependent on how the "horizon" of the grid is computed, the grid becomes to draw degenerate triangles and it seems like a projection problem but I can't really tell what about this angle causes the grid to spasm so severely. The code for grid generation is implemented as follows:


void generate_mesh(float camera_theta)
{
	if (grid_vbo_size != 0) {
		glDeleteVertexArrays(1, &grid_vao);
		glDeleteBuffers(1, &grid_vbo);
		glDeleteBuffers(1, &grid_ibo);
	}
	glGenVertexArrays(1, &grid_vao);
	glBindVertexArray(grid_vao);
	glGenBuffers(1, &grid_vbo);
	glBindBuffer(GL_ARRAY_BUFFER, grid_vbo);
	//was horizon = tan(camera_theta / 180 * M_PI);
	float horizon = tan(45 / 180.0 * M_PI);
	float s = std::min(1.1f, 0.5f + horizon * 0.50f);
	std::cout << s << std::endl;
	float vmargin = 0.1;
	float hmargin = 0.1;
	int size = int(ceil(HEIGHT * (s + vmargin) / grid_size) + 5) * int(ceil(WIDTH * (1.0 + 2.0 * hmargin) / grid_size) + 5);
	glm::vec4 *data = new glm::vec4[int(ceil(HEIGHT * (s + vmargin) / grid_size) + 5) * int(ceil(WIDTH * (1.0 + 2.0 * hmargin) / grid_size) + 5)];
	int n = 0;
	int nx = 0;
	for (float j = HEIGHT * s - 0.1; j > -HEIGHT * vmargin - grid_size; j -= grid_size) {
		nx = 0;
		for (float i = -WIDTH * hmargin; i < WIDTH * (1.0 + hmargin) + grid_size; i += grid_size) {
			data[n++] = glm::vec4(-1.0 + 2.0 * i / WIDTH, -1.0 + 2.0 * j / HEIGHT, 0.0, 1.0);
			nx++;
		}
	}
	glBufferData(GL_ARRAY_BUFFER, n * 16, data, GL_STATIC_DRAW);
	delete[] data;
	glGenBuffers(1, &grid_ibo);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, grid_ibo);
	grid_vbo_size = 0;
	GLuint *indices = new GLuint[6 * int(ceil(HEIGHT * (s + vmargin) / grid_size) + 4) * int(ceil(WIDTH * (1.0 + 2.0 * hmargin) / grid_size) + 4)];
	int nj = 0;
	for (float j = HEIGHT * s - 0.1; j > -HEIGHT * vmargin; j -= grid_size) {
		int ni = 0;
		for (float i = -WIDTH * hmargin; i < WIDTH * (1.0 + hmargin); i += grid_size) {
			indices[grid_vbo_size++] = ni + ((nj + 1) * nx);
			indices[grid_vbo_size++] = (ni + 1) + ((nj + 1) * nx);
			indices[grid_vbo_size++] = (ni + 1) + (nj * nx);
			indices[grid_vbo_size++] = (ni + 1) + (nj * nx);
			indices[grid_vbo_size++] = ni + ((nj + 1) * nx);
			indices[grid_vbo_size++] = ni + nj * nx;
			ni++;
		}
		nj++;
	}
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, grid_vbo_size * sizeof(GLuint), indices, GL_STATIC_DRAW);
	glVertexAttribPointer(0, 4, GL_FLOAT, false, 4 * sizeof(GL_FLOAT), (GLvoid *)0);
	glEnableVertexAttribArray(0);
	delete[] indices;
	glBindVertexArray(0);
}

As I mentioned above the following line:


float horizon = tan(45 / 180.0 * M_PI);

affects the angle at which the grid begins to become warped. In Bruneton's original code this angle was set to be the pitch of the camera but this still presents the bug (and other undesirable behavior.) The matrices used to project the grid are simply my standard projection and view matrices (along with their inverts) and seem to be set up correctly as they projected various other geometry as expected.

The actual grid is transformed like so in a shader:


vec2 ocean_pos(vec4 vertex)
{
	vec3 camera_dir = normalize((screen_to_camera * vertex).xyz);
	vec3 world_dir = (camera_to_world * vec4(camera_dir, 0.0)).xyz;
	//was t = -world_camera.z / world_dir.z;
	float t = -world_camera.y / world_dir.y;
	return world_camera.xz + t * world_dir.xz;
}
void main()
{
	vec2 u = ocean_pos(position);
	float s = sin(u.x) * cos(u.y);
	color = vec3(s);
	gl_Position = world_to_screen * vec4(u.x, s, u.y, 1.0);
} 

where world_to_screen = perspective matrix * view matrix, screen_to_camera = inverse perspective matrix, camera_to_world = inverse of view matrix, and world_camera is the position of the camera in world space.

In order to illustrate the problem further here is a screenshot of the grid with the camera pointed just under the problem angle:

[attachment=33137:angle_problem2.png]

And then when the camera is moved just above the angle:

[attachment=33138:angle_problem3.png]

Filled in:

[attachment=33139:angle_problem.png]

(the grid is being offset by sine functions to emphasize the bug but it is present in non offset grids.)

Has anyone who has implemented this encountered similar problems? Does anyone see if it's obvious why applying the aforementioned transformation would so warp a grid when the view is of a certain angle?

Thank you for your time.

Advertisement

Looks like the top row of vertices has bad positions. It's getting clipped in the first screenshot, so it doesn't show. My guess is the bad positions come from bad indices and the bad indices come from an off-by-one error when generating indices (probably on the loop bounds).

In the second image there you can definitely see that some of the indices are incorrect because there is lines going straight through the model in a lot of places. I'd re-check your indices and the way they are being calculated.

Looks like the top row of vertices has bad positions. It's getting clipped in the first screenshot, so it doesn't show. My guess is the bad positions come from bad indices and the bad indices come from an off-by-one error when generating indices (probably on the loop bounds).

In the second image there you can definitely see that some of the indices are incorrect because there is lines going straight through the model in a lot of places. I'd re-check your indices and the way they are being calculated.

I too thought the indices were the problem but I tried drawing the vertices without doing all of the projections and no problem was seen with the polygon construction:

[attachment=33167:angle_problem4.png]

this mesh is created with the same indices and vertices that the projected grid in the original post uses.

Looks like the top row of vertices has bad positions. It's getting clipped in the first screenshot, so it doesn't show. My guess is the bad positions come from bad indices and the bad indices come from an off-by-one error when generating indices (probably on the loop bounds).

In the second image there you can definitely see that some of the indices are incorrect because there is lines going straight through the model in a lot of places. I'd re-check your indices and the way they are being calculated.

I too thought the indices were the problem but I tried drawing the vertices without doing all of the projections and no problem was seen with the polygon construction:

attachicon.gifangle_problem4.png

this mesh is created with the same indices and vertices that the projected grid in the original post uses.

I feel like the code you wrote is too messy to easily debug it and picture what is going on here.

I feel like the code you wrote is too messy to easily debug it and picture what is going on here.

Can I clear something up for you then? Do you not understand the bounds of the loop?

Maybe it's division by zero or near zero in ocean_pos()

Maybe it's division by zero or near zero in ocean_pos()

Wow, a quick test of that yielded the attached image. Upside down, but with the problem resolved. Thanks for your help!

[attachment=33184:Screenshot 2016-09-07 04.59.37.png]

EDIT: In the case that anyone is interested in the fix:

the glitch happens whenever 'world_dir.y' from


float t = -ocean_camera_pos.y / world_dir.y;
return ocean_camera_pos.xz + (t) * world_dir.xz;

became positive making 't' become negative. Setting the variable like:


float t = -ocean_camera_pos.y / (min(world_dir.y, -0.001));

generally resolves the problem.

This topic is closed to new replies.

Advertisement