1. 程式人生 > >OpenGL 互動方式Trackball的實現程式碼

OpenGL 互動方式Trackball的實現程式碼

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().