TECH PLAY

電通総研

電通総研 の技術ブログ

843

みなさんこんにちは、 電通 総研コーポーレート本部システム推進部の佐藤太一です。 この記事では、 VS Code のDev Containerを使ってOSに依存しない Python の開発環境を構築する方法をステップ バイス テップで丁寧に説明します。 VS Code の利用経験があり、また Python によるアプリケーション開発に興味のある方を想定読者として記述しています。 Python の初心者から中級者向けを意識して書いていますので、意図して冗長な説明をしています。 すでに Python によるアプリケーション開発に十分に詳しい方は、まずはまとめだけ読んでみてください。私自身それほど Python のエコシステムに詳しいわけではありませんので、知識の抜け漏れは恐らくあるでしょう。そういった事に気が付いたら、Xなどの SNS でこの記事のURLを付けてコメントをしていただけると幸いです。 はじめに 事前の準備 最小限のDev Container Dev Containerの起動 devcontainer.jsonを編集する環境の構築 最小限のPython用VS Code拡張 uvの導入 Python仮想環境の構築 プロジェクト構成ファイルの作成 コンテナ作成時に動作するシェルスクリプトの追加 Pythonの仮想環境用ボリュームのマウント プロジェクトローカルに仮想環境を作る さぁ、Pythonを動かそう フォーマッタとLinterの導入 pytestの導入 テストコードの追加 テストをターミナルから動かす テストをGUIから動かす テストをデバッガから動かす テストカバレッジを計測する pytest-covの導入 テストカバレッジの微調整 実行時オプションの調整 カバレッジ除外の調整 VS Codeにカバレッジレポートを統合する VS Codeにおけるカバレッジレポートの確認方法 タスクランナーの導入 uvでのタスクランナー まとめ この記事で紹介している開発環境の構成ファイル .devcontainer/devcontainer.json .devcontainer/postCreateCommand.sh .gitignore pyproject.toml はじめに ソフトウェア開発をチームで行うにあたって、もっとも困難な課題の一つは開発環境の再現性を担保することです。 開発チームのメンバーは、それぞれが固有の経験と知識を持っていますし、プロジェクトに参画する際の契約が異なることも多いでしょう。全てのメンバーが、単一のプロジェクトに使いうる全ての 工数 を使ってプロジェクトに貢献できるわけではありません。人によっては、複数のプロジェクトを掛け持ちしていることはあります。 ある開発メンバーは慣れている Mac を使いたいと考える時、違うメンバーは会社から標準的に貸与される Windows で開発したいと考えるかもしれません。しかし、アプリケーションのデプロイ先は Ubuntu や Debian といった Linux だったりします。 こういった状況では、特定のメンバーでのみ発生する不具合が存在したり、または特定のメンバーのローカル環境でしかアプリケーションが動作しないといった事が起こります。 原因が明らかになれば、単にPATH 環境変数 の違いのような簡単なことかもしれませんが、その過程は往々にして困難なものになります。そういった細かい環境差異を原因とする問題の調査はシニアなメンバーにしかできないことが多いのです。 しかし、その調査の時間は明らかに無駄ですし、ほぼ何の価値もありません。 付加価値の高いシニアなメンバーの 工数 を、そういった調査で浪費することはプロジェクトにとって望ましくないでしょう。 この記事で紹介する手法では、Dockerベースのコンテナ技術を使うことで、メンバー毎の環境差分をできるだけ小さくできます。ハードウェアスペックやネットワークに起因するもの以外は差分が全く存在しなくなると言っても良いでしょう。つまり、開発メンバーのなかで誰かのマシンで起きた問題は、メンバー全員のマシンで再現します。 気になってきましたか?それでは、Dev Containerを使った開発環境構築について説明を始めます。 事前の準備 この記事が前提とする環境について軽く説明します。 まず、 VS Code を事前にインストールしておいてください。 次に、 Docker Desktop をインストールして動作する状態にしてください。基本的には単に インストーラ を実行すれば動作する状態になります。 そして、 VS Code に Dev Containers 拡張をインストールしておいてください。 最後に、作業用のプロジェクト ディレクト リを作成してください。ここでは、 devcontainer-python という ディレクト リを作成してプロジェクトのルート ディレクト リとしています。 最小限のDev Container まずは、Dev Containerで公式に提供されている Python の開発環境を導入してみましょう。 プロジェクトのルート ディレクト リに、 .devcontainer という ディレクト リを作って、その中に devcontainer.json というファイル名で以下の内容を保存します。 { " name ": " devcontainer-python ", " image ": " mcr.microsoft.com/devcontainers/python:3.12-bookworm ", " containerEnv ": { " TZ ": " Asia/Tokyo " } , " runArgs ": [ " --init " ] } name の値は、分かりやすい名前なら何でもいいです。ここでは、devcontainer- python としています。 image の値は、mcr. microsoft .com/devcontainers/ python :3.12-bookworm としています。これは、公式のイメージ名です。 ベースイメージや Python ランタイムのバージョンを別なものにしたい場合には、 https://github.com/microsoft/vscode-dev-containers/tree/main/containers/python-3 から探してください。 containerEnv の値は、コンテナ内で参照される 環境変数 です。ここでは タイムゾーン が Asia/Tokyo になるよう設定しています。時刻に起因する問題の調査は難しいので、ここで明示的に設定しています。 runArgs の値として --init を渡すことで、Dockerが /dev/init というシグナルハンドリング用のプロセスを起動してくれます。これによってコンテナを安定的にシャットダウンできるようになります。 Dev Containerの起動 それでは、作ったDev Containerを起動してみましょう。 プロジェクトのルート ディレクト リを VS Code で開いた上で画面左端のアイコンをクリックしてREMOTE EXPLORER を表示します。 reopen the current folder in a container のリンクをクリックするとDev Containerの起動が始まります。 画面右下に、Dev Containerの起動が始まったことを通知するダイアログが数秒間だけ表示されるので、サッとクリックしましょう。 クリックすると起動ログが流れていく様子を観察できます。 起動ログの流れが落ち着いたら、ターミナルを起動してみましょう。ログの右上あたりにある + ボタンです。Ctrlキーと@キーを同時に押しても構いません。 以下のようにターミナルが表示されますね。 コンテナ内では vscode というユーザーで、 workspace/devcontainer-python という ディレクト リをカレント ディレクト リにしています。 ディレクト リパスの devcontainer-python 部分はプロジェクトのルート ディレクト リと同じになっているはずです。 devcontainer. json を編集する環境の構築 ここから、devcontainer. json を編集しながら開発環境を構築していくので、まずは快適に json ファイルを編集できるようにしましょう。 devcontainer. json には、Dev Containerとして起動した VS Code を構成するための設定項目がありますので、それらを編集します。 { " name ": " devcontainer-python ", " image ": " mcr.microsoft.com/devcontainers/python:3.12-bookworm ", " containerEnv ": { " TZ ": " Asia/Tokyo " } , " runArgs ": [ " --init " ] , " customizations ": { " vscode ": { " settings ": { " editor.renderWhitespace ": " all ", " [json][jsonc] ": { " editor.defaultFormatter ": " esbenp.prettier-vscode ", " editor.formatOnSave ": true , " editor.codeActionsOnSave ": { " source.fixAll ": " explicit " } } } , " extensions ": [ " esbenp.prettier-vscode " ] } } } customizations というキーがDev Containerの構成を行うための設定項目です。この中に vscode という項目がありますね。 settings の中では、 VS Code の設定項目を管理します。 editor.renderWhitespace の値として、 all を設定しているのは、ファイルの中に紛れ込んだ全角スペースを見つけやすくするためです。私たちが IME を使っている以上、意図しない場所に全角スペースが入り込んでしまい、それによって理解が困難なエラーメッセージを読むことになるのは避けられません。全角スペースが見えていれば、そういったドハマりから抜け出しやすくなります。 [json][jsonc] の値として、いくつか設定しています。ちなみに、jsoncは、 JSON with commentsの略称です。 editor.defaultFormatter の値として、esbenp.prettier- vscode を設定しています。これによってprettierを使ったフォーマットが行われます。 editor.formatOnSave の値として、trueを設定することでファイル保存時にフォーマットが行われるようにしています。 editor.codeActionsOnSave の値として、source.fixAll に"explicit"を指定することで自動的に補正できるフォーマットエラーをprettierが積極的に補正してくれます。 extensions の中では、Dev Containerとして起動された VS Code にインストールされる VS Code 拡張を列挙します。ここでは、 JSON を自動フォーマットするための esbenp.prettier-vscode を設定しています。 最小限の Python 用 VS Code 拡張 次は、 Python 用の VS Code 拡張をいくつか追加していきましょう。 おすすめの拡張は、 Python Python Indent autoDocstring の三つです。他にも便利なものは多くありますが、特に議論の余地なく導入できるものはこれらです。 devcontainer. json のextensionsにこれらの拡張を追加すると、以下のようになります。 { " name ": " devcontainer-python ", " image ": " mcr.microsoft.com/devcontainers/python:3.12-bookworm ", " containerEnv ": { " TZ ": " Asia/Tokyo " } , " runArgs ": [ " --init " ] , " customizations ": { " vscode ": { " settings ": { " editor.renderWhitespace ": " all ", " [json][jsonc] ": { " editor.defaultFormatter ": " esbenp.prettier-vscode ", " editor.formatOnSave ": true , " editor.codeActionsOnSave ": { " source.fixAll ": " explicit " } } } , " extensions ": [ " esbenp.prettier-vscode ", " ms-python.python ", " njpwerner.autodocstring ", " KevinRose.vsc-python-indent " ] } } } devcontainer. json を修正したら、忘れずにDev Containerをリビルドしましょう。 リビルドするには、REMOTE EXPLORER を表示して動作しているコンテナを右クリックします。 ここで表示される コンテキストメニュー から Rebuild Container を選択します。 これで、 VS Code を Python 用のエディタとして使うための準備は整ったと言えます。 しかしながら、アプリケーションの開発環境と呼ぶには、まだまだ不足がありますので順次整えていきましょう。 uvの導入 Python でアプリケーション開発を行うなら、標準ライブラリを使うだけでなく巨大なコミュニティ内で提供されるモジュールを利用して、その恩恵にあずかりましょう。 pipコマンドだけで OSS のモジュールをインストールするという ストロングス タイルも良いものですが、私が推奨するのはuvを使ったモジュール管理です。以前はPoetryを推奨していましたが、今はRustで実装されていて高速に動作するuvをお勧めしています。 Dev Containerには、featuresという機能がありdevcontainer. json を少し書き加えるだけでuvを使えます。 { " name ": " devcontainer-python ", " image ": " mcr.microsoft.com/devcontainers/python:3.12-bookworm ", " containerEnv ": { " TZ ": " Asia/Tokyo " } , " runArgs ": [ " --init " ] , " features ": { " ghcr.io/jsburckhardt/devcontainer-features/uv:1 ": {} } , " customizations ": { " vscode ": { " settings ": { " editor.renderWhitespace ": " all ", " [json][jsonc] ": { " editor.defaultFormatter ": " esbenp.prettier-vscode ", " editor.formatOnSave ": true , " editor.codeActionsOnSave ": { " source.fixAll ": " explicit " } } } , " extensions ": [ " esbenp.prettier-vscode ", " ms-python.python ", " njpwerner.autodocstring ", " KevinRose.vsc-python-indent " ] } } } features の値として、 "ghcr.io/jsburckhardt/devcontainer-features/uv:1": {} が記述されていますね。 こうすることで、Dev Container起動時にuvがコンテナ内のゲストOSにグローバルインストールされるのです。 devcontainer. json を書き換えたらDev Containerをリビルドしましょう。 Python 仮想環境の構築 Dev Containerのリビルドから戻ってきたら、uvが使うプロジェクト構成ファイルを作りましょう。 プロジェクト構成ファイルの作成 プロジェクトのルート ディレクト リで、 uv init コマンドを実行するといくつかのファイルが作成されます。 特に重要なpyproject.tomlだけ抜粋します。 [project] name = "devcontainer-python" version = "0.1.0" description = "Add your description here" readme = "README.md" requires-python = ">=3.12" dependencies = [] このファイルを作る方法は色々ありますので、詳細を知りたい方はuvのドキュメントを参考にしてください。 Working on projects コンテナ作成時に動作する シェルスクリプト の追加 次は、コンテナをビルドした直後だけ動作する シェルスクリプト を追加します。 この シェルスクリプト を工夫するとコンテナ内で色んな作業をして、混乱状況になってもリビルドするだけで全てを元に戻せます。 まずは、簡単な シェルスクリプト から始めましょう。.devcontainer ディレクト リにpostCreateCommand.shというファイル名で以下の内容を保存します。このファイルの改行コードはLFにてください。 #!/bin/sh # postCreateCommand.sh echo "START Install" sudo chown -R vscode:vscode . echo "FINISH Install" 内容としては、プロジェクトのルート ディレクト リ以下にあるファイルや ディレクト リのオーナー権限を全て vscode ユーザーおよび vscode グループにするというものです。 以下のコマンドをターミナルで実行して実行権限を付与して下さい。 chmod +x .devcontainer/postCreateCommand.sh Dev Containerでは、ホストOS上にあるプロジェクトのルート ディレクト リを自動的にbindマウントしています。つまり、ゲストOSであるコンテナから見える既存のファイルや ディレクト リの権限がrootになってしまいます。Dev Container内で使うユーザーをrootにしても良いのですが、個人的には例えコンテナ内であったとしても普段使いするユーザーとしてrootを使いたくはありません。このような習慣を持つことで例えば、何か悪意のあるモジュールを誤ってインストールしてしまった時の被害を少しだけ低減できます。 便利な事にDev Containerが公式に提供しているコンテナイメージでは、 vscode ユーザーをsudoersとして登録しています。参考: common-debian.sh#L209-L213 この シェルスクリプト を動かすには、devcontainer. json にpostCreateCommandというキーで シェルスクリプト の ワークスペース からの 相対パス を設定します。 { " name ": " devcontainer-python ", " image ": " mcr.microsoft.com/devcontainers/python:3.12-bookworm ", " containerEnv ": { " TZ ": " Asia/Tokyo " } , " runArgs ": [ " --init " ] , " features ": { " ghcr.io/jsburckhardt/devcontainer-features/uv:1 ": {} } , " postCreateCommand ": " ./.devcontainer/postCreateCommand.sh ", " customizations ": { " vscode ": { " settings ": { " editor.renderWhitespace ": " all ", " [json][jsonc] ": { " editor.defaultFormatter ": " esbenp.prettier-vscode ", " editor.formatOnSave ": true , " editor.codeActionsOnSave ": { " source.fixAll ": " explicit " } } } , " extensions ": [ " esbenp.prettier-vscode ", " ms-python.python ", " njpwerner.autodocstring ", " KevinRose.vsc-python-indent " ] } } } devcontainer. json を書き換えたのでDev Containerをリビルドしましょう。 コンテナ作成時に、postCreateCommand.shが動作するのを確認できます。 これは作成時しか動作しないので、例えば単に VS Code を再起動しても動作しません。つまり少々コストの高い処理を記述しても待つのは一度だけになります。 Python の仮想環境用ボリュームのマウント 次は、uvが作る Python の仮想環境について考えてみましょう。 要はプロジェクトのルート ディレクト リ以下に、作成される .venv ディレクト リをどう扱うかということです。 取りうる選択肢として最初の候補は特に気にせず .gitignore ファイルへ .venv ディレクト リを追加する、というやり方です。プロジェクトのルート ディレクト リは、Dev Containerによって既にbindマウントされているのだから、そのままにしておいてもそれほど大きな問題はありません。とはいえ、ホストOSでは実行 不能 な実行バイナリが直接 ファイルシステム 上に現れるのは、あまり気持ちよくはありません。 次の選択肢は、 .venv ディレクト リを特別扱いしてvolumeマウントすることです。これによって、ホストOS上に Python の仮想環境が直接現れなくなります。加えて、volumeマウントはbindマウントするよりもI/Oのパフォーマンスが少しだけ改善します。また、gitなどの構成管理ツールから明示的に除外しなくていいのも利点です。 volumeマウントをDev Containerに追加するには、devcontainer. json にmountsという項目の設定を追加します。 { " name ": " devcontainer-python ", " image ": " mcr.microsoft.com/devcontainers/python:3.12-bookworm ", " containerEnv ": { " TZ ": " Asia/Tokyo " } , " runArgs ": [ " --init " ] , " features ": { " ghcr.io/jsburckhardt/devcontainer-features/uv:1 ": {} } , " postCreateCommand ": " ./.devcontainer/postCreateCommand.sh ", " mounts ": [ " source=venv-${devcontainerId},target=${containerWorkspaceFolder}/.venv,type=volume " ] , " customizations ": { " vscode ": { " settings ": { " editor.renderWhitespace ": " all ", " [json][jsonc] ": { " editor.defaultFormatter ": " esbenp.prettier-vscode ", " editor.formatOnSave ": true , " editor.codeActionsOnSave ": { " source.fixAll ": " explicit " } } } , " extensions ": [ " esbenp.prettier-vscode ", " ms-python.python ", " njpwerner.autodocstring ", " KevinRose.vsc-python-indent " ] } } } devcontainer. json では、設定値の中に ${ で始まり } で終わる部分があると、その中を変数として特別扱いします。 mountsの値だけを取り出してきたのがこれです。 "source=venv-${devcontainerId},target=${containerWorkspaceFolder}/.venv,type=volume" ここでは、 devcontainerId と containerWorkspaceFolder という変数が展開されてDockerの起動オプションに渡されます。それぞれにどんな値が入っているのかは、公式のマニュアルを確認してください。 https://containers.dev/implementors/json_reference/#variables-in-devcontainerjson プロジェクトローカルに仮想環境を作る 次は、いよいよ Python 仮想環境の構築です。と言ってもuvを使っているので極めて簡単です。 先ほど作った、postCreateCommand.sh にuv用のコマンドを2行を追加するだけです。 #!/bin/sh # postCreateCommand.sh echo "START Install" sudo chown -R vscode:vscode . uv venv --allow-existing uv sync echo "FINISH Install" まず、 uv venv --allow-existing コマンドを実行して仮想環境を作ります。 --allow-existing は既に.venv ディレクト リが存在する場合には、そのまま使うようにするオプションです。 uv venv コマンドが実行された時に既存の.venv ディレクト リが存在すると削除して再生成しようとするのですが、今回は .venv ディレクト リはボリュームマウントしているので削除できずにエラーになります。 もしここで uv venv --allow-existing --python 3.11 のように既にインストール済みの Python とは違ったバージョンの Python を指定すると自動的に実行バイナリをダウンロードしてきてくれます。 次の、 uv sync コマンドを実行するとpyproject.tomlに基づいて必要なライブラリやツールを自動的にインターネットからダウンロードしてくれます。 動作を確認するために、 シェルスクリプト の変更が終わったらDev Containerをリビルドしましょう。 さぁ、 Python を動かそう ここからは、いよいよ Python のコードを動かします。 ワークスペース のルート ディレクト リには uv init が作成した hello.py というファイルがあるはずです。 def main (): print ( "Hello from devcontainer-python!" ) if __name__ == "__main__" : main() 特別さのないコードですね。このファイルを開いた状態で VS Code の右下あたりに注目してください。 このような表示になっているなら、 Python の仮想環境に配置されている インタープリタ ーが使われています。 そうでない場合は赤く囲った内側をクリックしてください。そうすると、 インタープリタ ーを選択するダイアログが表示されるので、 .venv/bin/python というパスの インタープリタ ーをクリックすることで選択してください。 プロジェクト直下の.venv ディレクト リが VS Code に正しく認識された状態になると、ターミナルを起動した際、自動的にactivate スクリプト を実行してくれます。 これでuvで管理しているモジュールや実行バイナリが実行されるようになりました。 この状態を維持するために、devcontainer. json に python.defaultInterpreterPath という設定に ".venv/bin/python" を追加します。 設定追加後のdevcontainer. json は以下のようになります。 { " name ": " devcontainer-python ", " image ": " mcr.microsoft.com/devcontainers/python:3.12-bookworm ", " containerEnv ": { " TZ ": " Asia/Tokyo " } , " runArgs ": [ " --init " ] , " features ": { " ghcr.io/jsburckhardt/devcontainer-features/uv:1 ": {} } , " postCreateCommand ": " ./.devcontainer/postCreateCommand.sh ", " mounts ": [ " source=venv-${devcontainerId},target=${containerWorkspaceFolder}/.venv,type=volume " ] , " customizations ": { " vscode ": { " settings ": { " editor.renderWhitespace ": " all ", " python.defaultInterpreterPath ": " .venv/bin/python ", " [json][jsonc] ": { " editor.defaultFormatter ": " esbenp.prettier-vscode ", " editor.formatOnSave ": true , " editor.codeActionsOnSave ": { " source.fixAll ": " explicit " } } } , " extensions ": [ " esbenp.prettier-vscode ", " ms-python.python ", " njpwerner.autodocstring ", " KevinRose.vsc-python-indent " ] } } } devcontainer. json を書き換えたらDev Containerをリビルドしましょう。 フォーマッタとLinterの導入 コードを実行できるようになったので、次はフォーマッタとLinterを導入しましょう。ターミナルを VS Code から起動して以下のコマンドを実行します。 uv add ruff --dev これによって、 Python 用のコードフォーマッタかつ、LinterであるRuffがインストールされます。 ruff これらを VS Code と連携するための設定と拡張を追加しましょう。 { " name ": " devcontainer-python ", " image ": " mcr.microsoft.com/devcontainers/python:3.12-bookworm ", " containerEnv ": { " TZ ": " Asia/Tokyo " } , " runArgs ": [ " --init " ] , " features ": { " ghcr.io/jsburckhardt/devcontainer-features/uv:1 ": {} } , " postCreateCommand ": " ./.devcontainer/postCreateCommand.sh ", " mounts ": [ " source=venv-${devcontainerId},target=${containerWorkspaceFolder}/.venv,type=volume " ] , " customizations ": { " vscode ": { " settings ": { " editor.renderWhitespace ": " all ", " python.defaultInterpreterPath ": " .venv/bin/python ", " [python] ": { " editor.defaultFormatter ": " charliermarsh.ruff ", " editor.formatOnSave ": true , " editor.codeActionsOnSave ": { " source.fixAll ": " explicit ", " source.organizeImports ": " explicit " } } , " [json][jsonc] ": { " editor.defaultFormatter ": " esbenp.prettier-vscode ", " editor.formatOnSave ": true , " editor.codeActionsOnSave ": { " source.fixAll ": " explicit " } } } , " extensions ": [ " esbenp.prettier-vscode ", " ms-python.python ", " njpwerner.autodocstring ", " KevinRose.vsc-python-indent ", " charliermarsh.ruff " ] } } } ここでは、 以下のような変更を加えています。 [python] に設定を記述することでファイル保存時にフォーマットやLint、それに伴う自動補正が行われるようにしました。 charliermarsh.ruff を VS Code 拡張として追加しました。 RuffはBlackとの互換性のためにコードを折り返す際の基準とする文字数が 88 と異様に小さいのでここだけは設定を変更します。 Ruffの設定は、pyproject.tomlに記載します。 [project] name = "devcontainer-python" version = "0.1.0" description = "Add your description here" readme = "README.md" requires-python = ">=3.12" dependencies = [] [dependency-groups] dev = [ "ruff>=0.7.1", ] [tool.ruff] line-length = 200 ruffの設定を細かく調整することは少ないと思いますが、マニュアルはこちらです。 Configuring Ruff devcontainer. json を書き換えたのでDev Containerをリビルドですよね。 pytestの導入 コードを快適に書けるようになってきたので、次はテスティング フレームワーク の導入です。 Python には多くのテスティング フレームワーク がありますが、私が一番気に入っているのはpytestです。 uvを使ってpytestを導入しましょう。以下のコマンドを実行します。 uv add pytest --dev モジュールを追加したら VS Code の設定も変更します。 { " name ": " devcontainer-python ", " image ": " mcr.microsoft.com/devcontainers/python:3.12-bookworm ", " containerEnv ": { " TZ ": " Asia/Tokyo " } , " runArgs ": [ " --init " ] , " features ": { " ghcr.io/jsburckhardt/devcontainer-features/uv:1 ": {} } , " postCreateCommand ": " ./.devcontainer/postCreateCommand.sh ", " mounts ": [ " source=venv-${devcontainerId},target=${containerWorkspaceFolder}/.venv,type=volume " ] , " customizations ": { " vscode ": { " settings ": { " editor.renderWhitespace ": " all ", " python.defaultInterpreterPath ": " .venv/bin/python ", " python.testing.pytestArgs ": [ " tests ", " --capture=tee-sys ", " -vv " ] , " python.testing.pytestEnabled ": true , " [python] ": { " editor.defaultFormatter ": " charliermarsh.ruff ", " editor.formatOnSave ": true , " editor.codeActionsOnSave ": { " source.fixAll ": " explicit ", " source.organizeImports ": " explicit " } } , " [json][jsonc] ": { " editor.defaultFormatter ": " esbenp.prettier-vscode ", " editor.formatOnSave ": true , " editor.codeActionsOnSave ": { " source.fixAll ": " explicit " } } } , " extensions ": [ " esbenp.prettier-vscode ", " ms-python.python ", " njpwerner.autodocstring ", " KevinRose.vsc-python-indent ", " charliermarsh.ruff " ] } } } ここで追加したのは、以下の二つです。 python.testing.pytestEnabled を trueにすることで VS Code がpytestを使ってテストコードを検索します。 python.testing.pytestArgs にはpytest起動時のオプションを三つ設定しています。 最初の tests はこの ディレクト リ内にあるテストコードを実行するという意味です。 次の --capture=tee-sys は、テストコード内で標準出力された内容をpytestがキャプチャしてターミナルに出力してくれます。 最後の -vv を付けることで、pytestがキャプチャした出力を途中で切らずに全て出力します。 テストコードの追加 テストコードを追加して動かしてみましょう。 ワークスペース のルート ディレクト リに tests という ディレクト リを作って、その中にtest_sample.pyというファイル名で以下の内容を保存します。 # content of test_sample.py def inc (x): return x + 1 def test_answer (): assert inc( 3 ) == 5 与えられた数字に1を加算する関数と、それをテストする関数です。 テストをターミナルから動かす まずは、テストをターミナルで動かしてみましょう。以下のコマンドを実行します。 pytest 案の定 アサーション が正しくないのでテストは失敗します。 テストを GUI から動かす 次は、 VS Code からテストを動かしてみましょう。 左側のメニューからフラスコのようなアイコンをクリックしてTESTINGビューを表示した上で右向きの三角をクリックするとテストを実行できます。もしくは、単にテスト関数の近くにある右向き三角でも構いません。 予想通りテストは失敗します。 テストをデバッガから動かす 次はテストを デバッグ 実行しましょう。コードを見てもどうしてテストが失敗するのか分からない時は、デバッガを使うと便利です。 まずは、 ブレークポイント を設定します。エディタのガッター部分をクリックすると赤い〇が付いて、そこが ブレークポイント になります。 TESTINGビューの虫アイコンがついたボタンを押して実行するとデバッガが動作します。 設定された ブレークポイント で止まると変数の中身や スタックトレース が確認できますね。 これで快適にテストが実行できるようになったので、ソフトウェア開発環境としては十分だと言えるかもしれません。 高品質なソフトウェアを作るために、もうひと踏ん張りしてみましょう。 テスト カバレッジ を計測する テスト カバレッジ を取ることで、テストコードが意図したとおりにアプリケーションコードをテストできているか確認できるようにしましょう。 テスト カバレッジ は100%を目指して ホワイトボックステスト するのに使うと極めて不毛な時間を過ごすことになります。 しかし、大体75%~85%を目途に意図したとおりにアプリケーションコードが動作しているかを確認するのに使うと非常に便利です。自分は仕様を完全に把握しているのだと思っても抜け漏れは少なからずあるものです。 pytest- cov の導入 pytestには、 Coverage.py を使ってテスト カバレッジ をとれる プラグイン があるので、それを導入しましょう。以下のコマンドを実行します。 uv add pytest- cov --dev これでpytestを実行する際に カバレッジ を取得するオプションが使えます。例えば、以下のコマンドで カバレッジ を取得できます。 pytest -- cov =. -- cov -report html このコマンドを実行後はプロジェクトのルート ディレクト リに htmlcov という ディレクト リが作成されて、その中にhtmlで作成された カバレッジ レポートが格納されています。 HTMLでそれなりにきれいな表示がされていて、ソフトウェア開発が主たる業務でない人にとっては十分なレポートだと言えます。 テスト カバレッジ の微調整 テスト カバレッジ を取り始めると、細かい調整が必要になっていきます。 私自身、それほど Python に詳しいわけではありませんが、実案件での利用を通じて発生した微調整をいくつかご紹介します。 実行時オプションの調整 pyproject.tomlに [tool.coverage.run] という項目を追加します。 [project] name = "devcontainer-python" version = "0.1.0" description = "Add your description here" readme = "README.md" requires-python = ">=3.12" dependencies = [] [dependency-groups] dev = [ "pytest-cov>=5.0.0", "pytest>=8.3.3", "ruff>=0.7.1", ] [tool.ruff] line-length = 200 [tool.coverage.run] branch = true source = ["tests"] omit = ["tests/fixtures/*"] data_file = ".pytest_cache/.coverage" 設定項目は四つです。 branch を有効化することでブランチ カバレッジ を取得するようにしています。これによって カバレッジ の計測コストが若干上がります。 source に カバレッジ の計測対象となるファイルが格納されている ディレクト リを指定しています。 omit では、逆に カバレッジ の計測対象としないファイルが格納されている ディレクト リを指定しています。 data_file では、 カバレッジ 計測データのバイナリファイルを格納する ディレクト リをプロジェクトのルート ディレクト リから.pytest_cache ディレクト リ内に移動することで、普段はその存在を気にしないで済むようにしています。 カバレッジ 除外の調整 ここでは、pyproject.tomlに [tool.coverage.report] という項目を追加して細粒度の構文レベルで カバレッジ 取得対象から ソースコード を除外しています。 [project] name = "devcontainer-python" version = "0.1.0" description = "Add your description here" readme = "README.md" requires-python = ">=3.12" dependencies = [] [dependency-groups] dev = [ "pytest-cov>=5.0.0", "pytest>=8.3.3", "ruff>=0.7.1", ] [tool.ruff] line-length = 200 [tool.coverage.run] branch = true source = ["tests"] omit = ["tests/fixtures/*"] data_file = ".pytest_cache/.coverage" [tool.coverage.report] exclude_lines = [ "pragma: no cover", "def __repr__", "def __str__", "raise AssertionError", "raise NotImplementedError", "if __name__ == .__main__.:", "if TYPE_CHECKING:", "if typing.TYPE_CHECKING:", ] それぞれの詳細については、ここでは説明しません。 VS Code に カバレッジ レポートを統合する コードを書いている プログラマー としては、 カバレッジ レポートは普段使っているエディタ上に表示されてほしいものです。レポートを見るためだけにウィンドウを切り替えるのはわずらわしいですからね。 というわけで、拡張の導入と設定です。コード カバレッジ を表示する VS Code 拡張はいくつかあるのですが、私が試した範囲内では、 Coverage Gutters が最も期待通りに動作しました。 Coverage Guttersは、Coverage.pyが出力した XML のレポートを入力情報として使いますので、pyproject.tomlにレポートの出力先を設定します。 [project] name = "devcontainer-python" version = "0.1.0" description = "Add your description here" readme = "README.md" requires-python = ">=3.12" dependencies = [] [dependency-groups] dev = [ "pytest-cov>=5.0.0", "pytest>=8.3.3", "ruff>=0.7.1", ] [tool.ruff] line-length = 200 [tool.coverage.run] branch = true source = ["tests"] omit = ["tests/fixtures/*"] data_file = ".pytest_cache/.coverage" [tool.coverage.report] exclude_lines = [ "pragma: no cover", "def __repr__", "def __str__", "raise AssertionError", "raise NotImplementedError", "if __name__ == .__main__.:", "if TYPE_CHECKING:", "if typing.TYPE_CHECKING:", ] [tool.coverage.xml] output = ".pytest_cache/coverage.xml" 最後の二行が追加した項目です。これによって、出力レポートのタイプとして XML を指定した際に、.pytest_cache/coverage. xml へファイルが出力されます。 次は、devcontainer. json を修正します。 { " name ": " devcontainer-python ", " image ": " mcr.microsoft.com/devcontainers/python:3.12-bookworm ", " containerEnv ": { " TZ ": " Asia/Tokyo " } , " runArgs ": [ " --init " ] , " features ": { " ghcr.io/jsburckhardt/devcontainer-features/uv:1 ": {} } , " postCreateCommand ": " ./.devcontainer/postCreateCommand.sh ", " mounts ": [ " source=venv-${devcontainerId},target=${containerWorkspaceFolder}/.venv,type=volume " ] , " customizations ": { " vscode ": { " settings ": { " editor.renderWhitespace ": " all ", " python.defaultInterpreterPath ": " .venv/bin/python ", " python.testing.pytestArgs ": [ " tests ", " --capture=tee-sys ", " -vv " ] , " python.testing.pytestEnabled ": true , " [python] ": { " editor.defaultFormatter ": " charliermarsh.ruff ", " editor.formatOnSave ": true , " editor.codeActionsOnSave ": { " source.fixAll ": " explicit ", " source.organizeImports ": " explicit " } } , " coverage-gutters.showLineCoverage ": true , " coverage-gutters.showRulerCoverage ": true , " coverage-gutters.coverageFileNames ": [ " .pytest_cache/coverage.xml " ] , " [json][jsonc] ": { " editor.defaultFormatter ": " esbenp.prettier-vscode ", " editor.formatOnSave ": true , " editor.codeActionsOnSave ": { " source.fixAll ": " explicit " } } } , " extensions ": [ " esbenp.prettier-vscode ", " ms-python.python ", " njpwerner.autodocstring ", " KevinRose.vsc-python-indent ", " charliermarsh.ruff ", " ryanluker.vscode-coverage-gutters " ] } } } ここでは、三つの設定項目を追加すると共にインストールする拡張として ryanluker.vscode-coverage-gutters を追加しています。 coverage-gutters.showLineCoverage を有効化することで、行全体に色が付くようになります。 coverage-gutters.showRulerCoverage を有効化することで、行の左側にある目盛り部分に色が付くようになります。 coverage-gutters.coverageFileNames に設定しているパスと、pyproject.tomlが一致していることで、 カバレッジ レポートの結果を ryanluker.vscode-coverage-gutters が正しく処理できます。 devcontainer. json を書き換えたのでDev Containerをリビルドしましょう。 VS Code における カバレッジ レポートの確認方法 まずは、以下のコマンドを実行して カバレッジ レポートを作成します。 pytest -- cov =. -- cov -report xml 相変わらずテストは失敗していますね。しかし、 .pytest_cache/coverage.xml というファイルは出力されているはずです。これを使って カバレッジ を確認していきましょう。 VS Code の左下あたりに注目してください。 〇Watch という表示があるはずです。これをクリックすると カバレッジ レポートの表示が有効化されます。 カバレッジ レポートがエディタ上に表示されるとこのようになります。 これで、 カバレッジ レポートを確認しながらコードを書けるようになりました。 そろそろこの記事も終盤に差し掛かってきています。もう少しだけお付き合いください。 タ スクラン ナーの導入 最後は、プロジェクト構成ファイルであるpyproject.tomlに定型化された作業をタスクとして記述する方法を説明します。 長いコマンドでも構成ファイル内に書いてあればコマンドの実行ミスは無くなります。また、 GitHub ActionsのようなCIワークフローを構成する際にも、すでにタスクが定義されていれば非常に少ない 工数 で実現できます。 様々なタスク定義ツールがありますが、私が気に入って使っているのは、 Poe the Poet です。以下のコマンドを実行してインストールしましょう。 uv add poethepoet --dev インストールが終わったら、pyproject.tomlにここまで構成してきたタスクをいくつか書いてみましょう。 [project] name = "devcontainer-python" version = "0.1.0" description = "Add your description here" readme = "README.md" requires-python = ">=3.12" dependencies = [] [dependency-groups] dev = [ "poethepoet>=0.29.0", "pytest-cov>=5.0.0", "pytest>=8.3.3", "ruff>=0.7.1", ] [tool.ruff] line-length = 200 [tool.coverage.run] branch = true source = ["tests"] omit = ["tests/fixtures/*"] data_file = ".pytest_cache/.coverage" [tool.coverage.report] exclude_lines = [ "pragma: no cover", "def __repr__", "def __str__", "raise AssertionError", "raise NotImplementedError", "if __name__ == .__main__.:", "if TYPE_CHECKING:", "if typing.TYPE_CHECKING:", ] [tool.coverage.xml] output = ".pytest_cache/coverage.xml" [tool.poe.tasks] lint = "ruff check ." test = "pytest" cover = "pytest --cov=. --cov-report xml" fmt = "ruff format . --check" build = ["fmt", "lint", "test"] [tool.poe.tasks] 以下に書かれているものが実行可能なタスクです。ここでは、五つのタスクを定義しています。 lint タスクでは、 Ruffによるコードの検査を実行します。 testタスクでは、pytestを実行します。 coverタスクでは、コード カバレッジ を取得しながらpytestを実行します。 fmtタスクでは、Ruffによるコードの整形を実行します。 buildタスクでは、 fmtタスクを実行した後にlintタスクを実行し最後にtestタスクを実行します。 これらのタスクは、 poe コマンドから実行できます。例えば、lintタスクを実行する際には、以下のようなコマンドを実行します。 poe lint 非常に簡単ですね。testタスクのように内部的にはコマンドを引数なしで実行しているだけでもタスクとして定義しているのは、ツールの移行コストを下げるためです。 例えば、今回はlinterとしてRuffを使っていますが、使い込む過程で回避しようのない不具合が見つかりFlake8のような実績のあるツールに切り戻すことはありえます。そういった時にCIやCDのワークフローに対する影響をできるかぎり減らして移行できるようにするには、タ スクラン ナーによるコマンドの抽象化が有効です。 uvでのタ スクラン ナー poethepoetのような追加のライブラリ無しにuvだけで動作するタ スクラン ナーについて議論しているチケットがあります。 Using uv run as a task runner これが実装されれば、poethepoet のインストールは不要になるかもしれませんね。 まとめ ここまで、Dev Containerで Python アプリケーションの開発環境を構築する方法について説明してきました。 プロジェクトメンバーができるかぎり同じ環境でアプリケーション開発を行うことは、浪費される 工数 を減らしプロジェクトとして価値のある作業に集中するために必要な事です。 この記事では Python 開発環境を扱いましたが、同様の考え方でTypeScriptや、Rust、 Ruby 、 Java といった他の言語の開発環境を構築できます。 また、 GitHub Codespacesのような クラウド 開発環境の利用も難なくできます。 作っているアプリケーションの種類や、プロジェクトチーム内での役割次第ではブラウザ一つ動く環境さえあればソフトウェア開発できるというのは、非常に魅力的ですよね。 ここまで読んでいただいた皆さん、本当にお疲れさまでした。この記事を読んだ皆さんが、利便性の高い開発環境で本来のソフトウェア開発に集中できることを願って記事の結びとします。 この記事で紹介している開発環境の構成ファイル 最後に作った構成ファイルをまとめて紹介します。 .devcontainer/devcontainer. json { " name ": " devcontainer-python ", " image ": " mcr.microsoft.com/devcontainers/python:3.12-bookworm ", " containerEnv ": { " TZ ": " Asia/Tokyo " } , " runArgs ": [ " --init " ] , " features ": { " ghcr.io/jsburckhardt/devcontainer-features/uv:1 ": {} } , " postCreateCommand ": " ./.devcontainer/postCreateCommand.sh ", " mounts ": [ " source=venv-${devcontainerId},target=${containerWorkspaceFolder}/.venv,type=volume " ] , " customizations ": { " vscode ": { " settings ": { " editor.renderWhitespace ": " all ", " python.defaultInterpreterPath ": " .venv/bin/python ", " python.testing.pytestArgs ": [ " tests ", " --capture=tee-sys ", " -vv " ] , " python.testing.pytestEnabled ": true , " [python] ": { " editor.defaultFormatter ": " charliermarsh.ruff ", " editor.formatOnSave ": true , " editor.codeActionsOnSave ": { " source.fixAll ": " explicit ", " source.organizeImports ": " explicit " } } , " coverage-gutters.showLineCoverage ": true , " coverage-gutters.showRulerCoverage ": true , " coverage-gutters.coverageFileNames ": [ " .pytest_cache/coverage.xml " ] , " [json][jsonc] ": { " editor.defaultFormatter ": " esbenp.prettier-vscode ", " editor.formatOnSave ": true , " editor.codeActionsOnSave ": { " source.fixAll ": " explicit " } } } , " extensions ": [ " esbenp.prettier-vscode ", " ms-python.python ", " njpwerner.autodocstring ", " KevinRose.vsc-python-indent ", " charliermarsh.ruff ", " ryanluker.vscode-coverage-gutters " ] } } } .devcontainer/postCreateCommand.sh 改行コードはLFです。実行権限の付与を忘れずに。 #!/bin/sh # postCreateCommand.sh echo "START Install" sudo chown -R vscode:vscode . uv venv --allow-existing uv sync echo "FINISH Install" .gitignore 改行コードはLFです。 # Python-generated files __pycache__/ *.py[oc] build/ dist/ wheels/ *.egg-info # Virtual environments .venv *_cache pyproject.toml [project] name = "devcontainer-python" version = "0.1.0" description = "Add your description here" readme = "README.md" requires-python = ">=3.12" dependencies = [] [dependency-groups] dev = [ "poethepoet>=0.29.0", "pytest-cov>=5.0.0", "pytest>=8.3.3", "ruff>=0.7.1", ] [tool.ruff] line-length = 200 [tool.coverage.run] branch = true source = ["tests"] omit = ["tests/fixtures/*"] data_file = ".pytest_cache/.coverage" [tool.coverage.report] exclude_lines = [ "pragma: no cover", "def __repr__", "def __str__", "raise AssertionError", "raise NotImplementedError", "if __name__ == .__main__.:", "if TYPE_CHECKING:", "if typing.TYPE_CHECKING:", ] [tool.coverage.xml] output = ".pytest_cache/coverage.xml" [tool.poe.tasks] lint = "ruff check ." test = "pytest" cover = "pytest --cov=. --cov-report xml" fmt = "ruff format . --check" build = ["fmt", "lint", "test"] 執筆: @sato.taichi 、レビュー: @mizuno.kazuhiro ( Shodo で執筆されました )
こんにちは。X(クロス) イノベーション 本部 ソフトウェアデザインセンター セキュリティグループの耿です。 ElastiCache for Redis クラスタ ーの認証・認可方式のうち、 ロールベースのアクセスコントロール(RBAC) を AWS CDK で実装する方法に関する日本語の記事が見当たらなかったため、書き残しておきます。 ElastiCache for Redis の認証・認可方式 リソース構成 CDK による実装のサンプル ユーザーパスワード ユーザーとユーザーグループ VPC Redis クラスター Secrets Manager シークレットへのアクセス許可 ElastiCache for Redis の認証・認可方式 ElastiCache for Redis クラスタ ーに対する認証・認可には以下の方法があります。この記事では RBAC を対象とします。 認証なし Redis クラスタ ーと通信さえできれば全てのコマンドを実行可能 Redis AUTH 単一の トーク ン(パスワード)による認証 転送中の暗号化 ( TLS ) の有効化が必須 認証が成功すれば、Redis クラスタ ーの全コマンドを実行可能 RBAC(ロールベースのアクセスコントロール) Redis v6.0から導入された ACL を利用した認証・認可方式 ユーザー名とパスワードによる認証 複数のユーザーを作成可能 それぞれのユーザーが実施可能なコマンドとアクセス可能なキャッシュキーを制限可能 IAM Redis v7.0 から利用可能な認証・認可方式 有効期間が短い IAM 認証 トーク ンを利用 複数のユーザーを作成可能 それぞれのユーザーが実施可能なコマンドとアクセス可能なキャッシュキーを制限可能 IAM ポリシーにより、どの Redis ユーザーを利用できるか制御可能 リソース構成 CDK による実装を見る前に、リソース構成の概観を整理しておきます。 1つの Redis クラスタ ーには複数の Redis ユーザーグループを追加可能 1つの Redis ユーザーグループには複数の Redis ユーザーを所属可能 ユーザーはユーザー名とユーザーIDを持ち、複数のパスワードを設定可能 ユーザーに設定するアクセス文字列で、Redis クラスタ ーにおける実施可能なコマンドとアクセス可能なキャッシュキーが制限される Redis ユーザーのパスワードを IaC へ記載するのを回避するため、Secrets Manager を利用 アプリケーションの権限に応じて Secrets Manager シークレットへのアクセスを許可し、アプリケーション側の Redis クライアントで Redis ユーザー名とパスワードを利用する CDK による実装のサンプル こちら を参考にしました。 以下の例はシンプルに、1ユーザーグループ/1ユーザーの構成にしています。 ユーザーパスワード Secrets Managerでユーザーパスワードを生成します。 default という名前のユーザーは必須で作成する必要があり、今回はこの default ユーザーをそのまま利用することにします。以下はデフォルトの暗号鍵を利用しています。鍵の管理が必要な場合は別途 KMS キーを作成してください。 const userName = "default" ; const rbacUserSecret = new secretsmanager.Secret ( this , "RedisRbacUserSecret" , { secretName: "RedisRbacUserSecret" , generateSecretString: { secretStringTemplate: JSON .stringify ( { username: userName } ), generateStringKey: "password" , excludeCharacters: "@%*()_+=`~{}|[]\\:\";'?,./" , } , } ); ユーザーとユーザーグループ 執筆時点では CDK には ElastiCache の L2 コンスト ラク トが用意されていないため、L1 コンスト ラク トで作成します。 アクセス文字列 ( accessString ) でユーザーの権限を制限できますが、今回は全コマンドと全キャッシュキーへのアクセスを許可しています。 const userId = "user-1" ; const rbacUser = new elasticache.CfnUser ( this , "RedisRbacUser" , { userId: userId , userName: userName , engine: "redis" , accessString: "on ~* +@all" , passwords: [ rbacUserSecret.secretValueFromJson ( "password" ) .unsafeUnwrap () ] , } ); const rbacUserGroup = new elasticache.CfnUserGroup ( this , "RedisRbacUserGroup" , { userGroupId: "user-group" , engine: "redis" , userIds: [ rbacUser.userId ] , } ); rbacUserGroup.node.addDependency ( rbacUser ); CfnUser に渡すために、Secrets Manager シークレットで生成したパスワードに対して unsafeUnwrap() を呼んでいます。平文のパスワードは Redis ユーザーの作成に使われ、閲覧可能な場所には出現しないため安全な使い方になります。 VPC Redis クラスタ ーを配置する VPC とセキュリティグループを作成します。 const subnetName = "Isolated" ; const vpc = new ec2.Vpc ( this , "Vpc" , { subnetConfiguration: [ { cidrMask: 24 , name: subnetName , subnetType: ec2.SubnetType.PRIVATE_ISOLATED , } ] } ); const redisSG = new ec2.SecurityGroup ( this , 'RedisSG' , { vpc , allowAllOutbound: false , } ); Redis クラスタ ー Redis クラスタ ーを作成し、先に作成したユーザーグループを関連付けます。 const cluster = new elasticache.CfnReplicationGroup ( this , "RedisReplicationGroup" , { replicationGroupDescription: "my-replication-group" , engine: "redis" , engineVersion: "7.0" , cacheNodeType: "cache.t3.micro" , cacheSubnetGroupName: new elasticache.CfnSubnetGroup ( this , "SubnetGroup" , { description: "my-subnet-group" , subnetIds: vpc.selectSubnets ( { subnetGroupName: subnetName } ) .subnetIds , } ) .ref , cacheParameterGroupName: new elasticache.CfnParameterGroup ( this , "ParameterGroup" , { description: "my-parameter-group" , cacheParameterGroupFamily: "redis7" , } ) .ref , numNodeGroups: 1 , replicasPerNodeGroup: 1 , securityGroupIds: [ redisSG.securityGroupId ] , atRestEncryptionEnabled: true , transitEncryptionEnabled: true , userGroupIds: [ rbacUserGroup.userGroupId ] , } ); cluster.node.addDependency ( rbacUserGroup ); Secrets Manager シークレットへのアクセス許可 あとは Redis クラスタ ーへアクセスするアプリケーションが Secrets Manager シークレットにアクセスできるように、読み取り権限を付与すれば良いです。(CDKコードは省略します) CDK による Redis の RBAC の実装例はあまり見かけませんが、簡単に実装できました。 お読みいただいてありがとうございました。 私たちは同じチームで働いてくれる仲間を大募集しています!たくさんのご応募をお待ちしています。 セキュリティエンジニア(セキュリティ設計) 執筆: @kou.kinyo 、レビュー: @nakamura.toshihiro ( Shodo で執筆されました )
こんにちは。X(クロス) イノベーション 本部 ソフトウェアデザインセンター セキュリティグループの耿です。 ElastiCache for Redis クラスタ ーの認証・認可方式のうち、 ロールベースのアクセスコントロール(RBAC) を AWS CDK で実装する方法に関する日本語の記事が見当たらなかったため、書き残しておきます。 ElastiCache for Redis の認証・認可方式 リソース構成 CDK による実装のサンプル ユーザーパスワード ユーザーとユーザーグループ VPC Redis クラスター Secrets Manager シークレットへのアクセス許可 ElastiCache for Redis の認証・認可方式 ElastiCache for Redis クラスタ ーに対する認証・認可には以下の方法があります。この記事では RBAC を対象とします。 認証なし Redis クラスタ ーと通信さえできれば全てのコマンドを実行可能 Redis AUTH 単一の トーク ン(パスワード)による認証 転送中の暗号化 ( TLS ) の有効化が必須 認証が成功すれば、Redis クラスタ ーの全コマンドを実行可能 RBAC(ロールベースのアクセスコントロール) Redis v6.0から導入された ACL を利用した認証・認可方式 ユーザー名とパスワードによる認証 複数のユーザーを作成可能 それぞれのユーザーが実施可能なコマンドとアクセス可能なキャッシュキーを制限可能 IAM Redis v7.0 から利用可能な認証・認可方式 有効期間が短い IAM 認証 トーク ンを利用 複数のユーザーを作成可能 それぞれのユーザーが実施可能なコマンドとアクセス可能なキャッシュキーを制限可能 IAM ポリシーにより、どの Redis ユーザーを利用できるか制御可能 リソース構成 CDK による実装を見る前に、リソース構成の概観を整理しておきます。 1つの Redis クラスタ ーには複数の Redis ユーザーグループを追加可能 1つの Redis ユーザーグループには複数の Redis ユーザーを所属可能 ユーザーはユーザー名とユーザーIDを持ち、複数のパスワードを設定可能 ユーザーに設定するアクセス文字列で、Redis クラスタ ーにおける実施可能なコマンドとアクセス可能なキャッシュキーが制限される Redis ユーザーのパスワードを IaC へ記載するのを回避するため、Secrets Manager を利用 アプリケーションの権限に応じて Secrets Manager シークレットへのアクセスを許可し、アプリケーション側の Redis クライアントで Redis ユーザー名とパスワードを利用する CDK による実装のサンプル こちら を参考にしました。 以下の例はシンプルに、1ユーザーグループ/1ユーザーの構成にしています。 ユーザーパスワード Secrets Managerでユーザーパスワードを生成します。 default という名前のユーザーは必須で作成する必要があり、今回はこの default ユーザーをそのまま利用することにします。以下はデフォルトの暗号鍵を利用しています。鍵の管理が必要な場合は別途 KMS キーを作成してください。 const userName = "default" ; const rbacUserSecret = new secretsmanager.Secret ( this , "RedisRbacUserSecret" , { secretName: "RedisRbacUserSecret" , generateSecretString: { secretStringTemplate: JSON .stringify ( { username: userName } ), generateStringKey: "password" , excludeCharacters: "@%*()_+=`~{}|[]\\:\";'?,./" , } , } ); ユーザーとユーザーグループ 執筆時点では CDK には ElastiCache の L2 コンスト ラク トが用意されていないため、L1 コンスト ラク トで作成します。 アクセス文字列 ( accessString ) でユーザーの権限を制限できますが、今回は全コマンドと全キャッシュキーへのアクセスを許可しています。 const userId = "user-1" ; const rbacUser = new elasticache.CfnUser ( this , "RedisRbacUser" , { userId: userId , userName: userName , engine: "redis" , accessString: "on ~* +@all" , passwords: [ rbacUserSecret.secretValueFromJson ( "password" ) .unsafeUnwrap () ] , } ); const rbacUserGroup = new elasticache.CfnUserGroup ( this , "RedisRbacUserGroup" , { userGroupId: "user-group" , engine: "redis" , userIds: [ rbacUser.userId ] , } ); rbacUserGroup.node.addDependency ( rbacUser ); CfnUser に渡すために、Secrets Manager シークレットで生成したパスワードに対して unsafeUnwrap() を呼んでいます。平文のパスワードは Redis ユーザーの作成に使われ、閲覧可能な場所には出現しないため安全な使い方になります。 VPC Redis クラスタ ーを配置する VPC とセキュリティグループを作成します。 const subnetName = "Isolated" ; const vpc = new ec2.Vpc ( this , "Vpc" , { subnetConfiguration: [ { cidrMask: 24 , name: subnetName , subnetType: ec2.SubnetType.PRIVATE_ISOLATED , } ] } ); const redisSG = new ec2.SecurityGroup ( this , 'RedisSG' , { vpc , allowAllOutbound: false , } ); Redis クラスタ ー Redis クラスタ ーを作成し、先に作成したユーザーグループを関連付けます。 const cluster = new elasticache.CfnReplicationGroup ( this , "RedisReplicationGroup" , { replicationGroupDescription: "my-replication-group" , engine: "redis" , engineVersion: "7.0" , cacheNodeType: "cache.t3.micro" , cacheSubnetGroupName: new elasticache.CfnSubnetGroup ( this , "SubnetGroup" , { description: "my-subnet-group" , subnetIds: vpc.selectSubnets ( { subnetGroupName: subnetName } ) .subnetIds , } ) .ref , cacheParameterGroupName: new elasticache.CfnParameterGroup ( this , "ParameterGroup" , { description: "my-parameter-group" , cacheParameterGroupFamily: "redis7" , } ) .ref , numNodeGroups: 1 , replicasPerNodeGroup: 1 , securityGroupIds: [ redisSG.securityGroupId ] , atRestEncryptionEnabled: true , transitEncryptionEnabled: true , userGroupIds: [ rbacUserGroup.userGroupId ] , } ); cluster.node.addDependency ( rbacUserGroup ); Secrets Manager シークレットへのアクセス許可 あとは Redis クラスタ ーへアクセスするアプリケーションが Secrets Manager シークレットにアクセスできるように、読み取り権限を付与すれば良いです。(CDKコードは省略します) CDK による Redis の RBAC の実装例はあまり見かけませんが、簡単に実装できました。 お読みいただいてありがとうございました。 私たちは同じチームで働いてくれる仲間を大募集しています!たくさんのご応募をお待ちしています。 セキュリティエンジニア(セキュリティ設計) 執筆: @kou.kinyo 、レビュー: @nakamura.toshihiro ( Shodo で執筆されました )
こんにちは、金融ソリューション事業部の若本です。 前回の記事に続き、AIを用いてラップの生成を行います。 前回の記事は こちら です。 前回までのあらすじ ChatGPTでフリースタイル調のラップの生成をするためには、韻を別途取得するAIが必要でした。 そこで、語感踏みもある程度可能、かつ高速な韻の検索を実装しました。 韻を生成するパイプライン 今回の主目的は 「韻が踏めるラップをAIで生成すること」 です。 そのための最小限の構成として、韻のセットを複数取得して、その後にラップを生成することを考えます。 処理のパイプラインは下記の通りです。 「韻の検索」に加えて、主に追加で必要なものは下記の通りです。 ChatGPTに与えるプロンプト 韻を自動で取得する機能 1) ChatGPTに与えるプロンプト 下記のpromptをChatGPTに与えます。 単語/フレーズのセットを使用して、出力にラップを表示してください。 セットの中の単語/フレーズは連続して使用すること。 また、各セットのラップは短くし、同じ長さに収めること。 絶対に英語のみで出力してはいけません。 あなたはラッパーです。相手に向けてどちらかの方針でラップしてください。 1. 自分をアピールする 2. 相手を批判する 使用可能な表現:a yo、you know?、~してやる、そんな感じで、yeah 絶対に使用してはいけない表現:~だ、文末が「だ」で終わる表現 例: 単語/フレーズのセット: 1:単語A、単語B... 2:単語C、単語D... 出力: 1: ~省略~ 2: ~省略~ 単語/フレーズのセット: {data} 出力: {data}内には「1:単語A、単語B...」のような形で踏ませたい韻の情報を自動的に入れます。 ※「例:」の箇所は実際に使用したものから変えています。実際には、踏む単語と対応するラップをfew-shotとして与えました。 【試行錯誤した点】 条件付け(1~4行目) 口語調があまりにも多かった ため、短く区切るように指示しました。 ばらばらに韻を使うのではなく、なるべく隣接した場所で使うよう指示しています。 英語のフレーズを登録しているのですが、 英語のフレーズばかりだと英語でラップを始めます。 そのため、英語だけにしないように指示しています。 スタンス(6~8行目) 韻を踏む単語に引っ張られ過ぎて、ラッパーでなくなる ことが多かったのでスタンスを明確にしました。 表現の指示(10~11行目) ラッパーのよくある口癖を入れています。逆に、 ラッパーがあまり使わない印象のある「~だ」が目立ったので禁止しました。 few-shot(13行目~) 実際のラッパーの踏み方を例として与えました。出力の形式を覚えさせる意味合いが強いです。 OpenAIから プロンプトのTips も公開されていますので、適宜ご参照ください。 「~してはいけません」は守られない場合も多々あるため、禁止したい表現は英語で指示したほうが良い結果になりそうです。改良の余地は多々ありますが、目的達成には上記で十分でした。 2) 韻を自動で取得する機能 前回の記事の「韻の検索」を用いて、ランダムに単語のセットをN個選出します。 セットの中に含まれる単語/フレーズ数はM個です。 N, Mともにランダムな数字としました。どちらも最小値/最大値をお好みで指定します。 今回は多様性重視&ランダム性重視ということでこのような仕様としています。 選んだセット同士の相性が良くない可能性はありますが、何度も試していいものが出てくることを期待します。いわば 数打ちゃ当たる方式 です。 結果 上記の追加処理を統合します。筆者は flet を用いたアプリにしました。 ボタンをクリックすると5秒ほどで生成されたラップが返ってきます。 制作者の思い入れ補正もあると思いますが、手軽に出せて良い感じです。 ポンポン出せるだけでなく、たまにくらうものもあるので、ボタンを押す手が止まりません。 なお、韻の検索、ChatGPTの生成ともにランダムですので同じバースは出てきません。 ChatGPT バース集 以下、自動で生成されたラップのうち、くらったものをいくつか紹介します。 俺のラップで済ませる 言葉で訴える 伝わるはず 他のラッパーは食われる 俺の経験を踏まえる でもやっぱり好かれる ゆとりのあるstoryを語る 俺の強みはそこにある 解説:「済ませる/訴える/食われる/踏まえる/好かれる」「story/強み」の怒涛の踏み。言ってることは分かるようで分からないですが、余裕と自信を感じます。 Yo, check the format 俺のスタイルはforward Tobaccoの匂いが漂うstreet corner でも俺のラップはnever later このエリアで俺がrule the paper 言葉はweapon like a samurai saber 解説:英語も柔軟に取り込めています。オシャレ感があってすきです(語彙力)。 I write my rhymes with precision, like 清書 My stage presence is like a grand 劇場 My style's a fusion of East and 西洋 My skills keep growing, never stop 成長 解説:英語が出力の大半を占めることも。「My style's a fusion of East and 西洋」は韻を踏みつつも、このバースを象徴している良い パンチライン です。 多言語を操るChatGPTだからこその個性的なバース ですね。 おわりに 今回は手軽な方法でChatGPTのラップ生成を行いました。 上記のクオリティのものがザラに出てくるので、作っていて非常に楽しかったです。 今回は行っていませんが、対戦相手にアンサーしたり、曲のサンプリングをしたりなどもパイプラインを工夫すれば対応できるポテンシャルはあります(ここまでできればThis is HIPHOP ですね)。 ここまでお読みいただき、ありがとうございました。 皆さんもぜひGPTで色々遊んでみてください。Peace! 執筆: @wakamoto.ryosuke 、レビュー: @yamada.y ( Shodo で執筆されました )
こんにちは、金融ソリューション事業部の若本です。 前回の記事に続き、AIを用いてラップの生成を行います。 前回の記事は こちら です。 前回までのあらすじ ChatGPTでフリースタイル調のラップの生成をするためには、韻を別途取得するAIが必要でした。 そこで、語感踏みもある程度可能、かつ高速な韻の検索を実装しました。 韻を生成するパイプライン 今回の主目的は 「韻が踏めるラップをAIで生成すること」 です。 そのための最小限の構成として、韻のセットを複数取得して、その後にラップを生成することを考えます。 処理のパイプラインは下記の通りです。 「韻の検索」に加えて、主に追加で必要なものは下記の通りです。 ChatGPTに与えるプロンプト 韻を自動で取得する機能 1) ChatGPTに与えるプロンプト 下記のpromptをChatGPTに与えます。 単語/フレーズのセットを使用して、出力にラップを表示してください。 セットの中の単語/フレーズは連続して使用すること。 また、各セットのラップは短くし、同じ長さに収めること。 絶対に英語のみで出力してはいけません。 あなたはラッパーです。相手に向けてどちらかの方針でラップしてください。 1. 自分をアピールする 2. 相手を批判する 使用可能な表現:a yo、you know?、~してやる、そんな感じで、yeah 絶対に使用してはいけない表現:~だ、文末が「だ」で終わる表現 例: 単語/フレーズのセット: 1:単語A、単語B... 2:単語C、単語D... 出力: 1: ~省略~ 2: ~省略~ 単語/フレーズのセット: {data} 出力: {data}内には「1:単語A、単語B...」のような形で踏ませたい韻の情報を自動的に入れます。 ※「例:」の箇所は実際に使用したものから変えています。実際には、踏む単語と対応するラップをfew-shotとして与えました。 【試行錯誤した点】 条件付け(1~4行目) 口語調があまりにも多かった ため、短く区切るように指示しました。 ばらばらに韻を使うのではなく、なるべく隣接した場所で使うよう指示しています。 英語のフレーズを登録しているのですが、 英語のフレーズばかりだと英語でラップを始めます。 そのため、英語だけにしないように指示しています。 スタンス(6~8行目) 韻を踏む単語に引っ張られ過ぎて、ラッパーでなくなる ことが多かったのでスタンスを明確にしました。 表現の指示(10~11行目) ラッパーのよくある口癖を入れています。逆に、 ラッパーがあまり使わない印象のある「~だ」が目立ったので禁止しました。 few-shot(13行目~) 実際のラッパーの踏み方を例として与えました。出力の形式を覚えさせる意味合いが強いです。 OpenAIから プロンプトのTips も公開されていますので、適宜ご参照ください。 「~してはいけません」は守られない場合も多々あるため、禁止したい表現は英語で指示したほうが良い結果になりそうです。改良の余地は多々ありますが、目的達成には上記で十分でした。 2) 韻を自動で取得する機能 前回の記事の「韻の検索」を用いて、ランダムに単語のセットをN個選出します。 セットの中に含まれる単語/フレーズ数はM個です。 N, Mともにランダムな数字としました。どちらも最小値/最大値をお好みで指定します。 今回は多様性重視&ランダム性重視ということでこのような仕様としています。 選んだセット同士の相性が良くない可能性はありますが、何度も試していいものが出てくることを期待します。いわば 数打ちゃ当たる方式 です。 結果 上記の追加処理を統合します。筆者は flet を用いたアプリにしました。 ボタンをクリックすると5秒ほどで生成されたラップが返ってきます。 制作者の思い入れ補正もあると思いますが、手軽に出せて良い感じです。 ポンポン出せるだけでなく、たまにくらうものもあるので、ボタンを押す手が止まりません。 なお、韻の検索、ChatGPTの生成ともにランダムですので同じバースは出てきません。 ChatGPT バース集 以下、自動で生成されたラップのうち、くらったものをいくつか紹介します。 俺のラップで済ませる 言葉で訴える 伝わるはず 他のラッパーは食われる 俺の経験を踏まえる でもやっぱり好かれる ゆとりのあるstoryを語る 俺の強みはそこにある 解説:「済ませる/訴える/食われる/踏まえる/好かれる」「story/強み」の怒涛の踏み。言ってることは分かるようで分からないですが、余裕と自信を感じます。 Yo, check the format 俺のスタイルはforward Tobaccoの匂いが漂うstreet corner でも俺のラップはnever later このエリアで俺がrule the paper 言葉はweapon like a samurai saber 解説:英語も柔軟に取り込めています。オシャレ感があってすきです(語彙力)。 I write my rhymes with precision, like 清書 My stage presence is like a grand 劇場 My style's a fusion of East and 西洋 My skills keep growing, never stop 成長 解説:英語が出力の大半を占めることも。「My style's a fusion of East and 西洋」は韻を踏みつつも、このバースを象徴している良い パンチライン です。 多言語を操るChatGPTだからこその個性的なバース ですね。 おわりに 今回は手軽な方法でChatGPTのラップ生成を行いました。 上記のクオリティのものがザラに出てくるので、作っていて非常に楽しかったです。 今回は行っていませんが、対戦相手にアンサーしたり、曲のサンプリングをしたりなどもパイプラインを工夫すれば対応できるポテンシャルはあります(ここまでできればThis is HIPHOP ですね)。 ここまでお読みいただき、ありがとうございました。 皆さんもぜひGPTで色々遊んでみてください。Peace! 執筆: @wakamoto.ryosuke 、レビュー: @yamada.y ( Shodo で執筆されました )
こんにちは。最近MC battleにハマっております、ISID 金融ソリューション事業部の若本です。 イベント現場にも何度か足を運んでおり、行くたびにその月で一番大きい声を出しています。 さて、そんなラップにがっつり影響を受け、今回はラップができるAIを作成してみます。 なぜ韻を検索するのか ~GPTでできるよね?~ その前に、2023初頭の現在、工夫なしにAIで高度なラップはできません。 なので、 まずは韻を検索することから始めます。 「ChatGPTとかで普通にできるんじゃないの?」 と思われた方もいると思います。手始めにChatGPTに日本語でラップをさせてみましょう。 確かにラップしていますし、凄いことに違いはありません。が、真面目なラップすぎてヘッズには物足りません(個人の主観)。 これは、ChatGPTが韻を理解していないことが原因です。バースの意図はしっかり伝わりますが、肝心な韻がほぼ含まれていません。韻を理解していない理由は多々考えられますが、LLMにとって難しい問題設定になっているのと、データが足りていないの2つが主に考えられます。 要は、ChatGPTに韻を踏ませることはできません。 ChatGPTが韻を理解していないことは確認できました。 とはいえ、ラップ自体はそれらしくできるので、韻だけ与えてラップさせてみましょう。 先ほどのラップよりいい感じです。ラップできるやつ感が増しました。 韻をあらかじめ与えてあげることで、実際のラッパーに近いラップが実現できそうです。 とはいえ、毎回韻を指定するのは面倒なことこの上ないですよね。なので、 まずは似た韻を検索する仕組みを構築します。 韻について そもそも韻には下記のような種類があります。 完踏み 母音が完全一致するフレーズの組み合わせ e.g.) 「AI」(e-e-a-i)と「明細」(e-i(e)-a-i) 語感踏み 母音を濁したフレーズの組み合わせ e.g.) 機械学習 (i-a-i-a-u-u-u)とハイ拍手(a-i-a-u-u) その他 頭韻  前方の母音を合わせた組み合わせ e.g.)「 機械学習 」(i-a-i-…)と「嫌いなやつ」(i-a-i-…) 脚韻 後方の母音を合わせた組み合わせ e.g.)「AI」(e-e-a-i)と「限界」(e-n-a-i) 上記のうち、完踏みはフレーズの母音同士の一致率を計算するだけで済みます。これは、後述の古典的手法でも十分可能です。 しかし、語感踏みのような単語、例えば「文字数が明らかに違うけれど、早口にすると似て聞こえるような単語」は拾ってくることができません。 そこで、今回は「語感踏み」まで踏み込んだプログラムを目指します。 実用的な韻に必要なもの 上記を満たしていれば実用的な韻になるのかと問われると、答えは「否」です。 実際に使える"韻"を踏むためには、下記をはじめとして考慮すべき点があります。 英語も比較できる ラップでは英語のフレーズもたびたび登場します。そのため、日本語と英語を比較できることも重要になります。 意味的結びつきが強い単語を重視できる 前述の「AI」と「明細」で韻は踏めていますが、これらの単語で話を繋げることに苦労します。韻を踏みつつ、話を繋げやすい単語を選択する必要があります。 結果がすぐ出る 次々と言葉を紡ぐ必要があるので、すぐ結果が出ることも重要です。 上記以外にも実用上の観点が諸々あるかとは思いますが、今回は最低限上記に対応したいと思います。 韻を検索するAI 今回ベースとするのは、Universal Sentence Encoderによって得られたベクトル表現です。 つまり、 "韻"の情報をベクトル表現にします (以降、これを韻beddingと呼称します)。 やることは非常に簡単で、 1) 単語の読み方のうち母音だけ残し、2) 母音を連結した文字列(韻)からベクトルを取得します。 韻をベクトルすることにより、下記のようなメリットが享受できます。 韻をベクトル化することにより、「全体的な語感が近い単語」を引っ張ってくることができる(仮説) 検索速度が早い(ベクトルの最近傍探索ができる) 単語の意味と同じ尺度で評価できるようになる つまるところ「うまい、安い、早い」です。 事前に作成したフレーズ集の各単語について、2つのベクトル表現を取得します。 ここで、単語embeddingは単語の意味的な情報が、韻beddingは韻の情報が格納されています。 上記のようにして作成した韻bedding、単語embeddingをもとに、検索した単語と近いものを返します。 ベクトル近傍探索ライブラリである faiss を用いて、韻beddingから近い韻を持つものをN件取得し、その後下記の計算式で昇順ソートします。 ※今回は ユークリッド 距離を用いたため昇順ソートしています。 cosine 類似度×降順でも問題ありません。 類似度 = α×韻の類似度+(1.0-α) ×単語の類似度 αはお好みで0.0~1.0の間で変えてください。今回は単純に韻の精度が知りたいため、α=1.0の結果を見てみます。 使用したデータ mecab-ipadic-NEologd の辞書の一部を使用しました。 データとしては約10万語ほどの量になります。 結果 韻beddingでは下記のような結果が得られました。検索ワードは「エンジニア」です。 別単語扱いの「engineer」が最上位に来ているのがポイント高いです。英語でも検索できていることが分かります。 次点の「 メスシリンダー 」も踏まれるとクスっとなりそうなので良いです。 語感踏みのようなことができています ね。 とはいえ、そもそもベクトルでやる必要があるの?と思われるかもしれません。 そのため、文字同士の類似度を計算する手法で試した結果も確認してみます。 レーベンシュタイン距離 ジャロ・ウィンクラー距離 ゲシュタルト パターンマッチング 既存の手法はどれも似た結果になりました。 検索速度に目を向けると、ベクトル検索が5倍以上の差 を付けています。 今回の単語/フレーズ数は約10万です。1万を超えるとベクトル検索のほうが早くなってくる印象でした。 1検索速度 (s) 韻bedding 0.036 レーベンシュタイン距離 0.194 ジャロ・ウィンクラー距離 0.183 ゲシュタルト パターンマッチング 3.708 検索の質としても、文字数の制約が緩いベクトル表現のほうが面白い韻は多く(完全に好みですが)、 韻beddingの採用価値はある と感じました。 おわりに 今回作成したAIによって、登録したフレーズからそれらしき結果は得ることができました。 一方、検索だけでも下記のような課題が残っています。 アクセントの考慮が不十分 今回の手法では、発音のアクセントまで考慮した検索はできていません。 アクセント記号を含める処理( pyopenjtalk など)を噛ませることでアクセントもある程度考慮に含めることはできそうです。 フレーズ数の不足 事前に登録するフレーズを増やすことが重要ですが、これを集めるのが大変です。 本記事では mecab-ipadic-NEologd を使いましたが、「韻は踏めるものの使いづらいワード」も多くなってしまうので、データソースは気を付ける必要があります。 単語の組み合わせでマッチングできない 今回試した方法では、「登録してあるフレーズ」の中から韻を踏めるものを探しています。より実用的なものにしていくためには、単語の組み合わせからマッチングすることも考えていく必要もありそうです。 次回は、満を持して検索した韻から自動でラップ生成を行います。 (Part. 2に続く) ここまでお読みいただきありがとうございました。 執筆: @wakamoto.ryosuke 、レビュー: @yamada.y ( Shodo で執筆されました )
こんにちは。最近MC battleにハマっております、ISID 金融ソリューション事業部の若本です。 イベント現場にも何度か足を運んでおり、行くたびにその月で一番大きい声を出しています。 さて、そんなラップにがっつり影響を受け、今回はラップができるAIを作成してみます。 なぜ韻を検索するのか ~GPTでできるよね?~ その前に、2023初頭の現在、工夫なしにAIで高度なラップはできません。 なので、 まずは韻を検索することから始めます。 「ChatGPTとかで普通にできるんじゃないの?」 と思われた方もいると思います。手始めにChatGPTに日本語でラップをさせてみましょう。 確かにラップしていますし、凄いことに違いはありません。が、真面目なラップすぎてヘッズには物足りません(個人の主観)。 これは、ChatGPTが韻を理解していないことが原因です。バースの意図はしっかり伝わりますが、肝心な韻がほぼ含まれていません。韻を理解していない理由は多々考えられますが、LLMにとって難しい問題設定になっているのと、データが足りていないの2つが主に考えられます。 要は、ChatGPTに韻を踏ませることはできません。 ChatGPTが韻を理解していないことは確認できました。 とはいえ、ラップ自体はそれらしくできるので、韻だけ与えてラップさせてみましょう。 先ほどのラップよりいい感じです。ラップできるやつ感が増しました。 韻をあらかじめ与えてあげることで、実際のラッパーに近いラップが実現できそうです。 とはいえ、毎回韻を指定するのは面倒なことこの上ないですよね。なので、 まずは似た韻を検索する仕組みを構築します。 韻について そもそも韻には下記のような種類があります。 完踏み 母音が完全一致するフレーズの組み合わせ e.g.) 「AI」(e-e-a-i)と「明細」(e-i(e)-a-i) 語感踏み 母音を濁したフレーズの組み合わせ e.g.) 機械学習 (i-a-i-a-u-u-u)とハイ拍手(a-i-a-u-u) その他 頭韻  前方の母音を合わせた組み合わせ e.g.)「 機械学習 」(i-a-i-…)と「嫌いなやつ」(i-a-i-…) 脚韻 後方の母音を合わせた組み合わせ e.g.)「AI」(e-e-a-i)と「限界」(e-n-a-i) 上記のうち、完踏みはフレーズの母音同士の一致率を計算するだけで済みます。これは、後述の古典的手法でも十分可能です。 しかし、語感踏みのような単語、例えば「文字数が明らかに違うけれど、早口にすると似て聞こえるような単語」は拾ってくることができません。 そこで、今回は「語感踏み」まで踏み込んだプログラムを目指します。 実用的な韻に必要なもの 上記を満たしていれば実用的な韻になるのかと問われると、答えは「否」です。 実際に使える"韻"を踏むためには、下記をはじめとして考慮すべき点があります。 英語も比較できる ラップでは英語のフレーズもたびたび登場します。そのため、日本語と英語を比較できることも重要になります。 意味的結びつきが強い単語を重視できる 前述の「AI」と「明細」で韻は踏めていますが、これらの単語で話を繋げることに苦労します。韻を踏みつつ、話を繋げやすい単語を選択する必要があります。 結果がすぐ出る 次々と言葉を紡ぐ必要があるので、すぐ結果が出ることも重要です。 上記以外にも実用上の観点が諸々あるかとは思いますが、今回は最低限上記に対応したいと思います。 韻を検索するAI 今回ベースとするのは、Universal Sentence Encoderによって得られたベクトル表現です。 つまり、 "韻"の情報をベクトル表現にします (以降、これを韻beddingと呼称します)。 やることは非常に簡単で、 1) 単語の読み方のうち母音だけ残し、2) 母音を連結した文字列(韻)からベクトルを取得します。 韻をベクトルすることにより、下記のようなメリットが享受できます。 韻をベクトル化することにより、「全体的な語感が近い単語」を引っ張ってくることができる(仮説) 検索速度が早い(ベクトルの最近傍探索ができる) 単語の意味と同じ尺度で評価できるようになる つまるところ「うまい、安い、早い」です。 事前に作成したフレーズ集の各単語について、2つのベクトル表現を取得します。 ここで、単語embeddingは単語の意味的な情報が、韻beddingは韻の情報が格納されています。 上記のようにして作成した韻bedding、単語embeddingをもとに、検索した単語と近いものを返します。 ベクトル近傍探索ライブラリである faiss を用いて、韻beddingから近い韻を持つものをN件取得し、その後下記の計算式で昇順ソートします。 ※今回は ユークリッド 距離を用いたため昇順ソートしています。 cosine 類似度×降順でも問題ありません。 類似度 = α×韻の類似度+(1.0-α) ×単語の類似度 αはお好みで0.0~1.0の間で変えてください。今回は単純に韻の精度が知りたいため、α=1.0の結果を見てみます。 使用したデータ mecab-ipadic-NEologd の辞書の一部を使用しました。 データとしては約10万語ほどの量になります。 結果 韻beddingでは下記のような結果が得られました。検索ワードは「エンジニア」です。 別単語扱いの「engineer」が最上位に来ているのがポイント高いです。英語でも検索できていることが分かります。 次点の「 メスシリンダー 」も踏まれるとクスっとなりそうなので良いです。 語感踏みのようなことができています ね。 とはいえ、そもそもベクトルでやる必要があるの?と思われるかもしれません。 そのため、文字同士の類似度を計算する手法で試した結果も確認してみます。 レーベンシュタイン距離 ジャロ・ウィンクラー距離 ゲシュタルト パターンマッチング 既存の手法はどれも似た結果になりました。 検索速度に目を向けると、ベクトル検索が5倍以上の差 を付けています。 今回の単語/フレーズ数は約10万です。1万を超えるとベクトル検索のほうが早くなってくる印象でした。 1検索速度 (s) 韻bedding 0.036 レーベンシュタイン距離 0.194 ジャロ・ウィンクラー距離 0.183 ゲシュタルト パターンマッチング 3.708 検索の質としても、文字数の制約が緩いベクトル表現のほうが面白い韻は多く(完全に好みですが)、 韻beddingの採用価値はある と感じました。 おわりに 今回作成したAIによって、登録したフレーズからそれらしき結果は得ることができました。 一方、検索だけでも下記のような課題が残っています。 アクセントの考慮が不十分 今回の手法では、発音のアクセントまで考慮した検索はできていません。 アクセント記号を含める処理( pyopenjtalk など)を噛ませることでアクセントもある程度考慮に含めることはできそうです。 フレーズ数の不足 事前に登録するフレーズを増やすことが重要ですが、これを集めるのが大変です。 本記事では mecab-ipadic-NEologd を使いましたが、「韻は踏めるものの使いづらいワード」も多くなってしまうので、データソースは気を付ける必要があります。 単語の組み合わせでマッチングできない 今回試した方法では、「登録してあるフレーズ」の中から韻を踏めるものを探しています。より実用的なものにしていくためには、単語の組み合わせからマッチングすることも考えていく必要もありそうです。 次回は、満を持して検索した韻から自動でラップ生成を行います。 (Part. 2に続く) ここまでお読みいただきありがとうございました。 執筆: @wakamoto.ryosuke 、レビュー: @yamada.y ( Shodo で執筆されました )
こんにちは、ISID 金融ソリューション事業部の岡崎です。 今回は ZBrush で作成した3Dモデルを、UE5のタイムラインという機能を活用して、簡単なアニメーションを作成する手順をご紹介します。 はじめに UE5でオブジェクトにアニメーションをつける方法としてBlueprintのタイムラインを使用する方法があります。 タイムラインで設定した時間(タイミング)に、あらかじめ定義した値を出力する仕組みで、簡単なアニメーションが実装できます。 タイムラインを使用せずに、複雑なアニメーションを作成する手順も今後配信予定です。 今回はDCCツールの ZBrush を使用して、大砲の3Dオブジェクトを作成し、UE5内でその大砲を動かしたり弾を発射させるなどのアニメーションをつけていきます。 検証環境/ツール Unreal Engine5.1.1 ZBrush 2022.0.6 実装手順 ZBrush で大砲を作成 ZBrush で作成したオブジェクトをエクスポート UE5でFBXファイルをインポート UE5でタイムラインを使用したアニメーションの作成 1. ZBrush で大砲を作成 まず初めに ZBrush を使用して大砲の3Dオブジェクトを作成します。 今回詳しい作成方法は割愛しますが、 ZBrush の操作方法やオブジェクト作成手順につきましては、今後記事を配信予定です。 まずはバレル(銃身)部分を作成します。 今回は下記画像のようなバレルの形状で作成しています。 次にバレルを装着するベースの部分の作成を行います。 今回は本題ではないため割愛しますが、 ZBrush のローポリ編集を使用することで、下記画像のような無機質な形状も作成できます。 今回の実装のポイントとしては、バレルとベースの部分を2つの部品として、別々のオブジェクトで作成することです。 これにより、後述するタイムラインを利用したアニメーションに使用できます。 2. ZBrush で作成したオブジェクトをエクスポート ZBrush で作成したオブジェクトをUEで使用できるようにFBXに変換してエクスポートする必要があります。 ZBrush のZ プラグイン という機能を使用して、FBXエクスポートの手順を紹介します。 主にエクスポートで変更するのは下記部分になります。 出力する情報を選択 FBXのバージョンを選択 ファイルの形式を選択 メッシュを三角メッシュにするかどうか ポリペイントの出力をするかどうか エクスポート ① 出力する情報を選択 出力する情報を下記から選びます。 選択のみ : 選択したサブツール 表示のみ : 表示してあるサブツール すべて : 全てのサブツール 今回は、バレル部分とベースの部分で2種類のFBXを出力したかったので、 それぞれ個別に選択を行い「Select」を選びました。 ② FBXのバージョンを選択 「FBX2020」の部分をクリックすることで、バージョンが選択できます。 今回は「FBX2020」を選択しました。 ③ ファイルの形式を選択 ファイルの形式を下記から選びます。 bin ascii binの方がファイルサイズが小さく一般的なので、今回はこちらを選択しました。 ④ メッシュを三角メッシュにするかどうか メッシュを三角ポリゴンとしてエクスポートしたい場合はオンにします。 三角ポリゴンにすると、ポリゴン数が減りファイルが軽くなる代わりに、メッシュの再編集がしにくくなります。 今回は小さなファイルだったので、設定はオフのまま進めました。 ⑤ ポリペイントの出力をするかどうか ZBrush 上でもポリペイントという機能を使用することでオブジェクトに色をつけることができます。 サブツール内の筆のマークをオンにすることで、ポリペイントもFBXに含めることができます。 今回は、マテリアルや色の設定はUEで行うので、オフのまま進めます。 ⑥ エクスポート エクスポートボタンを押下し、モデルのエクスポートを行います。 3. UE5でFBXファイルをインポート 前工程で、バレルとベースの部分で2種類のFBXファイルを作成したので、 今回はそれらをUE5にインポートを行います。 今回は2種類のFBXファイル名を以下のように作成しています。 cannon.fbx cannon_base.fbx Content Drawerを開き、右クリックから「Import to Game」を押下し、前工程で作成した2つのFBXファイルをインポートします。 マテリアルに関してはUEで作成していくので、「Create New Materials」を選択します。   インポートすると下記キャプチャのに表示されます。 インポートしてきたcannon.fbxをダブルクリックしてアセットの編集画面を開きます。 想定していたものより小さくインポートされていたので、右側の詳細画面から「Transform」の値を変更します。 今回は「Import Uniform Scale」の値を16に変更します。またデフォルトでの向きを変更するために「Import Rotation」のY軸を90に変更します。 この設定値はインポートする際の設定値になるので、画面上部にある「Reimport Base Mesh」を押下し、再度インポートします。 インポートが完了するとオブジェクトの大きさや向きが変わります。 次にマテリアルを変更します。 UEでプロジェクトを作成時、Starter Contentsを含める設定をしていると様々なマテリアルがデフォルトでインストールされているので、今回はそこからマテリアルを選んでいきます。 右側の詳細画面からMaterial Slotsを開きます。 セレクトボックスから「M_Metal_ Chrome 」を選択します。最後にこのアセット編集画面を保存することで、FBXファイルのデフォルト値を変更できます。 cannon_base.fbxも同様に編集します。大きさや向きはcannon.fbxと同じように変更しました。 マテリアルに関しては、「M_Metal_Burnished_Steel」を選択しました。 同様に保存を行うことで、Content Drawerの表示もマテリアルが適応された形に変化します。 Content Drawerで2種類のFBXファイルを選択し、View Portに ドラッグ&ドロップ することで、オブジェクトの大きさやマテリアルの見え方を確認できます。 以上で、FBXのインポートの説明を終わります。次は実際にインポートしたファイルにBlueprintを利用してアニメーションをつけていきます。 4. UE5でタイムラインを使用したアニメーションの作成 大砲の球を作成 まずは大砲のアニメーションを作成する前に、大砲から発射される球を作っていきます。 Content Drawerを開き、大砲の球用のActorのBlueprintを作成します。 今回は「BP_projectile」という名前で生成しました。 Blueprintを作成したら、まずは コンポーネント を追加します。「 Sphere Collision」という衝突判定を行う コンポーネント を作成します。 「DefaultSceneRoot」に「 Sphere Collision」を ドラッグ&ドロップ を行います。 これでこの コンポーネント が親 コンポーネント として設定されます。 次に「DefaultSceneRoot」に設定した「 Sphere Collision」の子 コンポーネント として「 Sphere 」を追加します。 大きさやマテリアルを右側の詳細画面で変更します。 Transformから大きさを変更します。 今回はMaterialで「M_Metal_Gold」を選択しました。 指定が終わると、下記キャプチャのように大きさとマテリアルが変更されます。 次に「Projectile Movement」という コンポーネント を追加します。これはこのBlueprint内のオブジェクトを発射物として一定方向に移動させ続けることができます。 最後にContent Drawerに戻り、作成したBlueprintをView Portに ドラッグ&ドロップ して配置してみます。 下記動画のように移動している鉄球ができれば大砲の球は完成です。 次にインポートしてきた大砲にアニメーションをつけていきます。 大砲から球を発射させる 大砲の球を作った時と同じように新規のBlueprintを作成します。 今回は「BP_cannon」という名前で生成しました。 大砲の球の時と同様に「 Sphere 」の コンポーネント を追加し、「DefaultSceneRoot」に ドラッグ&ドロップ を行い親 コンポーネント とします。 ここでは名前を「cannon_base」と変更しました。 次に右側の「Static Mesh」から、FBXでインポートした「cannon_base」を選択します。 次に、「cannon_base」の子 コンポーネント として「cannon_barrel」という名前で「 Sphere 」の コンポーネント を追加します。 同様に「Static Mesh」から、FBXでインポートした「cannon」を選択します。 ここで詳細画面の「Collision Presets」を「No Collision」に変更しておきます。 次に、このActorをSpawnさせる際の位置や向きを取得するために「Arrow」 コンポーネント を追加します。 名前を「Projectile Spawn Point」に変更し、「cannon_barrel」の子 コンポーネント として設定します。 View Port上に矢印が追加されるので、大砲の球を生成したい位置に矢印を移動します。 次に、大砲から球を出すBlueprintを作成します。 「BP_cannon」のEvent Graphを開きます。 「Event BeginPlay」ノードから「Delay」ノードを作成し、「Duration」を1秒に設定します。次に「SpawnActor Actor from Class」ノードを繋ぎ、Classの属性を「BP_Projectile」を選択します。 左側のComponentsから「Projectile Spawn Point」を ドラッグ&ドロップ してView Portに配置します。 さらにそこから「Get World Location / Rotation」の2つのノードを作成し、「SpawnActor Actor from Class」のノードに繋ぎます。 繰り返し球が出るように、「SpawnActor Actor from Class」から「Delay」につなげることで、球の発射されるBlueprintは完了です。 ここで一旦、作成したBlueprintをView Portに配置してみます。 この段階ではまだバレルに動きはありませんが、バレルの先端から球が発射されるようになりました。 大砲の発射時にアニメーションをつける(バレルのアニメーション) 次にBlueprintのタイムラインという機能を使って大砲の発射時にアニメーションをつけていきます。 「BP_cannon」のEvent Graph上で右クリックを行い「Timeline」を検索しノードを作成します。 名前を「Cannon fire Timeline」と変更しました。ダブルクリックをすることでタイムラインの編集に入れます。 次に左上の「+Track」を押下し「Add Float Track」を選択し、「Cannon Barrel location」というトラックを作成します。 出てきたトラック上で右クリックで「Add key」を選択し、表の中にkeyを3つ作りました。 keyの値は下記の通りにしています。 Time:0.0 / Value :0 Time:0.2 / Value :70 Time:0.6 / Value :0 Event Graphに戻ると「Cannon fire Timeline」のノードに「Cannon Barrel Location」というアウトプットピンが追加されているのがわかります。 ここの返り値として、秒数ごとに上で設定した Value の値が排出されます。 今回の例で言うと、このタイムラインが発火したタイミングでValue0が返却され、0.2秒後にValue70が返却される。 さらに発火から0.6秒後にはValue0が返却されるという式になります。 次に設定した Value の分だけ大砲のバレル部分を動かして、球が発射される時の挙動を作っていきます。 左下からCannon Barrel コンポーネント を配置し、「Set Relative Location」と繋ぎます。 タイムラインで排出される Value の値を、バレルのX軸に対して適応することで、秒数ごとにバレルが伸び縮みする仕組みが作成できます。 次に、タイムラインが発火したことを知らせる値を作成します。 再びタイムライン編集画面に戻り、「+Track」を押下し、「Add Event Track」を選択し「Cannon fired」と名前を変更します。 発火したことを伝えるだけでいいので、値は気にせず「Time:0.0 / Value :0」のkeyを作成します。 「Cannon Fired」というピンが作成されたのを確認します。 「Event BeginPlay」から続く流れに、今回作成したタイムラインのノードを含めるために「SpawnActor Actor from Class」の実行ピンを「Cannon fire timeline」のPlay from Startピンに接続します。次に「Cannon Fired」ピンをDelayノードに接続し直します。 これにより球を発射するたびにバレルが動くようになります。 View Portに配置するとこのように見えます。ついにバレルが動くようになりました。 大砲の発射時にアニメーションをつける(爆発のエフェクト) 最後に、大砲が球を発射するときに、爆発のエフェクトを追加します。 「Spawn Emitter at Location」というノードを使うことで、任意のEmitter(今回は爆発エフェクト)を任意の場所に出すことができます。 Starter Contentsに含まれている「P_Explosion」をセレクトボックスから選びます。 次に「Get Actor Location」を作成し、現在の大砲の位置をBlueprint上に引用し、「Spawn Emitter at Location」のLocationピンに繋ぎます。 爆発が出る位置を、バレルの先端に変更したいので、「LocationX」だけの値を90に変更しました。 最後に、作成した爆発エフェクトのノードを大砲の球が生成される直前のノードに追加することで、爆発エフェクトを付加できます。 View Portで確認すると以下の動画のように見えます。 以上が、 ZBrush で作成した3Dモデルから、UE5のタイムラインという機能を活用して、簡単なアニメーションを作成する手順のご紹介でした。 所感 今回は ZBrush で作成したシンプルなモデルを、UEのタイムラインを用いてアニメーションさせました。 タイムラインで出力する値を利用する方法は、簡単なアニメーションを作るにはとても便利な手段だと感じました。 例えばユーザーに気づいてもらいたいアイテムを定期的に光らせたりするアニメーションは ノンゲーム の領域でも利用価値がとてもありそうです。 次回以降は、より複雑なアニメーションを作成する手順にも挑戦してみようと思います。 現在ISIDは web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! ISID採用ページ(Web3/メタバース/AI) 参考 https://www.udemy.com/course/unrealblueprint/learn/lecture/13833864#overview https://mononoco.com/creative/zbrush/fbx-exportimport 執筆: @okazaki.wataru 、レビュー: @wakamoto.ryosuke ( Shodo で執筆されました )
こんにちは、ISID 金融ソリューション事業部の岡崎です。 今回は ZBrush で作成した3Dモデルを、UE5のタイムラインという機能を活用して、簡単なアニメーションを作成する手順をご紹介します。 はじめに UE5でオブジェクトにアニメーションをつける方法としてBlueprintのタイムラインを使用する方法があります。 タイムラインで設定した時間(タイミング)に、あらかじめ定義した値を出力する仕組みで、簡単なアニメーションが実装できます。 タイムラインを使用せずに、複雑なアニメーションを作成する手順も今後配信予定です。 今回はDCCツールの ZBrush を使用して、大砲の3Dオブジェクトを作成し、UE5内でその大砲を動かしたり弾を発射させるなどのアニメーションをつけていきます。 検証環境/ツール Unreal Engine5.1.1 ZBrush 2022.0.6 実装手順 ZBrush で大砲を作成 ZBrush で作成したオブジェクトをエクスポート UE5でFBXファイルをインポート UE5でタイムラインを使用したアニメーションの作成 1. ZBrush で大砲を作成 まず初めに ZBrush を使用して大砲の3Dオブジェクトを作成します。 今回詳しい作成方法は割愛しますが、 ZBrush の操作方法やオブジェクト作成手順につきましては、今後記事を配信予定です。 まずはバレル(銃身)部分を作成します。 今回は下記画像のようなバレルの形状で作成しています。 次にバレルを装着するベースの部分の作成を行います。 今回は本題ではないため割愛しますが、 ZBrush のローポリ編集を使用することで、下記画像のような無機質な形状も作成できます。 今回の実装のポイントとしては、バレルとベースの部分を2つの部品として、別々のオブジェクトで作成することです。 これにより、後述するタイムラインを利用したアニメーションに使用できます。 2. ZBrush で作成したオブジェクトをエクスポート ZBrush で作成したオブジェクトをUEで使用できるようにFBXに変換してエクスポートする必要があります。 ZBrush のZ プラグイン という機能を使用して、FBXエクスポートの手順を紹介します。 主にエクスポートで変更するのは下記部分になります。 出力する情報を選択 FBXのバージョンを選択 ファイルの形式を選択 メッシュを三角メッシュにするかどうか ポリペイントの出力をするかどうか エクスポート ① 出力する情報を選択 出力する情報を下記から選びます。 選択のみ : 選択したサブツール 表示のみ : 表示してあるサブツール すべて : 全てのサブツール 今回は、バレル部分とベースの部分で2種類のFBXを出力したかったので、 それぞれ個別に選択を行い「Select」を選びました。 ② FBXのバージョンを選択 「FBX2020」の部分をクリックすることで、バージョンが選択できます。 今回は「FBX2020」を選択しました。 ③ ファイルの形式を選択 ファイルの形式を下記から選びます。 bin ascii binの方がファイルサイズが小さく一般的なので、今回はこちらを選択しました。 ④ メッシュを三角メッシュにするかどうか メッシュを三角ポリゴンとしてエクスポートしたい場合はオンにします。 三角ポリゴンにすると、ポリゴン数が減りファイルが軽くなる代わりに、メッシュの再編集がしにくくなります。 今回は小さなファイルだったので、設定はオフのまま進めました。 ⑤ ポリペイントの出力をするかどうか ZBrush 上でもポリペイントという機能を使用することでオブジェクトに色をつけることができます。 サブツール内の筆のマークをオンにすることで、ポリペイントもFBXに含めることができます。 今回は、マテリアルや色の設定はUEで行うので、オフのまま進めます。 ⑥ エクスポート エクスポートボタンを押下し、モデルのエクスポートを行います。 3. UE5でFBXファイルをインポート 前工程で、バレルとベースの部分で2種類のFBXファイルを作成したので、 今回はそれらをUE5にインポートを行います。 今回は2種類のFBXファイル名を以下のように作成しています。 cannon.fbx cannon_base.fbx Content Drawerを開き、右クリックから「Import to Game」を押下し、前工程で作成した2つのFBXファイルをインポートします。 マテリアルに関してはUEで作成していくので、「Create New Materials」を選択します。   インポートすると下記キャプチャのに表示されます。 インポートしてきたcannon.fbxをダブルクリックしてアセットの編集画面を開きます。 想定していたものより小さくインポートされていたので、右側の詳細画面から「Transform」の値を変更します。 今回は「Import Uniform Scale」の値を16に変更します。またデフォルトでの向きを変更するために「Import Rotation」のY軸を90に変更します。 この設定値はインポートする際の設定値になるので、画面上部にある「Reimport Base Mesh」を押下し、再度インポートします。 インポートが完了するとオブジェクトの大きさや向きが変わります。 次にマテリアルを変更します。 UEでプロジェクトを作成時、Starter Contentsを含める設定をしていると様々なマテリアルがデフォルトでインストールされているので、今回はそこからマテリアルを選んでいきます。 右側の詳細画面からMaterial Slotsを開きます。 セレクトボックスから「M_Metal_ Chrome 」を選択します。最後にこのアセット編集画面を保存することで、FBXファイルのデフォルト値を変更できます。 cannon_base.fbxも同様に編集します。大きさや向きはcannon.fbxと同じように変更しました。 マテリアルに関しては、「M_Metal_Burnished_Steel」を選択しました。 同様に保存を行うことで、Content Drawerの表示もマテリアルが適応された形に変化します。 Content Drawerで2種類のFBXファイルを選択し、View Portに ドラッグ&ドロップ することで、オブジェクトの大きさやマテリアルの見え方を確認できます。 以上で、FBXのインポートの説明を終わります。次は実際にインポートしたファイルにBlueprintを利用してアニメーションをつけていきます。 4. UE5でタイムラインを使用したアニメーションの作成 大砲の球を作成 まずは大砲のアニメーションを作成する前に、大砲から発射される球を作っていきます。 Content Drawerを開き、大砲の球用のActorのBlueprintを作成します。 今回は「BP_projectile」という名前で生成しました。 Blueprintを作成したら、まずは コンポーネント を追加します。「 Sphere Collision」という衝突判定を行う コンポーネント を作成します。 「DefaultSceneRoot」に「 Sphere Collision」を ドラッグ&ドロップ を行います。 これでこの コンポーネント が親 コンポーネント として設定されます。 次に「DefaultSceneRoot」に設定した「 Sphere Collision」の子 コンポーネント として「 Sphere 」を追加します。 大きさやマテリアルを右側の詳細画面で変更します。 Transformから大きさを変更します。 今回はMaterialで「M_Metal_Gold」を選択しました。 指定が終わると、下記キャプチャのように大きさとマテリアルが変更されます。 次に「Projectile Movement」という コンポーネント を追加します。これはこのBlueprint内のオブジェクトを発射物として一定方向に移動させ続けることができます。 最後にContent Drawerに戻り、作成したBlueprintをView Portに ドラッグ&ドロップ して配置してみます。 下記動画のように移動している鉄球ができれば大砲の球は完成です。 次にインポートしてきた大砲にアニメーションをつけていきます。 大砲から球を発射させる 大砲の球を作った時と同じように新規のBlueprintを作成します。 今回は「BP_cannon」という名前で生成しました。 大砲の球の時と同様に「 Sphere 」の コンポーネント を追加し、「DefaultSceneRoot」に ドラッグ&ドロップ を行い親 コンポーネント とします。 ここでは名前を「cannon_base」と変更しました。 次に右側の「Static Mesh」から、FBXでインポートした「cannon_base」を選択します。 次に、「cannon_base」の子 コンポーネント として「cannon_barrel」という名前で「 Sphere 」の コンポーネント を追加します。 同様に「Static Mesh」から、FBXでインポートした「cannon」を選択します。 ここで詳細画面の「Collision Presets」を「No Collision」に変更しておきます。 次に、このActorをSpawnさせる際の位置や向きを取得するために「Arrow」 コンポーネント を追加します。 名前を「Projectile Spawn Point」に変更し、「cannon_barrel」の子 コンポーネント として設定します。 View Port上に矢印が追加されるので、大砲の球を生成したい位置に矢印を移動します。 次に、大砲から球を出すBlueprintを作成します。 「BP_cannon」のEvent Graphを開きます。 「Event BeginPlay」ノードから「Delay」ノードを作成し、「Duration」を1秒に設定します。次に「SpawnActor Actor from Class」ノードを繋ぎ、Classの属性を「BP_Projectile」を選択します。 左側のComponentsから「Projectile Spawn Point」を ドラッグ&ドロップ してView Portに配置します。 さらにそこから「Get World Location / Rotation」の2つのノードを作成し、「SpawnActor Actor from Class」のノードに繋ぎます。 繰り返し球が出るように、「SpawnActor Actor from Class」から「Delay」につなげることで、球の発射されるBlueprintは完了です。 ここで一旦、作成したBlueprintをView Portに配置してみます。 この段階ではまだバレルに動きはありませんが、バレルの先端から球が発射されるようになりました。 大砲の発射時にアニメーションをつける(バレルのアニメーション) 次にBlueprintのタイムラインという機能を使って大砲の発射時にアニメーションをつけていきます。 「BP_cannon」のEvent Graph上で右クリックを行い「Timeline」を検索しノードを作成します。 名前を「Cannon fire Timeline」と変更しました。ダブルクリックをすることでタイムラインの編集に入れます。 次に左上の「+Track」を押下し「Add Float Track」を選択し、「Cannon Barrel location」というトラックを作成します。 出てきたトラック上で右クリックで「Add key」を選択し、表の中にkeyを3つ作りました。 keyの値は下記の通りにしています。 Time:0.0 / Value :0 Time:0.2 / Value :70 Time:0.6 / Value :0 Event Graphに戻ると「Cannon fire Timeline」のノードに「Cannon Barrel Location」というアウトプットピンが追加されているのがわかります。 ここの返り値として、秒数ごとに上で設定した Value の値が排出されます。 今回の例で言うと、このタイムラインが発火したタイミングでValue0が返却され、0.2秒後にValue70が返却される。 さらに発火から0.6秒後にはValue0が返却されるという式になります。 次に設定した Value の分だけ大砲のバレル部分を動かして、球が発射される時の挙動を作っていきます。 左下からCannon Barrel コンポーネント を配置し、「Set Relative Location」と繋ぎます。 タイムラインで排出される Value の値を、バレルのX軸に対して適応することで、秒数ごとにバレルが伸び縮みする仕組みが作成できます。 次に、タイムラインが発火したことを知らせる値を作成します。 再びタイムライン編集画面に戻り、「+Track」を押下し、「Add Event Track」を選択し「Cannon fired」と名前を変更します。 発火したことを伝えるだけでいいので、値は気にせず「Time:0.0 / Value :0」のkeyを作成します。 「Cannon Fired」というピンが作成されたのを確認します。 「Event BeginPlay」から続く流れに、今回作成したタイムラインのノードを含めるために「SpawnActor Actor from Class」の実行ピンを「Cannon fire timeline」のPlay from Startピンに接続します。次に「Cannon Fired」ピンをDelayノードに接続し直します。 これにより球を発射するたびにバレルが動くようになります。 View Portに配置するとこのように見えます。ついにバレルが動くようになりました。 大砲の発射時にアニメーションをつける(爆発のエフェクト) 最後に、大砲が球を発射するときに、爆発のエフェクトを追加します。 「Spawn Emitter at Location」というノードを使うことで、任意のEmitter(今回は爆発エフェクト)を任意の場所に出すことができます。 Starter Contentsに含まれている「P_Explosion」をセレクトボックスから選びます。 次に「Get Actor Location」を作成し、現在の大砲の位置をBlueprint上に引用し、「Spawn Emitter at Location」のLocationピンに繋ぎます。 爆発が出る位置を、バレルの先端に変更したいので、「LocationX」だけの値を90に変更しました。 最後に、作成した爆発エフェクトのノードを大砲の球が生成される直前のノードに追加することで、爆発エフェクトを付加できます。 View Portで確認すると以下の動画のように見えます。 以上が、 ZBrush で作成した3Dモデルから、UE5のタイムラインという機能を活用して、簡単なアニメーションを作成する手順のご紹介でした。 所感 今回は ZBrush で作成したシンプルなモデルを、UEのタイムラインを用いてアニメーションさせました。 タイムラインで出力する値を利用する方法は、簡単なアニメーションを作るにはとても便利な手段だと感じました。 例えばユーザーに気づいてもらいたいアイテムを定期的に光らせたりするアニメーションは ノンゲーム の領域でも利用価値がとてもありそうです。 次回以降は、より複雑なアニメーションを作成する手順にも挑戦してみようと思います。 現在ISIDは web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! ISID採用ページ(Web3/メタバース/AI) 参考 https://www.udemy.com/course/unrealblueprint/learn/lecture/13833864#overview https://mononoco.com/creative/zbrush/fbx-exportimport 執筆: @okazaki.wataru 、レビュー: @wakamoto.ryosuke ( Shodo で執筆されました )
こんにちは、X イノベーション 本部 ソフトウェアデザインセンター セキュリティグループの福山です。 好きな AWS サービスはSecurity HubとGuardDutyです。 はじめに 2023年4月10日にGuardDutyの新しい検出タイプが公表されましたので紹介します。 https://aws.amazon.com/jp/about-aws/whats-new/2023/04/amazon-guardduty-threat-detections-dns-traffic/ 追加された3つの検出タイプは、すべてDefense Evasionに関連しています。 Defense Evasionとは「防衛回避」のことで、セキュリティソフトに検知されないために攻撃者が攻撃を隠蔽することです。 今回、外部 DNS プロバイダーを使用するか、 HTTPS (DoH) または TLS (DoT) 経由で DNS トラフィック を送信するなどの手法を使用した防衛回避について検出できるようになりました。 各検出タイプについて、以下で説明します。 新しく追加された検出タイプ DefenseEvasion:EC2/UnusualDNSResolver 重要度:Medium(中) 使用可能なリージョン:GuardDutyがサポートされている全リージョン 検出ロジック:通常、EC2 インスタンス は、正当な DNS サーバーからの応答を受信することが期待されます。 この検出は、EC2 インスタンス が通常の DNS トラフィック パターンを学習し、長期間利用していないパブリック DNS リ ゾル バに対する DNS 通信を警告します。 補足:本機能が追加される前のGuardDutyには、デフォルトの AWS DNS リ ゾル バを使用して問題を見つけるといった制限がありました。 なので、例えばデフォルトの DNS 設定を Google DNS (8.8.8.8または8.8.4.4)とすると、攻撃は検出されずに任意の ドメイン にクエリを実行できてしまいます。 前提として、安易にデフォルトの AWS DNS 設定を外部 DNS に変えることは避けるべきですが、今回の機能追加によって検出できるようになりました。 参考: https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-ec2.html#defenseevasion-ec2-unusualdnsresolver DefenseEvasion:EC2/UnusualDoHActivity 重要度:Medium(中) 使用可能なリージョン:一部リージョン ※ を除くGuardDutyがサポートされている全リージョン 検出ロジック:この検出は、EC2 インスタンス の通常の DNS トラフィック パターンを学習し、長期間利用していないパブリックDoHサーバへのDoH通信を警告します。 補足:DoH( DNS over HTTPS )とは、 HTTPS を用いて DNS 通信を行う技術のことです。 従来の DNS は UDP による平文でしたが、これを HTTPS の技術を使用して暗号化します。 攻撃者は、DoHを使用して通常の DNS トラフィック を脅威検知から回避することがあります。 参考: https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-ec2.html#defenseevasion-ec2-unsualdohactivity DefenseEvasion:EC2/UnusualDoTActivity 重要度:Medium(中) 使用可能なリージョン:一部リージョン ※ を除くGuardDutyがサポートされている全リージョン 検出ロジック:この検出は、EC2 インスタンス の通常の DNS トラフィック パターンを学習し、長期間利用していないパブリックDoTサーバへのDoT通信を警告します。 補足:DoT( DNS over TLS )とは、 DNS 通信を TLS レベルで暗号化する技術のことです。 攻撃者は、DoTを使用して、通常の DNS トラフィック を脅威検知から回避することがあります。 参考: https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-ec2.html#defenseevasion-ec2-unusualdotactivity ※ 一部リージョン: AWS アジアパシフィック(大阪), AWS アジアパシフィック( ジャカルタ ), AWS アジアパシフィック(ソウル), 中国(北京、Sinnetが運営), 中国( 寧夏 、NWCDが運営) 所感 上記3タイプはデータソースとして VPC フローログを利用しているということで、今まで検出できなかったパターンを補完するアップデートとなりました。 DoHとDoTの利用はまだ普及していないため、「DefenseEvasion:EC2/UnusualDoHActivity」が検出される機会が増えそうです。 なお、直近で通信履歴がないものに対して検知されるため、誤検知も少なからず発生する可能性はあります。 検出されたらターゲットのIPを確認し、想定された正常な動きかどうか慎重に見極めた方が良さそうです。 私たちは同じチームで働いてくれる仲間を大募集しています!たくさんのご応募をお待ちしています。 セキュリティエンジニア(セキュリティ設計) 執筆: @fuku.dancho 、レビュー: @yamashita.tsuyoshi ( Shodo で執筆されました )
こんにちは、X イノベーション 本部 ソフトウェアデザインセンター セキュリティグループの福山です。 好きな AWS サービスはSecurity HubとGuardDutyです。 はじめに 2023年4月10日にGuardDutyの新しい検出タイプが公表されましたので紹介します。 https://aws.amazon.com/jp/about-aws/whats-new/2023/04/amazon-guardduty-threat-detections-dns-traffic/ 追加された3つの検出タイプは、すべてDefense Evasionに関連しています。 Defense Evasionとは「防衛回避」のことで、セキュリティソフトに検知されないために攻撃者が攻撃を隠蔽することです。 今回、外部 DNS プロバイダーを使用するか、 HTTPS (DoH) または TLS (DoT) 経由で DNS トラフィック を送信するなどの手法を使用した防衛回避について検出できるようになりました。 各検出タイプについて、以下で説明します。 新しく追加された検出タイプ DefenseEvasion:EC2/UnusualDNSResolver 重要度:Medium(中) 使用可能なリージョン:GuardDutyがサポートされている全リージョン 検出ロジック:通常、EC2 インスタンス は、正当な DNS サーバーからの応答を受信することが期待されます。 この検出は、EC2 インスタンス が通常の DNS トラフィック パターンを学習し、長期間利用していないパブリック DNS リ ゾル バに対する DNS 通信を警告します。 補足:本機能が追加される前のGuardDutyには、デフォルトの AWS DNS リ ゾル バを使用して問題を見つけるといった制限がありました。 なので、例えばデフォルトの DNS 設定を Google DNS (8.8.8.8または8.8.4.4)とすると、攻撃は検出されずに任意の ドメイン にクエリを実行できてしまいます。 前提として、安易にデフォルトの AWS DNS 設定を外部 DNS に変えることは避けるべきですが、今回の機能追加によって検出できるようになりました。 参考: https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-ec2.html#defenseevasion-ec2-unusualdnsresolver DefenseEvasion:EC2/UnusualDoHActivity 重要度:Medium(中) 使用可能なリージョン:一部リージョン ※ を除くGuardDutyがサポートされている全リージョン 検出ロジック:この検出は、EC2 インスタンス の通常の DNS トラフィック パターンを学習し、長期間利用していないパブリックDoHサーバへのDoH通信を警告します。 補足:DoH( DNS over HTTPS )とは、 HTTPS を用いて DNS 通信を行う技術のことです。 従来の DNS は UDP による平文でしたが、これを HTTPS の技術を使用して暗号化します。 攻撃者は、DoHを使用して通常の DNS トラフィック を脅威検知から回避することがあります。 参考: https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-ec2.html#defenseevasion-ec2-unsualdohactivity DefenseEvasion:EC2/UnusualDoTActivity 重要度:Medium(中) 使用可能なリージョン:一部リージョン ※ を除くGuardDutyがサポートされている全リージョン 検出ロジック:この検出は、EC2 インスタンス の通常の DNS トラフィック パターンを学習し、長期間利用していないパブリックDoTサーバへのDoT通信を警告します。 補足:DoT( DNS over TLS )とは、 DNS 通信を TLS レベルで暗号化する技術のことです。 攻撃者は、DoTを使用して、通常の DNS トラフィック を脅威検知から回避することがあります。 参考: https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-ec2.html#defenseevasion-ec2-unusualdotactivity ※ 一部リージョン: AWS アジアパシフィック(大阪), AWS アジアパシフィック( ジャカルタ ), AWS アジアパシフィック(ソウル), 中国(北京、Sinnetが運営), 中国( 寧夏 、NWCDが運営) 所感 上記3タイプはデータソースとして VPC フローログを利用しているということで、今まで検出できなかったパターンを補完するアップデートとなりました。 DoHとDoTの利用はまだ普及していないため、「DefenseEvasion:EC2/UnusualDoHActivity」が検出される機会が増えそうです。 なお、直近で通信履歴がないものに対して検知されるため、誤検知も少なからず発生する可能性はあります。 検出されたらターゲットのIPを確認し、想定された正常な動きかどうか慎重に見極めた方が良さそうです。 私たちは同じチームで働いてくれる仲間を大募集しています!たくさんのご応募をお待ちしています。 セキュリティエンジニア(セキュリティ設計) 執筆: @fuku.dancho 、レビュー: @yamashita.tsuyoshi ( Shodo で執筆されました )
ISID X(クロス) イノベーション 本部 の飯田です。 ブラックボックス 最適化を使って、なかなか 言語化 しにくい自分の思考パターン(好みの色)を表現する取り組みをしてみました。 ブラックボックス 最適化とOptuna ブラックボックス 最適化とは「ある関数の出力を最大化 (または最小化) するような入力を勾配情報などを使わずに探索する問題」と言われます。 参考 ある入力に対して、その出力結果だけしか取得することが出来ず、それ以外の情報が与えられないような状況で、結果を最適化(最小化・最大化)することを目的としています。 Optuna は株式会社Preferred Networksが開発しているハイパーパラメータの最適化を自動化するためのソフトウェア フレームワーク です。 optuna.org TPE (Tree-structured Parzen Estimator) 等の最適化 アルゴリズム が実装されています。 ライブラリの使い方も非常に簡単で、使いやすいと思っています。 つくったもの FastAPIで簡単な画面を作って、Optunaと組み合わせて、自分の好みの色を探してくれるWebアプリを作ってみました。 「色の好み」を ブラックボックス 関数と見立て、好みの値が最大になるような最適化をしていくイメージです。 内的な感覚(例:色の好み)の関数と見立てるという意味では、心理物理学という学問領域があります。 心理物理学は、外的な刺激と内的な感覚の対応関係を測定し、また 定量 的な計測をしようとする学問です。 「色の好み」を ブラックボックス 最適化で探る取り組みは、心理物理学に近しいものがあるかも知れません Web画面は下記のようなイメージです(今回、デザイン性は一切考慮しておりません笑) 結果 134試行を行った結果のベストパラメータは{'r': 59, 'g': 112, 'b': 160}でした。 実際に描画すると、こちらの色です。 自分の気持ちとしては、まぁまぁ合っている感じがしています。 最適化が進む様子はこんな様子でした。 コード 流れとしては、ざっくり下記のような流れです study.ask() で、過去の結果に基づいて、効率的に探索できる色を決定 FastAPI経由でHTML上で、その色を画面表示 提示された色の好みに対して点数を入力し、 API を呼びだす regist_trial(r,g,b,v)で、RGBと評価点を登録 メイン部分のコードは下記のような形です。 sampler = optuna.samplers.TPESampler(multivariate=True, n_startup_trials=10) study = optuna.create_study(direction='maximize', sampler=sampler) def regist_trial(r,g,b,v): trial = optuna.trial.create_trial( params={"r": int(r),"g": int(g),"b": int(b)}, distributions=study_search_space, value=int(v) ) study.add_trial(trial) def sugest_next_color(r,g,b,v): regist_trial(r,g,b,v) trial = study.ask() next_r = trial.suggest_int("r", 0, 255) next_g = trial.suggest_int("g",0, 255) next_b = trial.suggest_int("b",0, 255) return (next_r,next_g,next_b) 最後に Optunaは 機械学習 のハイパーパラメータ調整がまず浮かんで来ると思いますが、 ブラックボックス 最適化はいろいろな場面で有効活用できそうです。 料理や化学実験のレシピ・材料配合の最適化 システムのパラメータ調整 官能評価 などにも使えるんじゃないかと思います。 最適化が進む様子のアニメーションは見ていて気持ちいいですね。 最後に、私たちは一緒に働いてくれる仲間を募集しています! デジタル技術を社会課題解決につなげるようなプロジェクトを推進していきたいプロジェクトマネージャーやエンジニアを募集しています。 ぜひご応募ください! ソリューションアーキテクト スマートシティ導入コンサルタント/スマートシティ戦略コンサルタント 執筆: @iida.michitaka ( Shodo で執筆されました )
ISID X(クロス) イノベーション 本部 の三浦です。 筆者の関わっている案件では、コンテナ利用、 AWS Fargate利用を進めております。 AWS Fargateのお手軽さは非常に重宝しております。 しかし、そこで問題になってくるのが、管理作業(例: SQL 実行、EFSへのファイル配置)をどうするかです。 アプリケーション本体をせっかく AWS Fargateでやっているので、管理作業もできるだけサーバーレスでやりたいですよね? で、以前の記事では、『業務用の端末⇒ AWS _Fargate(ポート フォワ ード)⇒RDS』といった内容を書きました。 AWS FargateからSSMでRDSに接続 しかし、これには問題点がありまして、業務端末からのDBへの直接接続はセキュリ ティー ポリシー、データ持ち出しの観点で推奨されない場合があります。 そこで他のパターンとしては、『業務用の端末⇒ECS Exec(コンテナにログイン)⇒RDS』といった手段があるのですが、これには下記のような課題があります。 コンテナ内に本来不要なDB接続用のライブラリのインストールが必要 巨大な結果セットを返すような不適切な SQL 実行時にECSタスクのリソース(例:メモリ)を使用してしまいサービスによくない影響が出る コンテナ内に入ってオペレーションするのは、セキュリ ティー 的に望ましくない場合がある コンテナに本来不要なオペレーション用の権限をつけなければならない場合がある(例:データ授受用のs3へのアクセス権) ということで、業務用端末からDBへ直接接続せず、下記のようにセキュアな AWS CloudShellから下記のような経路で接続できないか試します。 『業務用の端末⇒ AWS CloudShell⇒ AWS Fargate(ポート フォワ ード)⇒RDS』 目次 目次 接続失敗編 接続成功編 接続失敗編 下記のように普通にフロントでstart-sessionするとstart-sessionのプロセスがフロントに残ることでそのままでは使えません。 ローカルならば他のターミナルを開いてそちらでDB接続のコマンドを実行すればいいのですが、 AWS CloudShellではそれができません。 aws ssm start-session \ --target ecs:${clusterID}_${taskid}_${CONTAINER_ID} \ --document-name AWS-StartPortForwardingSessionToRemoteHost \ --parameters "{\"host\":[\"XXXXXXXXXXXXXXX.ap-northeast-1.rds.amazonaws.com\"],\"portNumber\":[\"5432\"], \"localPortNumber\":[\"5432\"]}" 接続成功編 しかし、下記のようにstart-sessionをバックグランド実行することにより AWS CloudShellからポート フォワ ードして、RDSに接続可能です。 (aws ssm start-session \ --target ecs:${clusterID}_${taskid}_${CONTAINER_ID} \ --document-name AWS-StartPortForwardingSessionToRemoteHost \ --parameters "{\"host\":[\"xxxxxxxxxx.ap-northeast-1.rds.amazonaws.com\"],\"portNumber\":[\"5432\"], \"localPortNumber\":[\"5432\"]}" \ \ )& \ export PGPASSWORD=xxxxxxxxx psql -U userName -p 5432 -h localhost 終了処理は下記の「ps + kill」で可能です。 ということで、ちょっとした工夫で AWS CloudShellからRDSにポート フォワ ードで接続できるという内容でした。 執筆: @miura.toshihiko 、レビュー: 寺山 輝 (@terayama.akira) ( Shodo で執筆されました )
ISID X(クロス) イノベーション 本部 の三浦です。 筆者の関わっている案件では、コンテナ利用、 AWS Fargate利用を進めております。 AWS Fargateのお手軽さは非常に重宝しております。 しかし、そこで問題になってくるのが、管理作業(例: SQL 実行、EFSへのファイル配置)をどうするかです。 アプリケーション本体をせっかく AWS Fargateでやっているので、管理作業もできるだけサーバーレスでやりたいですよね? で、以前の記事では、『業務用の端末⇒ AWS _Fargate(ポート フォワ ード)⇒RDS』といった内容を書きました。 AWS FargateからSSMでRDSに接続 しかし、これには問題点がありまして、業務端末からのDBへの直接接続はセキュリ ティー ポリシー、データ持ち出しの観点で推奨されない場合があります。 そこで他のパターンとしては、『業務用の端末⇒ECS Exec(コンテナにログイン)⇒RDS』といった手段があるのですが、これには下記のような課題があります。 コンテナ内に本来不要なDB接続用のライブラリのインストールが必要 巨大な結果セットを返すような不適切な SQL 実行時にECSタスクのリソース(例:メモリ)を使用してしまいサービスによくない影響が出る コンテナ内に入ってオペレーションするのは、セキュリ ティー 的に望ましくない場合がある コンテナに本来不要なオペレーション用の権限をつけなければならない場合がある(例:データ授受用のs3へのアクセス権) ということで、業務用端末からDBへ直接接続せず、下記のようにセキュアな AWS CloudShellから下記のような経路で接続できないか試します。 『業務用の端末⇒ AWS CloudShell⇒ AWS Fargate(ポート フォワ ード)⇒RDS』 目次 目次 接続失敗編 接続成功編 接続失敗編 下記のように普通にフロントでstart-sessionするとstart-sessionのプロセスがフロントに残ることでそのままでは使えません。 ローカルならば他のターミナルを開いてそちらでDB接続のコマンドを実行すればいいのですが、 AWS CloudShellではそれができません。 aws ssm start-session \ --target ecs:${clusterID}_${taskid}_${CONTAINER_ID} \ --document-name AWS-StartPortForwardingSessionToRemoteHost \ --parameters "{\"host\":[\"XXXXXXXXXXXXXXX.ap-northeast-1.rds.amazonaws.com\"],\"portNumber\":[\"5432\"], \"localPortNumber\":[\"5432\"]}" 接続成功編 しかし、下記のようにstart-sessionをバックグランド実行することにより AWS CloudShellからポート フォワ ードして、RDSに接続可能です。 (aws ssm start-session \ --target ecs:${clusterID}_${taskid}_${CONTAINER_ID} \ --document-name AWS-StartPortForwardingSessionToRemoteHost \ --parameters "{\"host\":[\"xxxxxxxxxx.ap-northeast-1.rds.amazonaws.com\"],\"portNumber\":[\"5432\"], \"localPortNumber\":[\"5432\"]}" \ \ )& \ export PGPASSWORD=xxxxxxxxx psql -U userName -p 5432 -h localhost 終了処理は下記の「ps + kill」で可能です。 ということで、ちょっとした工夫で AWS CloudShellからRDSにポート フォワ ードで接続できるという内容でした。 執筆: @miura.toshihiko 、レビュー: 寺山 輝 (@terayama.akira) ( Shodo で執筆されました )
ISID X(クロス) イノベーション 本部 の飯田です。 ブラックボックス 最適化を使って、なかなか 言語化 しにくい自分の思考パターン(好みの色)を表現する取り組みをしてみました。 ブラックボックス 最適化とOptuna ブラックボックス 最適化とは「ある関数の出力を最大化 (または最小化) するような入力を勾配情報などを使わずに探索する問題」と言われます。 参考 ある入力に対して、その出力結果だけしか取得することが出来ず、それ以外の情報が与えられないような状況で、結果を最適化(最小化・最大化)することを目的としています。 Optuna は株式会社Preferred Networksが開発しているハイパーパラメータの最適化を自動化するためのソフトウェア フレームワーク です。 optuna.org TPE (Tree-structured Parzen Estimator) 等の最適化 アルゴリズム が実装されています。 ライブラリの使い方も非常に簡単で、使いやすいと思っています。 つくったもの FastAPIで簡単な画面を作って、Optunaと組み合わせて、自分の好みの色を探してくれるWebアプリを作ってみました。 「色の好み」を ブラックボックス 関数と見立て、好みの値が最大になるような最適化をしていくイメージです。 内的な感覚(例:色の好み)の関数と見立てるという意味では、心理物理学という学問領域があります。 心理物理学は、外的な刺激と内的な感覚の対応関係を測定し、また 定量 的な計測をしようとする学問です。 「色の好み」を ブラックボックス 最適化で探る取り組みは、心理物理学に近しいものがあるかも知れません Web画面は下記のようなイメージです(今回、デザイン性は一切考慮しておりません笑) 結果 134試行を行った結果のベストパラメータは{'r': 59, 'g': 112, 'b': 160}でした。 実際に描画すると、こちらの色です。 自分の気持ちとしては、まぁまぁ合っている感じがしています。 最適化が進む様子はこんな様子でした。 コード 流れとしては、ざっくり下記のような流れです study.ask() で、過去の結果に基づいて、効率的に探索できる色を決定 FastAPI経由でHTML上で、その色を画面表示 提示された色の好みに対して点数を入力し、 API を呼びだす regist_trial(r,g,b,v)で、RGBと評価点を登録 メイン部分のコードは下記のような形です。 sampler = optuna.samplers.TPESampler(multivariate=True, n_startup_trials=10) study = optuna.create_study(direction='maximize', sampler=sampler) def regist_trial(r,g,b,v): trial = optuna.trial.create_trial( params={"r": int(r),"g": int(g),"b": int(b)}, distributions=study_search_space, value=int(v) ) study.add_trial(trial) def sugest_next_color(r,g,b,v): regist_trial(r,g,b,v) trial = study.ask() next_r = trial.suggest_int("r", 0, 255) next_g = trial.suggest_int("g",0, 255) next_b = trial.suggest_int("b",0, 255) return (next_r,next_g,next_b) 最後に Optunaは 機械学習 のハイパーパラメータ調整がまず浮かんで来ると思いますが、 ブラックボックス 最適化はいろいろな場面で有効活用できそうです。 料理や化学実験のレシピ・材料配合の最適化 システムのパラメータ調整 官能評価 などにも使えるんじゃないかと思います。 最適化が進む様子のアニメーションは見ていて気持ちいいですね。 最後に、私たちは一緒に働いてくれる仲間を募集しています! デジタル技術を社会課題解決につなげるようなプロジェクトを推進していきたいプロジェクトマネージャーやエンジニアを募集しています。 ぜひご応募ください! ソリューションアーキテクト スマートシティ導入コンサルタント/スマートシティ戦略コンサルタント 執筆: @iida.michitaka ( Shodo で執筆されました )
こんにちは。ISID 金融ソリューション事業部の若本です。 生成系の画像/ 自然言語処理 AIが盛り上がりを見せる中、既存ソフトウェアへの組み込みや連携も活発に行われるようになってきました。 3DCG制作アプリケーションである Blender でも、アドオンを通じて Blender 内でAIが使いやすくなりつつあります。先日、diffusionが使用できるアドオンである「Stability for Blender 」がリリースされたのもその一例です。 今回はStability for Blender を用いてできることと、その手順をまとめます。 Stability for Blender について Stable Diffusionの開発を行っているStability AI社が提供する、 Blender 用のアドオン※です。 筆者は実施時点で最新であったv0.0.15をインストールして実施しました。v0.0.15時点の機能としては下記の3つがあります。 文章から画像生成 文章からテクスチャを生成する 画像から画像生成 Renderingから画像を生成する テクスチャからテクスチャを生成する 画像からアニメーション生成 Renderingからアニメーションを生成 DreamStudio (β版)に登録し、 API keyを発行する必要があります。 DreamStudioでは登録時に100creditほど使用可能になりますが、画像を生成するたびにcreditを消費していきますのでご注意ください。なお、 API keyとcreditともにDreamStudioのMy Accountから確認可能です。 ※Blender3.0以降がサポートされています。 Stability for blender を使ってみる stability for blender で出力するまでの一連の流れを説明します( Blender そのもののインストールは割愛します)。 公式サイト を参考に実施しました。 事前準備 リリースページ からアドオンのzipファイルをダウンロードします。前述の通り、筆者はv0.0.15をインストールしています。 blender を開き、メニューの「Edit>preferences>add-ons」を開きます。 右上部の「Install...」ボタンを押すとフォルダ エクスプローラ ーが開くので、先ほどダウンロードしたzipファイルを選択します。選択後、以下のような画面になっていればOKです。 画面に「Stability」のタブが表示されていればインストール完了となります。 DreamStudio (β版)に登録し、 API keyを取得します。 「Stability」のタブに API keyを入力すると以下のような画面になります。これで実施する準備が整いました。 文章からテクスチャを生成する Blender 上で与えた文章から画像を生成してみます。 まず、「init Type」の「Text Prompt Only」を選択します。 次に、promptの入力ボックスに欲しい画像を英語で指示します。今回は最初からデフォルトで設定されている下記promptをそのまま使用しました。 A dream of a distant galaxy 後は「Dream」を押下するだけで実行されます。 消費creditも併せて表示されるのは親切ですね。 10秒足らずで画像が生成されました。非常にお手軽です。 もちろん、生成した画像はテクスチャとしてそのまま使用できます。 Renderingから画像を生成する 画像から画像を生成する例として、Renderingの結果から画像を生成してみます。 まずは「init Type」の「Texture」を選択します。 メニューから「Render>Render image」を押下すると、新規ウィンドウでRenderingが実行されます。 カメラを設定している以前の記事のプロジェクトを使用しました。 Rendering画面からStabilityを実行します。 promptは以下としました。 An empty park with snow, concept art, matte painting, HQ, 4k 前述の通り、「Dream」を押下することで画像が生成されます。 Blender 上で見える景色をベースに画像を生成することができました。 ここで、「Stability」タブの「input Options」から各種パラメータを設定できます。 パラメータ名 概要 default Init Strength 入力画像にどれだけ準拠するかを設定します。 0.5 Prompt Strength プロンプトにどれだけ準拠するかを設定します。 7.5 Set seed Stable Diffusionのseedを設定します。seedを変えると異なる画像が出力されます。 555555 Steps Stable DiffusionのStep数を設定します。 50 Engine 使用するStable Diffusionモデルを設定します。(あくまで推測ですが、GENERATE_512_2_1は解像度512pixelで Stable diffusion ver2.1 を表していると思われます) GENERATE_512_2_1 Sampler Stable DiffusionのSampler(ノイズ除去 アルゴリズム )を設定します。 K_DPMPP_2S_ANCESTRAL 主に出力の品質や、入力のバランス(文章と画像の情報どちらを優先するか)に関するパラメータが設定できます。ここでは、「Init strength」を変更してみます。 「Init strength」を変化させると以下のようになります。 「Init strength」を大きくするほど、Renderingされた情報に準拠するような画像の生成がされました。 一方で、小さくするほどpromptの「An empty park with snow」が重視され、元々置いていたオブジェクトは別のものに解釈される/消えることが確認できました。 ※今回、Renderingからアニメーション生成の手順も実施しましたが、数時間経過しても処理が進まず断念しました。 おわりに 今回はStability for Blender の利用手順とできることを調査しました。 執筆時点でv0.0.16も公開されており、「高解像度化」が新たに機能として追加されるなど、活発に開発が続けられています。今後より使いやすく、かつ機能を拡充していくことでクリエイター支援ツールとして活用されることが期待されます。 AIモデルの3DCGソフトウェアへの組み込みは今後も様々な形で発展していくと思いますので、今後もキャッチアップしていきたいと思います。 ここまでお読みいただきありがとうございました。 現在ISIDは web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! ISID採用ページ 執筆: @wakamoto.ryosuke 、レビュー: @yamada.y ( Shodo で執筆されました )
こんにちは。ISID 金融ソリューション事業部の若本です。 生成系の画像/ 自然言語処理 AIが盛り上がりを見せる中、既存ソフトウェアへの組み込みや連携も活発に行われるようになってきました。 3DCG制作アプリケーションである Blender でも、アドオンを通じて Blender 内でAIが使いやすくなりつつあります。先日、diffusionが使用できるアドオンである「Stability for Blender 」がリリースされたのもその一例です。 今回はStability for Blender を用いてできることと、その手順をまとめます。 Stability for Blender について Stable Diffusionの開発を行っているStability AI社が提供する、 Blender 用のアドオン※です。 筆者は実施時点で最新であったv0.0.15をインストールして実施しました。v0.0.15時点の機能としては下記の3つがあります。 文章から画像生成 文章からテクスチャを生成する 画像から画像生成 Renderingから画像を生成する テクスチャからテクスチャを生成する 画像からアニメーション生成 Renderingからアニメーションを生成 DreamStudio (β版)に登録し、 API keyを発行する必要があります。 DreamStudioでは登録時に100creditほど使用可能になりますが、画像を生成するたびにcreditを消費していきますのでご注意ください。なお、 API keyとcreditともにDreamStudioのMy Accountから確認可能です。 ※Blender3.0以降がサポートされています。 Stability for blender を使ってみる stability for blender で出力するまでの一連の流れを説明します( Blender そのもののインストールは割愛します)。 公式サイト を参考に実施しました。 事前準備 リリースページ からアドオンのzipファイルをダウンロードします。前述の通り、筆者はv0.0.15をインストールしています。 blender を開き、メニューの「Edit>preferences>add-ons」を開きます。 右上部の「Install...」ボタンを押すとフォルダ エクスプローラ ーが開くので、先ほどダウンロードしたzipファイルを選択します。選択後、以下のような画面になっていればOKです。 画面に「Stability」のタブが表示されていればインストール完了となります。 DreamStudio (β版)に登録し、 API keyを取得します。 「Stability」のタブに API keyを入力すると以下のような画面になります。これで実施する準備が整いました。 文章からテクスチャを生成する Blender 上で与えた文章から画像を生成してみます。 まず、「init Type」の「Text Prompt Only」を選択します。 次に、promptの入力ボックスに欲しい画像を英語で指示します。今回は最初からデフォルトで設定されている下記promptをそのまま使用しました。 A dream of a distant galaxy 後は「Dream」を押下するだけで実行されます。 消費creditも併せて表示されるのは親切ですね。 10秒足らずで画像が生成されました。非常にお手軽です。 もちろん、生成した画像はテクスチャとしてそのまま使用できます。 Renderingから画像を生成する 画像から画像を生成する例として、Renderingの結果から画像を生成してみます。 まずは「init Type」の「Texture」を選択します。 メニューから「Render>Render image」を押下すると、新規ウィンドウでRenderingが実行されます。 カメラを設定している以前の記事のプロジェクトを使用しました。 Rendering画面からStabilityを実行します。 promptは以下としました。 An empty park with snow, concept art, matte painting, HQ, 4k 前述の通り、「Dream」を押下することで画像が生成されます。 Blender 上で見える景色をベースに画像を生成することができました。 ここで、「Stability」タブの「input Options」から各種パラメータを設定できます。 パラメータ名 概要 default Init Strength 入力画像にどれだけ準拠するかを設定します。 0.5 Prompt Strength プロンプトにどれだけ準拠するかを設定します。 7.5 Set seed Stable Diffusionのseedを設定します。seedを変えると異なる画像が出力されます。 555555 Steps Stable DiffusionのStep数を設定します。 50 Engine 使用するStable Diffusionモデルを設定します。(あくまで推測ですが、GENERATE_512_2_1は解像度512pixelで Stable diffusion ver2.1 を表していると思われます) GENERATE_512_2_1 Sampler Stable DiffusionのSampler(ノイズ除去 アルゴリズム )を設定します。 K_DPMPP_2S_ANCESTRAL 主に出力の品質や、入力のバランス(文章と画像の情報どちらを優先するか)に関するパラメータが設定できます。ここでは、「Init strength」を変更してみます。 「Init strength」を変化させると以下のようになります。 「Init strength」を大きくするほど、Renderingされた情報に準拠するような画像の生成がされました。 一方で、小さくするほどpromptの「An empty park with snow」が重視され、元々置いていたオブジェクトは別のものに解釈される/消えることが確認できました。 ※今回、Renderingからアニメーション生成の手順も実施しましたが、数時間経過しても処理が進まず断念しました。 おわりに 今回はStability for Blender の利用手順とできることを調査しました。 執筆時点でv0.0.16も公開されており、「高解像度化」が新たに機能として追加されるなど、活発に開発が続けられています。今後より使いやすく、かつ機能を拡充していくことでクリエイター支援ツールとして活用されることが期待されます。 AIモデルの3DCGソフトウェアへの組み込みは今後も様々な形で発展していくと思いますので、今後もキャッチアップしていきたいと思います。 ここまでお読みいただきありがとうございました。 現在ISIDは web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! ISID採用ページ 執筆: @wakamoto.ryosuke 、レビュー: @yamada.y ( Shodo で執筆されました )
(画像: Basic Theory of Physically-Based Rendering ) こんにちは!金融ソリューション事業部の山下です。 今回は、近年進化が目覚ましい3DCGグラフィックスのクオリティを支えるPBR(Physical Based Rendering)について紹介します。 本記事では、入門編としてPBRの基礎理論やワークフローを紹介します。 また応用編として、 Substance 3Dや Unreal Engine を用いた制作フローを紹介する記事も別途執筆予定です。 PBRマテリアルの生成事例については、 Substance Samplerを用いたこちらの記事 でも紹介しているので、ぜひご覧ください。 1. PBRとは? 2. PBRの基礎理論 2-1. エネルギー保存の法則:拡散(Diffuse)と鏡面(Specular)反射 2-2. 金属(Metal)と非金属(Non-Metal) 2-3. 吸収(Absorption)と散乱(Scattering) 2-4. フレネル効果(Fresnel Effect) 2-5. マイクロファセット理論 3. PBRにおける主要ワークフロー 3-1. Metal - Roughness 3-2. Specular - Glossiness 4. 各テクスチャマップの概要(Metallic/Roughnessワークフロー) 4-1. BaseColor(Albedo)Map 4-2. Metallic(金属度)Map 4-3. Roughness(粗さ)Map 4-4. Normal(法線)Map 4-5. Ambient Occlusion(環境遮蔽)Map 4-6. Height(高さ)Map 4-7. Specluar Level(鏡面反射レベル)Map まとめ 参考 1. PBRとは? PBR(Physical Based Rendering)とは、現実世界の光の振る舞いを近似させる3DCG レンダリング 手法です。 従来の レンダリング 手法では、物理原則に基づかない比較的シンプルな近似を行う「 Lambert反射モデル 」や「 Blinn–Phong反射モデル 」が採用されていました。 PBRの原理に沿ったマテリアルを使用することにより、リアルな質感や照明をシミュレートしてより正確かつ自然な見た目を実現できます。 PBRの活用は、2000年~2010年代にゲームや映画業界から始まりました。 GPU ハードウェアやGraphic API 、リアルタイム レンダリング 技術の進化に伴い普及が進んだ結果、現在では主要な3Dファイル形式( glTF 、 FBX など)においてもPBRパラメータが採用されています。 一点誤解されやすい点を補足しますと、PBRはトゥーン レンダリング やNPR(非フォトリアル レンダリング )などアニメ調の作品においても有効に機能する技術です。そのため、PBRは Pixar やDisneyの映画でも採用されています。 (画像: SIGGRAPH 2013 Course: Physically Based Shading in Theory and Practice ) 2. PBRの基礎理論 現実世界における光の挙動を再現するための主要な原理/法則について、以下、概要のみ紹介します。 2-1. エネルギー保存の法則 :拡散(Diffuse)と鏡面(Specular)反射 エネルギー保存の法則 は、物理学においてエネルギーが変換される過程でその総量が一定であることを示す基本原則です。 PBRにおいては、光の反射や散乱に関する計算手法( BRDF:Bidirectional Reflectance Distribution Function など)においてこの法則が適用されています。 具体的には、入射光が物体の表面に当たる際、光のエネルギーは以下2つに分散します。 拡散(Diffuse)反射光:物体の内部で散乱され、再び表面から放出される光。物体によって吸収/放出される光の波長が異なる。その結果、物体の色が異なって見える。 鏡面(Specular)反射光:物体の表面から反射される光。完全な平面では入射角と反射角が等しくなる。物体表面が滑らかなほどハイライトは小さく明るくなり、粗いほど大きく暗くなる。 (画像: Basic Theory of Physically-Based Rendering ) この法則に基づき、PBRでは「反射光の総エネルギーは、入射光のエネルギーと等しい」という現実世界の光の挙動を再現しています。 2-2. 金属(Metal)と非金属(Non-Metal) PBRマテリアルにおいて、「金属か非金属か」という観点は非常に重要になります。 表面が金属の場合、鏡面反射が強調されて拡散反射はほとんど存在しません。これにより、金属表面は光沢感が強く、反射が明確に現れます。一方、非金属の場合、鏡面反射は比較的弱くなり、逆に拡散反射の影響が強まります。 また、鏡面反射の色は、金属の場合は金属自体の色で染まります。一方、非金属の場合、鏡面反射はほとんど白に近い色になります。 (画像: THE PBR GUIDE BY ALLEGORITHMIC ) 2-3. 吸収(Absorption)と散乱(Scattering) 吸収は、物体が光を吸収する現象です。吸収された光は一般的には物体内部で熱エネルギーに変換されます。 散乱は、光が物体内部や表面で乱反射される現象です。 半透明な物体では、光が物体の内部で散乱した後に表面から出てくる現象(SSS:Subsurface Scattering)が発生します。特にこの現象は、人肌や大理石、ろうそくや乳製品などで顕著に現れます。 (画像: Wikipedia: Subsurface scattering) ) 2-4. フレネル効果(Fresnel Effect) (画像: Fresnel Reflection and Fresnel Reflection Modes Explained ) フレネル効果は、光が物体の表面と交差する角度によって、その反射率が変化する現象です。 例えば、上記画像のように水面を上から見下ろした際、近い部分(視線と物体表面の角度が大きい)では水面の底が透けて見えます。一方で遠い部分(視線と物体表面の角度が小さい)では、完全に反射光しか見えません。 つまり、視線と物体の表面となす角度が小さいほど(鋭い角度で見るほど)、反射率が高くなります。 このフレネル効果は、水だけでなく布や木などの物体にも全て発生します。 PBRではフレネル効果を考慮することで、物体表面の光沢感や金属質感を表現します。 (参考: THE PBR GUIDE BY ALLEGORITHMIC ) DCCツールにおいては、上記の通り正面から垂直に物体を捉えた点の反射率を「F0」として扱います。このF0は、物体のIOR(屈折率)によって一意に導き出すことができます。 (画像: THE PBR GUIDE BY ALLEGORITHMIC ) また、F0は一般的な非金属で2~5%程度、金属では60~70%程度となっており、いずれも側面における反射率はほぼ100%となります。 2-5. マイクロファセット理論 (画像: THE PBR GUIDE BY ALLEGORITHMIC ) マイクロファセット理論は、物体の表面を無数の微小な平面(マイクロファセット)の集合として扱う理論です。 マイクロファセットはそれぞれ異なる向きを持っているため、光はそれぞれ異なる方向に反射されます。物体表面全体における反射は、各マイクロファセットによる反射の合成として計算されます。 PBRでは、物体表面の粗さ(Roughness)や凹凸(Normal)といったテクスチャマップを用いることで、メッシュよりも細かい単位の光の散乱や反射を計算します。 3. PBRにおける主要ワークフロー PBRマテリアルを作成/利用する場合、Metallic/RoughnessとSpecular/Glossinessという2つのワークフローが主流です。 3-1. Metal - Roughness テクスチャマップによって「金属度」と「粗さ」を制御するワークフローです。 メリット 調整が直感的であり、シンプル。鏡面反射のマップを持たずF0値のデフォルトが設定される為、非金属における反射率の設定破綻がしにくい(物理的にありえない値にはなりにくい) 比較的、テクスチャのメモリ使用量が少ない(1 RGB:Albedo) ほとんどのDCCツールで採用されている デメリット F0値のコン トロール ができない。(※ UEではSpecular Inputが用意されている為デフォルト値からの調整が可能) 金属度マップは原則バイナリ値で表す為、低解像度の場合にテクスチャ補完によってEdge Artifactが発生しやすい 3-2. Specular - Glossiness テクスチャマップで「鏡面反射」と「光沢度」を制御するワークフローです。 メリット Specular MapにてF0値のコン トロール がしやすい 比較的、Edge artifactが発生しにくい デメリット 調整が難しい(F0値が物理法則的に破綻する可能性がある) 比較的、テクスチャのメモリ使用量が多い(2 RGB:Albedo, Specular) どちらのワークフローもそれぞれの利点があり用途や好みに応じて選択されますが、本記事では Unreal Engine でも採用されているMetallic/Roughnessワークフローに沿って説明します。 4. 各テクスチャマップの概要(Metallic/Roughnessワークフロー) PBRワークフローでは、複数の画像素材(テクスチャマップ)を用いてマテリアルを表現します。 各テクスチャ素材をDCCツールや ゲームエンジン において適切に組み合わせることで、PBRの表現が可能になります。 各マップにおけるテクスチャのサンプル画像は、 こちらの記事 にて、 Substance Samplerを用いて生成したこちらのマテリアルを使用します。 マテリアルの完成系は、こちらです。 4-1. BaseColor(Albedo)Map 物体の基本色を表すマップです。RGB( Vector 3)で、0~1の値として表します。 従来のカラーマップとは異なり光の影響や陰影、反射などは含まれていないため、Albedoマップは明るい色調になることが特徴です。 実際のテクスチャ制作ではDCCツールを用いる手法のほか、カメラと偏光フィルターを用いた撮影による制作手法などがあります。 4-2. Metallic(金属度)Map 表面の金属性を表すマップです。 グレースケールで、通常は黒か白かの2値のみで表現します(0が非金属、1が金属)。 今回のサンプル画像は非金属のため、黒一色となります。 白(値が高い)が金属、黒(値が低い)が非金属を示します。 金属マップは、物体の反射特性を制御し、金属質感や非金属質感を区別する役割があります。 4-3. Roughness(粗さ)Map 物体の表面の粗さを表すマップです。0~1のグレースケール値で表します(0がつるつる、1がざらざら)。 粗いほど表面がざらつき、光の散乱が大きくなります。 3Dモデルの表面の粗さを表現します。白(値が高い)は粗い表面、黒(値が低い)は滑らかな表面を示します。 粗さマップは、光の反射や散乱の度合いを制御し、質感や光沢感を調整する役割があります。 4-4. Normal(法線)Map 物体の表面の微細な凹凸を表すマップです。RGB( Vector 3)で表し、RGBがXYZ座標に対応するベクトルとして扱います。 RGB値によって、各 ピクセル の表面法線の向きが定義されています。 法線マップは、モデルのポリゴン数を増やさずにディテールを追加することができ、リアルな質感や陰影を表現する役割があります。これにより、高解像度のジオメトリを持たないモデルでも、細かいディテールを表現できます。 4-5. Ambient Occlusion(環境遮蔽)Map 表面の自然な陰影を表現するマップです。0~1のグレースケール値(0が遮蔽された黒、1が露出していて白)で表します。 表面の凹凸によって遮られる環境光を表現します。これにより、物体同士が接近している部分や凹んでいる部分に影ができ、リアルな陰影が表現されます。 4-6. Height(高さ)Map 物体表面の高低差を表すマップです。0~1のグレイスケール値(0が低く、1が高い)で高さを表します。 レンダリング を行う際に、高さの単位(ユニット)を設定する必要があります。 4-7. Specluar Level(鏡面反射レベル)Map 非金属物体に対する鏡面反射の係数を表すマップです。 UEではデフォルト値が0.5(F0が4%)として設定されていますが、この係数を調整できます。 Specular - GlossinessワークフローにおけるSpecular Mapとは異なる点についてご注意ください。 まとめ いかがでしたでしょうか。 近年では、人肌や大理石など物体表面内部に浸透する光の振る舞いを表現する SSS(サブサーフェススキャタリング) や、幅広いレンジの明るさ情報を用いる HDRI(ハイダイナミックレンジイメージ) など、様々な新しい技術が開発されています。 さらに、Unreal Engine5など ゲームエンジン の進化も伴うことで「現実と見紛う」レベルの3DCGがリアルタイム レンダリング 可能になっており、今後も3DCG技術の進化と ユースケース に目が離せません。 次回からは応用編として、 Substance Designerを用いたPBRマテリアルの制作や、 Unreal Engine における レンダリング 方法について紹介してきたいと思います。 現在ISIDは web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! ISID採用ページ(Web3/メタバース/AI) 参考 Basic Theory of Physically-Based Rendering SIGGRAPH 2013 Course: Physically Based Shading in Theory and Practice THE PBR GUIDE BY ALLEGORITHMIC 執筆: @yamashita.yuki 、レビュー: @wakamoto.ryosuke ( Shodo で執筆されました )
(画像: Basic Theory of Physically-Based Rendering ) こんにちは!金融ソリューション事業部の山下です。 今回は、近年進化が目覚ましい3DCGグラフィックスのクオリティを支えるPBR(Physical Based Rendering)について紹介します。 本記事では、入門編としてPBRの基礎理論やワークフローを紹介します。 また応用編として、 Substance 3Dや Unreal Engine を用いた制作フローを紹介する記事も別途執筆予定です。 PBRマテリアルの生成事例については、 Substance Samplerを用いたこちらの記事 でも紹介しているので、ぜひご覧ください。 1. PBRとは? 2. PBRの基礎理論 2-1. エネルギー保存の法則:拡散(Diffuse)と鏡面(Specular)反射 2-2. 金属(Metal)と非金属(Non-Metal) 2-3. 吸収(Absorption)と散乱(Scattering) 2-4. フレネル効果(Fresnel Effect) 2-5. マイクロファセット理論 3. PBRにおける主要ワークフロー 3-1. Metal - Roughness 3-2. Specular - Glossiness 4. 各テクスチャマップの概要(Metallic/Roughnessワークフロー) 4-1. BaseColor(Albedo)Map 4-2. Metallic(金属度)Map 4-3. Roughness(粗さ)Map 4-4. Normal(法線)Map 4-5. Ambient Occlusion(環境遮蔽)Map 4-6. Height(高さ)Map 4-7. Specluar Level(鏡面反射レベル)Map まとめ 参考 1. PBRとは? PBR(Physical Based Rendering)とは、現実世界の光の振る舞いを近似させる3DCG レンダリング 手法です。 従来の レンダリング 手法では、物理原則に基づかない比較的シンプルな近似を行う「 Lambert反射モデル 」や「 Blinn–Phong反射モデル 」が採用されていました。 PBRの原理に沿ったマテリアルを使用することにより、リアルな質感や照明をシミュレートしてより正確かつ自然な見た目を実現できます。 PBRの活用は、2000年~2010年代にゲームや映画業界から始まりました。 GPU ハードウェアやGraphic API 、リアルタイム レンダリング 技術の進化に伴い普及が進んだ結果、現在では主要な3Dファイル形式( glTF 、 FBX など)においてもPBRパラメータが採用されています。 一点誤解されやすい点を補足しますと、PBRはトゥーン レンダリング やNPR(非フォトリアル レンダリング )などアニメ調の作品においても有効に機能する技術です。そのため、PBRは Pixar やDisneyの映画でも採用されています。 (画像: SIGGRAPH 2013 Course: Physically Based Shading in Theory and Practice ) 2. PBRの基礎理論 現実世界における光の挙動を再現するための主要な原理/法則について、以下、概要のみ紹介します。 2-1. エネルギー保存の法則 :拡散(Diffuse)と鏡面(Specular)反射 エネルギー保存の法則 は、物理学においてエネルギーが変換される過程でその総量が一定であることを示す基本原則です。 PBRにおいては、光の反射や散乱に関する計算手法( BRDF:Bidirectional Reflectance Distribution Function など)においてこの法則が適用されています。 具体的には、入射光が物体の表面に当たる際、光のエネルギーは以下2つに分散します。 拡散(Diffuse)反射光:物体の内部で散乱され、再び表面から放出される光。物体によって吸収/放出される光の波長が異なる。その結果、物体の色が異なって見える。 鏡面(Specular)反射光:物体の表面から反射される光。完全な平面では入射角と反射角が等しくなる。物体表面が滑らかなほどハイライトは小さく明るくなり、粗いほど大きく暗くなる。 (画像: Basic Theory of Physically-Based Rendering ) この法則に基づき、PBRでは「反射光の総エネルギーは、入射光のエネルギーと等しい」という現実世界の光の挙動を再現しています。 2-2. 金属(Metal)と非金属(Non-Metal) PBRマテリアルにおいて、「金属か非金属か」という観点は非常に重要になります。 表面が金属の場合、鏡面反射が強調されて拡散反射はほとんど存在しません。これにより、金属表面は光沢感が強く、反射が明確に現れます。一方、非金属の場合、鏡面反射は比較的弱くなり、逆に拡散反射の影響が強まります。 また、鏡面反射の色は、金属の場合は金属自体の色で染まります。一方、非金属の場合、鏡面反射はほとんど白に近い色になります。 (画像: THE PBR GUIDE BY ALLEGORITHMIC ) 2-3. 吸収(Absorption)と散乱(Scattering) 吸収は、物体が光を吸収する現象です。吸収された光は一般的には物体内部で熱エネルギーに変換されます。 散乱は、光が物体内部や表面で乱反射される現象です。 半透明な物体では、光が物体の内部で散乱した後に表面から出てくる現象(SSS:Subsurface Scattering)が発生します。特にこの現象は、人肌や大理石、ろうそくや乳製品などで顕著に現れます。 (画像: Wikipedia: Subsurface scattering) ) 2-4. フレネル効果(Fresnel Effect) (画像: Fresnel Reflection and Fresnel Reflection Modes Explained ) フレネル効果は、光が物体の表面と交差する角度によって、その反射率が変化する現象です。 例えば、上記画像のように水面を上から見下ろした際、近い部分(視線と物体表面の角度が大きい)では水面の底が透けて見えます。一方で遠い部分(視線と物体表面の角度が小さい)では、完全に反射光しか見えません。 つまり、視線と物体の表面となす角度が小さいほど(鋭い角度で見るほど)、反射率が高くなります。 このフレネル効果は、水だけでなく布や木などの物体にも全て発生します。 PBRではフレネル効果を考慮することで、物体表面の光沢感や金属質感を表現します。 (参考: THE PBR GUIDE BY ALLEGORITHMIC ) DCCツールにおいては、上記の通り正面から垂直に物体を捉えた点の反射率を「F0」として扱います。このF0は、物体のIOR(屈折率)によって一意に導き出すことができます。 (画像: THE PBR GUIDE BY ALLEGORITHMIC ) また、F0は一般的な非金属で2~5%程度、金属では60~70%程度となっており、いずれも側面における反射率はほぼ100%となります。 2-5. マイクロファセット理論 (画像: THE PBR GUIDE BY ALLEGORITHMIC ) マイクロファセット理論は、物体の表面を無数の微小な平面(マイクロファセット)の集合として扱う理論です。 マイクロファセットはそれぞれ異なる向きを持っているため、光はそれぞれ異なる方向に反射されます。物体表面全体における反射は、各マイクロファセットによる反射の合成として計算されます。 PBRでは、物体表面の粗さ(Roughness)や凹凸(Normal)といったテクスチャマップを用いることで、メッシュよりも細かい単位の光の散乱や反射を計算します。 3. PBRにおける主要ワークフロー PBRマテリアルを作成/利用する場合、Metallic/RoughnessとSpecular/Glossinessという2つのワークフローが主流です。 3-1. Metal - Roughness テクスチャマップによって「金属度」と「粗さ」を制御するワークフローです。 メリット 調整が直感的であり、シンプル。鏡面反射のマップを持たずF0値のデフォルトが設定される為、非金属における反射率の設定破綻がしにくい(物理的にありえない値にはなりにくい) 比較的、テクスチャのメモリ使用量が少ない(1 RGB:Albedo) ほとんどのDCCツールで採用されている デメリット F0値のコン トロール ができない。(※ UEではSpecular Inputが用意されている為デフォルト値からの調整が可能) 金属度マップは原則バイナリ値で表す為、低解像度の場合にテクスチャ補完によってEdge Artifactが発生しやすい 3-2. Specular - Glossiness テクスチャマップで「鏡面反射」と「光沢度」を制御するワークフローです。 メリット Specular MapにてF0値のコン トロール がしやすい 比較的、Edge artifactが発生しにくい デメリット 調整が難しい(F0値が物理法則的に破綻する可能性がある) 比較的、テクスチャのメモリ使用量が多い(2 RGB:Albedo, Specular) どちらのワークフローもそれぞれの利点があり用途や好みに応じて選択されますが、本記事では Unreal Engine でも採用されているMetallic/Roughnessワークフローに沿って説明します。 4. 各テクスチャマップの概要(Metallic/Roughnessワークフロー) PBRワークフローでは、複数の画像素材(テクスチャマップ)を用いてマテリアルを表現します。 各テクスチャ素材をDCCツールや ゲームエンジン において適切に組み合わせることで、PBRの表現が可能になります。 各マップにおけるテクスチャのサンプル画像は、 こちらの記事 にて、 Substance Samplerを用いて生成したこちらのマテリアルを使用します。 マテリアルの完成系は、こちらです。 4-1. BaseColor(Albedo)Map 物体の基本色を表すマップです。RGB( Vector 3)で、0~1の値として表します。 従来のカラーマップとは異なり光の影響や陰影、反射などは含まれていないため、Albedoマップは明るい色調になることが特徴です。 実際のテクスチャ制作ではDCCツールを用いる手法のほか、カメラと偏光フィルターを用いた撮影による制作手法などがあります。 4-2. Metallic(金属度)Map 表面の金属性を表すマップです。 グレースケールで、通常は黒か白かの2値のみで表現します(0が非金属、1が金属)。 今回のサンプル画像は非金属のため、黒一色となります。 白(値が高い)が金属、黒(値が低い)が非金属を示します。 金属マップは、物体の反射特性を制御し、金属質感や非金属質感を区別する役割があります。 4-3. Roughness(粗さ)Map 物体の表面の粗さを表すマップです。0~1のグレースケール値で表します(0がつるつる、1がざらざら)。 粗いほど表面がざらつき、光の散乱が大きくなります。 3Dモデルの表面の粗さを表現します。白(値が高い)は粗い表面、黒(値が低い)は滑らかな表面を示します。 粗さマップは、光の反射や散乱の度合いを制御し、質感や光沢感を調整する役割があります。 4-4. Normal(法線)Map 物体の表面の微細な凹凸を表すマップです。RGB( Vector 3)で表し、RGBがXYZ座標に対応するベクトルとして扱います。 RGB値によって、各 ピクセル の表面法線の向きが定義されています。 法線マップは、モデルのポリゴン数を増やさずにディテールを追加することができ、リアルな質感や陰影を表現する役割があります。これにより、高解像度のジオメトリを持たないモデルでも、細かいディテールを表現できます。 4-5. Ambient Occlusion(環境遮蔽)Map 表面の自然な陰影を表現するマップです。0~1のグレースケール値(0が遮蔽された黒、1が露出していて白)で表します。 表面の凹凸によって遮られる環境光を表現します。これにより、物体同士が接近している部分や凹んでいる部分に影ができ、リアルな陰影が表現されます。 4-6. Height(高さ)Map 物体表面の高低差を表すマップです。0~1のグレイスケール値(0が低く、1が高い)で高さを表します。 レンダリング を行う際に、高さの単位(ユニット)を設定する必要があります。 4-7. Specluar Level(鏡面反射レベル)Map 非金属物体に対する鏡面反射の係数を表すマップです。 UEではデフォルト値が0.5(F0が4%)として設定されていますが、この係数を調整できます。 Specular - GlossinessワークフローにおけるSpecular Mapとは異なる点についてご注意ください。 まとめ いかがでしたでしょうか。 近年では、人肌や大理石など物体表面内部に浸透する光の振る舞いを表現する SSS(サブサーフェススキャタリング) や、幅広いレンジの明るさ情報を用いる HDRI(ハイダイナミックレンジイメージ) など、様々な新しい技術が開発されています。 さらに、Unreal Engine5など ゲームエンジン の進化も伴うことで「現実と見紛う」レベルの3DCGがリアルタイム レンダリング 可能になっており、今後も3DCG技術の進化と ユースケース に目が離せません。 次回からは応用編として、 Substance Designerを用いたPBRマテリアルの制作や、 Unreal Engine における レンダリング 方法について紹介してきたいと思います。 現在ISIDは web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! ISID採用ページ(Web3/メタバース/AI) 参考 Basic Theory of Physically-Based Rendering SIGGRAPH 2013 Course: Physically Based Shading in Theory and Practice THE PBR GUIDE BY ALLEGORITHMIC 執筆: @yamashita.yuki 、レビュー: @wakamoto.ryosuke ( Shodo で執筆されました )
はいどーもー! 早いもので新卒入社から丸3年、コミュニケーションIT事業部の宮澤響です! 本記事では、 Backlog API を利用した Backlog への課題の登録方法について、私が実際にハマったポイントを交えながらご紹介します。 背景・前提 Backlogとは どうしてAPIで課題登録を? サンプルコードについて 下準備 APIキーの発行 プロジェクトIDの確認 スペースのURLの確認 やってみる 必要なモジュールのインポートと変数の定義 課題の登録〜基本編〜 課題の登録〜カスタム属性編〜 課題の登録〜複数選択編〜 おまけで検証してみた まとめ 背景・前提 Backlogとは Backlogとは、株式会社 ヌーラボ が提供している課題管理・プロジェクト管理のサービスです。 シンプルで直感的に使えるデザインが特長であり、Git リポジトリ や Wiki としての機能も有しています。 どうして API で課題登録を? 弊社では同種のサービスとして Jira が全社展開されています。 そのため、私自身もこれまではJiraの利用経験しかありませんでした。 しかしこの度、チームの都合によりBacklogを利用することになり、利用方法を調査していたところ、BacklogにはJiraのように標準機能で自動化できる項目はあまりないことが分かりました。 そこで、Backlog API を利用することで一部の処理を自動化できないかと考え、手始めに基本となるであろう課題の登録をやってみることにしました。 サンプルコードについて 本記事のサンプルコードには TypeScript と axios を利用しています。 それぞれのインストール方法などについては本記事内では解説していませんので、適宜リンク先をご参照ください。 下準備 まずはBacklog API を利用するための下準備です。 API キーの発行 個人設定 > API から、 API キーを発行します。 必要に応じて メモ 欄に文字列を入力し、 登録 ボタンを押下します。 なお、Backlog API ではOAuth 2.0を利用した認証・認可方式も提供されていますが、Nulabアカウントを作成した上で開発するアプリケーションの登録を行う必要があるため、本記事では事前準備が不要な API キーを利用した方式を採用します。( 参考 ) プロジェクトIDの確認 課題の登録先となるプロジェクトのプロジェクトIDを確認します。 プロジェクトIDは、当該プロジェクトの 課題 ページや、 プロジェクト設定 ページのURLから確認できます。 プロジェクト情報の取得 の API を利用して確認することもできますが、今回は少し楽をしています。 スペースのURLの確認 課題の登録先となるプロジェクトが所属しているスペースのURLを確認します。 スペースやプロジェクトのあらゆる画面のURLの先頭にある https://xx.backlog.jp または https://xx.backlog.com の部分です。 xx の部分にはそれぞれのスペースのスペースIDが入ります。 やってみる 実際に API リク エス トのための ソースコード を記述していきます。 必要なモジュールのインポートと変数の定義 まずは必要なモジュールをインポートするとともに、ここまでに確認してきた内容から以下の変数を定義します。 // 必要なモジュールをインポートします import axios , { AxiosResponse } from "axios" ; // 右辺の""の中に先ほど発行したAPIキーの値を入力します const apiKey = "abcdefghijklmn" ; // 右辺の""の中に先ほど確認したプロジェクトIDの値を入力します const projectId = "123456" ; // 右辺の""の中に先ほど確認したスペースのURLの値の末尾に「/api/v2」を追加したものを入力します const baseUrl = "https://xx.backlog.jp/api/v2" ; なお、本記事では簡素化のために API キーを ソースコード 内に直接記載していますが、実際に運用をする際、特に GitHub などを利用して ソースコード を共有、公開する際は、 API キーのような認証情報は ソースコード 内に直接記載しない ことを強く推奨します。 (対応例: .gitignore で指定した .env ファイルに記載した値を dotenv を利用して読み込む、など) 課題の登録〜基本編〜 課題の追加 の API を利用します。 手始めに、必須とされている以下のパラメータのみを指定して、POSTリク エス トを送信してみます。 パラメータ名 型 内容 projectId 数値 課題を登録するプロジェクトのID summary 文字列 課題の件名 issueTypeId 数値 課題の種別のID priorityId 数値 課題の優先度のID なお、 issueTypeId や priorityId は、登録先のプロジェクトの 課題 ページの 高度な検索 タブや、 プロジェクト設定 の当該種別の編集ページのURLから確認するのが最も簡単です。 もちろん、 課題情報の取得 、 種別一覧の取得 、 優先度一覧の取得 、などの API を利用して確認することも可能です。 注意点として、Backlog API を利用する際は Content-Type が application/x-www-form-urlencoded である必要がある ため、 URLSearchParams を利用します。( 参考 ) const createIssue1 = async () : Promise < AxiosResponse > => { const url = ` ${ baseUrl } /issues?` ; const params = new URLSearchParams ( { apiKey , projectId , summary: "テスト1" , issueTypeId: "123456" , priorityId: "2" , } ); return await axios.post ( url + params ); } ; createIssue1 (); 無事に登録されました。 課題の登録〜カスタム属性編〜 続いて、以下のカスタム属性の値を指定してみます。 カスタム属性名 選択形式 選択肢 単一選択カスタム属性 単一選択(リスト形式) 選択肢1、選択肢2、選択肢3 複数選択カスタム属性 複数選択(リスト形式) 選択肢1、選択肢2、選択肢3 カスタム属性のパラメータ名は customField_xxxxx ( xxxxx はカスタム属性ID)です。 カスタム属性IDの確認方法は、これまでのものと同様、登録先のプロジェクトの 課題 ページの 高度な検索 タブのURL、 プロジェクト設定 の当該カスタム属性の編集ページのURL、 カスタム属性一覧の取得 の API などです。 注意点として、カスタム属性の値は、 選択肢名ではなく選択肢IDで指定 します。 課題の追加 の API リファレンスにはこのことが書かれていないので、若干のハマりポイントです。 const createIssue2 = async () : Promise < AxiosResponse > => { const url = ` ${ baseUrl } /issues?` ; const params = new URLSearchParams ( { apiKey , projectId , summary: "テスト2" , issueTypeId: "123456" , priorityId: "2" , // 「customField_12345: "選択肢1"」とすると400エラーになります customField_12345: "1" , // 同上 customField_67890: "1" , } ); return await axios.post ( url + params ); } ; createIssue2 (); 無事にカスタム属性の値が設定されました。 課題の登録〜複数選択編〜 続いて、複数選択が可能なカスタム属性に複数の値を指定してみます。 イメージとしては以下のような ソースコード なのですが、このままでは customField_67890 に複数の値を指定する部分で コンパイル エラー( Type 'string[]' is not assignable to type 'string'. )が発生してしまいます。 const createIssue3 = async () : Promise < AxiosResponse > => { const url = ` ${ baseUrl } /issues?` ; const params = new URLSearchParams ( { apiKey , projectId , summary: "テスト3" , issueTypeId: "123456" , priorityId: "2" , customField_12345: "1" , // これはコンパイルエラーになります customField_67890: [ "1" , "2" ] , } ); return await axios.post ( url + params ); } ; createIssue3 (); そのため、標準機能で対応する場合には、少し不格好ではありますが、 append() メソッドを利用して、1つ目の選択肢と2つ目の選択肢を分けて指定 します。 const createIssue3 = async () : Promise < AxiosResponse > => { const url = ` ${ baseUrl } /issues?` ; const params = new URLSearchParams ( { apiKey , projectId , summary: "テスト3" , issueTypeId: "123456" , priorityId: "2" , customField_12345: "1" , customField_67890: "1" , } ); params.append ( "customField_67890" , "2" ); return await axios.post ( url + params ); } ; createIssue3 (); 或いは、コンスト ラク タでは値を指定せず、 append() メソッドのみで全ての値を指定することも可能です。 const createIssue3 = async () : Promise < AxiosResponse > => { const url = ` ${ baseUrl } /issues?` ; const params = new URLSearchParams (); params.append ( "apiKey" , apiKey ); params.append ( "projectId" , projectId ); params.append ( "summary" , "テスト3" ); params.append ( "issueTypeId" , "123456" ); params.append ( "priorityId" , "2" ); params.append ( "customField_12345" , "1" ); params.append ( "customField_67890" , "1" ); params.append ( "customField_67890" , "2" ); return await axios.post ( url + params ); } ; createIssue3 (); なお、上述のサンプルコードはいずれも標準機能のみを用いてクエリパラメータを生成していましたが、外部ライブラリを利用すると、カスタム属性に複数の値を指定する部分を一行ですっきり記述できます。 例えば、 qs を利用すると、以下のようになります。 import qs from "qs" ; const createIssue3 = async () : Promise < AxiosResponse > => { const url = ` ${ baseUrl } /issues?` ; const params = qs.stringify ( { apiKey , projectId , summary: "テスト3" , issueTypeId: "123456" , priorityId: "2" , customField_12345: "1" , customField_67890: [ "1" , "2" ] , } , { indices: false } ); return await axios.post ( url + params ); } ; createIssue3 (); 無事に複数の値が設定されました。 これでひとまず課題登録に関する一連の試みは完了です。 おまけで検証してみた ちょっとしたおまけです。 上述のとおり、Backlog API では application/x-www-form-urlencoded でリク エス トを送信しますが、 application/json でのリク エス トに変更したらどうなるのでしょうか。 課題の登録には不要な検証ですが、気になったので試してみました。 const createIssue4 = async () : Promise < AxiosResponse > => { // apiKeyをdataに含めると401エラーになります const url = ` ${ baseUrl } /issues?apiKey= ${ apiKey } ` ; const data = { projectId , summary: "テスト4" , issueTypeId: "123456" , priorityId: "2" , customField_12345: "1" , customField_67890: [ "1" , "2" ] , } ; return await axios.post ( url , data ); } ; createIssue4 (); 結果としては、課題の登録自体はできるものの、カスタム属性の値が反映されませんでした。 (中途半端に成功する理由については不明です…もしご存知の方がいらっしゃれば、ぜひ本記事を引用して記事を執筆してみてください…!笑) まとめ 本記事では、Backlog API を利用したBacklogへの課題の登録方法について、サンプルコードとともにご紹介しました。 その際の主な注意点は以下です。 API キーのような認証情報は ソースコード 内に直接記載しない(Backlog API に限らず) Content-Type は application/x-www-form-urlencoded カスタム属性の値は選択肢名ではなく選択肢IDで指定 標準機能でパラメータに複数の値を指定するには、 append() メソッドで1つ目の選択肢と2つ目の選択肢を分けて指定 少しでもBacklog API の利用を検討している方々の参考になれば幸いです。 最後までお読みいただき、本当にありがとうございました! 私たちは同じ事業部で共に働いていただける仲間を募集しています! 私自身は対顧客のセキュリティに関する業務に従事していますので、そのような業務にご興味がありましたら、応募の際にお問い合わせください! みなさまのご応募、お待ちしています! クラウドアーキテクト アプリケーションアーキテクト 電通グループ向け基幹システムプロジェクトマネージャ 戦略的IT プロジェクトマネージャ/ITコンサルタント 執筆: @miyazawa.hibiki 、レビュー: Ishizawa Kento (@kent) ( Shodo で執筆されました )