openGL API 之glDrawElements()详解

openGL系列文章目录

官网

官方文档

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.

翻译

函数作用:使用count个元素定义一个几何序列,这些元素的索引值保存在indices数组中。
参数说明:
mode:接受的值和在glBegin()中接受的值一样,可以是GL_POLYGON、GL_TRIANGLES、GL_TRIANGLE_STRIP、GL_LINE_STRIP等。

count:组合几何图形的元素的个数,一般是点的个数。

type:indeices数组的数据类型,既然是索引,一般是整型的。

indices:索引数组

描述

GLDraweElements通过很少的子例程调用指定多个几何原语。不需要调用GL函数来传递每个单独的顶点、法线、纹理坐标、边标志或颜色,您可以预先指定顶点、法线等的单独数组,并使用它们通过对GLDrainElements的单个调用来构造一系列基本体图元。

当调用GLDraweElements时,它使用已启用数组中的计数顺序元素,从索引开始构造几何图元序列。模式指定构造哪种基本体以及数组元素如何构造这些基本体图元。如果启用了多个数组,则使用每个数组GLDrainElements修改的顶点属性在GLDrainElements返回后具有未指定的值。未修改的属性将保持其以前的值。

注意

仅当GL版本为3.2或更高版本时,GL_LINE_STRIP_ADJACENCY, GL_LINES_ADJACENCY, GL_TRIANGLE_STRIP_ADJACENCY and GL_TRIANGLES_ADJACENCY 才可用。

错误

如果模式不是可接受的值,则生成GL_INVALID_ENUM 。
如果计数为负数,则生成GL_INVALID_VALUE 。
如果非零缓冲区对象名称绑定到启用的数组或元素数组,并且缓冲区对象的数据存储当前已映射,则会生成GL_INVALID_操作。

版本支持

OpenGL版本

功能/特征名称2.0 2.1 3.0 3.1 3.2 3.3 4.0 4.1 4.2 4.3 4.4.5

微量元素✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔

另见

GLDrawArray、glDrawElementsInstanced、glDrawElementsBaseVertex、glDrawRangeElements

版权

版权所有©1991-2006 Silicon Graphics,Inc.版权所有©2010-2014 Khronos Group。本文档根据SGI自由软件B许可证进行许可。有关详细信息,请参阅https://khronos.org/registry/OpenGL-Refpages/LICENSES/LicenseRef-FreeB.txt.

例子

#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轴旋转量
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();           //获取顶点索引
	vector<glm::vec3> vert = myTorus.getVertices();   //得到每个顶点,其中任一个顶点都包含(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;   //顶点位置
	vector<float> tValues;   //纹理坐标
	vector<float> nValues;   //法线坐标

	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);

		//注意这里纹理坐标是S、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);

	//注意后面我们使用glDrawElements绘制,所以这里必须使用 GL_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)
{
	//注意如果把GL_DEPTH_BUFFER_BIT写成GL_DEPTH_BUFFER,渲染窗口会出现残影
	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);
	//右乘规则
	mvMat = vMat * mMat;

	//更改一个uniform矩阵变量或数组的值。要更改的uniform变量的位置由location指定,location的值应该由glGetUniformLocation函数返回
	// 将透视矩阵和MV 矩阵复制给相应的统一变量
	/*通过一致变量(uniform修饰的变量)引用将一致变量值传入渲染管线。
	  location : uniform的位置。
	  count : 需要加载数据的数组元素的数量或者需要修改的矩阵的数量。
	  transpose : 指明矩阵是列优先(column major)矩阵(GL_FALSE)还是行优先(row major)矩阵(GL_TRUE)。
	  value : 指向由count个元素的数组的指针。
	*/
	glUniformMatrix4fv(mvLoc, 1, GL_FALSE, glm::value_ptr(mvMat));
	glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(pMat));

	//处理顶点
	glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
	//指定了渲染时索引值为 index 的顶点属性数组的数据格式和位置
	/*Parameters
	index
		指定要修改的顶点属性的索引值

		size
		指定每个顶点属性的组件数量。必须为1、2、3或者4。初始值为4。(梦维:如position是由3个(x, y, z)组成,而颜色是4个(r, g, b, a))

		type
		指定数组中每个组件的数据类型。可用的符号常量有GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_FIXED, 和 GL_FLOAT,初始值为GL_FLOAT。

		normalized
		指定当被访问时,固定点数据值是否应该被归一化(GL_TRUE)或者直接转换为固定点值(GL_FALSE)。

		stride
		指定连续顶点属性之间的偏移量。如果为0,那么顶点属性会被理解为:它们是紧密排列在一起的。初始值为0。

		pointer
		指定一个指针,指向数组中第一个顶点属性的第一个组件。初始值为0。*/
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
	//启用或禁用通用顶点属性数组,参数0索引和着色器中的layout(location = 0)中的0相对应,顶点位置
	glEnableVertexAttribArray(0);

	//处理纹理
	glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(1);

	处理法线
	//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);

	//激活纹理
	
	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);*/

	//背面剔除,默认情况下,背面剔除是关闭的
	glEnable(GL_CULL_FACE | GL_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;
}

运行效果

源码下载

工程源码下载

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值