错误 C2338 ‘radians‘ only accept floating-point input

本文介绍了一个关于GLM库中角度转换函数radians的常见错误,并提供了详细的解决方案。通过修正参数类型,确保了OpenGL程序中正确实现了旋转操作。

系列文章目录

前言

错误 C2338 ‘radians’ only accept floating-point input

在这里插入图片描述
在这里插入图片描述

一、错误原因

从上图中看出glm::radians是模板实现,所以(glm::radians(40)函数参数必须是float类型

在这里插入图片描述

二、解决

把40改成40.0f
rMat = glm::rotate(glm::mat4(1.f), ((glm::radians(40)) * tf), glm::vec3(1.f, 0.f, 0.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使用模板定义,所以函数各个参数类型要一致
	projMat = glm::perspective(glm::radians(45.f), aspect, 0.01f, 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(glm::radians(45.f), aspect, 0.01f, 1000.f);

	cameraX = 0.f;
	cameraY = 0.f;
	cameraZ = 16.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));
		//旋转矩阵,绕y轴旋转
		rMat = glm::rotate(rMat, ((glm::radians(80.f) * tf)), glm::vec3(0.f, 1.f, 0.f));
		//旋转矩阵,绕z轴旋转
		rMat = glm::rotate(rMat, ((glm::radians(40.f) * 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;
}

源码下载

源码下载地址

<think>我们有一个SQL查询,它计算两个部分: 1. 0.088 * 0.001 * sum(6371 * 2 * ATAN2(...)) 2. sum(6371 * 2 * ATAN2(...)) 并且这个计算是基于两个城市的经纬度(出发和到达)。 首先,我们需要理解这个表达式:6371 * 2 * ATAN2(...) 是什么? 在计算地理空间距离时,常用Haversine公式或球面三角法来计算地球上两点之间的距离。这里使用的表达式看起来是Haversine公式的一种变体或近似。 通常,Haversine公式计算两点间的大圆距离(球面距离)的公式为: $$d = 2 \cdot R \cdot \arcsin\left(\sqrt{\sin^2\left(\frac{\Delta\phi}{2}\right) + \cos(\phi_1)\cos(\phi_2)\sin^2\left(\frac{\Delta\lambda}{2}\right)}\right)$$ 但有时也会用ATAN2函数来避免数值问题,另一种等价的表达式是: $$d = 2 \cdot R \cdot \arctan2\left( \sqrt{a}, \sqrt{1-a} \right)$$ 其中a是Haversine公式中的中间量。 然而,在代码中我们看到的表达式是:6371 * 2 * ATAN2(...),这里6371是地球半径(单位:千米)。所以这个表达式计算的是两点之间的球面距离(单位:千米)。 因此,整个表达式可以理解为: 第一部分:0.088 * 0.001 * [总距离(千米)] 0.088 * 0.001 = 0.000088,所以第一部分是将总距离乘以0.000088。 第二部分:总距离(千米) 那么,0.000088这个系数可能是什么?我们可以推测: 可能是某种转换系数,比如将距离转换为其他单位(如米:乘以1000,但这里不是),或者可能是某种成本系数(例如每公里的费用乘以总公里数,但这里系数非常小)。 但是,我们注意到0.088*0.001=8.8e-5,这个数值比较小。可能代表某种单位转换或者特定的成本系数。 另外,注意ATAN2函数通常接受两个参数:ATAN2(y, x)。在Haversine公式中,我们通常这样写: $$d = 2 \cdot R \cdot \arctan2\left( \sqrt{a}, \sqrt{1-a} \right)$$ 其中a = sin²(Δφ/2) + cos(φ1) * cos(φ2) * sin²(Δλ/2) 所以,我们假设在代码中ATAN2(...)部分实际上就是计算了上述的arctan2函数,那么整个表达式就是计算两点间的球面距离。 因此,整个SQL查询的作用是: 1. 计算所有记录中,每一对出发城市和到达城市的球面距离(单位:千米),然后求和(sum),得到总距离(单位:千米)。 2. 第一部分:将总距离乘以0.000088,这可能是为了将距离转换为另一种度量(比如某种成本,或者另一种单位)。例如,如果0.000088表示每公里某种资源的消耗量,那么第一部分就是总消耗量。 但是,具体0.088和0.001的含义需要结合业务。0.088可能是一个系数,乘以0.001可能是为了将单位从千米转换为其他单位(比如乘以0.001得到米?但是这里0.088*0.001=0.000088,所以不是简单的单位转换)。另一种可能是0.088是一个每公里的费用(比如每公里0.088元),然后乘以0.001可能是为了将单位转换为万元?或者是因为某种折扣?这需要业务背景。 如果没有业务背景,我们可以这样解释: 第一部分:总距离(千米)乘以0.000088,得到的结果可能是某种成本或转换后的值。 第二部分:总距离(千米)。 因此,这个查询返回两个值: 值1 = 0.000088 * 总距离(千米) 值2 = 总距离(千米) 注意:在SQL中,ATAN2函数通常有两个参数,所以代码中的ATAN2(...)应该是两个表达式,分别代表上面公式中的两个部分。由于你省略了具体参数,这里只能做一般性解释。 如果我们要写出完整的表达式,可能是这样的(假设有四个字段:出发地经度、纬度,目的地经度、纬度): 设: lat1 = 出发地纬度(弧度) lon1 = 出发地经度(弧度) lat2 = 目的地纬度(弧度) lon2 = 目的地经度(弧度) 那么计算距离的部分为: d = 6371 * 2 * ATAN2( SQRT(a), SQRT(1-a) ) 其中a = SIN((lat2-lat1)/2)^2 + COS(lat1)*COS(lat2)*SIN((lon2-lon1)/2)^2 但是,在代码中可能直接用一个表达式写出来,而没有中间变量a。例如: ATAN2( SQRT( POW(SIN((RADIANS(lat2)-RADIANS(lat1))/2),2) + COS(RADIANS(lat1))*COS(RADIANS(lat2))*POW(SIN((RADIANS(lon2)-RADIANS(lon1))/2),2) ), SQRT(1 - (POW(SIN((RADIANS(lat2)-RADIANS(lat1))/2),2) + COS(RADIANS(lat1))*COS(RADIANS(lat2))*POW(SIN((RADIANS(lon2)-RADIANS(lon1))/2),2))) ) 但要注意,有时也会使用另一种形式(使用a在分子,然后分母用1-a,但这样写可能数值不稳定,所以用ATAN2的两个参数形式)。还有一种常见的写法是直接将a作为中间量,然后使用ATAN2(SQRT(a), SQRT(1-a))。 因此,总结: 这个SQL查询计算了表中所有记录的两个城市间(出发和到达)的球面距离总和(千米),以及该总和乘以0.000088的值。 可能的应用场景: 例如,计算一个物流公司所有运输路线的总运输距离,以及总运输成本(如果0.000088表示每公里的运输成本,那么这里乘以0.000088?但注意0.000088太小了,可能是每公里的成本是0.088元,然后乘以0.001可能是为了转换成千元单位?所以总成本以千元为单位)。或者,这个系数可能是碳排放系数(每公里消耗0.000088吨二氧化碳?)等。 由于没有具体业务背景,我们只能这样解释。 注意:在写这个表达式时,要确保经纬度已经转换为弧度,因为三角函数通常使用弧度。 所以,回答用户的问题: 这段SQL代码的含义是计算表中所有记录(每一行代表一条从出发城市到到达城市的路线)的球面距离(单位:千米)的总和,以及这个总和乘以0.000088的值。 具体计算距离的公式是球面距离公式(Haversine公式的变体),使用地球半径6371千米。 第一部分(0.088*0.001*sum(...))可能是对总距离进行某种转换或应用一个系数(比如成本系数),第二部分就是总距离。 如果用户需要更精确的解释,可能需要提供系数的具体含义或业务背景。 另外,注意代码中ATAN2函数的具体参数被省略了,所以这里只能给出一般性解释。 </think> ### SQL代码解释 您提供的SQL代码用于计算基于出发城市和到达城市经纬度的球面距离,并进行加权求和计算。以下是逐部分解析: ```sql SELECT 0.088 * 0.001 * SUM(6371 * 2 * ATAN2(...)), SUM(6371 * 2 * ATAN2(...)) FROM 表 ``` #### 1. **核心距离计算:`6371 * 2 * ATAN2(...)`** - **6371**:地球平均半径(单位:千米),用于球面距离计算。 - **ATAN2函数**:计算两点间的大圆距离(Haversine公式的核心部分),完整形式通常为: ```sql ATAN2( SQRT( (SIN((lat2 - lat1)/2))^2 + COS(lat1)*COS(lat2)*(SIN((lon2 - lon1)/2))^2 ), SQRT(1 - (SIN((lat2 - lat1)/2))^2 + COS(lat1)*COS(lat2)*(SIN((lon2 - lon1)/2))^2 ) ) ``` 其中: - `lat1, lon1`:出发城市的纬度和经度(弧度制) - `lat2, lon2`:到达城市的纬度和经度(弧度制) - **计算结果**:`6371 * 2 * ATAN2(...)` 表示两点间的球面距离(单位:千米)[^1]。 #### 2. **加权计算:`0.088 * 0.001 * SUM(...)`** - **SUM(距离)**:对表中所有记录的球面距离求和。 - **系数 `0.088 * 0.001`**: - `0.001`:将千米转换为公里(1公里=1000米)。 - `0.088`:业务特定系数(如运输成本、碳排放系数等)。 - 整体意义:将总距离(千米)转换为业务指标(如总成本/排放量)。 #### 3. **最终输出** - **第一列**:`0.000088 * 总距离` → 加权后的业务指标。 - **第二列**:`总距离`(千米)→ 原始距离总和。 #### 应用场景 此代码适用于: 1. **物流成本计算**:根据运输距离计算总运费(系数=单位距离运费)。 2. **碳排放统计**:将距离转换为碳排放量(系数=每公里排放因子)。 3. **路径规划分析**:聚合多条路线的总距离和加权指标[^2]。 ### 注意事项 1. **单位转换**:确保经纬度已转为弧度制(`RADIANS()`函数)。 2. **性能优化**:大数据表建议添加空间索引。 3. **公式验证**:检查ATAN2参数是否完整(示例中省略了具体参数)。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值