華為應用市場在2022年HDC大會期間發布了一款3D水流主題,基于華為HMS Core Scene Kit服務能力,展現立體靈動的水流島嶼,可跟隨用戶指尖實作實時流體波動效果,既趣味又解壓,

讓變幻莫測的物質來實作我們在影視和游戲等多種應用場景中的奇思妙想,從早期步驟繁重的特效制作演變到如今,已經有了更為輕量易用的解題范式,只需花費10分鐘便可打造一個逼真的3D流體效果,

什么是Scene Kit流體模擬?
Scene Kit即圖形引擎服務,提供輕量級3D圖形渲染引擎,可以為游戲、AR & VR等移動端應用提供易于使用的渲染介面,助力打造精致酷炫的視覺體驗,
Scene Kit的3D流體技術,目前支持移動端水、油、巖漿等不同型別的物理真實流體模擬,服務中包含原子化介面,場景話介面,離線簡模插件,實時光追插件等,
性能優越:基于三維圖形渲染框架和演算法,提供高性能低功耗的三維立體場景構建能力,
輕量易用:提供場景化移動應用介面,簡化三維圖形應用開發,易于為二維圖形應用拓展構建三維立體場景,
效果逼真:基于物理的渲染能力,提供高畫質三維場景效果和沉浸式圖形體驗,
實操環節:用3D流體實作逼真水流效果
開發環境
安裝Android Studio 3.6.1及以上,
JDK 1.8(推薦),
您的應用應滿足以下條件:
minSdkVersion 19及以上,
targetSdkVersion 30(推薦),
compileSdkVersion 30(推薦),
Gradle 5.4.1及以上(推薦),
如果同時使用多個HMS Core的服務,則需要使用各個Kit對應的最大值,
測驗應用的設備:Android 4.4及以上,
開發配置
詳細準備步驟請參考圖形引擎服務開發者聯盟官網,
開發準備
Android Studio的代碼庫配置在Gradle插件7.0以下版本、7.0版本和7.1及以上版本有所不同,請根據您當前的Gradle插件版本,選擇對應的配置程序,
-
在“buildscript > repositories”中配置HMS Core SDK的Maven倉地址,
-
如果App中添加了“agconnect-services.json”檔案則需要在“buildscript > dependencies”中增加agcp插件配置,打開專案級“settings.gradle”檔案,配置HMS Core SDK的Maven倉地址,
buildscript {
repositories {
google()
jcenter()
// 配置HMS Core SDK的Maven倉地址,
maven {url 'https://developer.huawei.com/repo/'}
}
dependencies {
...
// 增加agcp插件配置,推薦您使用最新版本的agcp插件,
classpath 'com.huawei.agconnect:agcp:
1.6.0.300
'
}
}
dependencyResolutionManagement {
...
repositories {
google()
jcenter()
// 配置HMS Core SDK的Maven倉地址,
maven {url 'https://developer.huawei.com/repo/'}
}
}
添加權限
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
<uses-permission android:name="android.permission.CAMERA" />
開發步驟
-
創建兩個Activity:MainActivity和SampleActivity,其中MainActivity負責完成SceneKit初始化,SampleActivity用于容納渲染視圖,并呈現最終效果,
-
在MainActivity中添加初始化標識和初始化方法,在初始化方法中設定SceneKit全域屬性,并使用同步初始化介面initializeSync初始化SceneKit,
private static final int REQ_CODE_UPDATE_SCENE_KIT = 10001;
private boolean initialized = false;
private void initializeSceneKit() {
// 如果已經初始化,不再重復初始化,
if (initialized) {
return;
}
// 創建SceneKit屬性,配置AppId與圖形后端API,
SceneKit.Property property = SceneKit.Property.builder()
.setAppId("${app_id}")
.setGraphicsBackend(SceneKit.Property.GraphicsBackend.GLES)
.build();
try {
// 使用同步介面進行初始化,
SceneKit.getInstance()
.setProperty(property)
.initializeSync(getApplicationContext());
initialized = true;
Toast.makeText(this, "SceneKit initialized", Toast.LENGTH_SHORT).show();
} catch (UpdateNeededException e) {
// 捕獲需要升級例外,拉起升級Activity,
startActivityForResult(e.getIntent(), REQ_CODE_UPDATE_SCENE_KIT);
} catch (Exception e) {
// 處理初始化例外,
Toast.makeText(this, "failed to initialize SceneKit: " + e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
- 復寫MainActivity的onActivityResult方法,處理升級結果
// resultCode為-1時代表升級成功,其他resultCode均代表升級失敗,
private static final int RES_CODE_UPDATE_SUCCESS = -1;
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// 如果升級成功,嘗試重新初始化,
if (requestCode == REQ_CODE_UPDATE_SCENE_KIT
&& resultCode == RES_CODE_UPDATE_SUCCESS) {
try {
SceneKit.getInstance()
.initializeSync(getApplicationContext());
initialized = true;
Toast.makeText(this, "SceneKit initialized", Toast.LENGTH_SHORT).show();
} catch (Exception e) {
// 重新嘗試初始化時不再捕獲升級例外,
Toast.makeText(this, "failed to initialize SceneKit: " + e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
}
- 在MainActivity的Layout檔案中添加按鈕,用于跳轉至SampleActivity,
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_height="match_parent">
<Button
android:layout_
android:layout_height="wrap_content"
android:text="@string/btn_render_view_demo_text"
android:onClick="onBtnRenderViewDemoClicked"/>
</LinearLayout>
- 在MainActivity中添加按鈕回呼,
public void onBtnRenderViewDemoClicked(View view) {
// 如果未初始化,先初始化,
if (!initialized) {
initializeSceneKit();
return;
}
// 跳轉到SampleActivity,
startActivity(new Intent(this, SampleActivity.class));
}
- 新建渲染視圖子類XRenderView,需要在這個子類中添加相機與燈光組件,詳情請見布置場景,
.public class XRenderView extends RenderView {
public XRenderView(Context context) {
//...
prepareScene();
}
//...
}
- 創建SampleActivity為展示效果頁面
public class DemoActivity extends Activity implements SensorEventListener {
private XRenderView renderView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
renderView = new XRenderView(this);
setContentView(renderView);
//...
}
}
- 添加createFluidSecene方法用于流體布局
3D流體組件不支持動態加載,只有在流體場景初始化時設定,才生效
private Node fluidNode;
private void createFluidScene(Context context) {
// 創建3D流體節點,
fluidNode = renderView.getScene().createNode("fluidNode");
// 添加3D流體組件,
FluidComponent fluidComponent = fluidNode.addComponent(FluidComponent.descriptor());
}
- 創建流體邊界形狀,設定流體體積量
private void createFluidScene(Context context) {
// ...
// 創建球形流體邊界形狀,
SdfSphereShape sphere = fluidComponent.createSdfSphereShape();
// 設定球體半徑,
sphere.setRadius(12.0f);
// 設定流體體積量,
fluidComponent.setFluidVolume(0.4f);
}
- 注冊傳感器事件,獲取加速度傳感器資訊
private SensorManager sensorManager;
private Sensor sensor;
private int rotation;
private void createFluidScene(Context context) {
// ...
sensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
// 獲取加速度傳感器,
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
}
@Override
protected void onResume() {
super.onResume();
// 注冊傳感器事件,
sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL);
//...
}
@Override
protected void onPause() {
super.onPause();
// 去注冊傳感器事件,
sensorManager.unregisterListener(this);
//...
}
- 復寫傳感器onSensorChanged方法,根據加速度傳感器獲取的值,更新流體系統的重力加速度,
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor == null) {
return;
}
// 加速度傳感器,
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
int x = (int) event.values[0];
int y = (int) event.values[1];
int z = (int) event.values[2];
int gravityX = 0;
int gravityY = 0;
int gravityZ = 0;
// 螢屏的旋轉資訊,
rotation = this.getWindowManager().getDefaultDisplay().getRotation();
// 根據螢屏的旋轉角度,更新加速度資訊,
switch (rotation) {
case Surface.ROTATION_0:
gravityX = -x;
gravityY = -y;
gravityZ = -z;
break;
case Surface.ROTATION_90:
gravityX = y;
gravityY = -x;
gravityZ = -z;
break;
case Surface.ROTATION_180:
gravityX = x;
gravityY = y;
gravityZ = -z;
break;
case Surface.ROTATION_270:
gravityX = -y;
gravityY = x;
gravityZ = -z;
break;
default:
break;
}
FluidComponent fluidComponent = fluidNode.getComponent(FluidComponent.descriptor());
if (fluidComponent != null) {
// 設定流體系統的重力加速度,
fluidComponent.setGravity(new Vector3(gravityX, gravityY, gravityZ));
}
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
- 新建觸屏互動手勢事件子類GestureEvent,
public class GestureEvent implements View.OnTouchListener {
private FluidComponent fluidComponent;
private int surfaceWidth;
private int surfaceHeight;
private boolean sceneReady;
public GestureEvent(FluidComponent fluidComponent, int surfaceWidth, int surfaceHeight) {
this.fluidComponent = fluidComponent;
this.surfaceWidth = surfaceWidth;
this.surfaceHeight = surfaceHeight;
sceneReady = fluidComponent != null && surfaceWidth != 0 && surfaceHeight != 0;
}
@Override
public boolean onTouch(View view, MotionEvent event) {
if (!sceneReady) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
// 流體觸屏互動介面,滑動流體,模擬流體晃動的效果,
fluidComponent.setGesture(event.getX() / surfaceWidth, event.getY() / surfaceHeight);
break;
case MotionEvent.ACTION_UP:
fluidComponent.setGesture(-1.0f, -1.0f);
break;
default:
break;
}
return true;
}
}
- 注冊觸屏互動的手勢事件,支持觸屏滑動流體,模擬流動飛濺效果,復寫DemoActivity中的onTouchEvent方法,
private GestureEvent gesture;
private void createFluidScene(Context context) {
// ...
// 獲取螢屏資訊,
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
// 注冊手勢事件,
gesture = new GestureEvent(fluidComponent, displayMetrics.widthPixels, displayMetrics.heightPixels);
}
@Override
public boolean onTouchEvent(MotionEvent motionEvent) {
if (gesture != null) {
gesture.onTouch(renderView, motionEvent);
}
return true;
}
- 在DemoActivity的onCreate方法中呼叫createFluidScene方法,完成流體場景創建,
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
//...
createFluidScene();
}
完成上述步驟后,運行應用,啟動DemoActivity,就能夠看見流體在不可視的球形邊界內流動,細節詳情見原子化介面3D流體示例代碼,
了解更多詳情>>
訪問華為開發者聯盟官網
獲取開發指導檔案
華為移動服務開源倉庫地址:GitHub、Gitee
關注我們,第一時間了解 HMS Core 最新技術資訊~
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/539973.html
標籤:Android
上一篇:汽車行業:充分借力資料價值,推動數字化營銷鏈路倍訓
下一篇:如何用3D流體實作逼真水流效果?
