Advertisement

Draw Quadrangles

Started by June 30, 2020 06:52 AM
7 comments, last by Tommato 4 years, 7 months ago

Hi All

Porting old code to 4.1 I've found the glDrawElements with GL_QUADS doesn't draw and generates error. Yes, I heard the quadrangles are deprectated long ago, but hoped it won't touch me ;-) Looking in web I've not found yet how to draw them with newer OpenGL? Just 2 triangles is not a way to go, at least I need “wireframe” too.

Thx

This depends on what you're drawing.

If you're just drawing a single quad for e.g. a billboarded sprite or some 2D UI elements, you can express it as a triangle fan or strip.

If you're drawing a fullscreen quad for post-processing you could consider using a fullscreen triangle instead.

If you're drawing many quads in a particle system you can use fans or strips with primitive restart, or triangles with an index buffer. You could also do point-to-quad expansion in a geometry shader.

If you're drawing cubes you could do likewise.

Or alternatively you could create a compatibility context and just use GL_QUADS, which may the more pragmatic option.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

Advertisement

It's not the most optimal way to draw many quads, but it works for one textured quad. You might have to change the vertex order in vertex_data, from 021 to 012 and 032 to 023.

	void draw(GLuint shader_program, size_t x, size_t y, size_t win_width, size_t win_height)
	{
		y = win_height - y;

		glBindTexture(GL_TEXTURE_2D, tex_handle);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

		float hp_ratio = static_cast<float>(this->hit_points) / static_cast<float>(this->max_hit_points);

		RGB_uchar rgb = HSBtoRGB(static_cast<unsigned short int>(100.0f*hp_ratio),
			static_cast<unsigned char>(100),
			static_cast<unsigned char>(100));

		for (size_t i = 1; i < width - 1; i++)
		{
			for (size_t j = 1; j < height - 1; j++)
			{
				size_t index = 4 * (j * width + i);

				if (i == 1 || i == 2 || j == 1 || j == 2 || i == width - 2 || j == height - 2 || i == width - 3 || j == height - 3)
				{
					texture_data[index + 0] = rgb.r;
					texture_data[index + 1] = rgb.g;
					texture_data[index + 2] = rgb.b;
					texture_data[index + 3] = 255;
				}
			}
		}

		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, static_cast<GLsizei>(width), static_cast<GLsizei>(height), 0, GL_RGBA, GL_UNSIGNED_BYTE, &amp;amp;texture_data[0]);

		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

		complex<float> v0w(static_cast<float>(x), static_cast<float>(y));
		complex<float> v1w(static_cast<float>(x), static_cast<float>(y + this->height));
		complex<float> v2w(static_cast<float>(x + this->width), static_cast<float>(y + this->height));
		complex<float> v3w(static_cast<float>(x + this->width), static_cast<float>(y));

		complex<float> v0ndc = get_ndc_coords_from_window_coords(win_width, win_height, v0w);
		complex<float> v1ndc = get_ndc_coords_from_window_coords(win_width, win_height, v1w);
		complex<float> v2ndc = get_ndc_coords_from_window_coords(win_width, win_height, v2w);
		complex<float> v3ndc = get_ndc_coords_from_window_coords(win_width, win_height, v3w);

		vector<GLfloat> vertex_data = {

			// 3D position, 2D texture coordinate
			v0ndc.real(), v0ndc.imag(), 0,   0, 1, // vertex 0
			v2ndc.real(), v2ndc.imag(), 0,   1, 0, // vertex 2
			v1ndc.real(), v1ndc.imag(), 0,   0, 0, // vertex 1
			
			v0ndc.real(), v0ndc.imag(), 0,   0, 1, // vertex 0
			v3ndc.real(), v3ndc.imag(), 0,   1, 1, // vertex 3
			v2ndc.real(), v2ndc.imag(), 0,   1, 0 // vertex 2
		};

		GLuint components_per_vertex = 5;
		GLuint components_per_position = 3;
		GLuint components_per_tex_coord = 2;
		GLuint num_vertices = static_cast<GLuint>(vertex_data.size()) / components_per_vertex;

		glBindBuffer(GL_ARRAY_BUFFER, vbo_handle);

		glBufferData(GL_ARRAY_BUFFER, vertex_data.size() * sizeof(GLfloat), &amp;amp;vertex_data[0], GL_STATIC_DRAW);

		glEnableVertexAttribArray(glGetAttribLocation(shader_program, "position"));
		glVertexAttribPointer(glGetAttribLocation(shader_program, "position"),
			components_per_position,
			GL_FLOAT,
			GL_FALSE,
			components_per_vertex * sizeof(GLfloat),
			NULL);

		glEnableVertexAttribArray(glGetAttribLocation(shader_program, "texcoord"));
		glVertexAttribPointer(glGetAttribLocation(shader_program, "texcoord"),
			components_per_tex_coord,
			GL_FLOAT,
			GL_TRUE,
			components_per_vertex * sizeof(GLfloat),
			(const GLvoid*)(components_per_position * sizeof(GLfloat)));

		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, tex_handle);
		glUniform1i(glGetUniformLocation(shader_program, "tex"), 0);

		glDrawArrays(GL_TRIANGLES, 0, num_vertices);
	}

Thx for your replies

21st Century Moose said:
This depends on what you're drawing.

I should draw any/arbitrary model loaded/given by user, the quads should stay quads because other code/features rely on this. Yes, with comnpatibility context GL_QUADS works but I need newer features (such as instancing) that's why the porting started.

taby said:
You might have to change the vertex order in vertex_data, from 021 to 012 and 032 to 023.

Sorry, not clear. Can I change indices order for glDrawElements (easy for me) and it will draw quads? (guess that not, too simple). And will it work for wireframe?

Ok, looking web and other apps code I see the most standard/canonical way is to use GL_LINES_ADJACENCY together with geometry shader. Ok, but does it means I need to activate this geometry shader for every quads drawing (and turn it off for other primitives)? Can I make an “universal” geometry shader that handles quads and passes-thru others?

Thank you

Tommato said:
Yes, with comnpatibility context GL_QUADS works but I need newer features (such as instancing) that's why the porting started.

You can use instancing with GL_QUADS in a compatibility context.

(You can even use instancing with ARB assembly programs, GL_QUADS and immediate mode if you wanted).

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

21st Century Moose said:
You can use instancing with GL_QUADS in a compatibility context. (You can even use instancing with ARB assembly programs, GL_QUADS and immediate mode if you wanted).

Can you please explain? The specification says

“glDrawElementsInstanced is available only if the GL version is 3.1 or greater”

So how can I use it with compatibility context that reports OpenGL 2.1 ?

Thank you

Advertisement

Unless you're on a mac, your compatibility context should be fully-featured and have version-parity with your core context.

Even if you're on a mac you can check for the instanced arrays extension: https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_instanced_arrays.txt

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

21st Century Moose said:
Even if you're on a mac you can check for the instanced arrays extension: https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_instanced_arrays.txt

Yes, I've this extension, but there are too many probs with very old GLSL. For example with instancing I need

attribute mat4 modelViewMatrix;

But with compatibility context

QOpenGLShader::compile(Vertex): ERROR: 0:163: GLSL 110 does not allow sub- or super-matrix constructors ;-(

Also I realize that 3.3 profile is already obsolete, but porting to “metal” (and Vulcan for Win platform) looks awful

This topic is closed to new replies.

Advertisement