2013-04-10 96 views
1

我正在iPad上製作一個2D的OpenGL應用程序。我需要實施捏/縮放。iOS OpenGL ES在2D世界中執行縮放變焦

我想移動攝像機(x,y)平面和控制攝像機x,yz價值與捏手勢。

update方法的每個幀i使視圖矩陣(照相機)這樣

lookAt = GLKMatrix4MakeLookAt(view_x, view_y, view_z, view_x, view_y, 0.0f, 0.0f, 1.0f, 0.0f);

其中view_x,view_y和view_z在程序中定義的啓動是這樣的: view_x = view_y = 0.0f; view_z = kStartZoom; kStartZoom是3000 所以相機處於(0,0,3000)並且看起來像(0,0,0)

處理夾點事件的幾乎可行的解決方案是

- (IBAction) handlePinch:(UIPinchGestureRecognizer*) recognizer { 
switch (recognizer.state) 
{ 
    case UIGestureRecognizerStateBegan: 
    { 
     if (recognizer.numberOfTouches == 2) 
     { 
      prevTouchOrigin1 = [recognizer locationOfTouch:0 inView:self.view]; 
      prevTouchOrigin2 = [recognizer locationOfTouch:1 inView:self.view]; 
     } 
    } break; 
    case UIGestureRecognizerStateChanged: 
    { 
     if (recognizer.numberOfTouches == 2) 
     { 
      CGFloat newDistance, oldDistance; 

      oldDistance = distanceBetweenTwoCGPoints(&prevTouchOrigin1, &prevTouchOrigin2); 
      currTouchOrigin1 = [recognizer locationOfTouch:0 inView:self.view]; 
      currTouchOrigin2 = [recognizer locationOfTouch:1 inView:self.view]; 

      newDistance = distanceBetweenTwoCGPoints(&currTouchOrigin1, &currTouchOrigin2); 

      if (newDistance == 0 || oldDistance == 0) 
      { 
       scaleFactor = 1; 
      } else { 
       scaleFactor = oldDistance/newDistance; 
      } 

      GLfloat check = view_z * scaleFactor; 
      if (check < kMinZoom || check > kMaxZoom) 
       return; 

      view_z *= scaleFactor; 

      // translate 

      // formula: newPos = currTouchOrigin + (objectOrigin - prevTouchOrigin) * scaleFactor 

      static CGPoint translationDelta; 
      GLfloat z_ratio = view_z_old/view_z; 

      newPos1.x = currTouchOrigin1.x - ((prevTouchOrigin1.x - view_x) * scaleFactor); 
      newPos1.y = currTouchOrigin1.y - ((prevTouchOrigin1.y - view_y) * scaleFactor); 

      newPos2.x = currTouchOrigin2.x - ((prevTouchOrigin2.x - view_x) * scaleFactor); 
      newPos2.y = currTouchOrigin2.y - ((prevTouchOrigin2.y - view_y) * scaleFactor); 

      midpoint = CGPointMidpoint(&newPos1, &newPos2); 

      translationDelta = CGPointMake(midpoint.x - view_x, midpoint.y - view_y); 

      view_x += translationDelta.x; 
      view_y -= translationDelta.y; 

      prevTouchOrigin1 = currTouchOrigin1; 
      prevTouchOrigin2 = currTouchOrigin2; 
     } 
    } break; 
    case UIGestureRecognizerStateEnded: 
    { 
    } break; 
    default : 
    { 
    } 
}} 

總是在工作。

我有更多的運動在x,y然後我需要這麼相機編織。

問題是我不是應用從屏幕座標到世界座標的一些變換?

可能是什麼問題?我正在研究的其他例子只根據前後手指位置之間的距離修改相機位置,這正是我正在做的。

+0

解決方案已關閉。 – 2013-04-17 15:59:45

回答

1

這是我的解決方案:

我有兩個班,一個需要照顧所有OpenGL的東西(RenderViewController)和另一個負責OpenGL部分和應用程序其他部分(EditViewController)之間的所有手勢識別器和通信。

這是關於手勢代碼:

EdtorViewController

它捕獲的手勢,併發送有關它們的信息到RenderViewController。你必須小心,因爲座標系統不同。

- (void) generateGestureRecognizers { 

    //Setup gesture recognizers 
    UIRotationGestureRecognizer *twoFingersRotate = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(twoFingersRotate:)]; 
    [self.hitView addGestureRecognizer:twoFingersRotate]; 

    UIPinchGestureRecognizer *twoFingersScale = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(twoFingersScale:)]; 
    [self.hitView addGestureRecognizer:twoFingersScale]; 

    UIPanGestureRecognizer *oneFingerPan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(oneFingerPan:)]; 
    [self.hitView addGestureRecognizer:oneFingerPan]; 


    [twoFingersRotate setDelegate:self]; 
    [twoFingersScale setDelegate:self]; 
    [oneFingerPan setDelegate:self]; 
} 

- (void) oneFingerPan:(UIPanGestureRecognizer *) recognizer {  

    //Handle pan gesture 
    CGPoint translation = [recognizer translationInView:self.hitView]; 
    CGPoint location = [recognizer locationInView:self.hitView]; 

    //Send info to renderViewController 
    [self.renderViewController translate:traslation]; 

    //Reset recognizer so change doesn't accumulate 
    [recognizer setTranslation:CGPointZero inView:self.hitView];  
} 

- (void) twoFingersRotate:(UIRotationGestureRecognizer *) recognizer { 

    //Handle rotation gesture 
    CGPoint locationInView = [recognizer locationInView:self.hitView]; 
    locationInView = CGPointMake(locationInView.x - self.hitView.bounds.size.width/2, locationInView.y - self.hitView.bounds.size.height/2); 

    if ([recognizer state] == UIGestureRecognizerStateBegan || [recognizer state] == UIGestureRecognizerStateChanged) { 

     //Send info to renderViewController 
     [self.renderViewController rotate:locationInView degrees:recognizer.rotation]; 

     //Reset recognizer 
     [recognizer setRotation:0.0]; 
    } 
} 

- (void) twoFingersScale:(UIPinchGestureRecognizer *) recognizer { 

    //Handle scale gesture 
    CGPoint locationInView = [recognizer locationInView:self.hitView]; 
    locationInView = CGPointMake(locationInView.x - self.hitView.bounds.size.width/2, locationInView.y - self.hitView.bounds.size.height/2); 

    if ([recognizer state] == UIGestureRecognizerStateBegan || [recognizer state] == UIGestureRecognizerStateChanged) { 

     //Send info to renderViewController 
     [self.renderViewController scale:locationInView ammount:recognizer.scale]; 

     //reset recognizer 
     [recognizer setScale:1.0]; 
    } 

} 

//This allows gestures recognizers to happen simultaniously 
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { 
    if (gestureRecognizer.view != otherGestureRecognizer.view) 
     return NO; 

    if ([gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]] || [otherGestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]]) 
     return NO; 

    return YES; 
} 

RenderViewController

對於每一幀,所述modelViewMatrix從其它三個臨時矩陣(平移,縮放和旋轉)

- (void) setup { 

    //Creates the modelViewMatrix from the initial position, rotation and scale 
    translatemt = GLKMatrix4Translate(GLKMatrix4Identity, initialPosition.x, initialPosition.y, 0.0); 
    scalemt = GLKMatrix4Scale(GLKMatrix4Identity, initialScale, initialScale, 1.0); 
    rotatemt = GLKMatrix4Rotate(GLKMatrix4Identity, initialRotation, 0.0, 0.0, 1.0); 
    self.modelViewMatrix = GLKMatrix4Multiply(GLKMatrix4Multiply(GLKMatrix4Multiply(translatemt, rotatemt), scalemt), GLKMatrix4Identity); 

    //set these back to identities to take further modifications (they'll update the modelViewMatrix) 
    scalemt = GLKMatrix4Identity; 
    rotatemt = GLKMatrix4Identity; 
    translatemt = GLKMatrix4Identity; 


    //rest of the OpenGL setup 
    [self setupOpengGL]; 

} 

//public interface 
- (void) translate:(CGPoint) location { 
    //Update the translation temporary matrix 
    translatemt = GLKMatrix4Translate(translatemt, location.x, -location.y, 0.0); 
} 

//public interface 
- (void) rotate:(CGPoint) location degrees:(CGFloat) degrees { 
    //Update the rotation temporary matrix 
    rotatemt = GLKMatrix4Translate(GLKMatrix4Identity, location.x, -location.y, 0.0); 
    rotatemt = GLKMatrix4Rotate(rotatemt, -degrees, 0.0, 0.0, 1.0); 
    rotatemt = GLKMatrix4Translate(rotatemt, -location.x, location.y, 0.0); 
} 

//public interface 
- (void) scale:(CGPoint) location ammount:(CGFloat) ammount { 
    //Update the scale temporary matrix 
    scalemt = GLKMatrix4Translate(GLKMatrix4Identity, location.x, -location.y, 0.0); 
    scalemt = GLKMatrix4Scale(scalemt, ammount, ammount, 1.0); 
    scalemt = GLKMatrix4Translate(scalemt, -location.x, location.y, 0.0); 
} 

- (void)update { 

    //this is done before every render update. It generates the modelViewMatrix from the temporary matrices 
    self.modelViewMatrix = GLKMatrix4Multiply(GLKMatrix4Multiply(GLKMatrix4Multiply(rotatemt, translatemt), scalemt), self.modelViewMatrix); 

    //And then set them back to identities 
    translatemt = GLKMatrix4Identity; 
    rotatemt = GLKMatrix4Identity; 
    scalemt = GLKMatrix4Identity; 

    //set the modelViewMatrix for the effect (this is assuming you are using OpenGL es 2.0, but it would be similar for previous versions 
    self.effect.transform.modelviewMatrix = self.modelViewMatrix; 
} 
+0

我一直在嘗試使這種方法工作正好3個小時,但都失敗了:)但是,即使它工作,我也需要移動相機而不是物體。當我們有很多對象時,修改每個對象的矩陣可能會花費很多......謝謝Odrakir的努力。通過閱讀你的答案,我得到了解決方案的想法。我需要爲屏幕輸入設置比率。完成後我會寫更多。 – 2013-04-15 15:53:00

+0

對不起,但您不應該爲每個對象都有一個modelViewMatrix。這只是整個程序的一個矩陣。 – Odrakir 2013-04-15 16:09:43

+0

我不關注。回顧一下:每個物體都有一個模型矩陣,它決定了物體在世界空間中的位置。然後我們有相機的一個視圖和一個投影矩陣。這就是我從DirectX學到的東西。在OpenGL中應該是相同的,視圖矩陣決定了相機的外觀和投影矩陣處理近,遠平面和其他東西。如果你不這麼想,請糾正我。 – 2013-04-16 07:23:18

0

此解決方案使用移動式攝像機上述的x,y來計算平面。 世界保持不變。 我們處於z位置,我們通過捏手勢來控制攝像機的移動。

需要的是將單位從屏幕空間轉換爲以點表示的點到開放的gl空間,用離散單位表示。

我得到這個常數,通過在x,y平面上以opengl單位繪製50x50平方, 以相機爲中心x = 0, y = 0, z = 100

然後我添加了兩個UIView s並設置了平移手勢來移動這兩個視圖。 通過平移手勢我物理中心他們的框架起源到opengl廣場的右上角和右下角。 輕擊手勢被設置爲NSLog他們的起源。 這就是我得到這個常數:

static const GLfloat k = 0.125f; // 445 CG units is 50 discrete OpenGL units at Z = 100; 0.112359f 

所以對於任意相機Z位置,我可以計算出多少規模那些我們從雙指縮放手指位置得到增量。

矩陣初始化一些init方法:

aspect = fabsf(self.view.bounds.size.width/self.view.bounds.size.height); 
projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(65.0f), aspect, 0.1f, 5000.0f); 
lookAt = GLKMatrix4MakeLookAt(view_x, view_y, view_z, view_x, view_y, 0.0f, 0.0f, 1.0f, 0.0f); 

所有伊娃的:

GLfloat view_x; 
GLfloat view_y; 
GLfloat view_z; 
GLfloat view_z_base; 

CGPoint currTouchOrigin1, currTouchOrigin2; 
CGPoint prevTouchOrigin1, prevTouchOrigin2; 
CGPoint newPos1, newPos2; 
CGPoint midpoint; 
CGFloat scaleFactor; 

我們通過view_x,view_y和view_z高德移動相機。

所有常量:

static const GLfloat k    = 0.125f; // 445 CG units is 50 discrete OpenGL units at Z = 100; 0.112359f 
static const GLfloat default_z  = 100.0f; 
static const GLfloat kMinZoom  = 30.0f; 
static const GLfloat kMaxZoom  = 4000.0f; 
static const GLfloat kStartZoom  = 200.0f; 

這是全雙指縮放處理:

- (IBAction) handlePinch:(UIPinchGestureRecognizer*) recognizer { 

switch (recognizer.state) 
     { 
      case UIGestureRecognizerStateBegan: 
      { 
       if (recognizer.numberOfTouches == 2) 
       { 
        prevTouchOrigin1 = [recognizer locationOfTouch:0 inView:self.view]; 
        prevTouchOrigin2 = [recognizer locationOfTouch:1 inView:self.view]; 
       } 
      } break; 
      case UIGestureRecognizerStateChanged: 
      { 
       if (recognizer.numberOfTouches == 2) 
       { 
        CGFloat newDistance, oldDistance; 

      oldDistance = distanceBetweenTwoCGPoints(&prevTouchOrigin1, &prevTouchOrigin2); 
      currTouchOrigin1 = [recognizer locationOfTouch:0 inView:self.view]; 
      currTouchOrigin2 = [recognizer locationOfTouch:1 inView:self.view]; 

      newDistance = distanceBetweenTwoCGPoints(&currTouchOrigin1, &currTouchOrigin2); 

      if (newDistance == 0 || oldDistance == 0) 
      { 
       scaleFactor = 1; 
       //break; 
      } else { 
       //scaleFactor = oldDistance/newDistance; 
       scaleFactor = newDistance/oldDistance; 
      } 

      GLfloat view_z_old = view_z; 
      view_z /= scaleFactor; 
      if (view_z < kMinZoom || view_z > kMaxZoom) 
      { 
       view_z = view_z_old; 
       return; 
      } 

      // translate 

      // formula: newPos = currTouchOrigin + (objectOrigin - prevTouchOrigin) * scaleFactor 

      //static CGPoint tmp1, tmp2; 
      static CGPoint translationDelta; 

      newPos1.x = currTouchOrigin1.x - ((prevTouchOrigin1.x - (screenRect.size.width/2)) * scaleFactor); 
      newPos1.y = currTouchOrigin1.y - ((prevTouchOrigin1.y - (screenRect.size.height/2)) * scaleFactor); 

      newPos2.x = currTouchOrigin2.x - ((prevTouchOrigin2.x - (screenRect.size.width/2)) * scaleFactor); 
      newPos2.y = currTouchOrigin2.y - ((prevTouchOrigin2.y - (screenRect.size.height/2)) * scaleFactor); 

      midpoint = CGPointMidpoint(&newPos1, &newPos2); 

      translationDelta = CGPointMake(midpoint.x - (screenRect.size.width/2), midpoint.y - (screenRect.size.height/2)); 

      static GLfloat r = 0.0f; 
      static GLfloat k2 = 0.0f; 

      r = view_z/default_z; 
      k2 = k * r; 

      // In openGL, coord sys if first quadrant based 
      view_x += -translationDelta.x * k2; 
      view_y += translationDelta.y * k2; 

      // store current coords for next event 
      prevTouchOrigin1 = currTouchOrigin1; 
      prevTouchOrigin2 = currTouchOrigin2; 
     } 
    } break; 
    case UIGestureRecognizerStateEnded: 
    { 

    } break; 
    default : 
    { 

    } 
    } 
} 
+0

這是我的問題的解決方案。然而,我已經標記Odrakir的答案是正確的,因爲他的回答幫助我走向正確的方向。他也投入了時間。我之前曾問過這類問題,根本沒有答覆。 Thanx Odrakir。 – 2013-04-17 15:59:29