Closed5
PlayCanvasのGaussian Splattingを試す

3D Gaussian Splatting自体知らなかったので何だろうと試す。
ただ、結論からいうとSOGS Self-Organizing Gaussians までは試してなくて、Compressing Gaussian Splatsを試しただけ。

写真を撮る
とりあえず部屋で適当に写真を撮る

点群データを作る
以下を参考にCOLMAPを使って、写真からplyファイルを作る。
結構特殊な操作手順な感じです。
実行完了までかなり時間がかかります。

Compressing Gaussian Splatsファイルを作る
以下でデータを変換する(?)
Pythonが新しいと動かないので3.9系で動かす。
pip install torch==1.12.1+cu116 torchvision==0.13.1+cu116 torchaudio==0.12.1 -f https://download.pytorch.org/whl/cu116
pip install plyfile tqdm opencv-python joblib
変換処理をする。
python convert.py -s データの場所
データの場所の配下にはimages
という画像が入った場所と、COLMAPの出力であるsparse/0/
を置いておく。
結果がoutputフォルダに出力される。
自分の場合はoutput\912e1328-5\point_cloud\iteration_30000
という場所にpoint_cloud.ply
というファイルが出力された。
次にCompressing Gaussian Splatsファイルを作る。
以下をCloneしてREADMEにある手順通りで動かす。
Webアプリが起動したらImportから先ほどのpoint_cloud.ply
を読み込む。
そのあとExportで圧縮したものを出力する。

PlayCanvasで表示する
viteで適当にプロジェクトを作って書く。
ちょっと試したいだけなのでplaycanvasのexampleのコードをほぼそのまま使っちゃう。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<script src="https://code.playcanvas.com/playcanvas-stable.min.js"></script>
<script type="module" src="/src/main.js"></script>
<canvas id="application-canvas"></canvas>
</body>
</html>
const canvas = /** @type {HTMLCanvasElement} */ (
document.getElementById("application-canvas")
);
window.focus();
const rootPath = "https://playcanvas.vercel.app/";
const gfxOptions = {
glslangUrl: `${rootPath}/static/lib/glslang/glslang.js`,
twgslUrl: `${rootPath}/static/lib/twgsl/twgsl.js`,
// disable antialiasing as gaussian splats do not benefit from it and it's expensive
antialias: false,
};
const device = await pc.createGraphicsDevice(canvas, gfxOptions);
device.maxPixelRatio = Math.min(window.devicePixelRatio, 2);
const createOptions = new pc.AppOptions();
createOptions.graphicsDevice = device;
createOptions.mouse = new pc.Mouse(document.body);
createOptions.touch = new pc.TouchDevice(document.body);
createOptions.componentSystems = [
pc.RenderComponentSystem,
pc.CameraComponentSystem,
pc.LightComponentSystem,
pc.ScriptComponentSystem,
pc.GSplatComponentSystem,
];
createOptions.resourceHandlers = [
pc.TextureHandler,
pc.ContainerHandler,
pc.ScriptHandler,
pc.GSplatHandler,
];
const app = new pc.AppBase(canvas);
app.init(createOptions);
// Set the canvas to fill the window and automatically change resolution to be the same as the canvas size
app.setCanvasFillMode(pc.FILLMODE_FILL_WINDOW);
app.setCanvasResolution(pc.RESOLUTION_AUTO);
// Ensure canvas is resized when window changes size
const resize = () => app.resizeCanvas();
window.addEventListener("resize", resize);
app.on("destroy", () => {
window.removeEventListener("resize", resize);
});
const assets = {
biker: new pc.Asset("gsplat", "gsplat", {
url: `point_cloud.compressed.ply`,
}),
orbit: new pc.Asset("script", "script", {
url: `${rootPath}/static/scripts/camera/orbit-camera.js`,
}),
};
const assetListLoader = new pc.AssetListLoader(
Object.values(assets),
app.assets
);
assetListLoader.load(() => {
app.start();
// create a splat entity and place it in the world
const biker = new pc.Entity();
biker.addComponent("gsplat", {
asset: assets.biker,
castShadows: true,
});
biker.setLocalPosition(-1.5, 0.05, 0);
biker.setLocalEulerAngles(180, 90, 0);
biker.setLocalScale(0.7, 0.7, 0.7);
app.root.addChild(biker);
// set alpha clip value, used by shadows and picking
biker.gsplat.material.setParameter("alphaClip", 0.4);
// Create an Entity with a camera component
const camera = new pc.Entity();
camera.addComponent("camera", {
clearColor: new pc.Color(0.2, 0.2, 0.2),
toneMapping: pc.TONEMAP_ACES,
});
camera.setLocalPosition(-0.8, 2, 3);
// add orbit camera script with a mouse and a touch support
camera.addComponent("script");
camera.script.create("orbitCamera", {
attributes: {
inertiaFactor: 0.2,
focusEntity: biker,
distanceMax: 60,
frameOnStart: false,
},
});
camera.script.create("orbitCameraInputMouse");
camera.script.create("orbitCameraInputTouch");
app.root.addChild(camera);
const ground = new pc.Entity();
ground.addComponent("render", {
type: "box",
material: material,
castShadows: false,
});
ground.setLocalScale(10, 1, 10);
ground.setLocalPosition(0, -0.45, 0);
app.root.addChild(ground);
// shadow casting directional light
// Note: it does not affect gsplat, as lighting is not supported there currently
const directionalLight = new pc.Entity();
directionalLight.addComponent("light", {
type: "directional",
color: pc.Color.WHITE,
castShadows: true,
intensity: 1,
shadowBias: 0.2,
normalOffsetBias: 0.05,
shadowDistance: 10,
shadowIntensity: 0.5,
shadowResolution: 2048,
shadowType: pc.SHADOW_PCSS_32F,
penumbraSize: 10,
penumbraFalloff: 4,
shadowSamples: 16,
shadowBlockerSamples: 16,
});
directionalLight.setEulerAngles(55, 0, 20);
app.root.addChild(directionalLight);
});
export { app };
表示される。
このスクラップは2ヶ月前にクローズされました