Ich musste das kürzlich selbst für eine WebGL-Anwendung lösen. Ich habe den vollständigen Quellcode angehängt, aber falls er nicht von Anfang an funktioniert, hier einige Tipps zum Debuggen:
- Debuggen Sie Ihre Unproject-Methode nicht in Ihrem Spiel. Wenn möglich, versuchen Sie, Komponententests zu schreiben, um das Erkennen von Fehlern zu vereinfachen.
- Stellen Sie sicher, dass die Ausgabestrahlen sowohl für die nahen als auch für die fernen Beschneidungsebenen gedruckt werden.
- Denken Sie daran, dass Matrixmathematik NICHT kommutativ ist. A x C! = C x A. Überprüfen Sie Ihre Mathematik.
Um auf einige der obigen Kommentare zu antworten, möchten Sie fast nie die Auswahl-APIs von OpenGL verwenden. Auf diese Weise können Sie vorhandene Elemente auswählen, z. B. wenn Sie ein Menü erstellt haben. In den meisten realen Szenarien wie der Bearbeitung von 3D-Modellen ist dies jedoch nicht möglich. Wo müssen Sie Geometrie als Ergebnis des Klickens hinzufügen.
Hier ist meine Implementierung. Hier ist nichts Magisches los. Nur JavaScript und Googles Closure-Bibliothek.
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;
};
Verwendung
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 + ')');
};
};