我一直在尝试让我的游戏能够在OpenGl中处理用户交互。我有一个圆圈,我希望能够检测到用户的印刷机。我知道我需要使用 Ray Picking ,我已经按照本书OpenGl 2 for Android进行了操作,并按照教程在3D空间中创建了一个空气曲棍球游戏并检测到用户点击事件。
现在我希望用我的知识开始简单的2D游戏,但是按照教程并应用我目前的知识并没有帮助我实现能够检测用户点击我的对象。
我假设我做错了,并且在3D和2D之间存在细微差别。
我有一个几何类如下:
public class Geometry {
public static class Point {
public final float x, y, z;
public Point(float x, float y, float z) {
this.x = x;
this.y = y;
this.z = z;
}
public Point translateY(float distance) {
return new Point(x, y + distance, z);
}
public Point translate(Vector vector) {
return new Point(
x + vector.x,
y + vector.y,
z + vector.z);
}
}
public static class Vector {
public final float x, y, z;
public Vector(float x, float y, float z) {
this.x = x;
this.y = y;
this.z = z;
}
public float length() {
return (float)Math.sqrt(
x * x
+ y * y
+ z * z);
}
public Vector crossProduct(Vector other) {
return new Vector(
(y * other.z) - (z * other.y),
(z * other.x) - (x * other.z),
(x * other.y) - (y * other.x));
}
public float dotProduct(Vector other) {
return x * other.x
+ y * other.y
+ z * other.z;
}
public Vector scale(float f) {
return new Vector(
x * f,
y * f,
z * f);
}
}
public static class Ray {
public final Point point;
public final Vector vector;
public Ray(Point point, Vector vector) {
this.point = point;
this.vector = vector;
}
}
public static class Circle {
public final Point center;
public final float radius;
public Circle(Point center, float radius) {
this.center = center;
this.radius = radius;
}
public Circle scale(float scale) {
return new Circle(center, radius * scale);
}
}
public static class Cylinder {
public final Point center;
public final float radius;
public final float height;
public Cylinder(Point center, float radius, float height) {
this.center = center;
this.radius = radius;
this.height = height;
}
}
public static class Sphere {
public final Point center;
public final float radius;
public Sphere(Point center, float radius) {
this.center = center;
this.radius = radius;
}
}
public static class Plane {
public final Point point;
public final Vector normal;
public Plane(Point point, Vector normal) {
this.point = point;
this.normal = normal;
}
}
public static Vector vectorBetween(Point from, Point to) {
return new Vector(
to.x - from.x,
to.y - from.y,
to.z - from.z);
}
public static boolean intersects(Sphere sphere, Ray ray) {
return distanceBetween(sphere.center, ray) < sphere.radius;
}
public static float distanceBetween(Point point, Ray ray) {
Vector p1ToPoint = vectorBetween(ray.point, point);
Vector p2ToPoint = vectorBetween(ray.point.translate(ray.vector), point);
float areaOfTriangleTimesTwo = p1ToPoint.crossProduct(p2ToPoint).length();
float lengthOfBase = ray.vector.length();
float distanceFromPointToRay = areaOfTriangleTimesTwo / lengthOfBase;
return distanceFromPointToRay;
}
public static Point intersectionPoint(Ray ray, Plane plane) {
Vector rayToPlaneVector = vectorBetween(ray.point, plane.point);
float scaleFactor = rayToPlaneVector.dotProduct(plane.normal)
/ ray.vector.dotProduct(plane.normal);
Point intersectionPoint = ray.point.translate(ray.vector.scale(scaleFactor));
return intersectionPoint;
}
}
我有一个表面视图onTouchEvent重写如下:
if(event.getAction() == MotionEvent.ACTION_DOWN){
if (event != null) {
final float normalizedX =
(event.getX() / (float) getWidth()) * 2 - 1;
final float normalizedY =
-((event.getY() / (float) getHeight()) * 2 - 1);
if (event.getAction() == MotionEvent.ACTION_DOWN) {
queueEvent(new Runnable() {
@Override
public void run() {
mRenderer.handleTouchPress(
normalizedX, normalizedY);
}
});
}
return true;
} else {
return false;
}
}
最后我有一个渲染器如下:
public class ImpulseRushRenderer implements Renderer {
private Geometry.Point circlePosition;
private boolean circlePressed= false;
private final Context context;
public Circle circle;
private float mMatrix[] = new float[16];
private float[] mTempMatrix = new float[16];
private final float[] mProjectionMatrix = new float[16];
private final float[] mViewMatrix = new float[16];
private final float[] mRotationMatrix = new float[16];
private final float[] mMVPMatrix = new float[16];
private final float[] mProjMatrix = new float[16];
private final float[] mVMatrix = new float[16];
private final float[] mModelMatrix = new float[16];
private final float[] tempMatrix = new float[16];
private final float[] invertedViewProjectionMatrix = new float[16];
public ImpulseRushRenderer(Context context) {
this.context = context;
}
LayoutInflater mInflater;
@Override
public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
glClearColor(0.1725490196078431f, 0.2431372549019608f, 0.3137254901960784f, 1.0f);
circle= new Circle();
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public void onSurfaceChanged(GL10 glUnused, int width, int height) {
glViewport(0, 0, width, height);
}
@Override
public void onDrawFrame(GL10 glUnused) {
glClear(GL_COLOR_BUFFER_BIT);
Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);
mTempMatrix = mModelMatrix.clone();
Matrix.multiplyMM(mModelMatrix, 0, mTempMatrix, 0, mRotationMatrix, 0);
mTempMatrix = mMVPMatrix.clone();
Matrix.multiplyMM(mMVPMatrix, 0, mTempMatrix, 0, mModelMatrix, 0);
Matrix.orthoM(mMatrix, 0, -1, 1, -1, 1, -1, 1);
Matrix.orthoM(mMatrix, 0, -1, 1, -1, 1, -1, 1);
Matrix.setIdentityM(mModelMatrix, 0);
circle.draw(mModelMatrix);
circlePosition= new Geometry.Point(0f, 100 / 2f, 0.4f);
}
private Ray convertNormalized2DPointToRay(
float normalizedX, float normalizedY) {
final float[] nearPointNdc = {normalizedX, normalizedY, -1, 1};
final float[] farPointNdc = {normalizedX, normalizedY, 1, 1};
final float[] nearPointWorld = new float[4];
final float[] farPointWorld = new float[4];
multiplyMV(
nearPointWorld, 0, invertedViewProjectionMatrix, 0, nearPointNdc, 0);
multiplyMV(
farPointWorld, 0, invertedViewProjectionMatrix, 0, farPointNdc, 0);
divideByW(nearPointWorld);
divideByW(farPointWorld);
Geometry.Point nearPointRay =
new Geometry.Point(nearPointWorld[0], nearPointWorld[1], nearPointWorld[2]);
Geometry.Point farPointRay =
new Geometry.Point(farPointWorld[0], farPointWorld[1], farPointWorld[2]);
return new Ray(nearPointRay,
Geometry.vectorBetween(nearPointRay, farPointRay));
}
public static int loadShader(int type, String shaderCode) {
int shader = GLES20.glCreateShader(type);
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
public void handleTouchPress(float normalizedX, float normalizedY) {
Geometry.Ray ray = convertNormalized2DPointToRay(normalizedX, normalizedY);
Geometry.Sphere circleBoundingSphere = new Geometry.Sphere(new Geometry.Point(
circlePosition.x,
circlePosition.y,
circlePosition.z),
100 / 2f);
circlePressed= Geometry.intersects(circleBoundingSphere , ray);
if (circlePressed) {
Log.i("circlePressed", "circle was pressed");
}
}
private void divideByW(float[] vector) {
vector[0] /= vector[3];
vector[1] /= vector[3];
vector[2] /= vector[3];
}
}
根据我目前的知识,从该书中的教程中,我应该正确地将其链接起来。
答案 0 :(得分:2)
在“私人Ray convertNormalized2DPointToRay”中, 你可以尝试将1放到nearPointWorld [3]和farPointWorld [3](1到W),因为乘法运算符将无法正常工作。
编辑
方法1 :距离函数
假设您已经拥有触摸位置,物体的中心位于同一空间及其半径内。
{{1}}
注意:您可以通过模型视图乘以中心来移动相机。 这种方法很好,因为它是数学的,不需要GPU使用。 您可以拥有一些3D距离功能以及如何使用sql injection
Methode 2 :颜色挑选(算法)
当场景中有大量对象或对象很复杂时,此方法很棒。
您的方法也很棒,但结果只是您点击的位置(或方向相机 - >您点击的位置)。然后,它更适合3D使用。