2013-04-10 93 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