たくさんの自由帳
Androidのお話
たくさんの自由帳
文字数(だいたい) : 12043
目次
本題
Q 興味ないので APK ファイルだけくれませんか
Q 興味ないので比較した写真だけ見せてくれませんか
Macbook Air 13 インチ
そのほか
詳しい話は大昔に書いたのでそっちも見てください
ARCore とは
環境
手順
3Dモデルを作る
Blender を開く
正方形を調整する
単位をミリメートル
サイズを位置を合わせる
保存する
何パターンか作った
アプリを作る
ARCore のサンプルコードをクローンする
サンプルコードを開く
Gradle 周りを新しくする
中身を書き換える
settings.gradle.kts
build.gradle.kts
gradle-wrapper.properties
app/build.gradle.kts
gradle/libs.versions.toml
最低限ビルドが通るまでのコミット
ちなみに起動すると
オブジェクトを取り込む
environmental_hdr 修正
HelloArActivity
HelloArRenderer
動かす
ソースコード
おわりに
おわりに2
おわりに3
おわりに4
どうもこんばんわ。
美容室に行ってみました。怖いのかなーって思ってましたが怖くなかったです。免許更新があったので切りに行ってみた。
なんか短い方が似合うって言われたけど短くしたことないから分からん :(
なんか髪の毛押し付けながらドライバーするとボリューム感へっておすすめらしいので試してみている
話はなんか髪を短くした方が似合いそう(?)とかの話とか、髪質が強い(?)から染めたりとかで柔らかくなるかも見たいな。陽キャすぎんか?
せっかくなので、なんかちょっと白髪混じってるの聞いてみた。
そしたら、ツヤがあるからそのままでいいんじゃないって言われたのでそのままにすることにした(?)
ついにiPhoneから折り畳みスマホが出るようです。どれくらいのサイズ感なのかは気になりますよね!?
そういうのに向いてる機能があります(?)。AndroidのAR機能というかAPIであるARCoreですね!
というわけで今回はiPhone Duoと同じサイズの3D オブジェクトをBlenderで作ってARCoreで表示してみます。
はい
ボトムシートに表示される項目は以下の通りです。
Pixel 11 Pro Fold閉じた時のサイズPixel 11 Pro Fold開いた時のサイズiPhone Duo閉じた時のサイズiPhone Duo開いた時のサイズいろいろ置いてみた、けど、なかなかこれというのが見つからんかった。
閉じたときはポケットティッシュを一回り大きくしたくらいだと思います。
開いたときは小説の本(A6らしい)が近い気がします。
ガリレオ、3つ目の映画まだ見てないな・・・
閉じたらトラックパッドくらい
開いてキーボードの上に乗せた感じ
AirDropのためになんか持ってるiPhone7(今ならPixelでAppleデバイスとAirDropできる)
こうしてみると思ったより小さい。
はがき(と同じサイズです!)閉じるとはがきより一回りくらい小さかった。
ひらくとはがきちょい大きいくらい
雑誌
AndroidでARアプリを作るためのAPI。ゲームエンジンを使わなくてもJava/Kotlinで利用できます。
しかしその場合、代わりにOpenGL ESを使って描画しているので、ゲームエンジンを避けてもどのみち難しいです。
結構むかしの話ですが、かつてGoogleが作ったSceneformっていうライブラリがありました。OpenGL ESの知識は一切不要、簡単なJava/Kotlinのコードを書くだけで現実世界にオブジェクトが置けたライブラリです。Googleのお家芸というかなんというかで、これはずっと前に廃止されました・・
ですが、Sceneformは今でも有志が引き継いでるはず!なのでそっちを使う方がいい気がします。
まあこんなことがあったのでAndroid XRもやる気があるのかは少し怪しいところです()
そうはいっても、今回やることはGoogleが書いてるサンプルコードを書き換えて、自分で作ったBlenderの3D オブジェクトを表示するように直すだけなので別に難しくないです。
| なまえ | あたい |
|---|---|
| Android | Pixel 11 Pro Fold |
| Blender | 5.2.2 |
| ARCore | 1.56 |
| Android Studio | Android Studio Rabbit 1 2026.2.1 |
3D モデルを作るGoogleのARCoreサンプルコードをクローンしてビルドが通るようになるまで直す3D モデルを表示するように直すBlenderを入れます。ポータブル版でもインストール版でもどっちでもいいと思います。
いっぱい使うならインストール版がいいのかな、私はポータブル版を入れました。
ポータブル版ならダウンロードしたzipを解凍しておいてください。
blender-launcher.exeを開きます。日本語が選べるっぽいので選びました。どっちでもいいと思います。選んだらダイアログ?の外側を押して閉じました。
もうすでに正方形があるのでこれを使いますか!?
マウス押し込みで視点変更が、スクロールで前後に動かせます。で、なんか正方形の下半分が埋まってますね。。。Z軸の0地点に正方形が来てほしいです。
何が原因なのかというと、オブジェクトの原点が真ん中にあるせいらしい。
これを底面に移動させて、位置をZ軸の0地点に合わせれば、底面と0が一致し下に埋まることはなくなるはずです!
左上がオブジェクトモードになっているので、Tabキーを押して編集モードに切り替えます。左上がそうなると思います。
次に数字の3キーを押します。この状態で正方形の底面が見えるようにマウス押し込みで視点変更しつつ、底面をクリックします。選んだ面だけ色が変わるハズ!
つぎに、ShiftとSキーを押すとメニューが出るので、カーソル→選択物を選びます。
最後にTabキーを押して元のモードに戻し、正方形を右クリックし、原点を調整→原点を3Dカーソルに移動を選びます。
これで、三次元に伸びてる矢印が正方形の底に表示されるはずです。矢印が出ない場合は左の縦にアイコンが並んでる部分から矢印のアイコンを選んでください(移動ってやつ)
スマホの大きさとかはミリメートル表記なので、先に変えちゃいますか。
右のパネル?からシーンを選んで、単位を開き長さをミリメートルに変更します。
これでもまだ下半分が埋まってます。これを調整するための画面を出します。Nキーを押してください。
するとなんか左から出てくると思います。
まず埋まってるのはZ軸が0になってないせいですね。0にします。これで埋まらなくなります。
また、寸法のところはiPhone Duoの大きさに合わせます。大きさはAppleのサイトから見れます。
出来ました、とりあえず開いてる状態作りました。たぶんあってます。
数字を合わせるだけですね!
Blenderで編集する予定があれば、ツールバーからファイル→保存を選んで.blend形式で保存。
もう完成して3D モデルを書きだすなら、ファイル→エクスポート→wavefront (.obj)形式がARCoreのサンプルコードで使われている形式なので、objで書き出してください。
適当な名前を付けて保存してください。
保存すると.obj形式ともう一つ何か生成されるのですが、.objしか使いません!
閉じたバージョンも作りました。また、Pixel 11 Pro Foldも同じように作りました。まあ私は手元にあるので作らなくてもいいのですが。(下取りに出したのにまだ高い!)
Kotlin + OpenGL ESのサンプルコードはこれです。gitリポジトリの中に複数のサンプルコードがあるので、これだけが欲しくても多分全部クローンする必要があります。ファイルサイズが切りなる場合は--depth=1を付けてgit cloneするとよいかも?
git clone https://github.com/google-ar/arcore-android-sdk.git --depth=1hello_ar_kotlinフォルダをAndroid Studioで開きます。
・・・・あ、Gradle周りが古すぎて一発ではビルドが通らないかも。
このダイアログ通りにJDKのバージョン(Javaのバージョン)を変える方法もありますが、GradleやAGPのバージョンを新しくしてビルドを通す方法もあります。
今回は後者のバージョンをすべて新しくする方法で行きます。ダイアログはバツで閉じます。
(org.jetbrains.kotlin:kotlin-stdlibとか逆に今通るか・・?とっくのとうに不要になったので)
いうまでもなく、Gradle周りのバージョンを新しくするのはARCore関係ないです。
2026年10月現在。多分もっと新しいバージョンが出てると思いますが、ここでは私が公開してるアプリで使っているバージョンを使うようにします(コピペしたいだけです)
まず最近の.gradleファイルは.gradle.ktsというKotlinを使った形式になるので、以下の3つのファイルを.ktsに変更してください
ビルドすら通ってないので名前変更で警告が出てもRefactorやOKを選んでください。
settings.gradle → settings.gradle.ktsbuild.gradle → build.gradle.ktsapp/build.gradle → app/build.gradle.ktsコピペできない部分はコメントで!
また、今回はあとでJetpack Composeでボトムシートを作りたいため、Jetpack Compose込みのライブラリ設定になります。
pluginManagement {
repositories {
google {
content {
includeGroupByRegex("com\\.android.*")
includeGroupByRegex("com\\.google.*")
includeGroupByRegex("androidx.*")
}
}
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
rootProject.name = "iPhoneDuoSameSizeObjectARCore" // ここはプロジェクトの名前にしてください
include(":app")appフォルダの方じゃないbuild.gradle.kts
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.compose.compiler) apply false
}gradle/wrapper/gradle-wrapper.propertiesにあります。
#Mon Aug 03 01:07:56 JST 2026
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=553c78f50dafcd54d65b9a444649057857469edf836431389695608536d6b746
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/distsplugins {
alias(libs.plugins.android.application)
alias(libs.plugins.compose.compiler) // もし Room や Hilt で ksp や databinding で kapt 使ってるならここに追加
}
android {
namespace = "com.google.ar.core.examples.kotlin.helloar" // これもアプリケーションIDに合わせるのがセオリーか
compileSdk {
version = release(37)
}
defaultConfig {
applicationId = "com.google.ar.core.examples.kotlin.helloar" // アプリケーションID
// AR Optional apps must declare minSdkVersion >= 14.
// AR Required apps must declare minSdkVersion >= 24.
minSdk = 24 // SDK バージョンが違う場合はこの辺を変更してください
targetSdk = 37
versionCode = 1
versionName = "1.0"
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
buildTypes {
release {
optimization {
enable = true // ちな難読化のファイルは app/src/main/keepRules/rules.keep に移動になりました
}
}
}
buildFeatures {
compose = true // viewbinding / databinding 使ってるなら
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
}
dependencies {
// ARCore (Google Play Services for AR) library.
implementation("com.google.ar:core:1.56.0")
// Obj - a simple Wavefront OBJ file loader
// https://github.com/javagl/Obj
implementation("de.javagl:obj:0.4.0")
implementation("androidx.appcompat:appcompat:1.1.0")
implementation("androidx.lifecycle:lifecycle-common-java8:2.2.0")
implementation("com.google.android.material:material:1.1.0")
// --- ここまで ARCore のサンプルコードで使っている依存 ---
// jetpack compose
implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.ui)
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3)
implementation(libs.androidx.navigation3.ui)
implementation(libs.androidx.navigation3.runtime)
}とりあえず上記のbuild.gradle.ktsで使っているライブラリのみ。
既存の書き方をバージョンカタログにするとかはお任せします。
[versions]
agp = "9.3.1"
kotlin = "2.4.10"
activityCompose = "1.13.0"
composeBom = "2026.08.00"
nav3Core = "1.1.7"
[libraries]
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
androidx-navigation3-runtime = { module = "androidx.navigation3:navigation3-runtime", version.ref = "nav3Core" }
androidx-navigation3-ui = { module = "androidx.navigation3:navigation3-ui", version.ref = "nav3Core" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }ここまでやるとビルドが通るハズなのですが、まあうまくいかないかと。Gradle エラーは我々が直せるレベルの話か怪しいときがよくあるから困るよな~
なんかピンみたいなのが置ける
app/src/main/assets/modelsに.objを置いてください。
ちなみに↑のオブジェクトはpawn.objです。
テクスチャを作ってない(3Dモデルの画像を作ってない)ので、テクスチャから色を取らずに単色にする処理を書く必要があります。
でその処理はたぶんここです。
albedoは白色(1.0, 1.0, 1.0)として、その下のroughnessMetallicAmbientOcclusionの値の出所は忘れてしまいました。(え?)
void Pbr_CreateMaterialParameters(const in vec2 texCoord,
const in sampler2D albedoTexture,
const in sampler2D pbrTexture,
const in sampler2D dfgTexture,
const in ShadingParameters shading,
out MaterialParameters material) {
// Read the material parameters from the textures
// vec3 albedo = texture(albedoTexture, texCoord).rgb;
// vec3 roughnessMetallicAmbientOcclusion = texture(pbrTexture, texCoord).rgb;
// テクスチャを作ってないので
vec3 albedo = vec3(1.0, 1.0, 1.0);
vec3 roughnessMetallicAmbientOcclusion = vec3(0.5, 0.0, 1.0);
// Roughness inputs are perceptually linear; convert them to regular roughness
// values. Roughness levels approaching 0 will make specular reflections
// completely invisible, so cap the lower bound. This value was chosen such
// that (kMinPerceptualRoughness^4) > 0 in fp16 (i.e. 2^(-14/4), rounded up).
// https://github.com/google/filament/blob/main/shaders/src/common_material.fs#L2
// 以下省略...ここで、3Dモデルを切り替えるUIを作るためにJetpack Composeを用意して切り替えUIも作ります。Jetpack ComposeのActivityではComponentActivity()を継承してるっぽいのでそうします。
class HelloArActivity : ComponentActivity() { // ComponentActivity に直す次に、setContentView()をJetpack Composeで描画するのでsetContentに置き換えます。
ただ、setContentView()にはARを描画していたSurfaceViewがあるため、AndroidViewで埋め込む必要があります。
onCreate()のsetContentView()をこんな感じに治します。ボトムシートで切り替えもできる!
また、onCreate()関数にOptIn ExperimentalMaterial3Apiを付けろって言われると思うので付けてください。
あとHelloArRendererのクラスも書き換わってるので次直します。
// Set up Hello AR UI.
view = HelloArView(this)
lifecycle.addObserver(view)
setContent {
// 既にある view は使いまわす + 自作部分だけ compose
Box {
AndroidView(factory = { view.root })
val isShowObjSelectBottomSheet = remember { mutableStateOf(false) }
if (isShowObjSelectBottomSheet.value) {
ModalBottomSheet(onDismissRequest = { isShowObjSelectBottomSheet.value = false }) {
LazyColumn {
items(HelloArRenderer.OBJECT_LIST) { obj ->
Text(
text = obj,
modifier = Modifier
.clickable {
renderer.nextTapAddMeshObjName = obj
isShowObjSelectBottomSheet.value = false
}
.padding(10.dp)
.align(Alignment.CenterHorizontally)
)
HorizontalDivider()
}
}
}
}
Button(
modifier = Modifier.align(Alignment.BottomCenter),
onClick = { isShowObjSelectBottomSheet.value = true }
) {
Text(text = "オブジェクト選択")
}
}
}まずはobjのファイル名をcompanion objectで定数として定義しておきましょう。↑のActivityで使うのと、このRendererで使うのでここで定義しておきます。
companion object {
// 以下省略...
val OBJECT_LIST = listOf(
"google_pixel_11_profold_collapsed_same_size_obj.obj",
"google_pixel_11_profold_expanded_same_size_obj.obj",
"iphone_duo_collapsed_same_size_obj.obj",
"iphone_duo_expanded_same_size_obj.obj"
)
}つぎに、オブジェクトを複数変数に保持できるように配列、正しくはオブジェクトのファイル名とオブジェクトの連想配列にします。
なのでlateinit var virtualObjectMesh: Meshを連想配列に直します。
// Virtual object key:OBJECT_LIST value:Mesh
private val virtualObjectMeshHashMap = hashMapOf<String, Mesh>()
lateinit var virtualObjectShader: Shader
lateinit var virtualObjectAlbedoTexture: Texture
lateinit var virtualObjectAlbedoInstantPlacementTexture: Textureつぎに、一番最後に変数を一つ。これは、次画面を押してオブジェクトをARに追加する際に、OBJECT_LISTのどのオブジェクトを追加するかを保持するための変数です。HelloArActivityからこの変数を操作して切り替えできるように。nextTapAddMeshObjName
val session
get() = activity.arCoreSessionHelper.session
val displayRotationHelper = DisplayRotationHelper(activity)
val trackingStateHelper = TrackingStateHelper(activity)
// 次追加するオブジェクト
var nextTapAddMeshObjName = OBJECT_LIST[0]つぎに、virtualObjectMeshをロードしていた箇所で、代わりに連想配列にロードしたものを格納するようにします。
そういえばフラグメントシェーダーで結局テクスチャを使わずに固定の色を表示するようになった。で、たぶんGPUは賢いので使われないUniform変数(今回はテクスチャ)は削除され、渡しても渡す宛がなくなるのでコメントアウトします。
// assets にある obj をまとめて読み込む
virtualObjectMeshHashMap += OBJECT_LIST.associateWith { Mesh.createFromAsset(render, "models/$it") }
virtualObjectShader =
Shader.createFromAssets(
render,
"shaders/environmental_hdr.vert",
"shaders/environmental_hdr.frag",
mapOf("NUMBER_OF_MIPMAP_LEVELS" to cubemapFilter.numberOfMipmapLevels.toString())
)
// .setTexture("u_AlbedoTexture", virtualObjectAlbedoTexture)
// .setTexture("u_RoughnessMetallicAmbientOcclusionTexture", virtualObjectPbrTexture)
.setTexture("u_Cubemap", cubemapFilter.filteredCubemapTexture)
.setTexture("u_DfgTexture", dfgTexture)つぎに、onDrawFrameまで移動します。
画面更新のために何度も呼ばれ、ここでOpenGL ESを使って描画しているわけですね。
render.draw(virtualObjectMesh, virtualObjectShader, virtualSceneFramebuffer)に渡しているvirtualObjectMeshが描画したい3Dオブジェクトになります。
ここを連想配列から取り出す。。ではまだダメで。
というのも、ここは画面更新のたびに呼び出されるため、追加したオブジェクトを渡してあげる必要があります。
ここでrender.draw(virtualObjectMeshHashMap[nextTapAddMeshObjName] ...)で渡すと、追加したオブジェクトのすべてがnextTapAddMeshObjNameのオブジェクトになってしまいます。
そうならないためには、追加したオブジェクトが何なのかを保持しておけばよさそうですね。
オブジェクトを決めるのは画面を押したとき。何を追加したかを保持してrender.draw()ではその保持しているオブジェクトを描画するようにします。
ここではオブジェクトの位置?とかが入ってるdata classがあるので、これに何のオブジェクト化を追加で保持させます。wrappedAnchorsがAR内に追加した(表示する)3Dオブジェクトのリストになります。
for ((anchor, trackable, mesh) in wrappedAnchors.filter { it.anchor.trackingState == TrackingState.TRACKING }) {
// Get the current pose of an Anchor in world space. The Anchor pose is updated
// during calls to session.update() as ARCore refines its estimate of the world.
anchor.pose.toMatrix(modelMatrix, 0)
// Calculate model/view/projection matrices
Matrix.multiplyMM(modelViewMatrix, 0, viewMatrix, 0, modelMatrix, 0)
Matrix.multiplyMM(modelViewProjectionMatrix, 0, projectionMatrix, 0, modelViewMatrix, 0)
// Update shader properties and draw
virtualObjectShader.setMat4("u_ModelView", modelViewMatrix)
virtualObjectShader.setMat4("u_ModelViewProjection", modelViewProjectionMatrix)
val texture =
if ((trackable as? InstantPlacementPoint)?.trackingMethod ==
InstantPlacementPoint.TrackingMethod.SCREENSPACE_WITH_APPROXIMATE_DISTANCE
) {
virtualObjectAlbedoInstantPlacementTexture
} else {
virtualObjectAlbedoTexture
}
virtualObjectShader.setTexture("u_AlbedoTexture", texture)
render.draw(mesh, virtualObjectShader, virtualSceneFramebuffer)
}あとすこし、handleTap()関数まで移動してください。wrappedAnchors.add()している箇所があると思います。まだdata classを書き換えてないので赤くなると思いますが、第三引数を追加します。
描画したい3Dオブジェクトですね。先述の通り、画面に表示されるオブジェクトがすべて同じにならないよう、このタイミングで記憶するようにします。
wrappedAnchors.add(WrappedAnchor(firstHitResult.createAnchor(), firstHitResult.trackable, virtualObjectMeshHashMap[nextTapAddMeshObjName]!!))さいご。WrappedAnchorに描画したい3Dオブジェクトを保持するための引数を追加するだけ。
/**
* Associates an Anchor with the trackable it was attached to. This is used to be able to check
* whether or not an Anchor originally was attached to an {@link InstantPlacementPoint}.
*/
private data class WrappedAnchor(
val anchor: Anchor,
val trackable: Trackable,
val mesh: Mesh
)これで起動すると、下にボタンが表示されて、3Dオブジェクトを選択できるようになり、画面を押すと選択した3DオブジェクトがAR内に設置されるはずです!
ちなみに20個までしか表示できないはずです。wrappedAnchorsに追加する前に超えていたら最後から消すみたいなのが入っているためです。
なんで20なのかは知りません、負荷的な話ですかね?
はい
まじで全然関係ない話になるわけですが、Android搭載メガネXREAL AURAがついに買えるらしい。10年以上前にYouTubeで見たGoogle Glassがついに実現するのか?!。
完全にスルーしてたわけですが、予約時点で1500ドルは超えない値段で発売するって言っててちょっと高いなあ~って
だったのが、発売すると1279ドルスタートで思ったよりかは安かった。ハイエンドスマホの値段とあんまり変わらない、もっとべらぼうに高いと思ってた。
(まあ普通に全然高いんですけど!!)
もっと高くなるもんだと思ってたから下取りにしてPixel 11 Pro Foldに変えたけど、10 Pro Foldをそのまま続けてこっち買うか考えてたかもしれない・・・
よくある?iPhone 対 Androidスマホのクリックベイト記事にならないように書きましたが、もしそう感じてしまったらすいません。
まじでもういいよiPhoneとAndroidの比較というか。煽りすぎ。
機嫌悪いときに見つけた日には、Google Discover(ホーム画面の一番左にあるやつ、Chromeにあるやつ)からWeb サイトごとまるまる非表示にしてる。
もしおりたたみスマホ気になってる人がいれば。
わたし的には、折り目はマジで気にならない。触ればわかるし光の当たり方によっては折り目はちゃんと見える。
でもそれだけ。普通に使えるし。こんなもんだよ。
それこそおりたたみスマホ黎明期は分からないけど今時そんなひどいことある?
折り目はもう仕方ない気がします。今でもスマホの電池は繰り返し使うほど使える時間が短くなってくるし。
最近はあんまり聞かなくなりましたが、iPhone X (2017)で有機EL (OLED)が採用されたとき、人々は画面が焼き付くのではないかの話題で持ちきりになってた時と同じように見えます。
(電池マークとかアンテナマークの部分が常に表示されるから残像が残るのではないか)
折り目なんかよりもハイエンド以上の値段を払ってもなお、望遠カメラが無いのが不満・・・難しいのかな
ぼーっとYouTubeみてたんですけど
NeoとDuoって名前の組み合わせ、言われるまで気付かなかった。Macbook Neoの時点で規則性に気付いていれば、UltraでもFoldでもなくDuoだって予想できた可能性がある?