OpenGL 互動方式Trackball的實現程式碼
阿新 • • 發佈:2019-02-05
Trackball是將螢幕上捕捉到的滑鼠位置轉換到一個球的表面,根據它確定旋轉軸和旋轉角度。看下面的程式碼就能明白;
將滑鼠位置對映到球面(或雙曲面,當滑鼠位置超過球的範圍時)上
更新變換矩陣:
滑鼠事件響應:
首先定義一些全域性變數:
//camera transform variables int state = 0, oldX=0, oldY=0; float r_angle=0, dist = -2; glm::vec3 r_axis_local=glm::vec3(1.0f, 0.0f, 0.0f); glm::mat4 Tr; glm::mat4 R=glm::mat4(1.0); glm::mat4 MV,P,MVP;
將滑鼠位置對映到(-1.0,1.0)範圍內
glm::vec2 scaleMouse(glm::vec2 coords, glm::vec2 viewport) { return glm::vec2( static_cast<float>(coords.x*2.f) / static_cast<float>(viewport.x) - 1.f, 1.f - static_cast<float>(coords.y*2.f) / static_cast<float>(viewport.y) ); }
將滑鼠位置對映到球面(或雙曲面,當滑鼠位置超過球的範圍時)上
設定物體旋轉矩陣的引數:glm::vec3 projectToSphere(glm::vec2 xy) { static const float sqrt2 = sqrtf(2.f); glm::vec3 result; float d = glm::length(xy); float size_=2; if (d < size_ * sqrt2 / 2.f) { // Inside sphere // The factor "sqrt2/2.f" make a smooth changeover from sphere to hyperbola. If we leave // factor 1/sqrt(2) away, the trackball would bounce at the changeover. result.z = sqrtf(size_ * size_ - d*d); } else { // On hyperbola float t = size_ / sqrt2; result.z = t*t / d; } result.x = xy.x; result.y = xy.y; return glm::normalize(result); }
void SetRotateParameter(glm::vec2 newMouse,glm::vec2 oldMouse) { if (newMouse == oldMouse) { // Zero rotation -> do nothing return; } // First, figure out z-coordinates for projection of P1 and P2 to deformed sphere glm::vec3 p1 = projectToSphere(oldMouse); glm::vec3 p2 = projectToSphere(newMouse); glm::vec3 r_axis_world = glm::cross(p1, p2); glm::vec3 d = p1 - p2; r_angle= 180*glm::length(d); //transform rotate axis from world coordinate to local coordinate glm::vec3 r_axis_local_end=glm::vec3(glm::inverse(MV)*glm::vec4(r_axis_world,1)); glm::vec3 r_axis_local_start=glm::vec3(glm::inverse(MV)*glm::vec4(0.0,0.0,0.0,1)); r_axis_local=r_axis_local_end-r_axis_local_start; R=glm::rotate(R, r_angle, r_axis_local); }
更新變換矩陣:
void UpdateMatrix()
{
Tr= glm::translate(glm::mat4(1.0f),glm::vec3(0.0f, 0.0f, dist));
MV=Tr*R;
//get the combined modelview projection matrix
MVP = P*MV;
}
滑鼠事件響應:
void OnMouseDown(int button, int s, int x, int y)
{
if (s == GLUT_DOWN)
{
oldX = x;
oldY = y;
}
if(button == GLUT_MIDDLE_BUTTON)
state = 0;
else
state = 1;
}
void OnMouseMove(int x, int y)
{
if (state == 0) {
dist += (y - oldY)/50.0f;
} else {
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT,viewport);
glm::vec2 newMouse = scaleMouse( glm::vec2(x, y), glm::vec2(viewport[2],viewport[3]));
glm::vec2 oldMouse = scaleMouse( glm::vec2(oldX, oldY), glm::vec2(viewport[2],viewport[3]));
SetRotateParameter(newMouse,oldMouse);
}
oldX = x;
oldY = y;
glutPostRedisplay();
}
最後記得在OnRender()函式中呼叫UpdateMatrix().