Bir fare tıklamasını bir ışına nasıl dönüştürebilirim?


18

Perspektif izdüşümüm var. Kullanıcı ekranı tıkladığında, fare noktasından yansıyan yakın ve uzak düzlemler arasındaki ışını hesaplamak istiyorum, böylece dünyamla bazı ışın kavşak kodu yapabilirim.

Kendi matris ve vektör ve ışın sınıflarımı kullanıyorum ve hepsi beklendiği gibi çalışıyor.

Ancak, ışını dünya koordinatlarına dönüştürmeye çalıştığımda, uzaklarım her zaman 0,0,0 olarak bitiyor ve bu yüzden ışınım, fare tıklamasından nesnenin yerine nesnenin ortasına gidiyor. (Yakın ve uzaktaki x ve y koordinatları aynıdır, sadece birbirlerinin negatif oldukları z koordinatlarında farklılık gösterirler)

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);

Neyi yanlış yaptım? (Fare noktasının projeksiyonunu nasıl kaldırırsınız?)


Sadece meraktan, yerleşik GL_SELECTION modunu kullanmamanızın bir nedeni var mı? (burada bilgi)
Ricket

@Ricket Onaylanmadı mı? Bilmiyorum, ama oldukça emin.
Notabene

X ve y piksel olarak mı? sorun bu olurdu ...
Notabene

Ve vec3'ü matrix4 ile çarpıyorsunuz. Bu iyi değil ... vec_t (1., 2., 3., 4.) yaptığınızda hangi sayı 4. sıraya giriyor?
Notabene

@ notebene Vay be, sanırım öyle, hiçbir fikrim yoktu. Bu tartışma iyi ve bakmam gereken bir alternatif sunuyor ve şimdi gerçekten bu soruya iyi bir cevap bekliyorum.
Ricket

Yanıtlar:


14

Son zamanlarda bunu bir WebGL uygulaması için kendim çözmek zorunda kaldım. Tam kaynak kodunu ekledim, ancak sizin için yarasadan hemen çalışmıyorsa bazı hata ayıklama ipuçları var:

  1. Projenizdeki unproject yönteminizde hata ayıklama. Mümkünse, neyin yanlış gittiğini izole etmeyi kolaylaştırmak için birim testi stil testleri yazmaya çalışın.
  2. Hem yakın hem de uzak kırpma düzlemleri için çıkış ışınlarını yazdırdığınızdan emin olun.
  3. Matris matematiğinin değişmeli olmadığını unutmayın. A x C! = C x A. Matematiğinizi iki kez kontrol edin.

Ayrıca, yukarıdaki bazı yorumlara yanıt vermek için, neredeyse hiçbir zaman OpenGL'nin seçim API'larını kullanmak istemezsiniz. Bu, bir menü oluşturuyormuş gibi mevcut öğeleri seçmenize yardımcı olur, ancak 3D model düzenleme gibi çoğu gerçek dünya senaryolarını gerçekleştiremez. Tıklama sonucunda geometri eklemeniz gereken yer.

İşte benim uygulama. Burada sihir yok. Sadece JavaScript ve Google'ın Closure kütüphanesi.

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;
};

kullanım

  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 + ')');
  };
};

Hangi durumda (sonuçArr [3] == 0.0) doğru olarak değerlendirilir?
Halsafar

3

Kodunuzla ilgili herhangi bir sorun görmüyorum. Bu yüzden sadece bazı önerilerim var:

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;

Ve matris çarpımını (p*mv).inverse(); . Sadece glMatrisler nutara olarak hafızaya aktarılır. (bu belki büyük, üzgünüm)

Başka bir
engelleme Çıkarma, pikselden ışın almanın tek yolu değildir. Bu benim raycaster kodumu:

//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);

Sen (Gölgelendiricide işine yarayacağı) görünümü matrisinden cameraRight, Yukarı ve Yön alabilirsiniz opengl Görüntüle matris

Renk Toplama
Renk toplama, tıklanabilir her nesneyi başka (düz) bir renkle oluşturduğunuz ve ardından tıklatılan noktada rengin değerini okuduğunuz bir tekniktir. Opengl'de GL_SELECTION olarak çalıştı. Ancak artık kullanımdan kaldırıldı. Ancak 1 ek render geçişi olarak renk seçimini kodlamak kolaydır.

Çerçeve arabelleğini kullanın ve ekran çözünürlüğü ile aynı çözünürlükte doku oluşturma, sadece parça gölgelendiricide renk döndüren basit gölgelendirici kullanın. "Renk geçirme" işleminden sonra, clickedPoint x ve y ile doku adresleme değerini okuyun. Okuduğunuz renkle nesne bulun. Kolay ve oldukça hızlı.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.