如何在 Vuforia AR 库上渲染 LibGDX modelInstance?
How can I render a LibGDX modelInstance on Vuforia AR library?
我知道社区中的大多数问题至少应该用一些代码来解决。但我完全迷失在这里,我什至不知道从哪里开始。我想要做的是使用 Vuforia AR 库来渲染 LibGDX 3D 模型实例。但是,我不知道如何让 Vuforia 渲染 modelInstances 或使用 libGDX 相机作为相机。
我进行了外部研究,但未能找到有用的信息。有没有人可以帮助我开始使用它?
请注意,我并不特别精通 Vuforia AR,但这个问题有一段时间没有得到解答,所以我会试一试。
LibGDX中的Camera本质上只是两个4x4矩阵的包装器,视图矩阵Camera#view
和投影矩阵Camer#projection
(还有另一个矩阵,模型矩阵,用于世界 space 转换,但是我相信 [但不是 100% 确定] 在 LibGDX 中,这个矩阵已经合并到视图矩阵中 [所以 Camera#view
实际上是模型-视图矩阵]).
无论如何,从这里开始,除非有一个我不知道的更简单的解决方案,否则您应该能够使用这些底层矩阵来处理 Vuforia 和 LibGDX api 之间的投影。
(推荐进一步阅读:3D图形中的模型、视图、投影矩阵)
接下来是使用 Vuforia 渲染 LibGDX 3D 模型实例。这个问题的一般解决方案是将 ModelInstance 转换为 Vuforia 可以识别的东西。这将通过获取 LibGDX 模型表示的 mesh/vertex 数据并将其直接输入 Vuforia 来完成。
IMO,解决这个问题的最好方法是使用模型数据的核心表示,它可以很容易地同时提供给 Vuforia 和 LibGDX(例如,某种文件格式,既可以识别,或者作为原始的 FloatBuffers,应该很容易包装并提供给 API)。作为参考,LibGDX 将模型作为顶点信息存储在 FloatBuffers 集合中,可通过 Model#meshes
或 ModelInstance#model#meshes
.
访问
好的。所以我最终设法将这两个库结合起来。我不确定我所做的是否是最有效的工作方式,但它对我有用。
首先,我以 Vuforia 的示例应用程序为基础。具体使用 FrameMarkers 示例。
我打开了一个空的 LibGDX 项目,导入了 Vuforia jar 并复制了 SampleApplicationControl、SampleApplicationException、SampleApplicationGLView、SampleApplicationSession、FrameMarkerRenderer 和 FrameMarker。
接下来,我在 LibGDX 的 AndroidLauncher class 上创建了一些属性并初始化了所有 Vuforia Stuff:
public class AndroidLauncher extends AndroidApplication implements SampleApplicationControl{
private static final String LOGTAG = "FrameMarkers";
// Our OpenGL view:
public SampleApplicationGLView mGlView;
public SampleApplicationSession vuforiaAppSession;
// Our renderer:
public FrameMarkerRenderer mRenderer;
MyGDX gdxRender;
// The textures we will use for rendering:
public Vector<Texture> mTextures;
public RelativeLayout mUILayout;
public Marker dataSet[];
public GestureDetector mGestureDetector;
public LoadingDialogHandler loadingDialogHandler = new LoadingDialogHandler(
this);
// Alert Dialog used to display SDK errors
private AlertDialog mErrorDialog;
boolean mIsDroidDevice = false;
@Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
vuforiaAppSession = new SampleApplicationSession(this);
vuforiaAppSession.setmActivity(this);
AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
// Load any sample specific textures:
mTextures = new Vector<Texture>();
loadTextures();
startLoadingAnimation();
vuforiaAppSession.initAR(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
gdxRender = new MyGDX (vuforiaAppSession);
gdxRender.setTextures(mTextures);
initialize(gdxRender, config);
mGestureDetector = new GestureDetector(this, new GestureListener());
mIsDroidDevice = android.os.Build.MODEL.toLowerCase().startsWith(
"droid");
}
我需要设置 activity,所以我在 SampleApplicationSession 上创建了 setmActivity()
。
之后我实现了 Libgdx ApplicationAdapter class 并将 vuforiaAppSession 作为属性传递以访问我初始化的所有内容。
public class MyGDX extends ApplicationAdapter {
ModelInstance modelInstanceHouse;
private AnimationController controller;
Matrix4 lastTransformCube;
// Constants:
static private float kLetterScale = 25.0f;
static private float kLetterTranslate = 25.0f;
// OpenGL ES 2.0 specific:
private static final String LOGTAG = "FrameMarkerRenderer";
private int shaderProgramID = 0;
private Vector<com.mygdx.robot.Texture> mTextures;
//SampleApplicationSession vuforiaAppSession;
PerspectiveCamera cam;
ModelBuilder modelBuilder;
Model model;
ModelInstance instance;
ModelBatch modelBatch;
static boolean render;
public SampleApplicationSession vuforiaAppSession;
public MyGDX ( SampleApplicationSession vuforiaAppSession){
super();
this.vuforiaAppSession = vuforiaAppSession;
}
要牢记的最后一件重要事情是 render()
方法。我基于 FrameMarkerRenderer 的渲染方法。它有一个布尔值,在相机启动时被激活。所以我简单地更改了 vuforia AR 初始化和 render()
方法中的变量。我不得不打开相机和单位矩阵并将模型乘以 modelViewMatrix。
@Override
public void render() {
if (render) {
// Clear color and depth buffer
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
// Get the state from Vuforia and mark the beginning of a rendering
// section
State state = Renderer.getInstance().begin();
// Explicitly render the Video Background
Renderer.getInstance().drawVideoBackground();
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
// We must detect if background reflection is active and adjust the
// culling direction.
// If the reflection is active, this means the post matrix has been
// reflected as well,
// therefore standard counter clockwise face culling will result in
// "inside out" models.
GLES20.glEnable(GLES20.GL_CULL_FACE);
GLES20.glCullFace(GLES20.GL_BACK);
cam.update();
modelBatch.begin(cam);
if (Renderer.getInstance().getVideoBackgroundConfig().getReflection() == VIDEO_BACKGROUND_REFLECTION.VIDEO_BACKGROUND_REFLECTION_ON)
GLES20.glFrontFace(GLES20.GL_CW); // Front camera
else
GLES20.glFrontFace(GLES20.GL_CCW); // Back camera
// Set the viewport
int[] viewport = vuforiaAppSession.getViewport();
GLES20.glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
// Did we find any trackables this frame?
for (int tIdx = 0; tIdx < state.getNumTrackableResults(); tIdx++)
{
// Get the trackable:
TrackableResult trackableResult = state.getTrackableResult(tIdx);
float[] modelViewMatrix = Tool.convertPose2GLMatrix(
trackableResult.getPose()).getData();
// Choose the texture based on the target name:
int textureIndex = 0;
// Check the type of the trackable:
assert (trackableResult.getType() == MarkerTracker.getClassType());
MarkerResult markerResult = (MarkerResult) (trackableResult);
Marker marker = (Marker) markerResult.getTrackable();
textureIndex = marker.getMarkerId();
float[] modelViewProjection = new float[16];
Matrix.translateM(modelViewMatrix, 0, -kLetterTranslate, -kLetterTranslate, 0.f);
Matrix.scaleM(modelViewMatrix, 0, kLetterScale, kLetterScale, kLetterScale);
Matrix.multiplyMM(modelViewProjection, 0, vuforiaAppSession.getProjectionMatrix().getData(), 0, modelViewMatrix, 0);
SampleUtils.checkGLError("FrameMarkers render frame");
cam.view.idt();
cam.projection.idt();
cam.combined.idt();
Matrix4 temp3 = new Matrix4(modelViewProjection);
modelInstanceHouse.transform.set(temp3);
modelInstanceHouse.transform.scale(0.05f, 0.05f, 0.05f);
controller.update(Gdx.graphics.getDeltaTime());
modelBatch.render(modelInstanceHouse);
}
GLES20.glDisable(GLES20.GL_DEPTH_TEST);
modelBatch.end();
}
代码很多,但我希望它能帮助那些试图开始集成这两个库的人。我不认为这是有效的,但这是我唯一的解决方案。
Pablo 的回答非常好,但是,如果您对有点不同的方法(从 LibGDX 调用 Vuforia,而不是其他方式)感兴趣,以及更完整的示例,这里是 github repo 使用简单的 3D 模型渲染器。
我知道社区中的大多数问题至少应该用一些代码来解决。但我完全迷失在这里,我什至不知道从哪里开始。我想要做的是使用 Vuforia AR 库来渲染 LibGDX 3D 模型实例。但是,我不知道如何让 Vuforia 渲染 modelInstances 或使用 libGDX 相机作为相机。
我进行了外部研究,但未能找到有用的信息。有没有人可以帮助我开始使用它?
请注意,我并不特别精通 Vuforia AR,但这个问题有一段时间没有得到解答,所以我会试一试。
LibGDX中的Camera本质上只是两个4x4矩阵的包装器,视图矩阵Camera#view
和投影矩阵Camer#projection
(还有另一个矩阵,模型矩阵,用于世界 space 转换,但是我相信 [但不是 100% 确定] 在 LibGDX 中,这个矩阵已经合并到视图矩阵中 [所以 Camera#view
实际上是模型-视图矩阵]).
无论如何,从这里开始,除非有一个我不知道的更简单的解决方案,否则您应该能够使用这些底层矩阵来处理 Vuforia 和 LibGDX api 之间的投影。
(推荐进一步阅读:3D图形中的模型、视图、投影矩阵)
接下来是使用 Vuforia 渲染 LibGDX 3D 模型实例。这个问题的一般解决方案是将 ModelInstance 转换为 Vuforia 可以识别的东西。这将通过获取 LibGDX 模型表示的 mesh/vertex 数据并将其直接输入 Vuforia 来完成。
IMO,解决这个问题的最好方法是使用模型数据的核心表示,它可以很容易地同时提供给 Vuforia 和 LibGDX(例如,某种文件格式,既可以识别,或者作为原始的 FloatBuffers,应该很容易包装并提供给 API)。作为参考,LibGDX 将模型作为顶点信息存储在 FloatBuffers 集合中,可通过 Model#meshes
或 ModelInstance#model#meshes
.
好的。所以我最终设法将这两个库结合起来。我不确定我所做的是否是最有效的工作方式,但它对我有用。
首先,我以 Vuforia 的示例应用程序为基础。具体使用 FrameMarkers 示例。
我打开了一个空的 LibGDX 项目,导入了 Vuforia jar 并复制了 SampleApplicationControl、SampleApplicationException、SampleApplicationGLView、SampleApplicationSession、FrameMarkerRenderer 和 FrameMarker。
接下来,我在 LibGDX 的 AndroidLauncher class 上创建了一些属性并初始化了所有 Vuforia Stuff:
public class AndroidLauncher extends AndroidApplication implements SampleApplicationControl{
private static final String LOGTAG = "FrameMarkers";
// Our OpenGL view:
public SampleApplicationGLView mGlView;
public SampleApplicationSession vuforiaAppSession;
// Our renderer:
public FrameMarkerRenderer mRenderer;
MyGDX gdxRender;
// The textures we will use for rendering:
public Vector<Texture> mTextures;
public RelativeLayout mUILayout;
public Marker dataSet[];
public GestureDetector mGestureDetector;
public LoadingDialogHandler loadingDialogHandler = new LoadingDialogHandler(
this);
// Alert Dialog used to display SDK errors
private AlertDialog mErrorDialog;
boolean mIsDroidDevice = false;
@Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
vuforiaAppSession = new SampleApplicationSession(this);
vuforiaAppSession.setmActivity(this);
AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
// Load any sample specific textures:
mTextures = new Vector<Texture>();
loadTextures();
startLoadingAnimation();
vuforiaAppSession.initAR(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
gdxRender = new MyGDX (vuforiaAppSession);
gdxRender.setTextures(mTextures);
initialize(gdxRender, config);
mGestureDetector = new GestureDetector(this, new GestureListener());
mIsDroidDevice = android.os.Build.MODEL.toLowerCase().startsWith(
"droid");
}
我需要设置 activity,所以我在 SampleApplicationSession 上创建了 setmActivity()
。
之后我实现了 Libgdx ApplicationAdapter class 并将 vuforiaAppSession 作为属性传递以访问我初始化的所有内容。
public class MyGDX extends ApplicationAdapter {
ModelInstance modelInstanceHouse;
private AnimationController controller;
Matrix4 lastTransformCube;
// Constants:
static private float kLetterScale = 25.0f;
static private float kLetterTranslate = 25.0f;
// OpenGL ES 2.0 specific:
private static final String LOGTAG = "FrameMarkerRenderer";
private int shaderProgramID = 0;
private Vector<com.mygdx.robot.Texture> mTextures;
//SampleApplicationSession vuforiaAppSession;
PerspectiveCamera cam;
ModelBuilder modelBuilder;
Model model;
ModelInstance instance;
ModelBatch modelBatch;
static boolean render;
public SampleApplicationSession vuforiaAppSession;
public MyGDX ( SampleApplicationSession vuforiaAppSession){
super();
this.vuforiaAppSession = vuforiaAppSession;
}
要牢记的最后一件重要事情是 render()
方法。我基于 FrameMarkerRenderer 的渲染方法。它有一个布尔值,在相机启动时被激活。所以我简单地更改了 vuforia AR 初始化和 render()
方法中的变量。我不得不打开相机和单位矩阵并将模型乘以 modelViewMatrix。
@Override
public void render() {
if (render) {
// Clear color and depth buffer
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
// Get the state from Vuforia and mark the beginning of a rendering
// section
State state = Renderer.getInstance().begin();
// Explicitly render the Video Background
Renderer.getInstance().drawVideoBackground();
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
// We must detect if background reflection is active and adjust the
// culling direction.
// If the reflection is active, this means the post matrix has been
// reflected as well,
// therefore standard counter clockwise face culling will result in
// "inside out" models.
GLES20.glEnable(GLES20.GL_CULL_FACE);
GLES20.glCullFace(GLES20.GL_BACK);
cam.update();
modelBatch.begin(cam);
if (Renderer.getInstance().getVideoBackgroundConfig().getReflection() == VIDEO_BACKGROUND_REFLECTION.VIDEO_BACKGROUND_REFLECTION_ON)
GLES20.glFrontFace(GLES20.GL_CW); // Front camera
else
GLES20.glFrontFace(GLES20.GL_CCW); // Back camera
// Set the viewport
int[] viewport = vuforiaAppSession.getViewport();
GLES20.glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
// Did we find any trackables this frame?
for (int tIdx = 0; tIdx < state.getNumTrackableResults(); tIdx++)
{
// Get the trackable:
TrackableResult trackableResult = state.getTrackableResult(tIdx);
float[] modelViewMatrix = Tool.convertPose2GLMatrix(
trackableResult.getPose()).getData();
// Choose the texture based on the target name:
int textureIndex = 0;
// Check the type of the trackable:
assert (trackableResult.getType() == MarkerTracker.getClassType());
MarkerResult markerResult = (MarkerResult) (trackableResult);
Marker marker = (Marker) markerResult.getTrackable();
textureIndex = marker.getMarkerId();
float[] modelViewProjection = new float[16];
Matrix.translateM(modelViewMatrix, 0, -kLetterTranslate, -kLetterTranslate, 0.f);
Matrix.scaleM(modelViewMatrix, 0, kLetterScale, kLetterScale, kLetterScale);
Matrix.multiplyMM(modelViewProjection, 0, vuforiaAppSession.getProjectionMatrix().getData(), 0, modelViewMatrix, 0);
SampleUtils.checkGLError("FrameMarkers render frame");
cam.view.idt();
cam.projection.idt();
cam.combined.idt();
Matrix4 temp3 = new Matrix4(modelViewProjection);
modelInstanceHouse.transform.set(temp3);
modelInstanceHouse.transform.scale(0.05f, 0.05f, 0.05f);
controller.update(Gdx.graphics.getDeltaTime());
modelBatch.render(modelInstanceHouse);
}
GLES20.glDisable(GLES20.GL_DEPTH_TEST);
modelBatch.end();
}
代码很多,但我希望它能帮助那些试图开始集成这两个库的人。我不认为这是有效的,但这是我唯一的解决方案。
Pablo 的回答非常好,但是,如果您对有点不同的方法(从 LibGDX 调用 Vuforia,而不是其他方式)感兴趣,以及更完整的示例,这里是 github repo 使用简单的 3D 模型渲染器。