ฉันจะแปลงการคลิกเมาส์เป็นรังสีได้อย่างไร


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 ด้วยเมทริกซ์ 4 นั่นไม่ดี ... หมายเลขใดที่จะเข้าสู่อันดับที่ 4 เมื่อคุณทำ vec_t (อันดับ 1, 2, 3, 4)
Notabene

@notebene ว้าวฉันคิดว่ามันคือฉันไม่มีเงื่อนงำ การอภิปรายนี้เป็นสิ่งที่ดีและเป็นทางเลือกที่ฉันจะต้องพิจารณาและตอนนี้ฉันหวังว่าจะได้คำตอบที่ดีสำหรับคำถามนี้
Ricket

คำตอบ:


14

ฉันเพิ่งต้องแก้ปัญหานี้ด้วยตัวเองสำหรับแอปพลิเคชัน WebGL ฉันได้แนบซอร์สโค้ดที่สมบูรณ์ แต่ในกรณีที่มันไม่ทำงานทันทีสำหรับคุณที่นี่มีเคล็ดลับการแก้ไขข้อบกพร่องบางอย่าง:

  1. อย่าแก้ปัญหาวิธีการ unproject ของคุณในเกมของคุณ ถ้าเป็นไปได้ให้ลองเขียนการทดสอบรูปแบบการทดสอบหน่วยเพื่อให้ง่ายต่อการแยกสิ่งที่ผิดพลาดออกไป
  2. ตรวจสอบให้แน่ใจว่าได้พิมพ์รังสีเอาท์พุตสำหรับระนาบการตัดทั้งใกล้และไกล
  3. จำไว้ว่าคณิตศาสตร์เมทริกซ์นั้นไม่ใช่การสลับสับเปลี่ยน A x C! = C x A. ตรวจสอบคณิตศาสตร์ของคุณอีกครั้ง

นอกจากนี้ในการตอบกลับความคิดเห็นด้านบนคุณแทบไม่เคยต้องการใช้ API การเลือกของ OpenGL เลย ที่ช่วยให้คุณเลือกรายการที่มีอยู่เช่นถ้าคุณกำลังสร้างเมนูอย่างไรก็ตามมันล้มเหลวในการดำเนินการสถานการณ์จริงส่วนใหญ่เช่นการแก้ไขแบบจำลอง 3 มิติ ตำแหน่งที่คุณต้องการเพิ่มรูปทรงเรขาคณิตอันเป็นผลมาจากการคลิก

นี่คือการดำเนินการของฉัน ไม่มีอะไรวิเศษเกิดขึ้นที่นี่ เพียงแค่ 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) ประเมินเป็นจริง
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;

และพยายามที่จะเปลี่ยนเมทริกซ์ multiplacation (p*mv).inverse();ไป เพียงเพราะ glMatrices เปลี่ยนเป็น Nutaraly ในหน่วยความจำ (นี่อาจจะเป็นเรื่องใหญ่ขอโทษ)

การฉายรังสีอีกแบบหนึ่งการปล่อย
รังสีไม่ได้เป็นเพียงวิธีการรับรังสีจากพิกเซล นี่คือรหัสของฉันสำหรับ 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 and Direction จากเมทริกซ์มุมมอง (ซึ่งส่วนใหญ่จะเป็นประโยชน์ในการ shader) ดูเมทริกซ์ใน OpenGL

การเลือกสีการเลือก
สีเป็นเทคนิคที่คุณแสดงทุกออบเจกต์ที่คลิกได้ด้วยสีอื่น (ทึบ) แล้วอ่านค่าของสีที่จุดที่คลิก มันทำงานเป็น GL_SELECTION ใน OpenGL แต่ตอนนี้เลิกใช้แล้ว แต่การเลือกสีนั้นง่ายต่อการกำหนดรหัสเป็นรหัสผ่านการเรนเดอร์เพิ่มเติม 1 รายการ

ใช้เฟรมบัฟเฟอร์และเรนเดอร์เป็นพื้นผิวที่มีความละเอียดเช่นเดียวกับความละเอียดของหน้าจอใช้ shader ง่ายซึ่งเพิ่งคืนสีในส่วนของ shader หลังจาก "color pass" อ่านค่าจากการกำหนดพื้นผิวด้วย clickedPoint x และ y ค้นหาวัตถุที่มีสีที่คุณอ่าน ง่ายและค่อนข้างเร็ว

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.