5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

LiveViewでVRMファイルを読み込んで口パクする

Last updated at Posted at 2025-12-09

このコラムはLiveViewでVRMファイルを読み込んで口パクするプログラムを作ります

下記のコラムの延長になります

環境構築

$ git clone https://github.com/ymn-t-yamanashi/live_vrm.git
$ cd assets
$ npm i
$ cd ../
$ mix deps.get
$ mix phx.server

下記のURLアクセス

実行イメージ

自作ライブラリ

基本はElixirで使えるようにラップしてます

lib/three_web/live/cg/cg_helper.ex
defmodule ThreeWeb.Cg.CgHelper do
  use Phoenix.LiveView

  def add_cube(socket, name, x, y, z, color) do
    push_event(socket, "addCube", %{name: name, x: x, y: y, z: z, color: color})
  end

  def add_plane(socket, name, x, y, color) do
    push_event(socket, "addPlane", %{name: name, x: x, y: y, color: color})
  end

  def rotation(socket, name, x, y, z) do
    push_event(socket, "rotation", %{name: name, x: x, y: y, z: z})
  end

  def position(socket, name, x, y, z) do
    push_event(socket, "position", %{name: name, x: x, y: y, z: z})
  end

  def load_model(socket, name, path) do
    push_event(socket, "loadModel", %{name: name, path: path})
  end

  def get_bone(socket, name) do
    push_event(socket, "getBone", %{name: name})
  end

+ def set_blend_shape(socket, name, key, value) do
+   push_event(socket, "setBlendShape", %{name: name, key: key, value: value})
+ end

  def rotation_bone(socket, name, bone_name, x, y, z) do
    push_event(socket, "rotationBone", %{name: name, bone_name: bone_name, x: x, y: y, z: z})
  end

  def load_texture(socket, name, path) do
    push_event(socket, "loadTexture", %{name: name, path: path})
  end

  def set_texture(socket, obj_name, texture_name) do
    push_event(socket, "setTexture", %{obj_name: obj_name, texture_name: texture_name})
  end

  @doc """
  Three.jsシーンにCanvasテクスチャでテキストを表示する平面オブジェクトを追加します。
  """
  def add_text_plane(socket, name, text_content, font_size, text_color) do
    push_event(socket, "addTextPlane", %{
      name: name,
      textContent: text_content,
      fontSize: font_size,
      textColor: text_color
    })
  end

  @doc """
  既存のテキスト平面オブジェクトの文字内容とスタイルを更新します。
  """
  def set_text_plane_text(socket, name, new_text_content, font_size, text_color) do
    push_event(socket, "setTextPlaneText", %{
      name: name,
      newTextContent: new_text_content,
      fontSize: font_size,
      textColor: text_color
    })
  end

  @doc """
  Three.jsシーンから指定された名前のオブジェクトを削除します。
  """
  def remove_object(socket, name) do
    push_event(socket, "removeObject", %{name: name})
  end

  def set_size(socket) do
    push_event(socket, "setSize", %{})
  end
end

前回からの追加差分

set_blend_shape

アバターの表現する関数

set_blend_shape(socket, name, key, value)
  • socket: ソケット
  • name: 3Dオブジェクトの名前 (文字列)
  • key: 表現するキー 「あ」の口なら aa
  • value: 値 例: 1 (口を全開に開くなら1 閉じるなら0)

キーの例

アバターによって違いますが、下記は動作する可能性あります
vrmのバージョンによってはキーが違います

キー 意味
aa あの口
ih いの口
ou うの口
ee えの口
oh おの口
happy 喜び
angry 怒り
sad 悲しみ
relaxed 落ち着き・通常

サンプルプログラム

口とウインクするサンプル

lib/three_web/live/cg/index.ex
defmodule ThreeWeb.CgLive.Index do
  use ThreeWeb, :live_view
  import ThreeWeb.Cg.CgHelper

  def mount(_params, _session, socket) do
    Process.send_after(self(), :update, 500)

    socket =
      socket
      |> assign(data: initialization_character_data())
      |> load_model("test", "images/test.vrm")

    {:ok, main(socket)}
  end

  # @impl true
  def handle_info(:update, socket) do
    Process.send_after(self(), :update, 250)
    {:noreply, main(socket)}
  end

  def handle_event("load_model", %{"name" => "test", "status" => "completion"}, socket) do
    socket =
      socket
      |> position("test", 0, -1.4, 4.5)
      |> position("test", 0, -1.4, 4.5)
      |> rotation("test", 0, 3.1, 0)
      |> rotation_bone("test", "J_Bip_R_UpperArm", -1.0, 1.2, 0.5)
      |> rotation_bone("test", "J_Bip_L_UpperArm", -1.0, -1.2, -0.5)
      |> set_blend_shape("test", "aa", 0.2)
      |> set_blend_shape("test", "blink", 1.0)

    {:noreply, socket}
  end

  defp initialization_character_data() do
    0
  end

  defp main(socket) do
    character_data = update(socket.assigns.data)

    socket
    |> set_blend_shape("test", "aa", character_data)
    |> set_blend_shape("test", "blink", character_data)
    |> assign(data: character_data)
  end

  defp update(1), do: 0
  defp update(0), do: 1
end

このライブラリーを改造するなら

この2ファイルをいじれば、関数を増やせます

ソース

5
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?