编程语言
首页 > 编程语言> > matrix_transform.inl文件报错assert(abs(aspect - std::numeric_limits<T>::epsilon()) > static_cast<T>(0));

matrix_transform.inl文件报错assert(abs(aspect - std::numeric_limits<T>::epsilon()) > static_cast<T>(0));

作者:互联网

OpenGL系列文章目录

文章目录


前言

当在openGL中使用glm库报错:
assert(abs(aspect - std::numeric_limits::epsilon()) > static_cast(0));

在这里插入图片描述

一、matrix_transform.inl文件报错

1.报错位置

下图中三个绿色框中的代码出错了,
在这里插入图片描述

2.定位问题

原来这句代码引起问题:

projMat = glm::perspective(glm::radians(60.0f), aspect, 0.01f, 1000.f);

在这里插入图片描述
在这里插入图片描述
上图中x值发生了异常,-nan(ind)
在这里插入图片描述
可能的情况:

          1.分母为”0”,如果分母为零,自然时不能得到一个确定的数字的。

          2.对负数开平方。

          3.有些编译器在对无穷大与无穷小的计算时也会出现此类情况。

3.问题解决

估计是在以下计算中得到无穷大或者无穷小或者分母无穷接近0的情况了

rMat = glm::rotate(glm::mat4(1.f), 1.75f * tf, glm::vec3(1.f, 0.f, 0.f));
rMat = glm::rotate(rMat, 1.75f * tf, glm::vec3(0.f, 1.f, 0.f));
rMat = glm::rotate(rMat, 1.75f * tf, glm::vec3(0.f, 0.f, 1.f));
projMat = glm::perspective(glm::radians(60.0f), aspect, 0.01f, 1000.f);

我把代码修改成下面,就没有报错了

        //旋转矩阵,绕x轴旋转
		rMat = glm::rotate(glm::mat4(1.f), glm::radians(40.f) * tf, glm::vec3(1.f, 0.f, 0.f));
		 //rMat = glm::rotate(glm::mat4(1.f), 1.75f * tf, glm::vec3(1.f, 0.f, 0.f));
		//旋转矩阵,绕y轴旋转
		rMat = glm::rotate(rMat, glm::radians(50.f) * tf, glm::vec3(0.f, 1.f, 0.f));
		 //rMat = glm::rotate(rMat, 1.75f * tf, glm::vec3(0.f, 1.f, 0.f));
		//旋转矩阵,绕z轴旋转
		rMat = glm::rotate(rMat, glm::radians(60.f) * tf, glm::vec3(0.f, 0.f, 1.f));
		 //rMat = glm::rotate(rMat, 1.75f * tf, glm::vec3(0.f, 0.f, 1.f));

代码

#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 <iostream>
#include <string>
#include <fstream>

using namespace std;

//#define GLM_FORCE_RADIANS

static const int Screen_Width = 1920;
static const int Screen_Height = 1080;

static const int numberVAOs = 1;
static const int numberVBOs = 2;

GLuint renderingProgram = 0;
GLuint vao[numberVAOs] = { 0 };
GLuint vbo[numberVBOs] = { 0 };

float cameraX = 0.f, cameraY = 0.f, cameraZ = 0.f;
float cubeX = 0.f, cubeY = 0.f, cubeZ = 0.f;

int g_width = 0, g_height = 0, g_displayLoopi = 0;
float aspect = 0.f;
float tf = 0.f;

GLuint mvLoc = 0, projLoc = 0;

glm::mat4 mMat(1.f), vMat(1.f), mvMat(1.f), projMat(1.f), tMat(1.f), rMat(1.f);

void setupVertices()
{
	float vertexPositions[108] =
	{ 
		-1.0f,  1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f,
		1.0f, -1.0f, -1.0f, 1.0f,  1.0f, -1.0f, -1.0f,  1.0f, -1.0f,
		1.0f, -1.0f, -1.0f, 1.0f, -1.0f,  1.0f, 1.0f,  1.0f, -1.0f,
		1.0f, -1.0f,  1.0f, 1.0f,  1.0f,  1.0f, 1.0f,  1.0f, -1.0f,
		1.0f, -1.0f,  1.0f, -1.0f, -1.0f,  1.0f, 1.0f,  1.0f,  1.0f,
		-1.0f, -1.0f,  1.0f, -1.0f,  1.0f,  1.0f, 1.0f,  1.0f,  1.0f,
		-1.0f, -1.0f,  1.0f, -1.0f, -1.0f, -1.0f, -1.0f,  1.0f,  1.0f,
		-1.0f, -1.0f, -1.0f, -1.0f,  1.0f, -1.0f, -1.0f,  1.0f,  1.0f,
		-1.0f, -1.0f,  1.0f,  1.0f, -1.0f,  1.0f,  1.0f, -1.0f, -1.0f,
		1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f,  1.0f,
		-1.0f,  1.0f, -1.0f, 1.0f,  1.0f, -1.0f, 1.0f,  1.0f,  1.0f,
		1.0f,  1.0f,  1.0f, -1.0f,  1.0f,  1.0f, -1.0f,  1.0f, -1.0f
	};

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

	glGenBuffers(numberVBOs, vbo);
	glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions), vertexPositions, GL_STATIC_DRAW);
}

//窗口大小改变时比例位置不变
void window_size_callback(GLFWwindow* window, int newWidth, int newHeight)
{
	aspect = (float)newWidth / (float)newHeight;
	glViewport(0, 0, newWidth, newHeight);
	//必须要创建透视投影矩阵计算公式,否则第一帧渲染时,立方体不在视口中 :1.0472 radians = 60 degrees
	//glm::perspective使用模板定义,所以函数各个参数类型要一致  1.0472f
	projMat = glm::perspective(glm::radians(60.0f), aspect, 0.01f, 1000.f);
	//projMat = glm::perspective(1.0472f, aspect, 0.1f, 1000.f);
}

void init(GLFWwindow* window)
{
	renderingProgram = Utils::createShaderProgram("vertShader.glsl", "fragShader.glsl");
	/*GLFW在这里和这里解释文档中的两个坐标系。
		简而言之,窗口坐标是相对于监视器和 / 或窗口的,并且以不一定对应于真实屏幕像素的人造单元给出。 当DPI缩放被激活时(例如,在带有视网膜显示器的Mac上),情况尤其如此。
		与窗口坐标相比,帧缓冲区的大小与像素相关,以便与glViewport OpenGLs要求相匹配。
		请注意,在某些系统上,窗口坐标和像素坐标可以相同,但这不一定是正确的。*/
	glfwGetFramebufferSize(window, &g_width, &g_height);
	aspect = (float)g_width / (float)g_height;

	//必须要创建透视投影矩阵计算公式,否则第一帧渲染时,立方体不在视口中 :1.0472 radians = 60 degrees
	//glm::perspective使用模板定义,所以函数各个参数类型要一致
	projMat = glm::perspective(1.0472f, aspect, 0.01f, 1000.f);

	cameraX = 0.f;
	cameraY = 0.f;
	cameraZ = 32.f;

	setupVertices();
}

void display(GLFWwindow* window, float currentTime)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glClearColor(0.f, 0.7f, 1.f, 1.f);

	//启动着色器程序,在GPU上安装GLSL代码,这不会运行着色器程序,
	glUseProgram(renderingProgram);

	//获取uniform变量在着色器程序中的位置序号,通过该序号可以设置一致变量的值,如果没有该变量则返回-1
	mvLoc = glGetUniformLocation(renderingProgram, "mv_matrix");
	projLoc = glGetUniformLocation(renderingProgram, "proj_matrix");

	//移动相机矩阵
	vMat = glm::translate(glm::mat4(1.f), glm::vec3(cameraX, cameraY, cameraZ));

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

	for (g_displayLoopi = 0; g_displayLoopi < 36; g_displayLoopi++)
	{
		tf = currentTime + g_displayLoopi;
		//平移矩阵
		tMat = glm::translate(glm::mat4(1.f), glm::vec3(glm::sin(.35f * tf) * 8.f, glm::cos(.52f * tf) * 8.f, glm::sin(.70f * tf) * 8.f));
		//旋转矩阵,绕x轴旋转
		rMat = glm::rotate(glm::mat4(1.f), glm::radians(40.f) * tf, glm::vec3(1.f, 0.f, 0.f));
		 //rMat = glm::rotate(glm::mat4(1.f), 1.75f * tf, glm::vec3(1.f, 0.f, 0.f));
		//旋转矩阵,绕y轴旋转
		rMat = glm::rotate(rMat, glm::radians(50.f) * tf, glm::vec3(0.f, 1.f, 0.f));
		 //rMat = glm::rotate(rMat, 1.75f * tf, glm::vec3(0.f, 1.f, 0.f));
		//旋转矩阵,绕z轴旋转
		rMat = glm::rotate(rMat, glm::radians(60.f) * tf, glm::vec3(0.f, 0.f, 1.f));
		 //rMat = glm::rotate(rMat, 1.75f * tf, glm::vec3(0.f, 0.f, 1.f));

		/*请注意最后一行中的矩阵乘法——
		操作中tMat 和rMat 的顺序很重要。它计算两个变换的结合,平移放在左边,旋转放在右边。
		当顶点随后乘以此矩阵时,计算从右到左进行,这意味着首先完成旋转,然后才是平移。
		变换的应用顺序很重要,改变顺序会导致不同的行为。图4.7 显示了为立方体设置了动画后
		显示的一些帧。*/
		mMat = tMat * rMat;
		//如果mMat和vMat位置变化,将看不到立方体
		mvMat = mMat * vMat;

		//更改一个uniform矩阵变量或数组的值。要更改的uniform变量的位置由location指定,location的值应该由glGetUniformLocation函数返回
	// 将透视矩阵和MV 矩阵复制给相应的统一变量
		glUniformMatrix4fv(mvLoc, 1, GL_FALSE, glm::value_ptr(mvMat));
		glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projMat));

		//glBindVertexArray(vao[0]);
		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);
		glEnableVertexAttribArray(0);

		//开启深度测试
		glEnable(GL_DEPTH_TEST);
		//指定用于深度缓冲比较值;
		glDepthFunc(GL_LEQUAL);
		glDrawArrays(GL_TRIANGLES, 0, 36);

	}
}

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

	/*因为我们要使用OpenGL 4.6,所以我们把GLFW_CONTEXT_VERSION_MAJOR和GLFW_CONTEXT_VERSION_MINOR对应的hint都设置为4和6。
	因为我们要使用OpenGL核心模式(这个后面会提到更多),所以我们把GLFW_OPENGL_PROFILE对应的hint设置为GLFW_OPENGL_CORE_PROFILE,
	表示使用OpenGL核心模式。最后,把GLFW_RESIZABLE对应的hint设置为GLFW_FALSE,表示窗口不允许用户调整大小。
	之所以这样做是因为如果允许用户调整大小,大小发生变化后,窗口的绘制区域默认不变(依然是原来窗口的区域),
	也就是说窗口上绘制的图像的大小、位置不会发生改变。为了避免这种现象发生,我们就简单地不让用户调整窗口大小
	(当然也有更好的方法,就是用GLFW设置一个窗口大小的回调函数,但这样比较简单)。*/
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
	glfwWindowHint(GLFW_OPENGL_CORE_PROFILE, GLFW_OPENGL_PROFILE);

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

	/*此函数使调用线程上的指定窗口的 OpenGL 或 OpenGL ES 上下文成为当前上下文。
	  一次只能在单个线程上使上下文成为当前上下文,并且每个线程一次只能有一个当前上下文。
	  在线程之间移动上下文时,必须先使其在旧线程上变为非当前状态,然后再在新线程上变为当前状态。
	*/
	glfwMakeContextCurrent(window);
	//glfwSetFramebufferSizeCallback()和glfwSetWindowSizeCallback区别
	//glfwSetFramebufferSizeCallback(window, window_size_callback);
	glfwSetWindowSizeCallback(window, window_size_callback);

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

	/*此函数设置当前 OpenGL 或 OpenGL ES 上下文的交换间隔,即从调用glfwSwapBuffers开始等待的屏幕更新次数,
	  然后再交换缓冲区并返回。这有时称为垂直同步、垂直回扫同步或仅vsync。
	  支持WGL_EXT_swap_control_tear和GLX_EXT_swap_control_tear扩展的上下文也接受负交换间隔,这允许驱动程序立即交换,
	  即使帧到达有点晚。您可以使用glfwExtensionSupported检查这些扩展。
	  上下文必须在调用线程上是最新的。在没有当前上下文的情况下调用此函数将导致GLFW_NO_CURRENT_CONTEXT错误。
	  此功能不适用于 Vulkan。如果您使用 Vulkan 进行渲染,请改为查看交换链的当前模式。
	*/
	glfwSwapInterval(1);

	init(window);

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

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

	return 0;
}

运行结果

在这里插入图片描述

源码下载

源码下载地址

标签:std,rMat,1.0,matrix,矩阵,报错,tf,GL,glm
来源: https://blog.csdn.net/aoxuestudy/article/details/122121529