Detailed explanation of glDrawElements() of openGL API

Posted by Dr.Flink on Sat, 22 Jan 2022 19:33:22 +0100

openGL series article directory

Official website

Official documents

Name
glDrawElements — render primitives from array data

C Specification
void glDrawElements( GLenum mode,
GLsizei count,
GLenum type,
const void * indices);

Parameters
mode
Specifies what kind of primitives to render. Symbolic constants GL_POINTS, GL_LINE_STRIP, GL_LINE_LOOP, GL_LINES, GL_LINE_STRIP_ADJACENCY, GL_LINES_ADJACENCY, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_TRIANGLES, GL_TRIANGLE_STRIP_ADJACENCY, GL_TRIANGLES_ADJACENCY and GL_PATCHES are accepted.

count
Specifies the number of elements to be rendered.

type
Specifies the type of the values in indices. Must be one of GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, or GL_UNSIGNED_INT.

indices
Specifies a pointer to the location where the indices are stored.

Description
glDrawElements specifies multiple geometric primitives with very few subroutine calls. Instead of calling a GL function to pass each individual vertex, normal, texture coordinate, edge flag, or color, you can prespecify separate arrays of vertices, normals, and so on, and use them to construct a sequence of primitives with a single call to glDrawElements.

When glDrawElements is called, it uses count sequential elements from an enabled array, starting at indices to construct a sequence of geometric primitives. mode specifies what kind of primitives are constructed and how the array elements construct these primitives. If more than one array is enabled, each is used.

Vertex attributes that are modified by glDrawElements have an unspecified value after glDrawElements returns. Attributes that aren't modified maintain their previous values.

Notes
GL_LINE_STRIP_ADJACENCY, GL_LINES_ADJACENCY, GL_TRIANGLE_STRIP_ADJACENCY and GL_TRIANGLES_ADJACENCY are available only if the GL version is 3.2 or greater.

Errors
GL_INVALID_ENUM is generated if mode is not an accepted value.

GL_INVALID_VALUE is generated if count is negative.

GL_INVALID_OPERATION is generated if a geometry shader is active and mode is incompatible with the input primitive type of the geometry shader in the currently installed program object.

GL_INVALID_OPERATION is generated if a non-zero buffer object name is bound to an enabled array or the element array and the buffer object's data store is currently mapped.

Version Support
OpenGL Version
Function / Feature Name 2.0 2.1 3.0 3.1 3.2 3.3 4.0 4.1 4.2 4.3 4.4 4.5
glDrawElements ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔
See Also
glDrawArrays, glDrawElementsInstanced, glDrawElementsBaseVertex, glDrawRangeElements

Copyright
Copyright © 1991-2006 Silicon Graphics, Inc. Copyright © 2010-2014 Khronos Group. This document is licensed under the SGI Free Software B License. For details, see https://khronos.org/registry/OpenGL-Refpages/LICENSES/LicenseRef-FreeB.txt.

translate

Function function: use count elements to define a geometric sequence, and the index values of these elements are saved in the indexes array.
Parameter Description:
mode: the accepted value is the same as the value accepted in glBegin(), which can be GL_POLYGON,GL_TRIANGLES,GL_TRIANGLE_STRIP,GL_LINE_STRIP, etc.

count: the number of elements of the combined geometry, usually the number of points.

Type: the data type of the indeces array. Since it is an index, it is generally an integer.

Indexes: index array

describe

Gldrawelements specifies multiple geometry primitives with few subroutine calls. Instead of calling the GL function to pass each individual vertex, normal, texture coordinates, edge flag or color, you can specify a separate array of vertices, normals, etc. in advance and use them to construct a series of primitive elements through a single call to gldraineelements.

When gldrawelements is called, it uses the count order elements in the enabled array to construct a sequence of geometry from the index. The pattern specifies which primitives to construct and how array elements construct these primitives. If multiple arrays are enabled, vertex attributes modified with each array gldraineelements have unspecified values after gldraineelements is returned. Unmodified properties retain their previous values.

be careful

GL is available only when GL version is 3.2 or later_ LINE_ STRIP_ ADJACENCY, GL_ LINES_ ADJACENCY, GL_ TRIANGLE_ STRIP_ ADJACENCY and GL_ TRIANGLES_ Adjacency is available.

error

If the mode is not an acceptable value, GL is generated_ INVALID_ ENUM .
If the count is negative, GL is generated_ INVALID_ VALUE .
If a non-zero buffer object name is bound to an enabled array or array of elements, and the data store of the buffer object is currently mapped, GL is generated_ INVALID_ Operation.

Version support

OpenGL version

Function / feature name 2.0 2.1 3.0 3.1 3.2 3.3 4.0 4.1 4.2 4.3 4.4.5

trace element ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔

See also

GLDrawArray,glDrawElementsInstanced,glDrawElementsBaseVertex,glDrawRangeElements

copyright

copyright © Copyright 1991-2006 Silicon Graphics, Inc © 2010-2014 Khronos Group. This document is licensed under the SGI free software B license. For more information, see https://khronos.org/registry/OpenGL-Refpages/LICENSES/LicenseRef-FreeB.txt.

example

#include "glew/glew.h"
#include "glfw/glfw3.h"
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "glm/gtc/type_ptr.hpp"
#include "Utils.h"
#include "Torus.h"
#include "SOIL2/SOIL2.h"
#include "camera.h"
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

static const float pai = 3.1415926f;


float toRadins(float degress)
{
	return (degress * 2.f * pai / 360.f);
}

GLuint renderingProgram = 0;
static const int numVAOs = 1;
static const int numVBOs = 4;
GLuint vao[numVAOs] = { 0 };
GLuint vbo[numVBOs] = { 0 };

static const int screen_width = 1920;
static const int screen_height = 1080;
int width = 0;
int height = 0;

float cameraX = 0.f, cameraY = 0.f, cameraZ = 0.f;
float torusLocX = 0.f, torusLocY = 0.f, torusLocZ = 0.f;

GLuint torusTextureId = 0;
float rotAmt = 0.f;   //Y-axis rotation
GLuint mvLoc = 0;
GLuint projLoc = 0;

float aspect = 0.f;

glm::mat4 mMat(1.f), vMat(1.f), pMat(1.f), mvMat(1.f);

Torus myTorus(0.5f, 0.2f, 48);
Camera camera(glm::vec3(0.f, 0.f, 5.f));
float deltaTime = 0.f;
float lastFrame = 0.f;

GLboolean keys[1024] = { GL_FALSE };
GLboolean firstMouse = GL_TRUE;

float lastLocX = 0.f;
float lastLocY = 0.f;

void processKeyInput(GLFWwindow* window);

void setupVertices(void)
{
	vector<int> ind = myTorus.getIndices();           //Get vertex index
	vector<glm::vec3> vert = myTorus.getVertices();   //Get each vertex, and any vertex contains (x, y, z)
	vector<glm::vec2> tex = myTorus.getTexCoords();
	vector<glm::vec3> norm = myTorus.getNormals();
	vector<glm::vec3> sTang = myTorus.getStangents();
	vector<glm::vec3> tTang = myTorus.getTtangents();

	vector<float> pValues;   //Vertex Position 
	vector<float> tValues;   //Texture coordinates
	vector<float> nValues;   //Normal coordinates

	for (int i=0; i<myTorus.getNumVertices(); i++)
	{
		pValues.push_back(vert[i].x);
		pValues.push_back(vert[i].y);
		pValues.push_back(vert[i].z);

		//Note that the texture coordinates here are S and T
		tValues.push_back(tex[i].s);   
		tValues.push_back(tex[i].t);
		
		nValues.push_back(norm[i].x);
		nValues.push_back(norm[i].y);
		nValues.push_back(norm[i].z);
	}

	glGenVertexArrays(numVAOs, vao);
	glBindVertexArray(vao[0]);

	glGenBuffers(numVBOs, vbo);
	glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
	glBufferData(GL_ARRAY_BUFFER, pValues.size() * sizeof(float), &(pValues[0]), GL_STATIC_DRAW);

	glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
	glBufferData(GL_ARRAY_BUFFER, tValues.size() * sizeof(float), &(tValues[0]), GL_STATIC_DRAW);

	glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
	glBufferData(GL_ARRAY_BUFFER, nValues.size() * sizeof(float), &(nValues[0]), GL_STATIC_DRAW);

	//Note that we use glDrawElements to draw later, so GL must be used here_ ELEMENT_ ARRAY_ BUFFER
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[3]);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, ind.size() * sizeof(float), &(ind[0]), GL_STATIC_DRAW);
}

void init(GLFWwindow* window)
{
	renderingProgram = Utils::createShaderProgram("vertShader.glsl", "fragShader.glsl");
	cameraX = 0.f, cameraY = 0.f, cameraZ = 2.f;
	torusLocX = 0.f, torusLocY = 0.f, torusLocZ = -0.5f;

	glfwGetFramebufferSize(window, &width, &height);
	aspect = (float)width / (float)height;
	pMat = glm::perspective(glm::radians(45.f), aspect, 0.01f, 1000.f);

	setupVertices();
	torusTextureId = Utils::loadTexture("resources/brick1.jpg");
}

void key_movement_callback()
{
	if (keys[GLFW_KEY_W])
	{
		camera.ProcessKeyboard(FORWARD, deltaTime);
	}
	if (keys[GLFW_KEY_S])
	{
		camera.ProcessKeyboard(BACKWARD, deltaTime);
	}
	if (keys[GLFW_KEY_A])
	{
		camera.ProcessKeyboard(LEFT, deltaTime);
	}
	if (keys[GLFW_KEY_D])
	{
		camera.ProcessKeyboard(RIGHT, deltaTime);
	}
}

void display(GLFWwindow* window, float currentTime)
{
	//Note that if GL_DEPTH_BUFFER_ Write bit as GL_DEPTH_BUFFER, residual shadows appear in the rendering window
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glClearColor(0.f, 0.1f, 0.8f, 1.f);

	glUseProgram(renderingProgram);

	GLfloat currentTimeFrame = glfwGetTime();
	//deltaTime = currentTime - lastFrame;
	//lastFrame = currentTime;
	

	mvLoc = glGetUniformLocation(renderingProgram, "mv_matrix");
	projLoc = glGetUniformLocation(renderingProgram, "proj_matrix");

	key_movement_callback();

	vMat = camera.GetViewMatrix();
	//vMat = glm::translate(glm::mat4(1.f), glm::vec3(cameraX, cameraY, -cameraZ));
	mMat = glm::translate(glm::mat4(1.f), glm::vec3(torusLocX, torusLocY, torusLocZ));
	mMat = glm::rotate(mMat, toRadins(60.f), glm::vec3(1.f, 0.f, 0.f));
	aspect = (float)screen_width / (float)screen_height;
	pMat = glm::perspective(camera.Zoom, aspect, 0.01f, 1000.f);
	//Right multiplication rule
	mvMat = vMat * mMat;

	//Change the value of a uniform matrix variable or array. The location of the uniform variable to be changed is specified by location, and the value of location should be returned by the glGetUniformLocation function
	// Copy the perspective matrix and MV matrix to the corresponding unified variables
	/*Pass the consistent variable value into the rendering pipeline through the consistent variable (uniform decorated variable) reference.
	  location : uniform The location of the.
	  count : The number of array elements that need to load data or the number of matrices that need to be modified.
	  transpose : Indicates whether the matrix is a column major matrix (GL_FALSE) or a row major matrix (GL_TRUE).
	  value : Pointer to an array of count elements.
	*/
	glUniformMatrix4fv(mvLoc, 1, GL_FALSE, glm::value_ptr(mvMat));
	glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(pMat));

	//Render Vertes 
	glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
	//Specifies the data format and location of the vertex attribute array whose index value is index at render time
	/*Parameters
	index
		Specifies the index value of the vertex attribute to modify

		size
		Specifies the number of components per vertex attribute. Must be 1, 2, 3, or 4. The initial value is 4. (dream dimension: for example, position is composed of 3 (x, y, z) and 4 (r, g, b, a))

		type
		Specifies the data type of each component in the array. The available symbolic constants are GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_FIXED, and GL_FLOAT, the initial value is GL_FLOAT. 

		normalized
		Specifies whether fixed point data values should be normalized (GL_TRUE) or directly converted to fixed point values (GL_FALSE) when accessed.

		stride
		Specifies the offset between successive vertex attributes. If it is 0, the vertex attributes will be understood as: they are closely arranged together. The initial value is 0.

		pointer
		Specifies a pointer to the first component of the first vertex attribute in the array. The initial value is 0.*/
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
	//Enable or disable the general vertex attribute array, the parameter 0 index corresponds to 0 in the layout(location = 0) in the shader, and the vertex position
	glEnableVertexAttribArray(0);

	//Process texture
	glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(1);

	Process normals
	//glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
	//glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0);
	//glEnableVertexAttribArray(2);

	//glBindBuffer(GL_ARRAY_BUFFER, vbo[3]);
	//glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 0, 0);
	//glEnableVertexAttribArray(3);

	//Activate texture
	
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, torusTextureId);
	/*glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_NEAREST_MIPMAP_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_NEAREST_MIPMAP_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_NEAREST_MIPMAP_NEAREST);
	glBindTexture(GL_TEXTURE_2D, sunTextureId);*/

	//Backface culling is off by default
	glEnable(GL_CULL_FACE | GL_DEPTH_TEST);  //Open back culling and depth test
	glFrontFace(GL_CCW); 
	//glDepthFunc(GL_LEQUAL);
	
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[3]);
	glDrawElements(GL_TRIANGLES, myTorus.getIndices().size(), GL_UNSIGNED_INT, 0);
}

void window_size_callback(GLFWwindow* window, int newWidth, int newHeight)
{
	
	glViewport(0, 0, newWidth, newHeight);
	aspect = (float)newWidth / (float)newHeight;
	pMat = glm::perspective(glm::radians(45.f), aspect, 0.01f, 1000.f);
}

void mouse_move_callback(GLFWwindow* window, double xPos, double yPos)
{
	if (firstMouse)
	{
		lastLocX = xPos;
		lastLocY = yPos;
		firstMouse = GL_FALSE;
	}

	double offsetLocX = xPos - lastLocX;
	double offsetLocY = lastLocY - yPos;

	lastLocX = xPos;
	lastLocY = yPos;

	camera.ProcessMouseMovement(offsetLocX, offsetLocY);
}

void mouse_scroll_callback(GLFWwindow* window, double xPos, double yPos)
{
	camera.ProcessMouseScroll(yPos * 0.1f);
}


void processKeyInput(GLFWwindow* window)
{
	float currentFrame = glfwGetTime();
	deltaTime = currentFrame - lastFrame;
	lastFrame = currentFrame;

	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
	{
		glfwSetWindowShouldClose(window, GLFW_TRUE);
	}
	if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
	{
		camera.ProcessKeyboard(FORWARD, deltaTime);
	}
	if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
	{
		camera.ProcessKeyboard(BACKWARD, deltaTime);
	}
	if (glfwGetKey(window, GLFW_KEY_A) == GLFW_KEY_A)
	{
		camera.ProcessKeyboard(LEFT, deltaTime);
	}
	if (glfwGetKey(window, GLFW_KEY_D))
	{
		camera.ProcessKeyboard(RIGHT, deltaTime);
	}

}

void press_key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
	if ((key == GLFW_KEY_ESCAPE) && (action == GLFW_PRESS))
	{
		glfwSetWindowShouldClose(window, GL_TRUE);
	}
	if (action == GLFW_PRESS)
	{
		keys[key] = GL_TRUE;
	}
	else if (action == GLFW_RELEASE)
	{
		//glfwSetWindowShouldClose(window, GL_FALSE);
		keys[key] = GL_FALSE;
	}
}

int main(int argc, char** argv)
{
	int glfwState = glfwInit();
	if (GLFW_FALSE == glfwState)
	{
		cout << "GLFW initialize failed,invoke glfwInit()......Error file:" << __FILE__ << "......Error line:" << __LINE__ << endl;
		glfwTerminate();
		exit(EXIT_FAILURE);
	}

	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
	glfwWindowHint(GLFW_OPENGL_CORE_PROFILE, GLFW_OPENGL_PROFILE);
	glfwWindowHint(GLFW_RESIZABLE, GL_TRUE);

	GLFWwindow* window = glfwCreateWindow(screen_width, screen_height, "Draw torus", nullptr, nullptr);
	if (!window)
	{
		cout << "GLFW create window failed,invoke glfwCreateWindow()......Error file:" << __FILE__ << "......Error line:" << __LINE__ << endl;
		glfwTerminate();
		exit(EXIT_FAILURE);
	}

	glfwMakeContextCurrent(window);
	glfwSetKeyCallback(window, press_key_callback);
	glfwSetWindowSizeCallback(window, window_size_callback);
	glfwSetCursorPosCallback(window, mouse_move_callback);
	glfwSetScrollCallback(window, mouse_scroll_callback);

	int glewState = glewInit();
	if (GLEW_OK != glewState)
	{
		cout << "GLEW initialize failed,invoke glewInit()......Error file:" << __FILE__ << "......Error line:" << __LINE__ << endl;
		glfwTerminate();
		exit(EXIT_FAILURE);
	}

	glfwSwapInterval(1);
	init(window);

	while (!glfwWindowShouldClose(window))
	{
		display(window, (float)glfwGetTime());
		glfwSwapBuffers(window);
		glfwPollEvents();
	}

	glfwDestroyWindow(window);
	glfwTerminate();
	exit(EXIT_SUCCESS);


	return 0;
}

Operation effect

Source download

Project source code download

Topics: OpenGL