经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring Boot » 查看文章
【Filament】立方体贴图(6张图)
来源:cnblogs  作者:little_fat_sheep  时间:2024/3/1 8:42:32  对本文有异议

1 前言

? 本文通过一个立方体贴图的例子,讲解三维纹理贴图(子网格贴图)的应用,案例中使用 6 张不同的图片给立方体贴图,图片如下。

img

? 读者如果对 Filament 不太熟悉,请回顾以下内容。

2 立方体贴图

? 本文项目结构如下,完整代码资源 → Filament立方体贴图(6张图)

img

2.1 基础类

? 为方便读者将注意力聚焦在 Filament 的输入上,轻松配置复杂的环境依赖逻辑,笔者仿照 OpenGL ES 的写法,抽出了 FLSurfaceView、BaseModel、Mesh、MaterialUtils 和 TextureUtils 类。FLSurfaceView 与 GLSurfaceView 的功能类似,承载了渲染环境配置;BaseModel 用于管理模型的网格和材质;Mesh 用于管理模型的顶点属性;MaterialUtils 和 TextureUtils 中分别提供了一些材质和纹理相关的工具。

? build.gradle

  1. ...
  2. android {
  3. ...
  4. aaptOptions { // 在应用程序打包过程中不压缩的文件
  5. noCompress 'filamat', 'ktx'
  6. }
  7. }
  8. dependencies {
  9. implementation fileTree(dir: '../libs', include: ['*.aar'])
  10. ...
  11. }

? 说明:在项目根目录下的 libs 目录中,需要放入以下 aar 文件,它们源自Filament环境搭建中编译生成的 aar。

img

? FLSurfaceView.java

  1. package com.zhyan8.multitexture.filament;
  2. import android.content.Context;
  3. import android.graphics.Point;
  4. import android.view.Choreographer;
  5. import android.view.Surface;
  6. import android.view.SurfaceView;
  7. import com.google.android.filament.Camera;
  8. import com.google.android.filament.Engine;
  9. import com.google.android.filament.EntityManager;
  10. import com.google.android.filament.Filament;
  11. import com.google.android.filament.Renderer;
  12. import com.google.android.filament.Scene;
  13. import com.google.android.filament.Skybox;
  14. import com.google.android.filament.SwapChain;
  15. import com.google.android.filament.View;
  16. import com.google.android.filament.Viewport;
  17. import com.google.android.filament.android.DisplayHelper;
  18. import com.google.android.filament.android.FilamentHelper;
  19. import com.google.android.filament.android.UiHelper;
  20. import java.util.ArrayList;
  21. /*
  22. * Filament中待渲染的SurfaceView
  23. * 功能可以类比OpenGL ES中的GLSurfaceView
  24. * 用于创建Filament的渲染环境
  25. */
  26. public class FLSurfaceView extends SurfaceView {
  27. public static int RENDERMODE_WHEN_DIRTY = 0; // 用户请求渲染才渲染一帧
  28. public static int RENDERMODE_CONTINUOUSLY = 1; // 持续渲染
  29. protected int mRenderMode = RENDERMODE_CONTINUOUSLY; // 渲染模式
  30. protected Choreographer mChoreographer; // 消息控制
  31. protected DisplayHelper mDisplayHelper; // 管理Display(可以监听分辨率或刷新率的变化)
  32. protected UiHelper mUiHelper; // 管理SurfaceView、TextureView、SurfaceHolder
  33. protected Engine mEngine; // 引擎(跟踪用户创建的资源, 管理渲染线程和硬件渲染器)
  34. protected Renderer mRenderer; // 渲染器(用于操作系统窗口, 生成绘制命令, 管理帧延时)
  35. protected Scene mScene; // 场景(管理渲染对象、灯光)
  36. protected View mView; // 存储渲染数据(View是Renderer操作的对象)
  37. protected Camera mCamera; // 相机(视角管理)
  38. protected Point mDesiredSize; // 渲染分辨率
  39. protected float[] mSkyboxColor; // 背景颜色
  40. protected SwapChain mSwapChain; // 操作系统的本地可渲染表面(native renderable surface, 通常是一个window或view)
  41. protected FrameCallback mFrameCallback = new FrameCallback(); // 帧回调
  42. protected ArrayList<RenderCallback> mRenderCallbacks; // 每一帧渲染前的回调(一般用于处理模型变换、相机变换等)
  43. static {
  44. Filament.init();
  45. }
  46. public FLSurfaceView(Context context) {
  47. super(context);
  48. mChoreographer = Choreographer.getInstance();
  49. mDisplayHelper = new DisplayHelper(context);
  50. mRenderCallbacks = new ArrayList<>();
  51. }
  52. public void init() { // 初始化
  53. setupSurfaceView();
  54. setupFilament();
  55. setupView();
  56. setupScene();
  57. }
  58. public void setRenderMode(int renderMode) { // 设置渲染模式
  59. mRenderMode = renderMode;
  60. }
  61. public void addRenderCallback(RenderCallback renderCallback) { // 添加渲染回调
  62. if (renderCallback != null) {
  63. mRenderCallbacks.add(renderCallback);
  64. }
  65. }
  66. public void requestRender() { // 请求渲染
  67. mChoreographer.postFrameCallback(mFrameCallback);
  68. }
  69. public void onResume() { // 恢复
  70. mChoreographer.postFrameCallback(mFrameCallback);
  71. }
  72. public void onPause() { // 暂停
  73. mChoreographer.removeFrameCallback(mFrameCallback);
  74. }
  75. public void onDestroy() { // 销毁Filament环境
  76. mChoreographer.removeFrameCallback(mFrameCallback);
  77. mRenderCallbacks.clear();
  78. mUiHelper.detach();
  79. mEngine.destroyRenderer(mRenderer);
  80. mEngine.destroyView(mView);
  81. mEngine.destroyScene(mScene);
  82. mEngine.destroyCameraComponent(mCamera.getEntity());
  83. EntityManager entityManager = EntityManager.get();
  84. entityManager.destroy(mCamera.getEntity());
  85. mEngine.destroy();
  86. }
  87. protected void setupScene() { // 设置Scene参数
  88. }
  89. protected void onResized(int width, int height) { // Surface尺寸变化时回调
  90. double zoom = 1;
  91. double aspect = (double) width / (double) height;
  92. mCamera.setProjection(Camera.Projection.ORTHO,
  93. -aspect * zoom, aspect * zoom, -zoom, zoom, 0, 1000);
  94. }
  95. private void setupSurfaceView() { // 设置SurfaceView
  96. mUiHelper = new UiHelper(UiHelper.ContextErrorPolicy.DONT_CHECK);
  97. mUiHelper.setRenderCallback(new SurfaceCallback());
  98. if (mDesiredSize != null) {
  99. mUiHelper.setDesiredSize(mDesiredSize.x, mDesiredSize.y);
  100. }
  101. mUiHelper.attachTo(this);
  102. }
  103. private void setupFilament() { // 设置Filament参数
  104. mEngine = Engine.create();
  105. // mEngine = (new Engine.Builder()).featureLevel(Engine.FeatureLevel.FEATURE_LEVEL_0).build();
  106. mRenderer = mEngine.createRenderer();
  107. mScene = mEngine.createScene();
  108. mView = mEngine.createView();
  109. mCamera = mEngine.createCamera(mEngine.getEntityManager().create());
  110. }
  111. private void setupView() { // 设置View参数
  112. float[] color = mSkyboxColor != null ? mSkyboxColor : new float[] {0, 0, 0, 1};
  113. Skybox skybox = (new Skybox.Builder()).color(color).build(mEngine);
  114. mScene.setSkybox(skybox);
  115. if (mEngine.getActiveFeatureLevel() == Engine.FeatureLevel.FEATURE_LEVEL_0) {
  116. mView.setPostProcessingEnabled(false); // FEATURE_LEVEL_0不支持post-processing
  117. }
  118. mView.setCamera(mCamera);
  119. mView.setScene(mScene);
  120. }
  121. /*
  122. * 帧回调
  123. */
  124. private class FrameCallback implements Choreographer.FrameCallback {
  125. @Override
  126. public void doFrame(long frameTimeNanos) { // 渲染每帧数据
  127. if (mRenderMode == RENDERMODE_CONTINUOUSLY) {
  128. mChoreographer.postFrameCallback(this); // 请求下一帧
  129. }
  130. mRenderCallbacks.forEach(callback -> callback.onCall());
  131. if (mUiHelper.isReadyToRender()) {
  132. if (mRenderer.beginFrame(mSwapChain, frameTimeNanos)) {
  133. mRenderer.render(mView);
  134. mRenderer.endFrame();
  135. }
  136. }
  137. }
  138. }
  139. /*
  140. * Surface回调
  141. */
  142. private class SurfaceCallback implements UiHelper.RendererCallback {
  143. @Override
  144. public void onNativeWindowChanged(Surface surface) { // Native窗口改变时回调
  145. if (mSwapChain != null) {
  146. mEngine.destroySwapChain(mSwapChain);
  147. }
  148. long flags = mUiHelper.getSwapChainFlags();
  149. if (mEngine.getActiveFeatureLevel() == Engine.FeatureLevel.FEATURE_LEVEL_0) {
  150. if (SwapChain.isSRGBSwapChainSupported(mEngine)) {
  151. flags = flags | SwapChain.CONFIG_SRGB_COLORSPACE;
  152. }
  153. }
  154. mSwapChain = mEngine.createSwapChain(surface, flags);
  155. mDisplayHelper.attach(mRenderer, getDisplay());
  156. }
  157. @Override
  158. public void onDetachedFromSurface() { // 解绑Surface时回调
  159. mDisplayHelper.detach();
  160. if (mSwapChain != null) {
  161. mEngine.destroySwapChain(mSwapChain);
  162. mEngine.flushAndWait();
  163. mSwapChain = null;
  164. }
  165. }
  166. @Override
  167. public void onResized(int width, int height) { // Surface尺寸变化时回调
  168. mView.setViewport(new Viewport(0, 0, width, height));
  169. FilamentHelper.synchronizePendingFrames(mEngine);
  170. FLSurfaceView.this.onResized(width, height);
  171. }
  172. }
  173. /*
  174. * 每一帧渲染前的回调
  175. * 一般用于处理模型变换、相机变换等
  176. */
  177. public static interface RenderCallback {
  178. void onCall();
  179. }
  180. }

? BaseModel.java

  1. package com.zhyan8.multitexture.filament;
  2. import android.content.Context;
  3. import com.google.android.filament.Box;
  4. import com.google.android.filament.Engine;
  5. import com.google.android.filament.EntityManager;
  6. import com.google.android.filament.Material;
  7. import com.google.android.filament.MaterialInstance;
  8. import com.google.android.filament.RenderableManager;
  9. import com.google.android.filament.RenderableManager.PrimitiveType;
  10. import com.google.android.filament.Texture;
  11. import com.google.android.filament.TransformManager;
  12. import com.zhyan8.multitexture.filament.Mesh.SubMesh;
  13. import com.zhyan8.multitexture.filament.utils.MaterialUtils;
  14. import com.zhyan8.multitexture.filament.utils.TextureUtils;
  15. /*
  16. * 模型基类
  17. * 管理模型的网格、材质、渲染id
  18. */
  19. public class BaseModel {
  20. private static String TAG = "BaseModel";
  21. protected Context mContext; // 上下文
  22. protected Engine mEngine; // Filament引擎
  23. protected TransformManager mTransformManager; // 模型变换管理器
  24. protected Mesh mMesh; // 模型网格
  25. protected SubMesh[] mSubMeshes; // 子网格
  26. protected Material[] mMaterials; // 模型材质
  27. protected MaterialInstance[] mMaterialInstances; // 模型材质实例
  28. protected Texture[] mTextures; // 纹理
  29. protected int mRenderable; // 渲染id
  30. protected int mTransformComponent; // 模型变换组件的id
  31. protected Box mBox; // 渲染区域
  32. protected FLSurfaceView.RenderCallback mRenderCallback; // 每一帧渲染前的回调(一般用于处理模型变换、相机变换等)
  33. public BaseModel(Context context, Engine engine) {
  34. mContext = context;
  35. mEngine = engine;
  36. mTransformManager = mEngine.getTransformManager();
  37. }
  38. public int getRenderable() { // 获取渲染id
  39. return mRenderable;
  40. }
  41. public FLSurfaceView.RenderCallback getRenderCallback() { // 获取渲染回调
  42. return mRenderCallback;
  43. }
  44. public void destroy() { // 销毁模型
  45. mEngine.destroyEntity(mRenderable);
  46. if (mMesh != null) {
  47. mMesh.destroy();
  48. }
  49. if (mTextures != null) {
  50. for (int i = 0; i < mTextures.length; i++) {
  51. mEngine.destroyTexture(mTextures[i]);
  52. }
  53. }
  54. if (mMaterialInstances != null) {
  55. for (int i = 0; i < mMaterialInstances.length; i++) {
  56. mEngine.destroyMaterialInstance(mMaterialInstances[i]);
  57. }
  58. }
  59. if (mMaterials != null) {
  60. for (int i = 0; i < mMaterials.length; i++) {
  61. mEngine.destroyMaterial(mMaterials[i]);
  62. }
  63. }
  64. EntityManager entityManager = EntityManager.get();
  65. entityManager.destroy(mRenderable);
  66. }
  67. protected int getRenderable(PrimitiveType primitiveType, int vertexCount) { // 获取渲染id
  68. int renderable = EntityManager.get().create();
  69. if (mSubMeshes == null) {
  70. mSubMeshes = new SubMesh[] {new SubMesh(0, 0, vertexCount - 1, vertexCount)};
  71. }
  72. RenderableManager.Builder builder = new RenderableManager.Builder(mSubMeshes.length).boundingBox(mBox);
  73. for (int i = 0; i < mSubMeshes.length; i++) {
  74. builder.geometry(i, primitiveType, mMesh.getVertexBuffer(), mMesh.getIndexBuffer(),
  75. mSubMeshes[i].offset, mSubMeshes[i].minIndex, mSubMeshes[i].maxIndex, mSubMeshes[i].indexCount)
  76. .material(i, mMaterialInstances[i]);
  77. }
  78. builder.build(mEngine, renderable);
  79. return renderable;
  80. }
  81. protected Material[] loadMaterials(String materialPath) { // 加载材质
  82. Material material = MaterialUtils.loadMaterial(mContext, mEngine, materialPath);
  83. if (material != null) {
  84. return new Material[] {material};
  85. }
  86. return null;
  87. }
  88. protected Material[] loadMaterials(String[] materialPaths) { // 加载材质
  89. Material[] materials = new Material[materialPaths.length];
  90. for (int i = 0; i < materials.length; i++) {
  91. materials[i] = MaterialUtils.loadMaterial(mContext, mEngine, materialPaths[i]);
  92. }
  93. return materials;
  94. }
  95. protected MaterialInstance[] getMaterialInstance(Material[] materials) { // 获取材质实例
  96. MaterialInstance[] materialInstances = new MaterialInstance[materials.length];
  97. for (int i = 0; i < materials.length; i++) {
  98. materialInstances[i] = materials[i].createInstance();
  99. }
  100. return materialInstances;
  101. }
  102. protected MaterialInstance[] getMaterialInstance(Material material, int count) { // 获取材质实例
  103. MaterialInstance[] materialInstances = new MaterialInstance[count];
  104. for (int i = 0; i < count; i++) {
  105. materialInstances[i] = material.createInstance();
  106. }
  107. return materialInstances;
  108. }
  109. protected Texture[] loadTextures(String texturePath) { // 加载纹理
  110. Texture texture = TextureUtils.getTexture(mContext, mEngine, texturePath);
  111. if (texture != null) {
  112. return new Texture[] {texture};
  113. }
  114. return null;
  115. }
  116. protected Texture[] loadTextures(String[] texturePaths) { // 加载纹理
  117. Texture[] textures = new Texture[texturePaths.length];
  118. for (int i = 0; i < textures.length; i++) {
  119. textures[i] = TextureUtils.getTexture(mContext, mEngine, texturePaths[i]);
  120. }
  121. return textures;
  122. }
  123. }

? Mesh.java

  1. package com.zhyan8.multitexture.filament;
  2. import com.google.android.filament.Engine;
  3. import com.google.android.filament.IndexBuffer;
  4. import com.google.android.filament.VertexBuffer;
  5. import com.google.android.filament.VertexBuffer.AttributeType;
  6. import com.google.android.filament.VertexBuffer.VertexAttribute;
  7. import java.nio.ByteBuffer;
  8. import java.nio.ByteOrder;
  9. /*
  10. * 网格
  11. * 用于管理模型的顶点属性和顶点索引
  12. */
  13. public class Mesh {
  14. private Engine mEngine; // Filament引擎
  15. private VertexBuffer mVertexBuffer; // 顶点属性缓存
  16. private IndexBuffer mIndexBuffer; // 顶点索引缓存
  17. public Mesh(Engine engine) {
  18. mEngine = engine;
  19. }
  20. public Mesh(Engine engine, float[] vertices, short[] indices) {
  21. mEngine = engine;
  22. setVertices(vertices);
  23. setIndices(indices);
  24. }
  25. public Mesh(Engine engine, VertexPosCol[] vertices, short[] indices) {
  26. mEngine = engine;
  27. setVertices(vertices);
  28. setIndices(indices);
  29. }
  30. public Mesh(Engine engine, VertexPosUV[] vertices, short[] indices) {
  31. mEngine = engine;
  32. setVertices(vertices);
  33. setIndices(indices);
  34. }
  35. public void setVertices(float[] vertices) { // 设置顶点属性
  36. mVertexBuffer = getVertexBuffer(vertices);
  37. }
  38. public void setVertices(VertexPosCol[] vertices) { // 设置顶点属性
  39. mVertexBuffer = getVertexBuffer(vertices);
  40. }
  41. public void setVertices(VertexPosUV[] vertices) { // 设置顶点属性
  42. mVertexBuffer = getVertexBuffer(vertices);
  43. }
  44. public void setIndices(short[] indices) { // 设置顶点索引
  45. mIndexBuffer = getIndexBuffer(indices);
  46. }
  47. public VertexBuffer getVertexBuffer() { // 获取顶点属性缓存
  48. return mVertexBuffer;
  49. }
  50. public IndexBuffer getIndexBuffer() { // 获取顶点索引缓存
  51. return mIndexBuffer;
  52. }
  53. public void destroy() {
  54. mEngine.destroyVertexBuffer(mVertexBuffer);
  55. mEngine.destroyIndexBuffer(mIndexBuffer);
  56. }
  57. private VertexBuffer getVertexBuffer(float[] values) { // 获取顶点属性缓存
  58. ByteBuffer vertexData = getByteBuffer(values);
  59. int vertexCount = values.length / 3;
  60. int vertexSize = Float.BYTES * 3;
  61. VertexBuffer vertexBuffer = new VertexBuffer.Builder()
  62. .bufferCount(1)
  63. .vertexCount(vertexCount)
  64. .attribute(VertexBuffer.VertexAttribute.POSITION, 0, VertexBuffer.AttributeType.FLOAT3, 0, vertexSize)
  65. .build(mEngine);
  66. vertexBuffer.setBufferAt(mEngine, 0, vertexData);
  67. return vertexBuffer;
  68. }
  69. private VertexBuffer getVertexBuffer(VertexPosCol[] values) { // 获取顶点属性缓存
  70. ByteBuffer vertexData = getByteBuffer(values);
  71. int vertexCount = values.length;
  72. int vertexSize = VertexPosCol.BYTES;
  73. VertexBuffer vertexBuffer = new VertexBuffer.Builder()
  74. .bufferCount(1)
  75. .vertexCount(vertexCount)
  76. .attribute(VertexAttribute.POSITION, 0, AttributeType.FLOAT3, 0, vertexSize)
  77. .attribute(VertexAttribute.COLOR, 0, AttributeType.UBYTE4, 3 * Float.BYTES, vertexSize)
  78. .normalized(VertexBuffer.VertexAttribute.COLOR)
  79. .build(mEngine);
  80. vertexBuffer.setBufferAt(mEngine, 0, vertexData);
  81. return vertexBuffer;
  82. }
  83. private VertexBuffer getVertexBuffer(VertexPosUV[] values) { // 获取顶点属性缓存
  84. ByteBuffer vertexData = getByteBuffer(values);
  85. int vertexCount = values.length;
  86. int vertexSize = VertexPosUV.BYTES;
  87. VertexBuffer vertexBuffer = new VertexBuffer.Builder()
  88. .bufferCount(1)
  89. .vertexCount(vertexCount)
  90. .attribute(VertexBuffer.VertexAttribute.POSITION, 0, VertexBuffer.AttributeType.FLOAT3, 0, vertexSize)
  91. .attribute(VertexBuffer.VertexAttribute.UV0, 0, VertexBuffer.AttributeType.FLOAT2, 3 * Float.BYTES, vertexSize)
  92. .build(mEngine);
  93. vertexBuffer.setBufferAt(mEngine, 0, vertexData);
  94. return vertexBuffer;
  95. }
  96. private IndexBuffer getIndexBuffer(short[] values) { // 获取顶点索引缓存
  97. ByteBuffer indexData = getByteBuffer(values);
  98. int indexCount = values.length;
  99. IndexBuffer indexBuffer = new IndexBuffer.Builder()
  100. .indexCount(indexCount)
  101. .bufferType(IndexBuffer.Builder.IndexType.USHORT)
  102. .build(mEngine);
  103. indexBuffer.setBuffer(mEngine, indexData);
  104. return indexBuffer;
  105. }
  106. private ByteBuffer getByteBuffer(float[] values) { // float数组转换为ByteBuffer
  107. ByteBuffer byteBuffer = ByteBuffer.allocate(values.length * Float.BYTES);
  108. byteBuffer.order(ByteOrder.nativeOrder());
  109. for (int i = 0; i < values.length; i++) {
  110. byteBuffer.putFloat(values[i]);
  111. }
  112. byteBuffer.flip();
  113. return byteBuffer;
  114. }
  115. private ByteBuffer getByteBuffer(short[] values) { // short数组转换为ByteBuffer
  116. ByteBuffer byteBuffer = ByteBuffer.allocate(values.length * Short.BYTES);
  117. byteBuffer.order(ByteOrder.nativeOrder());
  118. for (int i = 0; i < values.length; i++) {
  119. byteBuffer.putShort(values[i]);
  120. }
  121. byteBuffer.flip();
  122. return byteBuffer;
  123. }
  124. private ByteBuffer getByteBuffer(VertexPosCol[] values) { // VertexPosCol数组转换为ByteBuffer
  125. ByteBuffer byteBuffer = ByteBuffer.allocate(values.length * VertexPosCol.BYTES);
  126. byteBuffer.order(ByteOrder.nativeOrder());
  127. for (int i = 0; i < values.length; i++) {
  128. values[i].put(byteBuffer);
  129. }
  130. byteBuffer.flip();
  131. return byteBuffer;
  132. }
  133. private ByteBuffer getByteBuffer(VertexPosUV[] values) { // VertexPosUV数组转换为ByteBuffer
  134. ByteBuffer byteBuffer = ByteBuffer.allocate(values.length * VertexPosUV.BYTES);
  135. byteBuffer.order(ByteOrder.nativeOrder());
  136. for (int i = 0; i < values.length; i++) {
  137. values[i].put(byteBuffer);
  138. }
  139. byteBuffer.flip();
  140. return byteBuffer;
  141. }
  142. /*
  143. * 子网格
  144. */
  145. public static class SubMesh {
  146. public int offset;
  147. public int minIndex;
  148. public int maxIndex;
  149. public int indexCount;
  150. public SubMesh() {}
  151. public SubMesh(int offset, int minIndex, int maxIndex, int indexCount) {
  152. this.offset = offset;
  153. this.minIndex = minIndex;
  154. this.maxIndex = maxIndex;
  155. this.indexCount = indexCount;
  156. }
  157. }
  158. /*
  159. * 顶点数据(位置+颜色)
  160. * 包含顶点位置和颜色
  161. */
  162. public static class VertexPosCol {
  163. public static int BYTES = 16;
  164. public float x;
  165. public float y;
  166. public float z;
  167. public int color;
  168. public VertexPosCol() {}
  169. public VertexPosCol(float x, float y, float z, int color) {
  170. this.x = x;
  171. this.y = y;
  172. this.z = z;
  173. this.color = color;
  174. }
  175. public ByteBuffer put(ByteBuffer buffer) { // VertexPosCol转换为ByteBuffer
  176. buffer.putFloat(x);
  177. buffer.putFloat(y);
  178. buffer.putFloat(z);
  179. buffer.putInt(color);
  180. return buffer;
  181. }
  182. }
  183. /*
  184. * 顶点数据(位置+纹理坐标)
  185. * 包含顶点位置和纹理坐标
  186. */
  187. public static class VertexPosUV {
  188. public static int BYTES = 20;
  189. public float x;
  190. public float y;
  191. public float z;
  192. public float u;
  193. public float v;
  194. public VertexPosUV() {}
  195. public VertexPosUV(float x, float y, float z, float u, float v) {
  196. this.x = x;
  197. this.y = y;
  198. this.z = z;
  199. this.u = u;
  200. this.v = v;
  201. }
  202. public ByteBuffer put(ByteBuffer buffer) { // VertexPosUV转换为ByteBuffer
  203. buffer.putFloat(x);
  204. buffer.putFloat(y);
  205. buffer.putFloat(z);
  206. buffer.putFloat(u);
  207. buffer.putFloat(v);
  208. return buffer;
  209. }
  210. }
  211. }

? MaterialUtils.java

  1. package com.zhyan8.multitexture.filament.utils;
  2. import android.content.Context;
  3. import android.content.res.AssetFileDescriptor;
  4. import android.os.Handler;
  5. import android.os.Looper;
  6. import android.util.Log;
  7. import com.google.android.filament.Engine;
  8. import com.google.android.filament.Material;
  9. import java.io.FileInputStream;
  10. import java.io.IOException;
  11. import java.nio.Buffer;
  12. import java.nio.ByteBuffer;
  13. import java.nio.channels.Channels;
  14. import java.nio.channels.ReadableByteChannel;
  15. /*
  16. * 材质工具类
  17. */
  18. public class MaterialUtils {
  19. private static String TAG = "MaterialUtils";
  20. public static Material loadMaterial(Context context, Engine engine, String materialPath) { // 加载材质
  21. Buffer buffer = readUncompressedAsset(context, materialPath);
  22. if (buffer != null) {
  23. Material material = (new Material.Builder()).payload(buffer, buffer.remaining()).build(engine);
  24. material.compile(
  25. Material.CompilerPriorityQueue.HIGH,
  26. Material.UserVariantFilterBit.ALL,
  27. new Handler(Looper.getMainLooper()),
  28. () -> Log.i(TAG, "Material " + material.getName() + " compiled."));
  29. engine.flush();
  30. return material;
  31. }
  32. return null;
  33. }
  34. private static Buffer readUncompressedAsset(Context context, String assetPath) { // 加载资源
  35. ByteBuffer dist = null;
  36. try {
  37. AssetFileDescriptor fd = context.getAssets().openFd(assetPath);
  38. try(FileInputStream fis = fd.createInputStream()) {
  39. dist = ByteBuffer.allocate((int) fd.getLength());
  40. try (ReadableByteChannel src = Channels.newChannel(fis)) {
  41. src.read(dist);
  42. }
  43. }
  44. } catch (IOException e) {
  45. e.printStackTrace();
  46. }
  47. if (dist != null) {
  48. return dist.rewind();
  49. }
  50. return null;
  51. }
  52. }

? TextureUtils.java

  1. package com.zhyan8.multitexture.filament.utils;
  2. import android.content.Context;
  3. import android.graphics.Bitmap;
  4. import android.graphics.BitmapFactory;
  5. import android.os.Handler;
  6. import android.util.Log;
  7. import com.google.android.filament.Engine;
  8. import com.google.android.filament.Texture;
  9. import com.google.android.filament.android.TextureHelper;
  10. import java.io.IOException;
  11. import java.io.InputStream;
  12. /*
  13. * 纹理工具类
  14. */
  15. public class TextureUtils {
  16. private static String TAG = "TextureUtils";
  17. public static Texture getTexture(Context context, Engine engine, String texturePath) { // 获取Texture
  18. Bitmap bitmap = loadBitmapFromAsset(context, texturePath);
  19. if (bitmap != null) {
  20. return generateTexture(engine, bitmap);
  21. }
  22. return null;
  23. }
  24. public static Texture getTexture(Context context, Engine engine, int resourceId) { // 获取Texture
  25. Bitmap bitmap = loadBitmapFromDrawable(context, resourceId);
  26. if (bitmap != null) {
  27. return generateTexture(engine, bitmap);
  28. }
  29. return null;
  30. }
  31. private static Texture generateTexture(Engine engine, Bitmap bitmap) { // 生成Texture
  32. Texture texture = new Texture.Builder()
  33. .width(bitmap.getWidth())
  34. .height(bitmap.getHeight())
  35. .sampler(Texture.Sampler.SAMPLER_2D)
  36. .format(Texture.InternalFormat.SRGB8_A8)
  37. .levels(0xff)
  38. .build(engine);
  39. TextureHelper.setBitmap(engine, texture, 0, bitmap, new Handler(), () ->
  40. Log.i(TAG, "getTexture, Bitmap is released.")
  41. );
  42. texture.generateMipmaps(engine);
  43. return texture;
  44. }
  45. private static Bitmap loadBitmapFromAsset(Context context, String assetPath) { // 从asset中加载bitmap
  46. Bitmap bitmap = null;
  47. try (InputStream inputStream = context.getAssets().open(assetPath)) {
  48. bitmap = BitmapFactory.decodeStream(inputStream);
  49. } catch (IOException e) {
  50. e.printStackTrace();
  51. }
  52. return bitmap;
  53. }
  54. private static Bitmap loadBitmapFromDrawable(Context context, int resourceId) { // 从drawable中加载bitmap
  55. BitmapFactory.Options options = new BitmapFactory.Options();
  56. options.inPremultiplied = true;
  57. Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resourceId, options);
  58. return bitmap;
  59. }
  60. }

2.2 业务类

? MainActivity.java

  1. package com.zhyan8.multitexture;
  2. import android.os.Bundle;
  3. import androidx.appcompat.app.AppCompatActivity;
  4. import com.zhyan8.multitexture.filament.FLSurfaceView;
  5. public class MainActivity extends AppCompatActivity {
  6. private FLSurfaceView mFLSurfaceView;
  7. @Override
  8. protected void onCreate(Bundle savedInstanceState) {
  9. super.onCreate(savedInstanceState);
  10. mFLSurfaceView = new MyFLSurfaceView(this);
  11. setContentView(mFLSurfaceView);
  12. mFLSurfaceView.init();
  13. mFLSurfaceView.setRenderMode(FLSurfaceView.RENDERMODE_CONTINUOUSLY);
  14. }
  15. @Override
  16. public void onResume() {
  17. super.onResume();
  18. mFLSurfaceView.onResume();
  19. }
  20. @Override
  21. public void onPause() {
  22. super.onPause();
  23. mFLSurfaceView.onPause();
  24. }
  25. @Override
  26. public void onDestroy() {
  27. super.onDestroy();
  28. mFLSurfaceView.onDestroy();
  29. }
  30. }

? MyFLSurfaceView.java

  1. package com.zhyan8.multitexture;
  2. import android.content.Context;
  3. import com.google.android.filament.Camera;
  4. import com.zhyan8.multitexture.filament.BaseModel;
  5. import com.zhyan8.multitexture.filament.FLSurfaceView;
  6. public class MyFLSurfaceView extends FLSurfaceView {
  7. private BaseModel mMyModel;
  8. public MyFLSurfaceView(Context context) {
  9. super(context);
  10. }
  11. public void init() {
  12. mSkyboxColor = new float[] {0.35f, 0.35f, 0.35f, 1};
  13. super.init();
  14. }
  15. @Override
  16. public void onDestroy() {
  17. mMyModel.destroy();
  18. super.onDestroy();
  19. }
  20. @Override
  21. protected void setupScene() { // 设置Scene参数
  22. mMyModel = new Cube(getContext(), mEngine);
  23. mScene.addEntity(mMyModel.getRenderable());
  24. addRenderCallback(mMyModel.getRenderCallback());
  25. }
  26. @Override
  27. protected void onResized(int width, int height) {
  28. double zoom = 0.25;
  29. double aspect = (double) width / (double) height;
  30. mCamera.setProjection(Camera.Projection.PERSPECTIVE,
  31. -aspect * zoom, aspect * zoom, -zoom, zoom, 0.3, 100);
  32. float[] eye = new float[] {1, 1, 1.5f};
  33. float[] center = new float[] {0, 0, 0};
  34. float[] up = new float[] {0, 1, 0};
  35. mCamera.lookAt(eye[0], eye[1], eye[2],center[0], center[1], center[2], up[0], up[1], up[2]);
  36. }
  37. }

? Cube.java

  1. package com.zhyan8.multitexture;
  2. import android.content.Context;
  3. import android.opengl.Matrix;
  4. import com.google.android.filament.Box;
  5. import com.google.android.filament.Engine;
  6. import com.google.android.filament.RenderableManager.PrimitiveType;
  7. import com.google.android.filament.TextureSampler;
  8. import com.zhyan8.multitexture.filament.BaseModel;
  9. import com.zhyan8.multitexture.filament.Mesh;
  10. import com.zhyan8.multitexture.filament.Mesh.SubMesh;
  11. import com.zhyan8.multitexture.filament.Mesh.VertexPosUV;
  12. public class Cube extends BaseModel {
  13. private String materialPaths = "materials/square.filamat";
  14. private String[] texturePaths = new String[] {
  15. "textures/a1.jpg", "textures/a2.jpg", "textures/a3.jpg",
  16. "textures/a4.jpg", "textures/a6.jpg", "textures/a5.jpg"
  17. };
  18. private VertexPosUV[] mVertices; // 顶点坐标
  19. private short[] mIndices; // 顶点索引
  20. private float[] mModelMatrix; // 模型变换矩阵
  21. private float[] mRotateAxis; // 旋转轴
  22. private float mRotateAgree = 0; // 旋转角度
  23. public Cube(Context context, Engine engine) {
  24. super(context, engine);
  25. init();
  26. }
  27. private void init() {
  28. mBox = new Box(0.0f, 0.0f, 0.0f, 2.0f, 2.0f, 2.0f);
  29. mVertices = getVertices(0.5f);
  30. mIndices = getIndices();
  31. mMesh = new Mesh(mEngine, mVertices, mIndices);
  32. mSubMeshes = getSubMesh();
  33. mTextures = loadTextures(texturePaths);
  34. mMaterials = loadMaterials(materialPaths);
  35. mMaterialInstances = getMaterialInstance(mMaterials[0], mTextures.length);
  36. TextureSampler textureSampler = new TextureSampler();
  37. for (int i = 0; i < mMaterialInstances.length; i++) {
  38. mMaterialInstances[i].setParameter("mainTex", mTextures[i], textureSampler);
  39. }
  40. mRenderable = getRenderable(PrimitiveType.TRIANGLES, mIndices.length);
  41. mTransformComponent = mTransformManager.getInstance(mRenderable);
  42. mRenderCallback = () -> renderCallback();
  43. mModelMatrix = new float[16];
  44. mRotateAxis = new float[] { 0.5f, 1f, 1f };
  45. }
  46. private void renderCallback() {
  47. mRotateAgree = (mRotateAgree + 1) % 360;
  48. mRotateAxis[0] = mRotateAgree / 180f - 1;
  49. mRotateAxis[1] = (float) Math.sin(mRotateAgree / 180f * Math.PI * 0.7f);
  50. mRotateAxis[2] = (float) Math.cos(mRotateAgree / 180f * Math.PI * 0.5f);
  51. Matrix.setRotateM(mModelMatrix, 0, mRotateAgree, mRotateAxis[0], mRotateAxis[1], mRotateAxis[2]);
  52. mTransformManager.setTransform(mTransformComponent, mModelMatrix);
  53. }
  54. private VertexPosUV[] getVertices(float r) {
  55. VertexPosUV[] vertices = new VertexPosUV[] {
  56. // 前面
  57. new VertexPosUV(r, r, r, 0f, 1f), // 0
  58. new VertexPosUV(-r, r, r, 1f, 1f), // 1
  59. new VertexPosUV(-r, -r, r, 1f, 0f), // 2
  60. new VertexPosUV(r, -r, r, 0f, 0f), // 3
  61. // 后面
  62. new VertexPosUV(r, r, -r, 0f, 1f), // 4
  63. new VertexPosUV(-r, r, -r, 1f, 1f), // 5
  64. new VertexPosUV(-r, -r, -r, 1f, 0f), // 6
  65. new VertexPosUV(r, -r, -r, 0f, 0f), // 7
  66. // 上面
  67. new VertexPosUV(r, r, r, 0f, 1f), // 8
  68. new VertexPosUV(r, r, -r, 1f, 1f), // 9
  69. new VertexPosUV(-r, r, -r, 1f, 0f), // 10
  70. new VertexPosUV(-r, r, r, 0f, 0f), // 11
  71. // 下面
  72. new VertexPosUV(r, -r, r, 0f, 1f), // 12
  73. new VertexPosUV(r, -r, -r, 1f, 1f), // 13
  74. new VertexPosUV(-r, -r, -r, 1f, 0f), // 14
  75. new VertexPosUV(-r, -r, r, 0f, 0f), // 15
  76. // 右面
  77. new VertexPosUV(r, r, r, 0f, 1f), // 16
  78. new VertexPosUV(r, r, -r, 1f, 1f), // 17
  79. new VertexPosUV(r, -r, -r, 1f, 0f), // 18
  80. new VertexPosUV(r, -r, r, 0f, 0f), // 19
  81. // 左面
  82. new VertexPosUV(-r, r, r, 0f, 1f), // 20
  83. new VertexPosUV(-r, r, -r, 1f, 1f), // 21
  84. new VertexPosUV(-r, -r, -r, 1f, 0f), // 22
  85. new VertexPosUV(-r, -r, r, 0f, 0f) // 23
  86. };
  87. return vertices;
  88. }
  89. private short[] getIndices() {
  90. short[] indices = new short[] {
  91. 0, 1, 2, 0, 2, 3, // 前面
  92. 4, 6, 5, 4, 7, 6, // 上面
  93. 8, 9, 10, 8, 10, 11, // 右面
  94. 12, 14, 13, 12, 15, 14, // 后面
  95. 16, 18, 17, 16, 19, 18, // 下面
  96. 20, 21, 22, 20, 22, 23 // 左面
  97. };
  98. return indices;
  99. }
  100. private SubMesh[] getSubMesh() {
  101. int subMeshCount = 6;
  102. int vertexCount = 6;
  103. SubMesh[] subMeshes = new SubMesh[subMeshCount];
  104. for (int i = 0; i < subMeshCount; i++) {
  105. int offset = i * vertexCount;
  106. int minIndex = offset;
  107. int maxIndex = minIndex + vertexCount - 1;
  108. subMeshes[i] = new SubMesh(offset, minIndex, maxIndex, vertexCount);
  109. }
  110. return subMeshes;
  111. }
  112. }

? square.mat

  1. material {
  2. name : square,
  3. shadingModel : unlit, // 禁用所有lighting
  4. // 自定义变量参数
  5. parameters : [
  6. {
  7. type : sampler2d,
  8. name : mainTex
  9. }
  10. ],
  11. // 顶点着色器入参MaterialVertexInputs中需要的顶点属性
  12. requires : [
  13. uv0
  14. ]
  15. }
  16. fragment {
  17. void material(inout MaterialInputs material) {
  18. prepareMaterial(material); // 在方法返回前必须回调该函数
  19. material.baseColor = texture(materialParams_mainTex, getUV0());
  20. }
  21. }

? transform.bat

  1. @echo off
  2. setlocal enabledelayedexpansion
  3. set "srcFolder=../src/main/materials"
  4. set "distFolder=../src/main/assets/materials"
  5. for %%f in ("%srcFolder%\*.mat") do (
  6. set "matfile=%%~nf"
  7. matc -p mobile -a opengl -o "!matfile!.filamat" "%%f"
  8. move "!matfile!.filamat" "%distFolder%\!matfile!.filamat"
  9. )
  10. echo Processing complete.
  11. pause

? 说明:需要将 matc.exe 文件与 transform.bat 文件放在同一个目录下面,matc.exe 源自Filament环境搭建中编译生成的 exe 文件。双击 transform.bat 文件,会自动将 /src/main/materials/ 下面的所有 mat 文件全部转换为 filamat 文件,并移到 /src/main/assets/materials/ 目录下面。

? 运行效果如下。

img

? 说明:本文转自【Filament】立方体贴图(6张图)

原文链接:https://www.cnblogs.com/zhyan8/p/18024348

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号