마우스 클릭을 광선으로 변환하려면 어떻게해야합니까?


18

투시 투영이 있습니다. 사용자가 화면을 클릭하면 마우스 점에서 투사되는 근거리와 원거리 평면 사이의 광선을 계산하려고하므로 월드와의 광선 교차 코드를 수행 할 수 있습니다.

나는 내 자신의 행렬과 벡터 및 광선 클래스를 사용하고 있으며 모두 예상대로 작동합니다.

그러나 광선을 월드 좌표로 변환하려고하면 항상 먼 거리가 0,0,0으로 끝나므로 광선은 마우스 클릭에서 오브젝트 공간의 중심이 아닌 객체 공간의 중심으로 이동합니다. (근거리와 원거리의 x와 y 좌표는 동일하며 z 좌표에서만 서로 음수 인 위치가 다릅니다)

GLint vp[4];
glGetIntegerv(GL_VIEWPORT,vp);
matrix_t mv, p;
glGetFloatv(GL_MODELVIEW_MATRIX,mv.f);
glGetFloatv(GL_PROJECTION_MATRIX,p.f);
const matrix_t inv = (mv*p).inverse();
const float
    unit_x = (2.0f*((float)(x-vp[0])/(vp[2]-vp[0])))-1.0f,
    unit_y = 1.0f-(2.0f*((float)(y-vp[1])/(vp[3]-vp[1])));
const vec_t near(vec_t(unit_x,unit_y,-1)*inv);
const vec_t far(vec_t(unit_x,unit_y,1)*inv);
ray = ray_t(near,far-near);

내가 뭘 잘못 했니? (마우스 포인트를 어떻게 투영 해제합니까?)


호기심 때문에 내장 GL_SELECTION 모드를 사용하지 않는 이유가 있습니까? (여기에 정보)
Ricket

@Ricket 더 이상 사용되지 않습니까? 잘 모르겠지만 확실합니다.
Notabene

x와 y가 픽셀입니까? 그게 문제가 될 것입니다 ...
Notabene

그리고 vec3에 matrix4를 곱하고 있습니다. 그것은 좋지 않습니다 ... vec_t (1st, 2nd, 3th, 4th)를 할 때 어떤 숫자가 4 위를 차지합니까?
Notabene

@notebene 와우, 나는 단서가 없었습니다. 이 토론 은 훌륭하고 살펴볼 대안을 제공하며, 이제이 질문에 대한 좋은 답변을 기대하고 있습니다.
Ricket

답변:


14

최근에 WebGL 응용 프로그램에서이 문제를 직접 해결해야했습니다. 완전한 소스 코드를 첨부했지만 바로 작동하지 않는 경우 디버깅 팁이 있습니다.

  1. 게임에서 unproject 메소드를 디버깅하지 마십시오. 가능하면 단위 테스트 스타일 테스트를 작성하여 문제를 쉽게 분리하십시오.
  2. 근거리 및 원거리 자르기 평면 모두에 대해 출력 광선을 인쇄해야합니다.
  3. 행렬 수학은 정식이 아닙니다. A x C! = C x A. 수학을 다시 확인하십시오.

또한 위의 의견에 답하기 위해 OpenGL의 선택 API를 사용하고 싶지는 않습니다. 메뉴를 만드는 것처럼 기존 항목을 선택하는 데 도움이되지만 3D 모델 편집과 같은 대부분의 실제 시나리오는 수행하지 못합니다. 클릭으로 지오메트리를 추가해야하는 경우

여기 내 구현이 있습니다. 여기에는 마술이 없습니다. JavaScript와 Google의 폐쇄 라이브러리 만 있습니다.

gluUnProject

/**
 * Port of gluUnProject. Unprojects a 2D screen coordinate into the model-view
 * coordinates.
 * @param {Number} winX The window point for the x value.
 * @param {Number} winY The window point for the y value.
 * @param {Number} winZ The window point for the z value. This should range
 *    between 0 and 1. 0 meaning the near clipping plane and 1 for the far.
 * @param {goog.math.Matrix} modelViewMatrix The model-view matrix.
 * @param {goog.math.Matrix} projectMatrix The projection matrix.
 * @param {Array.<Number>} view the viewport coordinate array.
 * @param {Array.<Number>} objPos the model point result.
 * @return {Boolean} Whether or not the unprojection was successful.
 */
octorok.math.Matrix.gluUnProject = function(winX, winY, winZ,
                        modelViewMatrix, projectionMatrix,
                        viewPort, objPos) {
  // Compute the inverse of the perspective x model-view matrix.
  /** @type {goog.math.Matrix} */
  var transformMatrix =
    projectionMatrix.multiply(modelViewMatrix).getInverse();

  // Transformation of normalized coordinates (-1 to 1).
  /** @type {Array.<Number>} */
  var inVector = [
    (winX - viewPort[0]) / viewPort[2] * 2.0 - 1.0,
    (winY - viewPort[1]) / viewPort[3] * 2.0 - 1.0,
    2.0 * winZ - 1.0,
    1.0 ];

  // Now transform that vector into object coordinates.
  /** @type {goog.math.Matrix} */
  // Flip 1x4 to 4x1. (Alternately use different matrix ctor.
  var inMatrix = new goog.math.Matrix([ inVector ]).getTranspose();
  /** @type {goog.math.Matrix} */
  var resultMtx = transformMatrix.multiply(inMatrix);
  /** @type {Array.<Number>} */
  var resultArr = [
    resultMtx.getValueAt(0, 0),
    resultMtx.getValueAt(1, 0),
    resultMtx.getValueAt(2, 0),
    resultMtx.getValueAt(3, 0) ];

  if (resultArr[3] == 0.0) {
    return false;
  }

  // Invert to normalize x, y, and z values.
  resultArr[3] = 1.0 / resultArr[3];

  objPos[0] = resultArr[0] * resultArr[3];
  objPos[1] = resultArr[1] * resultArr[3];
  objPos[2] = resultArr[2] * resultArr[3];

  return true;
};

용법

  this.sys.event_mouseClicked = function(event) {
    // Relative x and y are computed via magic by SystemModule.
    // Should range from 0 .. viewport width/height.
    var winX = event.relativeX;
    var winY = event.relativeY;
    window.console.log('Camera at [' + me.camera.position_ + ']');
    window.console.log('Clicked [' + winX + ', ' + winY + ']');

    // viewportOriginX, viewportOriginY, viewportWidth, viewportHeight
    var viewPort = [0, 0, event.viewPortWidth, event.viewPortHeight];
    var objPos = [];  // out parameter.

    // The camera's model-view matrix is the result of gluLookAt.
    var modelViewMatrix = me.camera.getCameraMatrix();
    // The perspective matrix is the result of gluPerspective.
    var perspectiveMatrix = pMatrix.get();

    // Ray start
    var result1 = octorok.math.Matrix.gluUnProject(
      winX, winY, 0.0,
      modelViewMatrix, perspectiveMatrix,
      viewPort, objPos);
    window.console.log('Seg start: ' + objPos + ' (result:' + result1 + ')');

    // Ray end
    var result2 = octorok.math.Matrix.gluUnProject(
      winX, winY, 1.0,
      modelViewMatrix, perspectiveMatrix,
      viewPort, objPos);
    window.console.log('Seg end: ' + objPos + ' (result:' + result2 + ')');
  };
};

어떤 경우에 (resultArr [3] == 0.0) true로 평가됩니까?
Halsafar

3

코드에 아무런 문제가 없습니다. 그래서 나는 몇 가지 제안 만 있습니다.

unit_x = (2.0f*((float)(x-vp[0])/(vp[2]-vp[0])))-1.0f,
unit_y = (2.0f*((float)(y-vp[1])/(vp[3]-vp[1])))-1.0f;

그리고 행렬 다중 치환을로 변경하려고했습니다 (p*mv).inverse();. glMatrices가 메모리에 영양 적으로 전이되기 때문입니다. (이것은 아마 큰 것입니다, 죄송합니다)

또 다른
raycasting Unprojecting은 픽셀에서 광선을 얻는 방법 일뿐입니다. 이것은 raycaster의 코드입니다.

//works for right handed coordinates. So it is opengl friendly.
float mx = (float)((pixel.x - screenResolution.x * 0.5) * (1.0 / screenResolution.x) * camera.fovX * 0.5);
float my = (float)((pixel.y - screenResolution.y * 0.5) * (1.0 / screenResolution.x) * camera.fovX * 0.5);
float4 dx = cameraRight * mx;
float4 dy = cameraUp * my;

float3 dir = normalize(cameraDir + (dx + dy).xyz * 2.0);

뷰 매트릭스에서 cameraRight, Up 및 Direction을 얻을 수 있습니다 (주로 셰이더에 유용합니다) OpenGL의 뷰 매트릭스

색상 선택
색상 선택은 클릭 가능한 모든 객체를 다른 (단색) 색상으로 렌더링 한 다음 클릭 한 지점의 색상 값을 읽는 기술입니다. opengl에서 GL_SELECTION으로 일했습니다. 그러나 현재는 더 이상 사용되지 않습니다. 그러나 하나의 추가 렌더 패스로 색상 선택이 쉽게 코딩됩니다.

프레임 버퍼를 사용하고 화면 해상도와 동일한 해상도로 텍스처를 렌더링하고 조각 셰이더에서 색상을 반환하는 간단한 셰이더를 사용하십시오. "컬러 패스"후에 clickedPoint x 및 y로 텍스처 주소 지정 값을 읽습니다. 읽은 색으로 물체를 찾으십시오. 쉽고 빠르고.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.