BWG*_*BWG 5 c++ opengl matrix-multiplication
我需要在3D环境中实现“选择对象”。因此,我决定采用简单的方法,而不是采用健壮,准确的方法(例如光线投射)。首先,我将对象的世界位置转换为屏幕坐标:
glm::mat4 modelView, projection, accum;
glGetFloatv(GL_PROJECTION_MATRIX, (GLfloat*)&projection);
glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*)&modelView);
accum = projection * modelView;
glm::mat4 transformed = accum * glm::vec4(objectLocation, 1);
Run Code Online (Sandbox Code Playgroud)
随后是一些琐碎的代码,可将opengl坐标系转换为普通窗口坐标,并与鼠标进行简单的距离检查。但是效果不佳。为了从世界空间转换为屏幕空间,我需要在上面显示的函数的末尾再添加一个计算:
transformed.x /= transformed.z;
transformed.y /= transformed.z;
Run Code Online (Sandbox Code Playgroud)
我不明白为什么我必须这样做。我的印象是,一旦将您的顶点乘以累积的modelViewProjection矩阵,便得到了屏幕坐标。但是我必须除以Z才能使其正常工作。在我的openGL 3.3着色器中,我不必除以Z。这是为什么?
编辑:从opengl坐标系转换到屏幕坐标的代码是这样的:
int screenX = (int)((trans.x + 1.f)*640.f); //640 = 1280/2
int screenY = (int)((-trans.y + 1.f)*360.f); //360 = 720/2
Run Code Online (Sandbox Code Playgroud)
然后通过执行以下操作来测试鼠标是否在该点附近:
float length = glm::distance(glm::vec2(screenX, screenY), glm::vec2(mouseX, mouseY));
if(length < 50) {//you can guess the rest
Run Code Online (Sandbox Code Playgroud)
编辑#2
在鼠标单击事件后调用此方法:
glm::mat4 modelView;
glm::mat4 projection;
glm::mat4 accum;
glGetFloatv(GL_PROJECTION_MATRIX, (GLfloat*)&projection);
glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*)&modelView);
accum = projection * modelView;
float nearestDistance = 1000.f;
gameObject* nearest = NULL;
for(uint i = 0; i < objects.size(); i++) {
gameObject* o = objects[i];
o->selected = false;
glm::vec4 trans = accum * glm::vec4(o->location,1);
trans.x /= trans.z;
trans.y /= trans.z;
int clipX = (int)((trans.x+1.f)*640.f);
int clipY = (int)((-trans.y+1.f)*360.f);
float length = glm::distance(glm::vec2(clipX,clipY), glm::vec2(mouseX, mouseY));
if(length<50) {
nearestDistance = trans.z;
nearest = o;
}
}
if(nearest) {
nearest->selected = true;
}
mouseRightPressed = true;
Run Code Online (Sandbox Code Playgroud)
整个代码不完整,但是与我的问题相关的部分工作正常。“对象”向量仅包含一个用于我的测试的元素,因此循环完全不会妨碍您。
我已经弄清楚了。正如大卫·莱弗利先生指出的那样,
不过,通常在这种情况下,您会除以
.w而不是.z得到有用的东西。
我的.w价值观非常接近我的.z价值观,所以在我的代码中我更改了语句:
transformed.x /= transformed.z;
transformed.y /= transformed.z;
Run Code Online (Sandbox Code Playgroud)
到:
transformed.x /= transformed.w;
transformed.y /= transformed.w;
Run Code Online (Sandbox Code Playgroud)
而且它仍然像以前一样有效。
/sf/answers/724805791/解释了除以 w 将在管道中稍后完成。显然,因为我的代码只是将矩阵相乘,所以不存在“后续管道”。从某种意义上说,我只是很幸运,因为我的.z价值非常接近我的.w价值,所以有一种错觉,认为它正在发挥作用。