首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将鼠标转换为屏幕位置

将鼠标转换为屏幕位置
EN

Stack Overflow用户
提问于 2013-11-28 21:42:36
回答 2查看 2.2K关注 0票数 1

我正在使用Open制作一个三度投影游戏。我有以下的视图-投影转换:

代码语言:javascript
复制
float aspect = 1024.f/768.f;
glm::ortho<float>(-aspect*5, aspect*5, -5, 5, 0, 20) * glm::lookAt(glm::vec3(-std::sin(M_PI*36/180.f)*10,sin(M_PI*34/180.f)*10,10), glm::vec3(), glm::vec3(0,1,0));

屏幕分辨率为1024x768。我想把鼠标映射到屏幕上的位置。例如,我的游戏中的瓷砖是1x1。当我悬停在第一个瓷砖(它起源于屏幕的中心)时,我希望鼠标的位置在(0,0)和(1,1)之间。我不知道该怎么做。到目前为止,我所做的是通过这样做,将屏幕的线性视图(即正交投影)转换为:

代码语言:javascript
复制
glm::vec4 mousePos(x/768*10-apsect*5, 0, y/768*10-5, 0);

然而,我不知道从那里去哪里。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-12-11 01:40:10

这与我正在处理的iPad项目的等距界面非常相似。这与您的项目具有相同的屏幕分辨率。几天前,当我第一次读到你的问题时,我正在构建其他的项目代码。但是,需要构建用于选择对象的接口代码。因此,尝试开发代码是有意义的。我用一个空白的彩色棋盘设置了一个测试,以适应你的问题。

这是一个演示它的快速视频。请原谅它难看的样子。另外,请注意视频,光标后面的值是整数,但是代码会产生浮点值。我还创建了另一个函数来完成额外的工作,供我自己使用。如果这是你想要的,我会把它写在我的答案里。

http://youtu.be/JyddfSf57ic

这段代码故意冗长,因为我在数学上生疏了。因此,在过去的几天里,我花了很多时间重新学习点和交叉产品,也阅读以重心为中心的代码,但决定不这么做。

代码之前的最后一件事:问题中的投影矩阵有一个问题。在投影矩阵中包含摄像机变换。从技术上讲,这是允许的,但网上有很多资源表明这是错误的做法。

希望这能有所帮助!

代码语言:javascript
复制
void calculateTouchPointOnGrid(CGPoint screenTouch) {
    float backingWidth = 1024;
    float backingHeight = 768;
    float aspect = backingWidth / backingHeight;
    float zNear = 0;
    float zFar = 20;
    glm::detail::tvec3<float> unprojectedNearZ;
    glm::detail::tvec3<float> unprojectedFarZ;

    /*
     Window coordinates, including zNear 
     This code uses zNear and zFar as two arbitrary values of 
             magnitude along the direction (vector) of the camera's 
             view (affected by projection and model-view matrices) that enable
     determining the line/ray, originating from screenTouch (or 
             mouse click, etc) that later intersects the plane.
     */

    glm::vec3 win = glm::vec3( screenTouch.x, backingHeight - screenTouch.y, zNear);


    // Model & View matrix

    glm::detail::tmat4x4<float> modelTransformMatrix = glm::detail::tmat4x4<float>(1);
    modelTransformMatrix = glm::translate(modelTransformMatrix, glm::detail::tvec3<float>(0,0,-1));
    modelTransformMatrix = glm::rotate(modelTransformMatrix, 0.f, glm::detail::tvec3<float>(0,1,0));
    modelTransformMatrix = glm::scale(modelTransformMatrix, glm::detail::tvec3<float>(1,1,1));

    glm::detail::tmat4x4<float> modelViewMatrix = lookAtMat * modelTransformMatrix;


    /*
     Projection
    */
    const glm::detail::tmat4x4<float> projectionMatrix =
        glm::ortho<float>(-aspect*5, aspect*5, -5, 5, 0, 20);


    /* 
     Viewport
    */      
    const glm::vec4 viewport = glm::vec4(0, 0, backingWidth, backingHeight);

    /*
     Calculate two points on a line/ray based on the window coordinate (including arbitrary Z value), plus modelViewMatrix, projection matrix and viewport
    */
    unprojectedNearZ = glm::unProject(win,
                                 modelViewMatrix,
                                 projectionMatrix,
                                 viewport);
    win[2] = zFar;
    unprojectedFarZ = glm::unProject(win,
                                 modelViewMatrix,
                                 projectionMatrix,
                                 viewport);


            /*
     Define the start of the ray
     */

    glm::vec3 rayStart( unprojectedNearZ[0], unprojectedNearZ[1], unprojectedNearZ[2] );

    /*
     Determine the vector traveling parallel to the camera from the two
      unprojected points
    */

    float lookatVectX = unprojectedFarZ[0] - unprojectedNearZ[0];
    float lookatVectY = unprojectedFarZ[1] - unprojectedNearZ[1];
    float lookatVectZ = unprojectedFarZ[2] - unprojectedNearZ[2];

    glm::vec3 dir( lookatVectX, lookatVectY, lookatVectZ );



    /*
     Define three points on the plane that will define a triangle.
     Winding order does not matter.
     */

    glm::vec3 p0(0,0,0);
    glm::vec3 p1(1,0,0);
    glm::vec3 p2(0,0,1);

    /*
     And finally the destination of the calculations
     */

    glm::vec3 linePlaneIntersect;


    if (cartesianLineIntersectPlane(rayStart, dir, p0, p1, p2, linePlaneIntersect)) {
        // do work here using the linePlaneIntersect values
    }
}


bool cartesianLineIntersectPlane(glm::vec3 rayStart, glm::vec3 dir,
                                 glm::vec3 p0, glm::vec3 p1, glm::vec3 p2, glm::vec3 &linePlaneIntersect) {

    /*
     Create edge vectors to form the plane normal
     */
    glm::vec3 edge1 = p1 - p0;
    glm::vec3 edge2 = p2 - p0;

    /*
     Check if the ray direction is parallel to plane, before continuing
     */

    glm::vec3 perpendicularvector = glm::cross(dir, edge2);


    /*
     dot product of edge1 on perpendicular vector
     if orthogonal (approximately 0) then ray is parallel to plane, has 0 or infinite contact points
     */

    float det = glm::dot(edge1, perpendicularvector);
    float Epsilon = std::numeric_limits<float>::epsilon();
    if (det > -Epsilon && det < Epsilon)
        // Parallel, return false
        return false;


    /*
     Calculate the normalized/unit normal vector
     */

    glm::vec3 planeNormal = glm::normalize( glm::cross(edge1, edge2) );


    /*
     Calculate d, the dot product of the normal and any point on the plane.
     D is the magnitude of the plane's normal, to the origin.
     */

    float d = planeNormal[0] * p0[0] + planeNormal[1] * p0[1] + planeNormal[2] * p0[2];


    /*
     Take the x,y,z equations, ie:
     finalP.xyz = raystart.xyz + dir.xyz * t

     substitute them into the scalar equation for plane, ie:
     ax + by + cz = d

     and solve for t.  (This gets a bit wordy) t is the 
    multiplier on the direction vector, originating at raystart, 
    where it intersects the plane. 


     eg:

     ax + by + cz = d
     a(raystart.x + dir.x * t) + b(raystart.y + dir.y * t) + c(raystart.z + dir.z * t) = d
     a(raystart.x) + a(dir.x*t) + b(raystart.y) + b(dir.y*t) + c(raystart.z) + c(dir.z*t) = d
     a(raystart.x) + a(dir.x*t) + b(raystart.y) + b(dir.y*t) + c(raystart.z) + c(dir.z*t) - d = 0
     a(raystart.x) + b(raystart.y) + c(raystart.z) - d = - a(dir.x*t) - b(dir.y*t) - c(dir.z*t)
     (a(raystart.x) + b(raystart.y) + c(raystart.z) - d) / ( a(-dir.x) + b(-dir.y) + c(-dir.z) = t

     */

    float leftsideScalars = (planeNormal[0] * rayStart[0] +
                             planeNormal[1] * rayStart[1] +
                             planeNormal[2] * rayStart[2] - d);
    float directionDotProduct = (planeNormal[0] * (-dir[0]) +
                                 planeNormal[1] * (-dir[1]) +
                                 planeNormal[2] * (-dir[2]) );


    /*
     Final calculation of t, hurrah!
     */

    float t = leftsideScalars / directionDotProduct;


    /*
     This is the particular value of t for that line that lies
     in the plane. Then you can solve for x, y, and z by going
     back up to the line equations and substituting t back in.
     */

    linePlaneIntersect = glm::vec3(rayStart[0] + dir[0] * t,
                                   rayStart[1] + dir[1] * t,
                                   rayStart[2] + dir[2] * t
                                   );
    return true;
}
票数 1
EN

Stack Overflow用户

发布于 2013-12-10 16:47:12

您有屏幕空间坐标,并希望将其转换为模型空间。

您必须应用逆,才能从屏幕空间返回模型空间。

GLM有一个名为unProject的很好的函数,它可以为您查看示例代码1。

问题在于,您将屏幕空间中的鼠标坐标视为一个点。鼠标的位置应该被看作是一条光线,它正朝着你的光标看。如果这只是一个观点,你实际上对它的信息非常少。

因此,如果从鼠标中获得2d坐标作为屏幕空间,则需要将2d点转换为具有不同z值的两个不同的3d坐标)。

然后取消这些3d点的投影,得到两个模型空间点。这两点就是你的光线,代表你的鼠标。参见示例代码2。

然后,从三维射线,你可以回到平铺坐标,简单地做一个射线与瓷砖平面的交点。参见示例代码3。

示例代码1

我在这个例子中添加了完整的代码,这样您就可以使用值来查看发生了什么。

代码语言:javascript
复制
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <iostream>
#include <cmath>

using namespace glm;
using namespace std;

int main()
{
  //Our Matrices
  float aspect = 1024.f/ 768;
  vec4 viewPort = vec4(0.f,0.f,1024.f,768.f);
  mat4 projectionMatrix =  ortho<float>(0, 1024, 0, 768, 0, 20);
  mat4 modelWorldMatrix = lookAt(vec3(-sin(M_PI*36/180.f)*10,sin(M_PI*34/180.f)*10,10), vec3(), vec3(0,1,0)); 

  //Our position
  vec3 pos = vec3(1.0f,2.0f,3.0f);

  //Tranform it into screen space
  vec3 transformed = project(pos, modelWorldMatrix, projectionMatrix, viewPort);
  cout << transformed.x << " " << transformed.y << " " << transformed.z << endl;

  //Transform it back
  vec3 untransformed = unProject(transformed, modelWorldMatrix, projectionMatrix, viewPort);
  cout << untransformed.x << " " << untransformed.y << " " << untransformed.z << endl;

  //You'll see how the glm's unproject works

  return 0;
}

示例代码2

代码语言:javascript
复制
  //You have your screen space coordinates as x and y
  vec3 screenPoint1 = vec3(x, y, 0.f);
  vec3 screenPoint2 = vec3(x, y, 20.f);

  //Unproject both these points
  vec3 modelPoint1 = unProject(screenPoint1, modelWorldMatrix, projectionMatrix, viewPort);
  vec3 modelPoint2 = unProject(screenPoint2, modelWorldMatrix, projectionMatrix, viewPort);

  //This is your ray with the two points modelPoint1, modelPoint2

示例代码3

代码语言:javascript
复制
  //normalOfPlane is the normal of the plane. If it's a xy plane then the normal is vec3(0,0,1)
  //P0 is a point on the plane

  //L is the direction of your ray
  //L0 is a point on the ray
  vec3 L = modelPoint1 - modelPoint2;
  vec3 L0 = modelPoint1;

  //Solve for d where dot((d * L + L0 - P0), n) = 0
  float d = dot(P0 - L0,normalOfPlane) / dot(L, normalOfPlane);

  //Use d to get back to point on plane
  vec3 pointOnPlane = d * L + L0; 

  //Sound of trumpets in the background
  cout << pointOnPlane.x << " " << pointOnPlane.y << endl;
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20275127

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档