1 前言
本文主要介紹使用 Filament 實(shí)現(xiàn)紋理貼圖抖单,讀者如果對(duì) Filament 不太熟悉匾南,請(qǐng)回顧以下內(nèi)容潭袱。
Filament 紋理坐標(biāo)的 x栋烤、y 軸正方向分別朝右和朝上虎敦,其 y 軸正方向朝向與 OpenGL ES 和 libGDX 相反(詳見【OpenGL ES】紋理貼圖酿愧、【libGDX】Mesh紋理貼圖)沥潭,如下。
2 紋理貼圖
本文項(xiàng)目結(jié)構(gòu)如下嬉挡,完整代碼資源 → Filament紋理貼圖钝鸽。
2.1 自定義基類
為方便讀者將注意力聚焦在 Filament 的輸入上,輕松配置復(fù)雜的環(huán)境依賴邏輯庞钢,筆者仿照 OpenGL ES 的寫法拔恰,抽出了 FLSurfaceView 和 BaseModel 類。FLSurfaceView 與 GLSurfaceView 的功能類似基括,承載了渲染環(huán)境配置颜懊;BaseModel 中提供了一些 VertexBuffer、IndexBuffer阱穗、Material饭冬、Renderable 相關(guān)的工具類,方便子類直接使用這些工具類揪阶。
build.gradle
...
android {
...
aaptOptions { // 在應(yīng)用程序打包過程中不壓縮的文件
noCompress 'filamat', 'ktx'
}
}
dependencies {
implementation fileTree(dir: '../libs', include: ['*.aar'])
...
}
說明:在項(xiàng)目根目錄下的 libs 目錄中昌抠,需要放入以下 aar 文件,它們?cè)醋?a target="_blank">Filament環(huán)境搭建中編譯生成的 aar鲁僚。
FLSurfaceView.java
package com.zhyan8.texture.filament;
import android.content.Context;
import android.graphics.Point;
import android.view.Choreographer;
import android.view.Surface;
import android.view.SurfaceView;
import com.google.android.filament.Camera;
import com.google.android.filament.Engine;
import com.google.android.filament.EntityManager;
import com.google.android.filament.Filament;
import com.google.android.filament.Renderer;
import com.google.android.filament.Scene;
import com.google.android.filament.Skybox;
import com.google.android.filament.SwapChain;
import com.google.android.filament.View;
import com.google.android.filament.Viewport;
import com.google.android.filament.android.DisplayHelper;
import com.google.android.filament.android.FilamentHelper;
import com.google.android.filament.android.UiHelper;
import java.util.ArrayList;
/*
* Filament中待渲染的SurfaceView
* 功能可以類比OpenGL ES中的GLSurfaceView
* 用于創(chuàng)建Filament的渲染環(huán)境
*/
public class FLSurfaceView extends SurfaceView {
public static int RENDERMODE_WHEN_DIRTY = 0; // 用戶請(qǐng)求渲染才渲染一幀
public static int RENDERMODE_CONTINUOUSLY = 1; // 持續(xù)渲染
protected int mRenderMode = RENDERMODE_CONTINUOUSLY; // 渲染模式
protected Choreographer mChoreographer; // 消息控制
protected DisplayHelper mDisplayHelper; // 管理Display(可以監(jiān)聽分辨率或刷新率的變化)
protected UiHelper mUiHelper; // 管理SurfaceView炊苫、TextureView、SurfaceHolder
protected Engine mEngine; // 引擎(跟蹤用戶創(chuàng)建的資源, 管理渲染線程和硬件渲染器)
protected Renderer mRenderer; // 渲染器(用于操作系統(tǒng)窗口, 生成繪制命令, 管理幀延時(shí))
protected Scene mScene; // 場(chǎng)景(管理渲染對(duì)象冰沙、燈光)
protected View mView; // 存儲(chǔ)渲染數(shù)據(jù)(View是Renderer操作的對(duì)象)
protected Camera mCamera; // 相機(jī)(視角管理)
protected Point mDesiredSize; // 渲染分辨率
protected float[] mSkyboxColor; // 背景顏色
protected SwapChain mSwapChain; // 操作系統(tǒng)的本地可渲染表面(native renderable surface, 通常是一個(gè)window或view)
protected FrameCallback mFrameCallback = new FrameCallback(); // 幀回調(diào)
protected ArrayList<RenderCallback> mRenderCallbacks; // 每一幀渲染前的回調(diào)(一般用于處理模型變換侨艾、相機(jī)變換等)
static {
Filament.init();
}
public FLSurfaceView(Context context) {
super(context);
mChoreographer = Choreographer.getInstance();
mDisplayHelper = new DisplayHelper(context);
mRenderCallbacks = new ArrayList<>();
}
public void init() { // 初始化
setupSurfaceView();
setupFilament();
setupView();
setupScene();
}
public void setRenderMode(int renderMode) { // 設(shè)置渲染模式
mRenderMode = renderMode;
}
public void addRenderCallback(RenderCallback renderCallback) { // 添加渲染回調(diào)
if (renderCallback != null) {
mRenderCallbacks.add(renderCallback);
}
}
public void requestRender() { // 請(qǐng)求渲染
mChoreographer.postFrameCallback(mFrameCallback);
}
public void onResume() { // 恢復(fù)
mChoreographer.postFrameCallback(mFrameCallback);
}
public void onPause() { // 暫停
mChoreographer.removeFrameCallback(mFrameCallback);
}
public void onDestroy() { // 銷毀Filament環(huán)境
mChoreographer.removeFrameCallback(mFrameCallback);
mRenderCallbacks.clear();
mUiHelper.detach();
mEngine.destroyRenderer(mRenderer);
mEngine.destroyView(mView);
mEngine.destroyScene(mScene);
mEngine.destroyCameraComponent(mCamera.getEntity());
EntityManager entityManager = EntityManager.get();
entityManager.destroy(mCamera.getEntity());
mEngine.destroy();
}
protected void setupScene() { // 設(shè)置Scene參數(shù)
}
protected void onResized(int width, int height) { // Surface尺寸變化時(shí)回調(diào)
double zoom = 1;
double aspect = (double) width / (double) height;
mCamera.setProjection(Camera.Projection.ORTHO,
-aspect * zoom, aspect * zoom, -zoom, zoom, 0, 1000);
}
private void setupSurfaceView() { // 設(shè)置SurfaceView
mUiHelper = new UiHelper(UiHelper.ContextErrorPolicy.DONT_CHECK);
mUiHelper.setRenderCallback(new SurfaceCallback());
if (mDesiredSize != null) {
mUiHelper.setDesiredSize(mDesiredSize.x, mDesiredSize.y);
}
mUiHelper.attachTo(this);
}
private void setupFilament() { // 設(shè)置Filament參數(shù)
mEngine = Engine.create();
// mEngine = (new Engine.Builder()).featureLevel(Engine.FeatureLevel.FEATURE_LEVEL_0).build();
mRenderer = mEngine.createRenderer();
mScene = mEngine.createScene();
mView = mEngine.createView();
mCamera = mEngine.createCamera(mEngine.getEntityManager().create());
}
private void setupView() { // 設(shè)置View參數(shù)
float[] color = mSkyboxColor != null ? mSkyboxColor : new float[] {0, 0, 0, 1};
Skybox skybox = (new Skybox.Builder()).color(color).build(mEngine);
mScene.setSkybox(skybox);
if (mEngine.getActiveFeatureLevel() == Engine.FeatureLevel.FEATURE_LEVEL_0) {
mView.setPostProcessingEnabled(false); // FEATURE_LEVEL_0不支持post-processing
}
mView.setCamera(mCamera);
mView.setScene(mScene);
}
/*
* 幀回調(diào)
*/
private class FrameCallback implements Choreographer.FrameCallback {
@Override
public void doFrame(long frameTimeNanos) { // 渲染每幀數(shù)據(jù)
if (mRenderMode == RENDERMODE_CONTINUOUSLY) {
mChoreographer.postFrameCallback(this); // 請(qǐng)求下一幀
}
mRenderCallbacks.forEach(callback -> callback.onCall());
if (mUiHelper.isReadyToRender()) {
if (mRenderer.beginFrame(mSwapChain, frameTimeNanos)) {
mRenderer.render(mView);
mRenderer.endFrame();
}
}
}
}
/*
* Surface回調(diào)
*/
private class SurfaceCallback implements UiHelper.RendererCallback {
@Override
public void onNativeWindowChanged(Surface surface) { // Native窗口改變時(shí)回調(diào)
if (mSwapChain != null) {
mEngine.destroySwapChain(mSwapChain);
}
long flags = mUiHelper.getSwapChainFlags();
if (mEngine.getActiveFeatureLevel() == Engine.FeatureLevel.FEATURE_LEVEL_0) {
if (SwapChain.isSRGBSwapChainSupported(mEngine)) {
flags = flags | SwapChain.CONFIG_SRGB_COLORSPACE;
}
}
mSwapChain = mEngine.createSwapChain(surface, flags);
mDisplayHelper.attach(mRenderer, getDisplay());
}
@Override
public void onDetachedFromSurface() { // 解綁Surface時(shí)回調(diào)
mDisplayHelper.detach();
if (mSwapChain != null) {
mEngine.destroySwapChain(mSwapChain);
mEngine.flushAndWait();
mSwapChain = null;
}
}
@Override
public void onResized(int width, int height) { // Surface尺寸變化時(shí)回調(diào)
mView.setViewport(new Viewport(0, 0, width, height));
FilamentHelper.synchronizePendingFrames(mEngine);
FLSurfaceView.this.onResized(width, height);
}
}
/*
* 每一幀渲染前的回調(diào)
* 一般用于處理模型變換、相機(jī)變換等
*/
public static interface RenderCallback {
void onCall();
}
}
BaseModel.java
package com.zhyan8.texture.filament;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import com.google.android.filament.Box;
import com.google.android.filament.Engine;
import com.google.android.filament.EntityManager;
import com.google.android.filament.IndexBuffer;
import com.google.android.filament.Material;
import com.google.android.filament.MaterialInstance;
import com.google.android.filament.RenderableManager;
import com.google.android.filament.RenderableManager.PrimitiveType;
import com.google.android.filament.Texture;
import com.google.android.filament.TransformManager;
import com.google.android.filament.VertexBuffer;
import com.google.android.filament.VertexBuffer.AttributeType;
import com.google.android.filament.VertexBuffer.VertexAttribute;
import com.google.android.filament.android.TextureHelper;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
/*
* 模型基類
* 管理模型的材質(zhì)拓挥、頂點(diǎn)屬性唠梨、頂點(diǎn)索引、渲染id
*/
public class BaseModel {
private static String TAG = "BaseModel";
protected Context mContext; // 上下文
protected Engine mEngine; // Filament引擎
protected TransformManager mTransformManager; // 模型變換管理器
protected Material mMaterial; // 模型材質(zhì)
protected MaterialInstance mMaterialInstance; // 模型材質(zhì)實(shí)例
protected VertexBuffer mVertexBuffer; // 頂點(diǎn)屬性緩存
protected IndexBuffer mIndexBuffer; // 頂點(diǎn)索引緩存
protected int mRenderable; // 渲染id
protected int mTransformComponent; // 模型變換組件的id
protected Box mBox; // 渲染區(qū)域
protected FLSurfaceView.RenderCallback mRenderCallback; // 每一幀渲染前的回調(diào)(一般用于處理模型變換侥啤、相機(jī)變換等)
public BaseModel(Context context, Engine engine) {
mContext = context;
mEngine = engine;
mTransformManager = mEngine.getTransformManager();
}
public Material getMaterial() { // 獲取材質(zhì)
return mMaterial;
}
public VertexBuffer getVertexBuffer() { // 獲取頂點(diǎn)屬性緩存
return mVertexBuffer;
}
public IndexBuffer getIndexBuffer() { // 獲取頂點(diǎn)索引緩存
return mIndexBuffer;
}
public int getRenderable() { // 獲取渲染id
return mRenderable;
}
public FLSurfaceView.RenderCallback getRenderCallback() { // 獲取渲染回調(diào)
return mRenderCallback;
}
public void destroy() { // 銷毀模型
mEngine.destroyEntity(mRenderable);
mEngine.destroyVertexBuffer(mVertexBuffer);
mEngine.destroyIndexBuffer(mIndexBuffer);
mEngine.destroyMaterialInstance(mMaterialInstance);
mEngine.destroyMaterial(mMaterial);
EntityManager entityManager = EntityManager.get();
entityManager.destroy(mRenderable);
}
protected Material loadMaterial(String materialPath) { // 加載材質(zhì)
Buffer buffer = readUncompressedAsset(mContext, materialPath);
if (buffer != null) {
Material material = (new Material.Builder()).payload(buffer, buffer.remaining()).build(mEngine);
mMaterialInstance = material.createInstance();
material.compile(
Material.CompilerPriorityQueue.HIGH,
Material.UserVariantFilterBit.ALL,
new Handler(Looper.getMainLooper()),
() -> Log.i(TAG, "Material " + material.getName() + " compiled."));
mEngine.flush();
return material;
}
return null;
}
protected VertexBuffer getVertexBuffer(float[] values) { // 獲取頂點(diǎn)屬性緩存
ByteBuffer vertexData = getByteBuffer(values);
int vertexCount = values.length / 3;
int vertexSize = Float.BYTES * 3;
VertexBuffer vertexBuffer = new VertexBuffer.Builder()
.bufferCount(1)
.vertexCount(vertexCount)
.attribute(VertexAttribute.POSITION, 0, AttributeType.FLOAT3, 0, vertexSize)
.build(mEngine);
vertexBuffer.setBufferAt(mEngine, 0, vertexData);
return vertexBuffer;
}
protected VertexBuffer getVertexBuffer(VertexPosCol[] values) { // 獲取頂點(diǎn)屬性緩存
ByteBuffer vertexData = getByteBuffer(values);
int vertexCount = values.length;
int vertexSize = VertexPosCol.BYTES;
VertexBuffer vertexBuffer = new VertexBuffer.Builder()
.bufferCount(1)
.vertexCount(vertexCount)
.attribute(VertexAttribute.POSITION, 0, AttributeType.FLOAT3, 0, vertexSize)
.attribute(VertexAttribute.COLOR, 0, AttributeType.UBYTE4, 3 * Float.BYTES, vertexSize)
.normalized(VertexAttribute.COLOR)
.build(mEngine);
vertexBuffer.setBufferAt(mEngine, 0, vertexData);
return vertexBuffer;
}
protected VertexBuffer getVertexBuffer(VertexPosUV[] values) { // 獲取頂點(diǎn)屬性緩存
ByteBuffer vertexData = getByteBuffer(values);
int vertexCount = values.length;
int vertexSize = VertexPosUV.BYTES;
VertexBuffer vertexBuffer = new VertexBuffer.Builder()
.bufferCount(1)
.vertexCount(vertexCount)
.attribute(VertexAttribute.POSITION, 0, AttributeType.FLOAT3, 0, vertexSize)
.attribute(VertexAttribute.UV0, 0, AttributeType.FLOAT2, 3 * Float.BYTES, vertexSize)
.build(mEngine);
vertexBuffer.setBufferAt(mEngine, 0, vertexData);
return vertexBuffer;
}
protected IndexBuffer getIndexBuffer(short[] values) { // 獲取頂點(diǎn)索引緩存
ByteBuffer indexData = getByteBuffer(values);
int indexCount = values.length;
IndexBuffer indexBuffer = new IndexBuffer.Builder()
.indexCount(indexCount)
.bufferType(IndexBuffer.Builder.IndexType.USHORT)
.build(mEngine);
indexBuffer.setBuffer(mEngine, indexData);
return indexBuffer;
}
protected int getRenderable(PrimitiveType primitiveType, int vertexCount) { // 獲取渲染id
int renderable = EntityManager.get().create();
new RenderableManager.Builder(1)
.boundingBox(mBox)
.geometry(0, primitiveType, mVertexBuffer, mIndexBuffer, 0, vertexCount)
.material(0, mMaterialInstance)
.build(mEngine, renderable);
return renderable;
}
protected Texture getTexture(String texturePath) { // 獲取Texture
Bitmap bitmap = loadBitmapFromAsset(mContext, texturePath);
if (bitmap != null) {
return generateTexture(bitmap);
}
return null;
}
protected Texture getTexture(int resourceId) { // 獲取Texture
Bitmap bitmap = loadBitmapFromDrawable(resourceId);
if (bitmap != null) {
return generateTexture(bitmap);
}
return null;
}
private Texture generateTexture(Bitmap bitmap) { // 生成Texture
Texture texture = new Texture.Builder()
.width(bitmap.getWidth())
.height(bitmap.getHeight())
.sampler(Texture.Sampler.SAMPLER_2D)
.format(Texture.InternalFormat.SRGB8_A8)
.levels(0xff)
.build(mEngine);
TextureHelper.setBitmap(mEngine, texture, 0, bitmap, new Handler(), () ->
Log.i(TAG, "getTexture, Bitmap is released.")
);
texture.generateMipmaps(mEngine);
return texture;
}
private Buffer readUncompressedAsset(Context context, String assetPath) { // 加載資源
ByteBuffer dist = null;
try {
AssetFileDescriptor fd = context.getAssets().openFd(assetPath);
try(FileInputStream fis = fd.createInputStream()) {
dist = ByteBuffer.allocate((int) fd.getLength());
try (ReadableByteChannel src = Channels.newChannel(fis)) {
src.read(dist);
}
}
} catch (IOException e) {
e.printStackTrace();
}
if (dist != null) {
return dist.rewind();
}
return null;
}
private Bitmap loadBitmapFromAsset(Context context, String assetPath) { // 從asset中加載bitmap
Bitmap bitmap = null;
try (InputStream inputStream = context.getAssets().open(assetPath)) {
bitmap = BitmapFactory.decodeStream(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}
private Bitmap loadBitmapFromDrawable(int resourceId) { // 從drawable中加載bitmap
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPremultiplied = true;
Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), resourceId, options);
return bitmap;
}
private ByteBuffer getByteBuffer(float[] values) { // float數(shù)組轉(zhuǎn)換為ByteBuffer
ByteBuffer byteBuffer = ByteBuffer.allocate(values.length * Float.BYTES);
byteBuffer.order(ByteOrder.nativeOrder());
for (int i = 0; i < values.length; i++) {
byteBuffer.putFloat(values[i]);
}
byteBuffer.flip();
return byteBuffer;
}
private ByteBuffer getByteBuffer(short[] values) { // short數(shù)組轉(zhuǎn)換為ByteBuffer
ByteBuffer byteBuffer = ByteBuffer.allocate(values.length * Short.BYTES);
byteBuffer.order(ByteOrder.nativeOrder());
for (int i = 0; i < values.length; i++) {
byteBuffer.putShort(values[i]);
}
byteBuffer.flip();
return byteBuffer;
}
private ByteBuffer getByteBuffer(VertexPosCol[] values) { // VertexPosCol數(shù)組轉(zhuǎn)換為ByteBuffer
ByteBuffer byteBuffer = ByteBuffer.allocate(values.length * VertexPosCol.BYTES);
byteBuffer.order(ByteOrder.nativeOrder());
for (int i = 0; i < values.length; i++) {
values[i].put(byteBuffer);
}
byteBuffer.flip();
return byteBuffer;
}
private ByteBuffer getByteBuffer(VertexPosUV[] values) { // VertexPosUV數(shù)組轉(zhuǎn)換為ByteBuffer
ByteBuffer byteBuffer = ByteBuffer.allocate(values.length * VertexPosUV.BYTES);
byteBuffer.order(ByteOrder.nativeOrder());
for (int i = 0; i < values.length; i++) {
values[i].put(byteBuffer);
}
byteBuffer.flip();
return byteBuffer;
}
/*
* 頂點(diǎn)數(shù)據(jù)(位置+顏色)
* 包含頂點(diǎn)位置和顏色
*/
public static class VertexPosCol {
public static int BYTES = 16;
public float x;
public float y;
public float z;
public int color;
public VertexPosCol() {}
public VertexPosCol(float x, float y, float z, int color) {
this.x = x;
this.y = y;
this.z = z;
this.color = color;
}
public ByteBuffer put(ByteBuffer buffer) { // VertexPosCol轉(zhuǎn)換為ByteBuffer
buffer.putFloat(x);
buffer.putFloat(y);
buffer.putFloat(z);
buffer.putInt(color);
return buffer;
}
}
/*
* 頂點(diǎn)數(shù)據(jù)(位置+紋理坐標(biāo))
* 包含頂點(diǎn)位置和紋理坐標(biāo)
*/
public static class VertexPosUV {
public static int BYTES = 20;
public float x;
public float y;
public float z;
public float u;
public float v;
public VertexPosUV() {}
public VertexPosUV(float x, float y, float z, float u, float v) {
this.x = x;
this.y = y;
this.z = z;
this.u = u;
this.v = v;
}
public ByteBuffer put(ByteBuffer buffer) { // VertexPosUV轉(zhuǎn)換為ByteBuffer
buffer.putFloat(x);
buffer.putFloat(y);
buffer.putFloat(z);
buffer.putFloat(u);
buffer.putFloat(v);
return buffer;
}
}
}
2.2 紋理貼圖實(shí)現(xiàn)
MainActivity.java
package com.zhyan8.texture;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import com.zhyan8.texture.filament.FLSurfaceView;
public class MainActivity extends AppCompatActivity {
private FLSurfaceView mFLSurfaceView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mFLSurfaceView = new MyFLSurfaceView(this);
setContentView(mFLSurfaceView);
mFLSurfaceView.init();
mFLSurfaceView.setRenderMode(FLSurfaceView.RENDERMODE_CONTINUOUSLY);
}
@Override
public void onResume() {
super.onResume();
mFLSurfaceView.onResume();
}
@Override
public void onPause() {
super.onPause();
mFLSurfaceView.onPause();
}
@Override
public void onDestroy() {
super.onDestroy();
mFLSurfaceView.onDestroy();
}
}
MyFLSurfaceView.java
package com.zhyan8.texture;
import android.content.Context;
import com.google.android.filament.Camera;
import com.zhyan8.texture.filament.BaseModel;
import com.zhyan8.texture.filament.FLSurfaceView;
public class MyFLSurfaceView extends FLSurfaceView {
private BaseModel mMyModel;
public MyFLSurfaceView(Context context) {
super(context);
}
public void init() {
mSkyboxColor = new float[] {0.965f, 0.941f, 0.887f, 1};
super.init();
}
@Override
public void onDestroy() {
mMyModel.destroy();
super.onDestroy();
}
@Override
protected void setupScene() { // 設(shè)置Scene參數(shù)
mMyModel = new Square(getContext(), mEngine);
mScene.addEntity(mMyModel.getRenderable());
}
@Override
protected void onResized(int width, int height) {
mCamera.setProjection(Camera.Projection.ORTHO, -1, 1, -1, 1, 0, 10);
}
}
Square.java
package com.zhyan8.texture;
import android.content.Context;
import com.google.android.filament.Box;
import com.google.android.filament.Engine;
import com.google.android.filament.RenderableManager.PrimitiveType;
import com.google.android.filament.TextureSampler;
import com.zhyan8.texture.filament.BaseModel;
public class Square extends BaseModel {
private String materialPath = "materials/square.filamat";
private String texturePath = "textures/1.jpg";
private VertexPosUV[] mVertices = new VertexPosUV[] {
new VertexPosUV(-1f, -1f, 0f, 0f, 0f), // 左下
new VertexPosUV(1f, -1f, 0f, 1f, 0f), // 右下
new VertexPosUV(1f, 1f, 0f, 1f, 1f), // 右上
new VertexPosUV(-1f, 1f, 0f, 0f, 1f) // 左上
};
private short[] mIndex = new short[] {0, 1, 2, 0, 2, 3};
public Square(Context context, Engine engine) {
super(context, engine);
init();
}
private void init() {
mBox = new Box(0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.01f);
mMaterial = loadMaterial(materialPath);
mMaterialInstance.setParameter("mainTex", getTexture(texturePath), new TextureSampler());
mVertexBuffer = getVertexBuffer(mVertices);
mIndexBuffer = getIndexBuffer(mIndex);
mRenderable = getRenderable(PrimitiveType.TRIANGLES, mIndex.length);
}
}
square.mat
material {
name : square,
shadingModel : unlit, // 禁用所有l(wèi)ighting
// 自定義變量參數(shù)
parameters : [
{
type : sampler2d,
name : mainTex
}
],
// 頂點(diǎn)著色器入?yún)aterialVertexInputs中需要的頂點(diǎn)屬性
requires : [
uv0
]
}
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material); // 在方法返回前必須回調(diào)該函數(shù)
material.baseColor = texture(materialParams_mainTex, getUV0());
}
}
transform.bat
@echo off
setlocal enabledelayedexpansion
set "srcFolder=../src/main/materials"
set "distFolder=../src/main/assets/materials"
for %%f in ("%srcFolder%\*.mat") do (
set "matfile=%%~nf"
matc -p mobile -a opengl -o "!matfile!.filamat" "%%f"
move "!matfile!.filamat" "%distFolder%\!matfile!.filamat"
)
echo Processing complete.
pause
說明:需要將 matc.exe 文件與 transform.bat 文件放在同一個(gè)目錄下面当叭,matc.exe 源自Filament環(huán)境搭建中編譯生成的 exe 文件。雙擊 transform.bat 文件盖灸,會(huì)自動(dòng)將 /src/main/materials/ 下面的所有 mat 文件全部轉(zhuǎn)換為 filamat 文件蚁鳖,并移到 /src/main/assets/materials/ 目錄下面。
運(yùn)行效果如下赁炎。
聲明:本文轉(zhuǎn)自【Filament】紋理貼圖醉箕。