TECH PLAY

電通総研

電通総研 の技術ブログ

822

こんにちは、ISID 金融ソリューション事業部の岡崎です。今回はEpicGames社が提供する ゲームエンジン 、 UnrealEngine5 のPlugin「PixelStreaming」を使用し、WebUIからサーバー上のUE5の操作を行いました。 はじめに PixelStreamingを利用してWebUIからUEを操作するためには、UEアプリ上のBluePrintにURLクエリや JavaScript などを紐づける必要があります。 今回は、WebUIとUEアプリの連携をする為の方法について解説します。 BluePrintとは、UnrealEngineの スクリプティング システムです。 BluePrintではキーボードの入力やマウスの動きを感知し、UEアプリ上でのイベントを追加していくことができます。 全体の処理の流れとしては、以下の方法でご説明します。 WebUI上のボタンクリックで、任意の JSON をWebサーバー側に送信 UEのBluePrint側で value のキーに入った文字列を受け取る 受け取った文字列をUEアプリ上に文字列として表示させる 検証環境/ツール Unreal Engine5.1 AWS EC2 AMI Windows _Server-2022-English-Full-Base-2023.01.19 インスタンス タイプ g4dn.12xlarge EBS 250GB Chrome ver.110.0.5481.177 実施手順 1. AWS 上にEC2 インスタンス を立ち上げ、UnrealEngineを導入 2. UEプロジェクトを作成 3. UE BluePrintを作成 4. プレイヤーページのHTMLファイルを更新 5. UEアプリのパッケージング実行 1. AWS 上にEC2 インスタンス を立ち上げ、UnrealEngineを導入 金融ソリューション事業部の山下さん の記事を参考に、 AWS 上にEC2 インスタンス を立ち上げます。 ※上記記事では、AMIは「 Ubuntu 20.04 LTS」ですが、UE5.1のBluePrintエディター上の挙動がうまくいかなかったため、 「 Windows _Server-2022-English-Full-Base-2023.01.19」に変更して作業を行いました。 また、UEの挙動がとても重かったので、 インスタンス タイプも「g4dn.xlarge」から「g4dn.12xlarge」へ変更しております。 次に、UnrealEngineを導入します。 作成した Windows ServerにRDPを利用し接続してUnrealEngineを導入します。 今回は UnrealEngineの公式サイト の手順に従ってダウンロードを行いました。 2. UEプロジェクトを作成 今回の検証では「ThirdPersonTemplate」を使用してプロジェクトを作成します。 金融ソリューション事業部の山下さん の記事と同様に「PixelStreaming」の プラグイン を有効にし、 [ Always Show Touch Interface (タッチ インターフェースを常に表示) ] 設定も有効にします。 Additional Launch Parametersの設定も行います。 AudioMixer -PixelStreamingIP=localhost -PixelStreamingPort=8888 -AllowPixelStreamingCommands ※上記記事のパラメータに -AllowPixelStreamingCommands の記述を追加しています。 3. UE BluePrintを作成 UEのPixelStreaming プラグイン では、BluePrintにアクセスするための API が公開されているので、 これを利用してHTMLページで送信されるイベントをUEで受け取り、処理を走らせることができます。 まずは、 UnrealEngine公式のリファレンス を参考に、先ほど作成したプロジェクトで、BluePrintの設定をします。 ViewPort下部のContent DrawerからContentを選択し、右のスペースで右クリックを行い、BluePrintClassを作成します。 ここでParent Classは「Actor」を選択します。 作成されたBluePrintClassを選択することでBluePrintエディタに移動できます。 まずは、UEのBluePrint側でWebUIのボタンがクリックされた際、送られてくる任意の JSON から、 value のキーに入った文字列を受け取る処理を作成します。 (WebUIからボタンクリックで JSON を送る処理は後述します) PixelStreaming用のBluePrintAPIにアクセスするためには、PixelStreamingInputComponentを追加しないといけません。そのために「Add」ボタンより「Pixel Streaming Input」を選択します。 左側のComponents欄に追加された「Pixel Streaming Input」を右側のイベントグラフにドラッグしてきます。 作成された「Pixel Streaming Input」のノードの矢印から、「Bind Event to On Input Event」を選択し、ノードの作成を行います。 新規BluePrintを作成した際に自動生成される「Event BeginPlay」イベントと、先ほど作成した「Bind Event to On Input Event」をつなぎます。これにより、PixelStreamingを行っているWebUI上でのイベントをBluePrintAPIを通じて受け取ることができます。 ここでは受け取った文字列をUEアプリ上に文字列として表示させる処理を作成します。 先ほど作成した「Bind Event to On Input Event」から「Custom Event」ノードを作成します。今回は「UI Interaction」と 命名 します。 その「UI Interaction」にDescriptorにBluePrintAPIを通じてデータが送られてくるので、「Get Json String Value 」関数を使用し、 keyが「 Value 」の値を文字列として返す設定を行います。さらにそこから「Print String」関数を使用し、「UI Interaction」と連結させます。 この際「Print String」関数の設定で「Print to Screen」にチェックを入れるのを忘れないようにしてください。 これでWebUI上でボタンがクリックされた際、任意の Json を受け取り、 value のキーに入った文字列をBluePrintを通して、UEアプリ上に文字列として表示させる処理のBluePrintが完成しました。 最後にBluePrintの コンパイル と保存を忘れずに行い、BluePrintエディターを閉じます。最後にContent Drawerより、 作成したBluePrintをViewPortにドラッグして配置して、UE側の設定が完了します。 4. プレイヤーページのHTMLファイルを更新 WebUIのボタンクリックで任意の JSON をWebサーバー側に送信するために、 プレイヤーページのHTMLでは、 JavaScript を使用してBluePrintAPIを実行する必要があるので、その処理を追加していきます。 金融ソリューション事業部の山下さん の記事を参考にしてプロジェクトを作成している場合、 SignallingServerを起動するために使用したフォルダ Samples/PixelStreaming/WebServers/SignallingWebServer 配下に Publicフォルダやscriptsフォルダを見つけることができます。 ここにPixelStreamingを行う際のWebUIに使用するHTMLファイルやJSファイルが格納されています。 Public配下のplayer.htmlがPixelStreamingで表示される静的ファイルになります。 まずはBluePrintAPIを発火させるためのボタンを作成します。 HTMLファイル内の <div id="player"></div> を下記に変更します。 <div id="player"> <button type="button" style="position:absolute; z-index:1;" onclick="execPrintString('Hello PixelStreaming');">Hello</button> </div> 次に JavaScript を追加します。 bodyタグの末尾に下記のscriptタグを追加します。 <script type="text/javascript"> function execPrintString(text) { var descriptor = { Value: text } emitUIInteraction(descriptor); } </script> この「emitUIInteraction」の引数の Json や文字列がBluePrintAPIに渡り、先ほど作成したBluePrintの「UI Interaction」のDescriptorに格納されます。 これでHTMLファイルの更新の完了です。 5. UEアプリのパッケージング実行 次にUnrealEngineで作成したプロジェクトをパッケージ化します。 コンピュータ上のフォルダを参照して、パッケージ化されたアプリを配置する場所を決めます。 パッケージ化が完了すると、指定した場所に「 windows 」フォルダが配置されます。 その「 windows 」フォルダの中の作成したexeファイルにパラメータを付与するためにショートカットを作成します。 ショートカットを作成したら、そのショートカットのプロパティを選択し、ターゲットの欄に -AudioMixer -PixelStreamingIP=localhost -PixelStreamingPort=8888 -AllowPixelStreamingCommands の記述を追加します。 これでパッケージ化の完了です。 SignallingServerを起動し、上記で作成したexeファイルのショートカットからパッケージ化したアプリを開くことができます。 実際にEC2で設定した グローバルIP をブラウザで入力することでPixelStreamingしたWebサイトに接続できます。 WebUIの左上部に作成した「Hello」のボタンが設置されており、押下することで「Hello PixelStreaming」という文字列が表示されていれば成功です! 終わりに 今回はPixelStreamingでWebUIのカスタマイズに挑戦してみました。 まだボタンを追加してみただけですが、今後WebUIに欲しい要素をWeb側とUE側どちらにも追加していくことができるようになりました。 BluePrintAPIにはWebUIからの情報をUEに反映させるだけではなく、UE側の変更をWebUIに反映させることもできるようなので、今後はそちらの調査を行ってみようと思います。 現在ISIDは web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! ISID採用ページ(Web3/メタバース/AI) 参考 ・ https://docs.unrealengine.com/5.1/ja/customizing-the-player-web-page-in-unreal-engine/ ・ https://docs.unrealengine.com/5.1/ja/getting-started-with-pixel-streaming-in-unreal-engine/ ・ https://zenn.dev/pate_techmemo/articles/07721136756dcc 執筆: @okazaki.wataru 、レビュー: @wakamoto.ryosuke ( Shodo で執筆されました )
アバター
こんにちは、ISID 金融ソリューション事業部の岡崎です。今回はEpicGames社が提供する ゲームエンジン 、 UnrealEngine5 のPlugin「PixelStreaming」を使用し、WebUIからサーバー上のUE5の操作を行いました。 はじめに PixelStreamingを利用してWebUIからUEを操作するためには、UEアプリ上のBluePrintにURLクエリや JavaScript などを紐づける必要があります。 今回は、WebUIとUEアプリの連携をする為の方法について解説します。 BluePrintとは、UnrealEngineの スクリプティング システムです。 BluePrintではキーボードの入力やマウスの動きを感知し、UEアプリ上でのイベントを追加していくことができます。 全体の処理の流れとしては、以下の方法でご説明します。 WebUI上のボタンクリックで、任意の JSON をWebサーバー側に送信 UEのBluePrint側で value のキーに入った文字列を受け取る 受け取った文字列をUEアプリ上に文字列として表示させる 検証環境/ツール Unreal Engine5.1 AWS EC2 AMI Windows _Server-2022-English-Full-Base-2023.01.19 インスタンス タイプ g4dn.12xlarge EBS 250GB Chrome ver.110.0.5481.177 実施手順 1. AWS 上にEC2 インスタンス を立ち上げ、UnrealEngineを導入 2. UEプロジェクトを作成 3. UE BluePrintを作成 4. プレイヤーページのHTMLファイルを更新 5. UEアプリのパッケージング実行 1. AWS 上にEC2 インスタンス を立ち上げ、UnrealEngineを導入 金融ソリューション事業部の山下さん の記事を参考に、 AWS 上にEC2 インスタンス を立ち上げます。 ※上記記事では、AMIは「 Ubuntu 20.04 LTS」ですが、UE5.1のBluePrintエディター上の挙動がうまくいかなかったため、 「 Windows _Server-2022-English-Full-Base-2023.01.19」に変更して作業を行いました。 また、UEの挙動がとても重かったので、 インスタンス タイプも「g4dn.xlarge」から「g4dn.12xlarge」へ変更しております。 次に、UnrealEngineを導入します。 作成した Windows ServerにRDPを利用し接続してUnrealEngineを導入します。 今回は UnrealEngineの公式サイト の手順に従ってダウンロードを行いました。 2. UEプロジェクトを作成 今回の検証では「ThirdPersonTemplate」を使用してプロジェクトを作成します。 金融ソリューション事業部の山下さん の記事と同様に「PixelStreaming」の プラグイン を有効にし、 [ Always Show Touch Interface (タッチ インターフェースを常に表示) ] 設定も有効にします。 Additional Launch Parametersの設定も行います。 AudioMixer -PixelStreamingIP=localhost -PixelStreamingPort=8888 -AllowPixelStreamingCommands ※上記記事のパラメータに -AllowPixelStreamingCommands の記述を追加しています。 3. UE BluePrintを作成 UEのPixelStreaming プラグイン では、BluePrintにアクセスするための API が公開されているので、 これを利用してHTMLページで送信されるイベントをUEで受け取り、処理を走らせることができます。 まずは、 UnrealEngine公式のリファレンス を参考に、先ほど作成したプロジェクトで、BluePrintの設定をします。 ViewPort下部のContent DrawerからContentを選択し、右のスペースで右クリックを行い、BluePrintClassを作成します。 ここでParent Classは「Actor」を選択します。 作成されたBluePrintClassを選択することでBluePrintエディタに移動できます。 まずは、UEのBluePrint側でWebUIのボタンがクリックされた際、送られてくる任意の JSON から、 value のキーに入った文字列を受け取る処理を作成します。 (WebUIからボタンクリックで JSON を送る処理は後述します) PixelStreaming用のBluePrintAPIにアクセスするためには、PixelStreamingInputComponentを追加しないといけません。そのために「Add」ボタンより「Pixel Streaming Input」を選択します。 左側のComponents欄に追加された「Pixel Streaming Input」を右側のイベントグラフにドラッグしてきます。 作成された「Pixel Streaming Input」のノードの矢印から、「Bind Event to On Input Event」を選択し、ノードの作成を行います。 新規BluePrintを作成した際に自動生成される「Event BeginPlay」イベントと、先ほど作成した「Bind Event to On Input Event」をつなぎます。これにより、PixelStreamingを行っているWebUI上でのイベントをBluePrintAPIを通じて受け取ることができます。 ここでは受け取った文字列をUEアプリ上に文字列として表示させる処理を作成します。 先ほど作成した「Bind Event to On Input Event」から「Custom Event」ノードを作成します。今回は「UI Interaction」と 命名 します。 その「UI Interaction」にDescriptorにBluePrintAPIを通じてデータが送られてくるので、「Get Json String Value 」関数を使用し、 keyが「 Value 」の値を文字列として返す設定を行います。さらにそこから「Print String」関数を使用し、「UI Interaction」と連結させます。 この際「Print String」関数の設定で「Print to Screen」にチェックを入れるのを忘れないようにしてください。 これでWebUI上でボタンがクリックされた際、任意の Json を受け取り、 value のキーに入った文字列をBluePrintを通して、UEアプリ上に文字列として表示させる処理のBluePrintが完成しました。 最後にBluePrintの コンパイル と保存を忘れずに行い、BluePrintエディターを閉じます。最後にContent Drawerより、 作成したBluePrintをViewPortにドラッグして配置して、UE側の設定が完了します。 4. プレイヤーページのHTMLファイルを更新 WebUIのボタンクリックで任意の JSON をWebサーバー側に送信するために、 プレイヤーページのHTMLでは、 JavaScript を使用してBluePrintAPIを実行する必要があるので、その処理を追加していきます。 金融ソリューション事業部の山下さん の記事を参考にしてプロジェクトを作成している場合、 SignallingServerを起動するために使用したフォルダ Samples/PixelStreaming/WebServers/SignallingWebServer 配下に Publicフォルダやscriptsフォルダを見つけることができます。 ここにPixelStreamingを行う際のWebUIに使用するHTMLファイルやJSファイルが格納されています。 Public配下のplayer.htmlがPixelStreamingで表示される静的ファイルになります。 まずはBluePrintAPIを発火させるためのボタンを作成します。 HTMLファイル内の <div id="player"></div> を下記に変更します。 <div id="player"> <button type="button" style="position:absolute; z-index:1;" onclick="execPrintString('Hello PixelStreaming');">Hello</button> </div> 次に JavaScript を追加します。 bodyタグの末尾に下記のscriptタグを追加します。 <script type="text/javascript"> function execPrintString(text) { var descriptor = { Value: text } emitUIInteraction(descriptor); } </script> この「emitUIInteraction」の引数の Json や文字列がBluePrintAPIに渡り、先ほど作成したBluePrintの「UI Interaction」のDescriptorに格納されます。 これでHTMLファイルの更新の完了です。 5. UEアプリのパッケージング実行 次にUnrealEngineで作成したプロジェクトをパッケージ化します。 コンピュータ上のフォルダを参照して、パッケージ化されたアプリを配置する場所を決めます。 パッケージ化が完了すると、指定した場所に「 windows 」フォルダが配置されます。 その「 windows 」フォルダの中の作成したexeファイルにパラメータを付与するためにショートカットを作成します。 ショートカットを作成したら、そのショートカットのプロパティを選択し、ターゲットの欄に -AudioMixer -PixelStreamingIP=localhost -PixelStreamingPort=8888 -AllowPixelStreamingCommands の記述を追加します。 これでパッケージ化の完了です。 SignallingServerを起動し、上記で作成したexeファイルのショートカットからパッケージ化したアプリを開くことができます。 実際にEC2で設定した グローバルIP をブラウザで入力することでPixelStreamingしたWebサイトに接続できます。 WebUIの左上部に作成した「Hello」のボタンが設置されており、押下することで「Hello PixelStreaming」という文字列が表示されていれば成功です! 終わりに 今回はPixelStreamingでWebUIのカスタマイズに挑戦してみました。 まだボタンを追加してみただけですが、今後WebUIに欲しい要素をWeb側とUE側どちらにも追加していくことができるようになりました。 BluePrintAPIにはWebUIからの情報をUEに反映させるだけではなく、UE側の変更をWebUIに反映させることもできるようなので、今後はそちらの調査を行ってみようと思います。 現在ISIDは web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! ISID採用ページ(Web3/メタバース/AI) 参考 ・ https://docs.unrealengine.com/5.1/ja/customizing-the-player-web-page-in-unreal-engine/ ・ https://docs.unrealengine.com/5.1/ja/getting-started-with-pixel-streaming-in-unreal-engine/ ・ https://zenn.dev/pate_techmemo/articles/07721136756dcc 執筆: @okazaki.wataru 、レビュー: @wakamoto.ryosuke ( Shodo で執筆されました )
アバター
こんにちは!金融ソリューション事業部の山下です。 本記事では、 前回の記事 に続き、StableDiffusion生成画像からUnrealEngineで使用可能なマテリアルを生成します。 今回はノードベー スプログ ラミングなど不要ですので、さくっと終わります。 実施環境/ツール 実施手順 1. Stable Diffusionでパターン画像の生成 2. Substance 3D Samplerでマテリアル化 3. Unreal Engineでマテリアル適用、レンダリング 所感 参考 実施環境/ツール OS: Windows 11 pro GPU : NVIDIA GeForce RTX 3070Ti Laptop DCC: Adobe Substance 3D Sampler 3.4.1 Game Engine: Unreal Engine 5.1.0 AI Model:Stable Diffusion 2.1 実施手順 Stable Diffusionでパターン画像の生成 Substance 3D Samplerでマテリアル化 Unreal Engine でマテリアル適用、 レンダリング 1. Stable Diffusionでパターン画像の生成 ChatGPTで生成した以下プロンプトで、パターン画像を生成しました。 beautiful pattern of traditional japanese paper, Exquisite, intricate, delicate, elegant, sophisticated, subtle, timeless, stunning, lustrous, and graceful, 8k, seamless, no dof, use of gold --- (DeepL日本語訳) 伝統的な和紙の美しい模様、精巧、複雑、繊細、エレガント、洗練、繊細、時代を超えた、見事、光沢、優美、8K、シームレス、ノードフ、金使用 Sampling Stepsは100、Resolutionは768x768です。 バッチ処理 で300枚くらい生成したものから、良さそうなものを4パターンほど選定しました。 2. Substance 3D Samplerでマテリアル化 プロジェクト新規作成後、生成画像をレイヤータブに ドラッグ&ドロップ します。 「画像からマテリアル」を選択して「読み込み」を押下します。 マテリアルが作成されます。 上記はプレビュー形式を「布」で表示しております。 ちなみに、「服」形式などのプレビューも可能です。 このままでも十分マテリアルとして使えますが、よりリアルな質感にする為、刺繍フィルターを適用します。 「アセット」>「フィルター」>「Embroidery」を追加します。 質感に刺繍フィルターが加わり、よりリアルになりました。 マテリアル画像として書き出します。 「共有」>「書き出し」>「書き出し形式...」を押下します。 ディレクト リや必要なチャンネルを指定して、「書き出し」を押下します。 無事、アウトプット ディレクト リに画像が出力されていれば、マテリアルは完成です。 3. Unreal Engine でマテリアル適用、 レンダリング Content Drawerに、マテリアル画像をimportします。 マテリアルを新規作成して、ノードを接続します。 任意のメッシュにマテリアルを適用すれば完成です。 4パターンのうち、2パターン分のマテリアルを作成してメッシュに適用しました。 以下、MovieRenderQueueで レンダリング を行い、 After Effects で動画変換した結果になります。 所感 今回、StableDiffusion生成画像からフォトリアルなマテリアルを生成しました。これまでのマテリアル制作ワークフローにおけるプロセスを飛躍的に改善できる可能性があると感じました。 一方で、現状はAIに関する一定の リテラシー が求められることも否めません(プロンプトの調整やAIモデルの選定、必要に応じたファインチューニングなど)。 まだ誰もが使える形ではツール等に組み込まれていない以上、このままの形で一般普及するイメージはつきませんが、AI x 3DCGの領域は文字通り日進月歩で進化している為、想像以上に早期にブレークスルーが起きると思われます。引き続き本領域、注目していきたいと思います! 現在ISIDは web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! ISID採用ページ(Web3/メタバース/AI) 参考 https://www.youtube.com/watch?v=cL_ZYdkIqBU https://shop.cgworld.jp/shopdetail/000000001040/ https://www.adobe.com/jp/products/substance3d-sampler.html 執筆: @yamashita.yuki 、レビュー: @wakamoto.ryosuke ( Shodo で執筆されました )
アバター
こんにちは!金融ソリューション事業部の山下です。 本記事では、 前回の記事 に続き、StableDiffusion生成画像からUnrealEngineで使用可能なマテリアルを生成します。 今回はノードベー スプログ ラミングなど不要ですので、さくっと終わります。 実施環境/ツール 実施手順 1. Stable Diffusionでパターン画像の生成 2. Substance 3D Samplerでマテリアル化 3. Unreal Engineでマテリアル適用、レンダリング 所感 参考 実施環境/ツール OS: Windows 11 pro GPU : NVIDIA GeForce RTX 3070Ti Laptop DCC: Adobe Substance 3D Sampler 3.4.1 Game Engine: Unreal Engine 5.1.0 AI Model:Stable Diffusion 2.1 実施手順 Stable Diffusionでパターン画像の生成 Substance 3D Samplerでマテリアル化 Unreal Engine でマテリアル適用、 レンダリング 1. Stable Diffusionでパターン画像の生成 ChatGPTで生成した以下プロンプトで、パターン画像を生成しました。 beautiful pattern of traditional japanese paper, Exquisite, intricate, delicate, elegant, sophisticated, subtle, timeless, stunning, lustrous, and graceful, 8k, seamless, no dof, use of gold --- (DeepL日本語訳) 伝統的な和紙の美しい模様、精巧、複雑、繊細、エレガント、洗練、繊細、時代を超えた、見事、光沢、優美、8K、シームレス、ノードフ、金使用 Sampling Stepsは100、Resolutionは768x768です。 バッチ処理 で300枚くらい生成したものから、良さそうなものを4パターンほど選定しました。 2. Substance 3D Samplerでマテリアル化 プロジェクト新規作成後、生成画像をレイヤータブに ドラッグ&ドロップ します。 「画像からマテリアル」を選択して「読み込み」を押下します。 マテリアルが作成されます。 上記はプレビュー形式を「布」で表示しております。 ちなみに、「服」形式などのプレビューも可能です。 このままでも十分マテリアルとして使えますが、よりリアルな質感にする為、刺繍フィルターを適用します。 「アセット」>「フィルター」>「Embroidery」を追加します。 質感に刺繍フィルターが加わり、よりリアルになりました。 マテリアル画像として書き出します。 「共有」>「書き出し」>「書き出し形式...」を押下します。 ディレクト リや必要なチャンネルを指定して、「書き出し」を押下します。 無事、アウトプット ディレクト リに画像が出力されていれば、マテリアルは完成です。 3. Unreal Engine でマテリアル適用、 レンダリング Content Drawerに、マテリアル画像をimportします。 マテリアルを新規作成して、ノードを接続します。 任意のメッシュにマテリアルを適用すれば完成です。 4パターンのうち、2パターン分のマテリアルを作成してメッシュに適用しました。 以下、MovieRenderQueueで レンダリング を行い、 After Effects で動画変換した結果になります。 所感 今回、StableDiffusion生成画像からフォトリアルなマテリアルを生成しました。これまでのマテリアル制作ワークフローにおけるプロセスを飛躍的に改善できる可能性があると感じました。 一方で、現状はAIに関する一定の リテラシー が求められることも否めません(プロンプトの調整やAIモデルの選定、必要に応じたファインチューニングなど)。 まだ誰もが使える形ではツール等に組み込まれていない以上、このままの形で一般普及するイメージはつきませんが、AI x 3DCGの領域は文字通り日進月歩で進化している為、想像以上に早期にブレークスルーが起きると思われます。引き続き本領域、注目していきたいと思います! 現在ISIDは web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! ISID採用ページ(Web3/メタバース/AI) 参考 https://www.youtube.com/watch?v=cL_ZYdkIqBU https://shop.cgworld.jp/shopdetail/000000001040/ https://www.adobe.com/jp/products/substance3d-sampler.html 執筆: @yamashita.yuki 、レビュー: @wakamoto.ryosuke ( Shodo で執筆されました )
アバター
こんにちは。HCM事業部5年目の尾形です。 今回は「若手による仕事の紹介」として、 理系院卒・HCM事業部 所属という立場から私の仕事についてお話しします。記事タイトルはお気に入りの小説から。風景描写が好きです。 簡単な自己紹介 ISIDではかなりレアなのですが、 博士後期 課程を修了して入社し、HCM事業部に配属されました。 入社のきっかけは、同じ研究室の先輩が先に入社しており、働きやすそうだなと思ったことでした。 最近はインクを塗りあう某 シューティングゲーム にハマっています。 一応X帯です。 「人財」管理の システム開発 真面目な話に移ります。 この記事を読んでいる人の多くは就職活動中の方かと思いますが、 HCM事業部 と聞いて何をしている部署か分かるでしょうか? 入社するまで私もよく分かっていませんでした。 HCMとは Human Capital Management の略で、日本語では人財管理 1 と言われます。どの組織にどんなスキルを持った人がいるのか、企業規模が大きくなるほど管理は難しくなります。 そんな課題を解決するため、ISIDでは自社パッケージ 2 商品である統合HCMシステム「 POSITIVE 」 3 を提供しています。HCM事業部はPOSITIVEの開発、販売、導入を行っている部署で、私はPOSITIVEの開発に携わっています。 パッケージ開発という仕事 ISIDの業態は何かと聞かれれば、おそらく SIer ということになると思います。 SIer であればお客様の要望を伺い、ニーズを満たすシステムを開発していくのが本筋ですが、HCM事業部の仕事は大きく異なります。 POSITIVEというパッケージを開発している以上、特定のお客様の要望に沿って開発するのではなく、 利用される全てのお客様にとって価値のある改善 を重ねていく必要があります。 POSITIVE製品サイトの導入事例 4 に掲載されている通り、 ANAホールディングス 株式会社様や 明治安田生命保険 相互会社様など、皆さんもよく知るであろう多くの企業様でご利用いただいています。 業種・業態にとらわれない多種多様な企業がお客様となっており、私たちの業務にも非常に大きな責任が伴います。 さらなる人財活用に向けた改善 最近ではISO30414 5 という人的資本情報開示に関する標準化も行われ、社内のみならず投資家からも人財情報活用が注目を集めています。 そんな背景の中、 どんな機能・サービスを提供すればお客様に喜んで使ってもらえるか をよく考えて今後の対応を企画することが開発者に求められています。これは既存機能の改善だけに留まらず、全く新しいア イデア を創出する必要もあるため、自由度が高いがゆえに非常に難しい課題です。しかし同時に、これはとてもやりがいのある課題でもあり、私もチームの一員として日々議論を重ねています。 これだけだと意識だけ高いふわっとした内容になってしまうので、具体的に何をやっているのか付け加えておきます。 今後対応すべき内容を決定するための顧客訪問・ ヒアリ ング(オンライン・オフラインどちらも) 人財管理をめぐる市場動向調査(各種資料を基にした調査や、チーム内でのディスカッション) お客様に影響する法改正への対応 先に挙げたような大きな課題もありますが、他にも直近で対応すべき課題もあります。 POSITIVEは人財情報の管理だけでなく、 給与計算、就業管理 の機能も備えています 6 。 特に給与計算においては、 社会保険 や税制など、毎年のように法制度が改正されるため、その対応が必要になります。 これは特に緊急性が高く、お客様が円滑に業務を遂行するために迅速かつ確実な対応が求められます。 この対応に際し、具体的には以下のような業務にあたっています。 各種開発作業(システム設計、実装、テスト。全てやることもあれば一部担当することもあります。) 開発作業のプロジェクト管理(人員・スケジュール調整、開発体制の運用見直し) 一日のスケジュール 一日のスケジュールとしてはこんな感じです。 9:30始業、主にテレワークでの業務のため、やや夜型な生活になっています。 あくまで一例ですので、日によっては私用のため早く切り上げたり、もう少し遅い時間まで仕事を進めることもあります。 研究も仕事に活かせる 私の大学院時代の研究テーマは無線通信でした。 お察しの通り、当時の研究内容と今の業務内容は全く関係ありません。 しかしながら、研究活動を通して得たスキルは今の業務に大きく活きていると実感しています。 相手に伝えるスキル 研究成果は学会発表や論文という形で外部に公開しますが、自分の研究内容を他者に伝えるのはとても難しいことです。 それでもエッセンスを理解してもらうために、皆さんも伝え方には特に気を配り、苦心していると思います。 伝える内容が異なるだけで、 伝え方を考えるプロセスは業務でも日常的に発生します 。 うまく伝わらないことに起因する ディスコミュニケーション は業務に大きな支障をきたしますので、このスキルは一見地味ですが非常に重要です。 論理的思考力 ビジネス書でも良く見る単語ですが、研究活動では必須スキルです。 会社に入ってから研修をわざわざ受けて身に付けることも多いスキルを、 研究活動では当たり前のように使っている わけです。 問題は何か、原因は何か、改善すべきポイントはどこか。 こういったことを考え続ける点も、研究と仕事に共通する点の一つと言えます。 学生のうちに勉強しておいた方がいいこと よく「大学生のうちに勉強しておいた方がいいことは?」と聞かれますが、ビジネス系の知識は入社後からでも十分身に付けられます。 大学に通う期間は(院進学等もろもろ込みでも)10年もないと思います。 一方、会社に入ってから過ごす期間は会社が変わったとしても40年以上あるでしょう。 勉強に励むのはもちろんですが、友人と楽しく過ごす・趣味に没頭する・新しいことに挑戦するなど、今しかできないことに注力してもらえればと個人的には思います。 趣味でやっていたことが仕事の思わぬところで役に立つ 、なんてこともあります。 某ゲームでは、味方に気を配り自分のブキができる最大限のサポートをすることが非常に重要です。仕事でも同様に、周囲の状況を見つつ、自分の長所を活かしたサポートをすることがチームの KO勝ち プロジェクト成功につながります。 最後に ISIDは新卒入社者への教育・サポート体制が手厚く、働き始めるうえで不自由のないよう会社が尽力してくれています。 少しでも興味が湧いたら、是非新卒採用サイトも覗いてみてください。 www.isid.co.jp 執筆: @ogata 、レビュー: Ishizawa Kento (@kent) ( Shodo で執筆されました ) 近年ではヒトを資源(Resource)から資本(Capital)として捉える潮流があり、「人材」よりも「人財」という表現を使うことも多いです。 ↩ パッケージ商品とは、お店で売っているような既製品のことです。とはいえPOSITIVEは家電量販店等で販売しているわけではなく、お客様から問い合わせをいただいて個別に販売・導入を行っています。 ↩ POSITIVE紹介サイト(ISID): https://www.isid.co.jp/positive/ ↩ POSITIVE紹介サイト(ISID)-導入企業一覧: https://www.isid.co.jp/positive/case/ ↩ ISO30414(ISO): https://www.iso.org/standard/69338.html ↩ グループ展開されている大企業様向けに人事・給与・就業を一括で管理できるシステムを提供していることは、POSITIVEの強みの一つです。 ↩
アバター
こんにちは。HCM事業部5年目の尾形です。 今回は「若手による仕事の紹介」として、 理系院卒・HCM事業部 所属という立場から私の仕事についてお話しします。記事タイトルはお気に入りの小説から。風景描写が好きです。 簡単な自己紹介 ISIDではかなりレアなのですが、 博士後期 課程を修了して入社し、HCM事業部に配属されました。 入社のきっかけは、同じ研究室の先輩が先に入社しており、働きやすそうだなと思ったことでした。 最近はインクを塗りあう某 シューティングゲーム にハマっています。 一応X帯です。 「人財」管理の システム開発 真面目な話に移ります。 この記事を読んでいる人の多くは就職活動中の方かと思いますが、 HCM事業部 と聞いて何をしている部署か分かるでしょうか? 入社するまで私もよく分かっていませんでした。 HCMとは Human Capital Management の略で、日本語では人財管理 1 と言われます。どの組織にどんなスキルを持った人がいるのか、企業規模が大きくなるほど管理は難しくなります。 そんな課題を解決するため、ISIDでは自社パッケージ 2 商品である統合HCMシステム「 POSITIVE 」 3 を提供しています。HCM事業部はPOSITIVEの開発、販売、導入を行っている部署で、私はPOSITIVEの開発に携わっています。 パッケージ開発という仕事 ISIDの業態は何かと聞かれれば、おそらく SIer ということになると思います。 SIer であればお客様の要望を伺い、ニーズを満たすシステムを開発していくのが本筋ですが、HCM事業部の仕事は大きく異なります。 POSITIVEというパッケージを開発している以上、特定のお客様の要望に沿って開発するのではなく、 利用される全てのお客様にとって価値のある改善 を重ねていく必要があります。 POSITIVE製品サイトの導入事例 4 に掲載されている通り、 ANAホールディングス 株式会社様や 明治安田生命保険 相互会社様など、皆さんもよく知るであろう多くの企業様でご利用いただいています。 業種・業態にとらわれない多種多様な企業がお客様となっており、私たちの業務にも非常に大きな責任が伴います。 さらなる人財活用に向けた改善 最近ではISO30414 5 という人的資本情報開示に関する標準化も行われ、社内のみならず投資家からも人財情報活用が注目を集めています。 そんな背景の中、 どんな機能・サービスを提供すればお客様に喜んで使ってもらえるか をよく考えて今後の対応を企画することが開発者に求められています。これは既存機能の改善だけに留まらず、全く新しいア イデア を創出する必要もあるため、自由度が高いがゆえに非常に難しい課題です。しかし同時に、これはとてもやりがいのある課題でもあり、私もチームの一員として日々議論を重ねています。 これだけだと意識だけ高いふわっとした内容になってしまうので、具体的に何をやっているのか付け加えておきます。 今後対応すべき内容を決定するための顧客訪問・ ヒアリ ング(オンライン・オフラインどちらも) 人財管理をめぐる市場動向調査(各種資料を基にした調査や、チーム内でのディスカッション) お客様に影響する法改正への対応 先に挙げたような大きな課題もありますが、他にも直近で対応すべき課題もあります。 POSITIVEは人財情報の管理だけでなく、 給与計算、就業管理 の機能も備えています 6 。 特に給与計算においては、 社会保険 や税制など、毎年のように法制度が改正されるため、その対応が必要になります。 これは特に緊急性が高く、お客様が円滑に業務を遂行するために迅速かつ確実な対応が求められます。 この対応に際し、具体的には以下のような業務にあたっています。 各種開発作業(システム設計、実装、テスト。全てやることもあれば一部担当することもあります。) 開発作業のプロジェクト管理(人員・スケジュール調整、開発体制の運用見直し) 一日のスケジュール 一日のスケジュールとしてはこんな感じです。 9:30始業、主にテレワークでの業務のため、やや夜型な生活になっています。 あくまで一例ですので、日によっては私用のため早く切り上げたり、もう少し遅い時間まで仕事を進めることもあります。 研究も仕事に活かせる 私の大学院時代の研究テーマは無線通信でした。 お察しの通り、当時の研究内容と今の業務内容は全く関係ありません。 しかしながら、研究活動を通して得たスキルは今の業務に大きく活きていると実感しています。 相手に伝えるスキル 研究成果は学会発表や論文という形で外部に公開しますが、自分の研究内容を他者に伝えるのはとても難しいことです。 それでもエッセンスを理解してもらうために、皆さんも伝え方には特に気を配り、苦心していると思います。 伝える内容が異なるだけで、 伝え方を考えるプロセスは業務でも日常的に発生します 。 うまく伝わらないことに起因する ディスコミュニケーション は業務に大きな支障をきたしますので、このスキルは一見地味ですが非常に重要です。 論理的思考力 ビジネス書でも良く見る単語ですが、研究活動では必須スキルです。 会社に入ってから研修をわざわざ受けて身に付けることも多いスキルを、 研究活動では当たり前のように使っている わけです。 問題は何か、原因は何か、改善すべきポイントはどこか。 こういったことを考え続ける点も、研究と仕事に共通する点の一つと言えます。 学生のうちに勉強しておいた方がいいこと よく「大学生のうちに勉強しておいた方がいいことは?」と聞かれますが、ビジネス系の知識は入社後からでも十分身に付けられます。 大学に通う期間は(院進学等もろもろ込みでも)10年もないと思います。 一方、会社に入ってから過ごす期間は会社が変わったとしても40年以上あるでしょう。 勉強に励むのはもちろんですが、友人と楽しく過ごす・趣味に没頭する・新しいことに挑戦するなど、今しかできないことに注力してもらえればと個人的には思います。 趣味でやっていたことが仕事の思わぬところで役に立つ 、なんてこともあります。 某ゲームでは、味方に気を配り自分のブキができる最大限のサポートをすることが非常に重要です。仕事でも同様に、周囲の状況を見つつ、自分の長所を活かしたサポートをすることがチームの KO勝ち プロジェクト成功につながります。 最後に ISIDは新卒入社者への教育・サポート体制が手厚く、働き始めるうえで不自由のないよう会社が尽力してくれています。 少しでも興味が湧いたら、是非新卒採用サイトも覗いてみてください。 www.isid.co.jp 執筆: @ogata 、レビュー: Ishizawa Kento (@kent) ( Shodo で執筆されました ) 近年ではヒトを資源(Resource)から資本(Capital)として捉える潮流があり、「人材」よりも「人財」という表現を使うことも多いです。 ↩ パッケージ商品とは、お店で売っているような既製品のことです。とはいえPOSITIVEは家電量販店等で販売しているわけではなく、お客様から問い合わせをいただいて個別に販売・導入を行っています。 ↩ POSITIVE紹介サイト(ISID): https://www.isid.co.jp/positive/ ↩ POSITIVE紹介サイト(ISID)-導入企業一覧: https://www.isid.co.jp/positive/case/ ↩ ISO30414(ISO): https://www.iso.org/standard/69338.html ↩ グループ展開されている大企業様向けに人事・給与・就業を一括で管理できるシステムを提供していることは、POSITIVEの強みの一つです。 ↩
アバター
こんにちは!金融ソリューション事業部の山下です。 本記事では、画像生成AIのStableDiffusionとデプスマップ推定AIのBoostingMonocularDepthを用いて、上記のように板を押し出し加工したような(擬似)3Dモデル生成方法を紹介します。 3Dの モデリング および レンダリング は Houdini を使用します。 実施環境/ツール 実施手順 1. Stable Diffusion web UIのインストール 2. tex2imgで画像生成 3. BoostingMonocularDepthでデプスマップ生成 4. 生成画像をデプスマップを用いて3D化 完成イメージ 所感 参考 実施環境/ツール OS: Windows 11 pro GPU : NVIDIA GeForce RTX 3070Ti Laptop DCC:Houdini Indie version 19.5.493 Stable Diffusion 2.1 実施手順 Stable Diffusion web UIのインストール tex2imgで画像生成 BoostingMonocularDepthでデプスマップ生成 Houdiniで生成画像をデプスマップを用いて3D化 1. Stable Diffusion web UIのインストール 画像生成の為に、StableDiffusionの動作環境を構築します。 手順については XI本部三浦さんの記事 や オープンイノベーションラボ比嘉さんの記事 にて既に解説記事がありますので、こちらをご確認ください。 今回の記事では、ローカル環境にStable Diffusion web UI(AUTOMATIC1111)、Stable Diffusion 2.1をインストールしました。 2. tex2imgで画像生成 StableDiffusionに、以下のプロンプトを入力して画像を生成しました。 The marble woman bust statue stood in the center of the room. white marble surface gleaming in the soft light. The woman's face was finely carved. delicate features and a serene expression. Her eyes were closed, as if in peaceful contemplation. her lips were slightly curved in a faint smile. Her hair was styled in a series of curls that cascaded down her shoulders and back. the statue exuded a sense of grace and beauty, making it a true work of art. (DeepL日本語訳) 大理石の女性胸像が部屋の中央に立っていた。 白い大理石の表面が柔らかい光に照らされて光っている。その女性の顔には繊細な彫刻が施されている。 繊細な顔立ちで、穏やかな表情をしている。彼女の目は、まるで平和な思索に耽っているかのように閉じられている。 唇はわずかに曲がって、かすかな笑みを浮かべている。 彼女の髪は、肩から背中にかけて流れるような一連のカールをしている。 この像は、優雅さと美しさを醸し出しており、まさに芸術品と呼ぶにふさわしい。 ちなみにプロンプトはChatGPUを用いて作成しております。が、特筆すべき点はない為詳細は割愛します。 プロンプト生成方法にご興味のある方は、 イノラボ比嘉さんの一連の記事 なども参考になりますのでご覧ください。 3. BoostingMonocularDepthでデプスマップ生成 Depth生成前に、webuiのインターフェースから「stable-diffusion-webui-depthmap-script」Extensionのインストールを行います。インストールを行うと、以下のように「Depth」メニューが追加されます。 補足:執筆時、Extensionインストール時にエラーが出た為、以下 GitHub issueを立てて解決しました。 https://github.com/thygate/stable-diffusion-webui-depthmap-script/issues/55#issuecomment-1364693322 私の環境ではExtensionが依存しているライブラリ(NextViT)を手動でインストールする必要がありましたが、最新バージョンでは解決されているとのことです。 次に、2.で生成した画像をインプットして「Generate」ボタンを押下します。 手順4.の3Dモデル化の際に マッピング をする為、「Match input size」のチェックをONにします。 精度を高める為に「BOOST(multi-resolution merging)」のチェックONは推奨です。 参考論文: http://yaksoy.github.io/highresdepth/ 生成されたデプスマップです。 4. 生成画像をデプスマップを用いて3D化 いよいよ2.の生成画像と3.のデプスマップを用いて、 Houdini で3D化を行います。 今回作成したノードの全体像はこちらです。 Houdiniではこのように、ノードベースでプロシージャルに処理を行っていきます。 割とシンプルな処理ですが、一つずつ解説していきます(数字は上から数えたノード順)。 まず最初に、Gridノードでベースとなる板を作成します。 Primitive Typeは「Polygon」、押し出しの解像度を合わせる為にRows/Columnsは512とします。 26万2144ポリゴンの板が作成されました。 UV ProjectノードでUVを設定します。 レンダリング 時にマテリアルを設定する際に必要になります。 パラメータはデフォルトのままで大丈夫です。 Attribute from Mapノードで、デプスマップ情報を適用します。 「Texture Map」に画像パス、「Export Attribute」に任意の名称(今回は「depth」)を設定して、データ型を「Float」に変更します。 Geometry Spreadsheetを確認すると、各Pointのデータに列「depth」が追加され、float値が格納されています。 Attribute Blur ノードで、デプスマップ情報を滑らかにします。 Attributesに「depth」を設定します。他のパラメータはお好みですが、Blurring iterationsは10、Step Sizeは0.5を設定します。 Attribute from Mapノードで、生成画像のデータを読み込みます。 「Export Attribute」には「Cd」を設定します。 Attribute VOPノードで、各Pointに対してdepthを掛け合わせてGridを押し出します。 インプットから位置情報Pのベクトルを分解してZ軸を抽出した上で、bindノードから取得したdepth情報をsubtractしています。 後から押し出し加減をコン トロール する為、depthデータ取得後にrampノードを挟んでいます。 デフォルトだと押し出しが強すぎた為、以下のようにB-Spline補完を用いてrampを設定しました。 depthの値による押し出しができました。 Group Createノードで、外縁のPointを「sides」という名称でグループ化します。 Include by EdgesをEnable、「Unshared Edges」をONにします。 Attribute Createノードで、各Pointの法線ベクトルNをZ軸+方向に設定します。 PolyExtrudeノードで、「sides」グループのみ法線ベクトルN方向に押し出します。 Normalノードで、法線ベクトルNを元に戻しておきます。 Nullノードで「OUT」という名称をつけて、3D化は完成です。 完成イメージ Soraris Karmaで レンダリング した結果がこちらです。 今回は Solaris Karmaで レンダリング を行いました。ノードの最終系は以下になります。 作成した3DモデルをSOP importで読み込み、マテリアルを設定した後、ライティングとカメラ設定を行い、Karma XPUで レンダリング を行いました。記事が長くなってしまうので、今回は レンダリング 部分の詳細説明は割愛します。 所感 今回は複数の技術(ChatGPU、画像生成AI、進度推定AI)を組み合わせました。Houdiniを使うと3Dの細かいデータやパラメータを確認しながらプロシージャルに試行錯誤ができ、非常に強力なツールです。 Project Titan のように、Houdiniは ゲームエンジン との連携も進んでいる為、今後も様々な検証を行っていきたいと思います。 また、最近のAIの進歩には目覚ましいものがあり、特にマテリアル生成の領域では実用レベルの域に達していますので、こちらも別記事で紹介していきたいと思います! 現在ISIDは web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! ISID採用ページ(Web3/メタバース/AI) 参考 https://github.com/AUTOMATIC1111/stable-diffusion-webui https://github.com/thygate/stable-diffusion-webui-depthmap-script https://github.com/compphoto/BoostingMonocularDepth https://entagma.com/stable-diffusion-2-0-quickstart/ 執筆: @yamashita.yuki 、レビュー: @wakamoto.ryosuke ( Shodo で執筆されました )
アバター
こんにちは!金融ソリューション事業部の山下です。 本記事では、画像生成AIのStableDiffusionとデプスマップ推定AIのBoostingMonocularDepthを用いて、上記のように板を押し出し加工したような(擬似)3Dモデル生成方法を紹介します。 3Dの モデリング および レンダリング は Houdini を使用します。 実施環境/ツール 実施手順 1. Stable Diffusion web UIのインストール 2. tex2imgで画像生成 3. BoostingMonocularDepthでデプスマップ生成 4. 生成画像をデプスマップを用いて3D化 完成イメージ 所感 参考 実施環境/ツール OS: Windows 11 pro GPU : NVIDIA GeForce RTX 3070Ti Laptop DCC:Houdini Indie version 19.5.493 Stable Diffusion 2.1 実施手順 Stable Diffusion web UIのインストール tex2imgで画像生成 BoostingMonocularDepthでデプスマップ生成 Houdiniで生成画像をデプスマップを用いて3D化 1. Stable Diffusion web UIのインストール 画像生成の為に、StableDiffusionの動作環境を構築します。 手順については XI本部三浦さんの記事 や オープンイノベーションラボ比嘉さんの記事 にて既に解説記事がありますので、こちらをご確認ください。 今回の記事では、ローカル環境にStable Diffusion web UI(AUTOMATIC1111)、Stable Diffusion 2.1をインストールしました。 2. tex2imgで画像生成 StableDiffusionに、以下のプロンプトを入力して画像を生成しました。 The marble woman bust statue stood in the center of the room. white marble surface gleaming in the soft light. The woman's face was finely carved. delicate features and a serene expression. Her eyes were closed, as if in peaceful contemplation. her lips were slightly curved in a faint smile. Her hair was styled in a series of curls that cascaded down her shoulders and back. the statue exuded a sense of grace and beauty, making it a true work of art. (DeepL日本語訳) 大理石の女性胸像が部屋の中央に立っていた。 白い大理石の表面が柔らかい光に照らされて光っている。その女性の顔には繊細な彫刻が施されている。 繊細な顔立ちで、穏やかな表情をしている。彼女の目は、まるで平和な思索に耽っているかのように閉じられている。 唇はわずかに曲がって、かすかな笑みを浮かべている。 彼女の髪は、肩から背中にかけて流れるような一連のカールをしている。 この像は、優雅さと美しさを醸し出しており、まさに芸術品と呼ぶにふさわしい。 ちなみにプロンプトはChatGPUを用いて作成しております。が、特筆すべき点はない為詳細は割愛します。 プロンプト生成方法にご興味のある方は、 イノラボ比嘉さんの一連の記事 なども参考になりますのでご覧ください。 3. BoostingMonocularDepthでデプスマップ生成 Depth生成前に、webuiのインターフェースから「stable-diffusion-webui-depthmap-script」Extensionのインストールを行います。インストールを行うと、以下のように「Depth」メニューが追加されます。 補足:執筆時、Extensionインストール時にエラーが出た為、以下 GitHub issueを立てて解決しました。 https://github.com/thygate/stable-diffusion-webui-depthmap-script/issues/55#issuecomment-1364693322 私の環境ではExtensionが依存しているライブラリ(NextViT)を手動でインストールする必要がありましたが、最新バージョンでは解決されているとのことです。 次に、2.で生成した画像をインプットして「Generate」ボタンを押下します。 手順4.の3Dモデル化の際に マッピング をする為、「Match input size」のチェックをONにします。 精度を高める為に「BOOST(multi-resolution merging)」のチェックONは推奨です。 参考論文: http://yaksoy.github.io/highresdepth/ 生成されたデプスマップです。 4. 生成画像をデプスマップを用いて3D化 いよいよ2.の生成画像と3.のデプスマップを用いて、 Houdini で3D化を行います。 今回作成したノードの全体像はこちらです。 Houdiniではこのように、ノードベースでプロシージャルに処理を行っていきます。 割とシンプルな処理ですが、一つずつ解説していきます(数字は上から数えたノード順)。 まず最初に、Gridノードでベースとなる板を作成します。 Primitive Typeは「Polygon」、押し出しの解像度を合わせる為にRows/Columnsは512とします。 26万2144ポリゴンの板が作成されました。 UV ProjectノードでUVを設定します。 レンダリング 時にマテリアルを設定する際に必要になります。 パラメータはデフォルトのままで大丈夫です。 Attribute from Mapノードで、デプスマップ情報を適用します。 「Texture Map」に画像パス、「Export Attribute」に任意の名称(今回は「depth」)を設定して、データ型を「Float」に変更します。 Geometry Spreadsheetを確認すると、各Pointのデータに列「depth」が追加され、float値が格納されています。 Attribute Blur ノードで、デプスマップ情報を滑らかにします。 Attributesに「depth」を設定します。他のパラメータはお好みですが、Blurring iterationsは10、Step Sizeは0.5を設定します。 Attribute from Mapノードで、生成画像のデータを読み込みます。 「Export Attribute」には「Cd」を設定します。 Attribute VOPノードで、各Pointに対してdepthを掛け合わせてGridを押し出します。 インプットから位置情報Pのベクトルを分解してZ軸を抽出した上で、bindノードから取得したdepth情報をsubtractしています。 後から押し出し加減をコン トロール する為、depthデータ取得後にrampノードを挟んでいます。 デフォルトだと押し出しが強すぎた為、以下のようにB-Spline補完を用いてrampを設定しました。 depthの値による押し出しができました。 Group Createノードで、外縁のPointを「sides」という名称でグループ化します。 Include by EdgesをEnable、「Unshared Edges」をONにします。 Attribute Createノードで、各Pointの法線ベクトルNをZ軸+方向に設定します。 PolyExtrudeノードで、「sides」グループのみ法線ベクトルN方向に押し出します。 Normalノードで、法線ベクトルNを元に戻しておきます。 Nullノードで「OUT」という名称をつけて、3D化は完成です。 完成イメージ Soraris Karmaで レンダリング した結果がこちらです。 今回は Solaris Karmaで レンダリング を行いました。ノードの最終系は以下になります。 作成した3DモデルをSOP importで読み込み、マテリアルを設定した後、ライティングとカメラ設定を行い、Karma XPUで レンダリング を行いました。記事が長くなってしまうので、今回は レンダリング 部分の詳細説明は割愛します。 所感 今回は複数の技術(ChatGPU、画像生成AI、進度推定AI)を組み合わせました。Houdiniを使うと3Dの細かいデータやパラメータを確認しながらプロシージャルに試行錯誤ができ、非常に強力なツールです。 Project Titan のように、Houdiniは ゲームエンジン との連携も進んでいる為、今後も様々な検証を行っていきたいと思います。 また、最近のAIの進歩には目覚ましいものがあり、特にマテリアル生成の領域では実用レベルの域に達していますので、こちらも別記事で紹介していきたいと思います! 現在ISIDは web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! ISID採用ページ(Web3/メタバース/AI) 参考 https://github.com/AUTOMATIC1111/stable-diffusion-webui https://github.com/thygate/stable-diffusion-webui-depthmap-script https://github.com/compphoto/BoostingMonocularDepth https://entagma.com/stable-diffusion-2-0-quickstart/ 執筆: @yamashita.yuki 、レビュー: @wakamoto.ryosuke ( Shodo で執筆されました )
アバター
こんにちは、XI 本部ソフトウェアデザインセンター所属・新卒 1 年目の松本です。 昨年10月に配属されてから 3 ヶ月間、Atlassian Forge を使った アプリ開発 を担当しました。 そこで今回は、Forge の概要と実際の開発手順を解説していきます。 Forge に関する日本語記事はほとんどなく心細い想いをしたので、本記事が誰かの助けになれば幸いです。 Forge とは UI 構築手法 2 パターン UI kit Custom UI 開発手順の例 到達目標 執筆者の環境 ① Node.js の導入 ② Forge CLI の導入 ③ プロジェクトの作成 ④ ビルド・デプロイ・インストール ⑤ TypeScript への対応 ⑥ Atlassian REST API と UI kit フックを使い、必要な情報を取得してみる ⑦ 取得した値を表示する ⑧ 編集内容を提出できるようにする 開発を便利にする Tips UI kit を使ってみた感想 おわりに Forge とは Atlassian 社が提供する FaaS プラットフォームです。Atlassian のサービスをカスタマイズ・機能拡張するためのアプリを、ユーザー自身で簡単に作成できます。現在は、Jira、Jira Service Management、Confluence の3つのサービスに対応しており、作成したアプリはこれらのサービス上から利用することとなります。 アプリの例としては、 Jira の課題パネルから多言語翻訳を使えるようにするアプリ Jira の課題の健全性を、更新の滞りなどの情報をもとに判断してくれるアプリ Confluence に、 Google フォトに保存されている写真を表示できるようにするアプリ などが公式ドキュメントで紹介されています。 https://developer.atlassian.com/platform/forge/example-apps/ アプリ開発 に伴う機能実装以外の作業(ビルド・デプロイ・権限の管理・スケーリング・テナント管理などなど)は Atlassian 側でほとんど担ってくれるため、開発者は実現したい機能の実装に注力できます。 UI 構築手法 2 パターン アプリの UI 構築手法が 2 パターン用意されており、いずれかを選ぶ必要があります。 迅速・簡便な UI kit と 自由度が高い Custom UI です。 UI kit 提供された コンポーネント を組み合わせて UI を構築します。 コンポーネント は、各種入力フォーム・ボタン・テーブルなど、豊富に用意されています。いずれも コンポーネント も Atlassian 風のデザインとなっており、自分でデザインを考える手間が省けます(逆に、カスタマイズ性はほとんどありません)。 使い方は React の コンポーネント とよく似ています。また、フック機能も提供されており、こちらもほぼ React のそれです。 なお、 レンダリング は全てサーバー側で行われるため、相応の遅延が発生します。また、現状、 コンポーネント とフックは提供されたもの以外には使うことができないため、柔軟性に欠けます(Forge 開発チームの動向を見ていると、近々できるようになるかも?)。 Custom UI HTML・ CSS ・ JavaScript などの静的リソースを使用して、独自の UI を構築します。UI kit とは異なり、 レンダリング はユーザー側で行われるため、速いです(ただし、外部リソースへのアクセスはバックエンドを経由する必要がある、といったルールがあります)。 タイトルの通り、以降の開発手順では前者の UI kit を用います。Custom UI の解説はまたの機会に…。 開発手順の例 到達目標 手順の紹介に入る前に、本記事における開発の到達目標を明確にしておきます。 ここでは、Jira Service Management に「リク エス ト内容をリク エス ター自身が編集できる機能」を追加することを目標とします。 Jira Service Management とは、Jira を拡張したサービスデスク管理ツールです。Jira Service Management では現状、届いたリク エス ト(= 困っている人からの問い合わせ)の内容を管理者側から編集することはできても、リク エス ター側からはできないという制限があります。今回実装する機能は、こうした制限を解決するものです。 なお、Jira や Confluence を対象にした場合も同じような流れで開発を進められます。 完成イメージ 設置したボタンを押すと、 編集用のモーダルが開き、 編集して submit を押すと、 反映される。 執筆者の環境 主要なものだけあげておきます。 macOS Monterey 12.6 Visual Studio Code 1.74.3 node 16.17.1 Forge CLI 6.4.0 Forge API 2.7.0 React 18.2.0 それでは開発に入っていきましょう。 ① Node.js の導入 v14 以降の LTS release が必要です。入っていない場合はインストールしてください。 ② Forge CLI の導入 Forge CLI は、Forge アプリを管理するために使用する要のパッケージです。npm からインストールしてください。 npm install --save-dev @forge/cli インストール後、 API トーク ンを使用してログインする必要があります。 詳細な手順は、以下のドキュメントを参照してください。 https://developer.atlassian.com/platform/forge/getting-started/#log-in-with-an-atlassian-api-token ③ プロジェクトの作成 以下のコマンドでプロジェクトを作成します。 forge create コマンドを実行すると 3 つ質問されるので、順に答えていきます。 (1) アプリ名 好きなアプリ名をつけましょう。 (2) UIツール UI kit と Custom UI の選択です。前述の通り、今回は UI kit を選択します。 (3) モジュールのテンプレート Forge では、Jira などのサービスにアプリを組み込むための機能を「モジュール」として提供しています。作りたいアプリに適したモジュールを選択すると、そのモジュールに合わせて良い感じのテンプレートを作成してくれます。 今回は、Jira Service Management のリク エス ト閲覧画面に編集ボタンを付けたいので、「jira-service-management- portal -request-view-action」というモジュールを選択します。これは「リク エス ト閲覧画面にボタンが追加され、ボタンをクリックすると定義したアクションを走らせることができる」というモジュールです。 モジュール一覧は以下から確認できますので、作りたいアプリに合わせて選んでみてください。 https://developer.atlassian.com/platform/forge/manifest-reference/modules/ 以上3つの選択が完了すると、自動的に以下のようなプロジェクトが作成されます。 コードも少し覗いてみましょう。 // src/index.jsx import ForgeUI, { render, Text, PortalRequestViewAction, ModalDialog, useState } from '@forge/ui'; const App = () => { const [isOpen, setOpen] = useState(true); if (!isOpen) { return null; } return ( <ModalDialog header="Hello" onClose={() => setOpen(false)}> <Text>Hello world!</Text> </ModalDialog> ); }; export const run = render( <PortalRequestViewAction> <App/> </PortalRequestViewAction> ); React そっくりですね。簡単にコードの解説をしておくと… useState は UI kit が提供するフックの1つで、使い方は React の useState とほとんど同じです。 <ModalDialog> や <Text> は UI kit componets と呼ばれ、Atlssian 風デザインのパーツを提供するための コンポーネント です。 一方、<PortalRequestViewAction> は Function components と呼ばれ、アプリ作成時に選択したモジュールが提供する コンポーネント です。今回の例では、<PortalRequestViewAction> に囲まれた UI kit componets が、リク エス ト閲覧ページのボタンをクリックした際に表示されることとなります。 ④ ビルド・デプロイ・インストール ここで 1 度アプリをデプロイし、Atlassian サービス上で確認してみましょう。 アプリのルートで以下のコマンドを実行します。これ 1 つでエラーチェック・ビルド・デプロイまでを行ってくれる強力なコマンドです。 forge deploy デプロイ先は develop、staging、production の3つが用意されており、--environment (-e) オプションで指定できます (デフォルトは development)。今回は development にします。 続いて、以下のコマンドを実行し、デプロイしたアプリを自分が管理するテナントにインストールします。実行後にテナントの ドメイン を聞かれるので入力してください。 forge install これで、Atlassianのサービスからアプリを使えるようになりました。確認してみましょう。 今回は Jira Service Management のリク エス ト閲覧画面にボタンを追加するモジュールを使ったので、当該画面を見に行きます。 すると、以下のようにボタンが設置されており、 クリックするとモーダルが開いて「 Hello World !」と表示されます。 ここまで 1 ミリもコーディングをしていません。素晴らしいですね。 ⑤ TypeScript への対応 本ステップは任意ですが、UI kit では簡単に対応できるので、ぜひやっておきましょう。 以下の 4 つの作業を行います。 (1) tsconfig. json の作成 今回は以下のように設定しました。 // tsconfig.json { "compilerOptions": { "target": "es2020", "jsx": "react", "module": "commonjs", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true } } (2) ソースファイルの拡張子を変更 js(x) → ts(x) (3) TypeScript の プリプロセッサ を package. json に追加 必要な プリプロセッサ があれば追加しましょう。 ついでに、React がないと index. tsx で エラーが表示されてしまうので、react および対応する プリプロセッサ を npm からインストールするとともに、index. tsx でインポートしておきます。 // src/index.tsx import React from "react" なお、TypeScript の コンパイル は、前述した forge deploy コマンド実行時に一緒に実行してくれます。 ⑥ Atlassian REST API と UI kit フックを使い、必要な情報を取得してみる 続いて、編集に必要な情報を取得して、モーダルに表示してみます。 アプリと Atlassian サービスとのやり取りは、基本的には Atlassian が提供する REST API を介して行われます。加えて、現在閲覧しているリク エス トの ID など、UI kit のフックを介して取得する情報も一部あります。 Forgeでは、 REST API の認可は OAuth2.0 で行われており、権限を移譲されたアプリがユーザーに代わって API 呼び出しを行います。そのため、開発者は トーク ンなどの機密情報を管理する必要がなく、簡潔なコードで API を呼び出せます。 併せて、アプリにどの程度の権限(スコープ)を与えるかを、 マニフェスト ファイル (manifest.yml) に記述する必要があり、このスコープによって扱える API の範囲が決まります。 利用できる Atlassian REST API は以下から確認してください。その API に必要なスコープなどの情報も記載されています。 https://developer.atlassian.com/platform/forge/product-rest-api-reference/ 今回は、現在閲覧しているリク エス トの ID をUI kit の フック で取得し、その ID をもとに、リク エス トの設問文や問い合わせ内容を REST API で取得してみます。 現在閲覧しているページの情報は、useProductContext という UI kit フックで取得します。なお、本フックの返り値の型として ProductContext 型が提供されていますが、Jira のみに対応していて、Jira Service Management には対応していません。ですので、今回は ProductContextForJsm 型を自身で定義しています。 // src/index.tsx // ProductContextForJsm の型定義 (省略。「コード全文を見る」から確認できます。) const productContext = useProductContext() as ProductContextForJsm; const requestId = productContext.extensionContext.request.key; 次に、欲しい情報を REST API で取得します。まずは @forge/ api モジュールを npm からインストールするとともに、index. tsx でインポートします。 // src/index.tsx import api, { Route, route } from "@forge/api"; リク エス ト情報の取得には「Get customer request by id or key」という API を用います。この API を呼びだす関数を定義しましょう。 // src/index.tsx // ResponseJson の型定義 (省略。「コード全文を見る」から確認できます。) const fetchRequest = async (requestKey: string): Promise<ResponseJson> => { const response = await api .asApp() .requestJira(route`/rest/servicedeskapi/request/${requestKey}`, { headers: { Accept: "application/json", }, }); return await response.json(); }; API 呼び出しの関数は、useEffect 内で呼び出し、返り値は state に保持するのが良いでしょう。 state を追加し、 // src/index.tsx const [responseJson, setResponseJson] = useState<ResponseJson>({ requestFieldValues: [], }); useEffect 内で API を呼び出して、返り値を state にセットします。 // src/index.tsx useEffect(async () => { const responseJson = await fetchRequest(requestId); setResponseJson(responseJson); }, []); 最後に、使用する API を呼びだすために必要なスコープを、 マニフェスト ファイルに追加します。 // manifest.yml permissions: scopes: - "read:servicedesk-request" - "read:jira-work" コード全文を見る // src/index.tsx import api, { Route, route } from "@forge/api"; import ForgeUI, { render, Text, PortalRequestViewAction, ModalDialog, useState, useProductContext, useEffect, } from "@forge/ui"; import { ExtensionContext, ProductContext } from "@forge/ui/out/types"; import React from "react"; interface ProductContextForJsm extends ProductContext { extensionContext: ExtensionContextForJsm; } interface ExtensionContextForJsm extends ExtensionContext { request: { key: string }; } interface ResponseJson { requestFieldValues: Request[]; } interface Request { fieldId: string; label: string; value: string; } const App = () => { const [isOpen, setOpen] = useState(true); const [responseJson, setResponseJson] = useState<ResponseJson>({ requestFieldValues: [], }); useEffect(async () => { const responseJson = await fetchRequest(requestId); setResponseJson(responseJson); }, []); const productContext = useProductContext() as ProductContextForJsm; const requestId = productContext.extensionContext.request.key; const fetchRequest = async (requestKey: string): Promise<ResponseJson> => { const response = await api .asApp() .requestJira(route`/rest/servicedeskapi/request/${requestKey}`, { headers: { Accept: "application/json", }, }); return await response.json(); }; if (!isOpen) { return null; } return ( <ModalDialog header="Hello" onClose={() => setOpen(false)}> <Text>Hello world!</Text> </ModalDialog> ); }; export const run = render( <PortalRequestViewAction> <App /> </PortalRequestViewAction> ); ⑦ 取得した値を表示する 続いて、先ほど取得した値を画面に表示してみましょう。Jira Service Management では多様な設問タイプ(テキストボックス、 チェックボックス 、 ラジオボタン ・・・)がありますが、これら全てに対応しようとすると、本記事では収まりきりません。今回は簡易的に、リク エス トの「要約」欄のみを表示してみることにします。 これです。 Form、TextField という UI kit コンポーネント を使用するので、まずはインポートを追加します。Text コンポーネント はもう使わないので、消しておきます。 // src/index.tsx import ForgeUI, { render, PortalRequestViewAction, ModalDialog, useState, useProductContext, useEffect, Form, TextField, } from "@forge/ui"; 続いて、 REST API で受け取ったリク エス トの情報から要約欄の情報だけを抜き出し、 TextField コンポーネント に変換する関数を定義します。 // src/index.tsx const makeTextField = ( responseJson: ResponseJson ): JSX.Element | undefined => { const summary = responseJson.requestFieldValues.find( (request) => request.fieldId === "summary" ); if (!summary) { return; } return ( <TextField label={summary.label} name={summary.fieldId} defaultValue={summary.value} ></TextField> ); }; 最後に、先ほど作成した関数から返される TextField コンポーネント を Form コンポーネント で囲み、App 関数で返すようにします。現在「 Hello World 」を返している部分を変更します。 // src/index.tsx return ( <ModalDialog header="Edit" onClose={() => setOpen(false)}> <Form onSubmit={() => setOpen(false)}>{makeTextField(responseJson)}</Form> </ModalDialog> ); Form コンポーネント は onSubmit プロパティ(画面で submit ボタンが押された際に走る処理)の指定が必須ですが、一旦は、画面を閉じる処理を入れておきます。 それでは、画面で確認してみましょう、、、 の前に、アプリのスコープを変更したので、アップグレードが必要です。以下のコマンドでアップグレードを実行します。 forge install --upgrade これでアプリを利用可能になりました。 確認してみると、以下のように、要約欄の情報を取得できるようになっていると思います。 コード全文を見る // src/index.tsx import api, { Route, route } from "@forge/api"; import ForgeUI, { render, PortalRequestViewAction, ModalDialog, useState, useProductContext, useEffect, Form, TextField, } from "@forge/ui"; import { ExtensionContext, ProductContext } from "@forge/ui/out/types"; import React from "react"; interface ProductContextForJsm extends ProductContext { extensionContext: ExtensionContextForJsm; } interface ExtensionContextForJsm extends ExtensionContext { request: { key: string }; } interface ResponseJson { requestFieldValues: Request[]; } interface Request { fieldId: string; label: string; value: string; } const App = () => { const [isOpen, setOpen] = useState(true); const [responseJson, setResponseJson] = useState<ResponseJson>({ requestFieldValues: [], }); useEffect(async () => { const responseJson = await fetchRequest(requestId); setResponseJson(responseJson); }, []); const productContext = useProductContext() as ProductContextForJsm; const requestId = productContext.extensionContext.request.key; const fetchRequest = async (requestKey: string): Promise<ResponseJson> => { const response = await api .asApp() .requestJira(route`/rest/servicedeskapi/request/${requestKey}`, { headers: { Accept: "application/json", }, }); return await response.json(); }; const makeTextField = ( responseJson: ResponseJson ): JSX.Element | undefined => { const summary = responseJson.requestFieldValues.find( (request) => request.fieldId === "summary" ); if (!summary) { return; } return ( <TextField label={summary.label} name={summary.fieldId} defaultValue={summary.value} ></TextField> ); }; if (!isOpen) { return null; } return ( <ModalDialog header="Edit" onClose={() => setOpen(false)}> <Form onSubmit={() => setOpen(false)}>{makeTextField(responseJson)}</Form> </ModalDialog> ); }; export const run = render( <PortalRequestViewAction> <App /> </PortalRequestViewAction> ); ⑧ 編集内容を提出できるようにする それでは最後に、編集した内容を提出し、変更を反映できるようにします。 編集内容の確定には「Edit issue」という API を使用します。この API を呼びだす関数を作成します。この際、リク エス トボディには、submit ボタンが押された際に送られてくるデータをもとに作成した JSON を指定します。 // src/index.tsx const execEdit = async (submitted: {summary: string}) => { await api.asApp().requestJira(route`/rest/api/3/issue/${requestId}`, { method: "PUT", headers: { Accept: "application/json", "Content-Type": "application/json", }, body: `{"fields":{"summary":"${submitted["summary"]}"}}`, }); setOpen(false); }; そして、この関数を Form コンポーネント の onSubmit プロパティで指定します。 // src/index.tsx return ( <ModalDialog header="Edit" onClose={() => setOpen(false)}> <Form onSubmit={execEdit}>{makeTextField(responseJson)}</Form> </ModalDialog> ); これでOKです。 画面で確認する前に、新たに API を追加したので、書き込み権限をアプリに与えてアップグレードしましょう。 // manifest.yml permissions: scopes: - "read:servicedesk-request" - "read:jira-work" - "write:jira-work" 以上で全ての工程が完了です。実際に使ってみます。 要約欄を編集し、submit を押して…リロードすると… 反映されています! コード全文を見る // src/index.tsx import api, { Route, route } from "@forge/api"; import ForgeUI, { render, PortalRequestViewAction, ModalDialog, useState, useProductContext, useEffect, Form, TextField, } from "@forge/ui"; import { ExtensionContext, ProductContext } from "@forge/ui/out/types"; import React from "react"; interface ProductContextForJsm extends ProductContext { extensionContext: ExtensionContextForJsm; } interface ExtensionContextForJsm extends ExtensionContext { request: { key: string }; } interface ResponseJson { requestFieldValues: Request[]; } interface Request { fieldId: string; label: string; value: string; } const App = () => { const [isOpen, setOpen] = useState(true); const [responseJson, setResponseJson] = useState<ResponseJson>({ requestFieldValues: [], }); useEffect(async () => { const responseJson = await fetchRequest(requestId); setResponseJson(responseJson); }, []); const productContext = useProductContext() as ProductContextForJsm; const requestId = productContext.extensionContext.request.key; const fetchRequest = async (requestKey: string): Promise<ResponseJson> => { const response = await api .asApp() .requestJira(route`/rest/servicedeskapi/request/${requestKey}`, { headers: { Accept: "application/json", }, }); return await response.json(); }; const makeTextField = ( responseJson: ResponseJson ): JSX.Element | undefined => { const summary = responseJson.requestFieldValues.find( (request) => request.fieldId === "summary" ); if (!summary) { return; } return ( <TextField label={summary.label} name={summary.fieldId} defaultValue={summary.value} ></TextField> ); }; const execEdit = async (submitted: { summary: string }) => { await api.asApp().requestJira(route`/rest/api/3/issue/${requestId}`, { method: "PUT", headers: { Accept: "application/json", "Content-Type": "application/json", }, body: `{"fields":{"summary":"${submitted["summary"]}"}}`, }); setOpen(false); }; if (!isOpen) { return null; } return ( <ModalDialog header="Edit" onClose={() => setOpen(false)}> <Form onSubmit={execEdit}>{makeTextField(responseJson)}</Form> </ModalDialog> ); }; export const run = render( <PortalRequestViewAction> <App /> </PortalRequestViewAction> ); 開発を便利にする Tips 開発手順の紹介は以上ですが、開発を便利にする Tips も紹介しておきます。 ① ログの出し方 ログを出したい箇所に console.log("出したい内容") を入れ、 forge logs を実行することで、ログが確認できます。 いちいち上記コマンドを打つのは面倒ですが、次に紹介する Tunnel モードを組み合わせることで解消されます。 ② Tunnel モード ファイル保存時に自動的に再ビルドを実行してくれる機能です。ただし、development 環境へアプリをデプロイしている場合に限定されます。いちいち forge deploy コマンドを打つ手間が省けるほか、前述したログもリアルタイムで出してくれるため、非常に便利です。 使い方は以下を確認してください。 https://developer.atlassian.com/platform/forge/tunneling/ 注意点として、たまに、何のエラーも出ていないにもかかわらず変更が反映されない場合がありました。把握しておかないと永遠に時間を溶かすことになるので気をつけてください。 ③ モジュールの追加方法 forge create コマンドでプロジェクトを作成した直後はモジュールは1つだけですが、当然複数モジュールを扱うこともできます。 マニフェスト ファイル (manifest.yml) に追加すればOKです。以下のようなイメージ。 // manifest.yml modules: jiraServiceManagement:portalRequestViewAction: - key: module1 function: func1 title: モジュール1 jiraServiceManagement:queuePage: - key: module2 function: func2 title: モジュール2 function: - key: func1 handler: index.run1 - key: func2 handler: index.run2 表示するボタンの表記やアイコン、ボタンを押した際に最初に呼び出される関数などを変更したい場合も マニフェスト ファイルをいじります。 UI kit を使ってみた感想 本記事では UI kit を使って開発を進めてきましたが、メリット・デメリットともに強く感じたため、感想を記しておきます(おおむねコンセプト通りの感想ですが)。 メリット とにかく開発が速くて簡単です。デザインのことは何も考えなくて良いです(考える余地がないとも言う)。 コンポーネント の種類も割と豊富で、シンプルな機能を追加するだけであればあまり困らないと思います。 デメリット 想像以上に自由がききませんでした。現状だと、文字の色やサイズ、 コンポーネント 間の間隔などのちょっとしたところも変えられません。また、今回紹介した編集機能に関して言うと、フィールドに対応した コンポーネント が提供されておらず、工夫してもどうしようもない場合がありました(リッチテキストで入力する欄など)。 レンダリング 速度が遅いです。毎回バックエンドを経由しているので仕方ないのですが、入力値のバリデーションなどでは特に気になります。 機能が複雑化してくるとどうしても UI kit では物足りない場面があるため、今後は Custom UI を使用した開発にも取り組んでいきたいと思います。 おわりに 今回は、Atlassian Forge の概要と実際の開発手順を説明しました。データの取得・画面表示・登録と、基本的な動作をカバーしたつもりです。ニッチなツールではありますが、本記事が誰かのお役に立てば幸いです。 私たちは同じチームで働いてくれる仲間を探しています。今回のエントリで紹介したような仕事に興味のある方、ご応募お待ちしています。 ソリューションアーキテクト 執筆: @matsu ( Shodo で執筆されました )
アバター
こんにちは、XI 本部ソフトウェアデザインセンター所属・新卒 1 年目の松本です。 昨年10月に配属されてから 3 ヶ月間、Atlassian Forge を使った アプリ開発 を担当しました。 そこで今回は、Forge の概要と実際の開発手順を解説していきます。 Forge に関する日本語記事はほとんどなく心細い想いをしたので、本記事が誰かの助けになれば幸いです。 Forge とは UI 構築手法 2 パターン UI kit Custom UI 開発手順の例 到達目標 執筆者の環境 ① Node.js の導入 ② Forge CLI の導入 ③ プロジェクトの作成 ④ ビルド・デプロイ・インストール ⑤ TypeScript への対応 ⑥ Atlassian REST API と UI kit フックを使い、必要な情報を取得してみる ⑦ 取得した値を表示する ⑧ 編集内容を提出できるようにする 開発を便利にする Tips UI kit を使ってみた感想 おわりに Forge とは Atlassian 社が提供する FaaS プラットフォームです。Atlassian のサービスをカスタマイズ・機能拡張するためのアプリを、ユーザー自身で簡単に作成できます。現在は、Jira、Jira Service Management、Confluence の3つのサービスに対応しており、作成したアプリはこれらのサービス上から利用することとなります。 アプリの例としては、 Jira の課題パネルから多言語翻訳を使えるようにするアプリ Jira の課題の健全性を、更新の滞りなどの情報をもとに判断してくれるアプリ Confluence に、 Google フォトに保存されている写真を表示できるようにするアプリ などが公式ドキュメントで紹介されています。 https://developer.atlassian.com/platform/forge/example-apps/ アプリ開発 に伴う機能実装以外の作業(ビルド・デプロイ・権限の管理・スケーリング・テナント管理などなど)は Atlassian 側でほとんど担ってくれるため、開発者は実現したい機能の実装に注力できます。 UI 構築手法 2 パターン アプリの UI 構築手法が 2 パターン用意されており、いずれかを選ぶ必要があります。 迅速・簡便な UI kit と 自由度が高い Custom UI です。 UI kit 提供された コンポーネント を組み合わせて UI を構築します。 コンポーネント は、各種入力フォーム・ボタン・テーブルなど、豊富に用意されています。いずれも コンポーネント も Atlassian 風のデザインとなっており、自分でデザインを考える手間が省けます(逆に、カスタマイズ性はほとんどありません)。 使い方は React の コンポーネント とよく似ています。また、フック機能も提供されており、こちらもほぼ React のそれです。 なお、 レンダリング は全てサーバー側で行われるため、相応の遅延が発生します。また、現状、 コンポーネント とフックは提供されたもの以外には使うことができないため、柔軟性に欠けます(Forge 開発チームの動向を見ていると、近々できるようになるかも?)。 Custom UI HTML・ CSS ・ JavaScript などの静的リソースを使用して、独自の UI を構築します。UI kit とは異なり、 レンダリング はユーザー側で行われるため、速いです(ただし、外部リソースへのアクセスはバックエンドを経由する必要がある、といったルールがあります)。 タイトルの通り、以降の開発手順では前者の UI kit を用います。Custom UI の解説はまたの機会に…。 開発手順の例 到達目標 手順の紹介に入る前に、本記事における開発の到達目標を明確にしておきます。 ここでは、Jira Service Management に「リク エス ト内容をリク エス ター自身が編集できる機能」を追加することを目標とします。 Jira Service Management とは、Jira を拡張したサービスデスク管理ツールです。Jira Service Management では現状、届いたリク エス ト(= 困っている人からの問い合わせ)の内容を管理者側から編集することはできても、リク エス ター側からはできないという制限があります。今回実装する機能は、こうした制限を解決するものです。 なお、Jira や Confluence を対象にした場合も同じような流れで開発を進められます。 完成イメージ 設置したボタンを押すと、 編集用のモーダルが開き、 編集して submit を押すと、 反映される。 執筆者の環境 主要なものだけあげておきます。 macOS Monterey 12.6 Visual Studio Code 1.74.3 node 16.17.1 Forge CLI 6.4.0 Forge API 2.7.0 React 18.2.0 それでは開発に入っていきましょう。 ① Node.js の導入 v14 以降の LTS release が必要です。入っていない場合はインストールしてください。 ② Forge CLI の導入 Forge CLI は、Forge アプリを管理するために使用する要のパッケージです。npm からインストールしてください。 npm install --save-dev @forge/cli インストール後、 API トーク ンを使用してログインする必要があります。 詳細な手順は、以下のドキュメントを参照してください。 https://developer.atlassian.com/platform/forge/getting-started/#log-in-with-an-atlassian-api-token ③ プロジェクトの作成 以下のコマンドでプロジェクトを作成します。 forge create コマンドを実行すると 3 つ質問されるので、順に答えていきます。 (1) アプリ名 好きなアプリ名をつけましょう。 (2) UIツール UI kit と Custom UI の選択です。前述の通り、今回は UI kit を選択します。 (3) モジュールのテンプレート Forge では、Jira などのサービスにアプリを組み込むための機能を「モジュール」として提供しています。作りたいアプリに適したモジュールを選択すると、そのモジュールに合わせて良い感じのテンプレートを作成してくれます。 今回は、Jira Service Management のリク エス ト閲覧画面に編集ボタンを付けたいので、「jira-service-management- portal -request-view-action」というモジュールを選択します。これは「リク エス ト閲覧画面にボタンが追加され、ボタンをクリックすると定義したアクションを走らせることができる」というモジュールです。 モジュール一覧は以下から確認できますので、作りたいアプリに合わせて選んでみてください。 https://developer.atlassian.com/platform/forge/manifest-reference/modules/ 以上3つの選択が完了すると、自動的に以下のようなプロジェクトが作成されます。 コードも少し覗いてみましょう。 // src/index.jsx import ForgeUI, { render, Text, PortalRequestViewAction, ModalDialog, useState } from '@forge/ui'; const App = () => { const [isOpen, setOpen] = useState(true); if (!isOpen) { return null; } return ( <ModalDialog header="Hello" onClose={() => setOpen(false)}> <Text>Hello world!</Text> </ModalDialog> ); }; export const run = render( <PortalRequestViewAction> <App/> </PortalRequestViewAction> ); React そっくりですね。簡単にコードの解説をしておくと… useState は UI kit が提供するフックの1つで、使い方は React の useState とほとんど同じです。 <ModalDialog> や <Text> は UI kit componets と呼ばれ、Atlssian 風デザインのパーツを提供するための コンポーネント です。 一方、<PortalRequestViewAction> は Function components と呼ばれ、アプリ作成時に選択したモジュールが提供する コンポーネント です。今回の例では、<PortalRequestViewAction> に囲まれた UI kit componets が、リク エス ト閲覧ページのボタンをクリックした際に表示されることとなります。 ④ ビルド・デプロイ・インストール ここで 1 度アプリをデプロイし、Atlassian サービス上で確認してみましょう。 アプリのルートで以下のコマンドを実行します。これ 1 つでエラーチェック・ビルド・デプロイまでを行ってくれる強力なコマンドです。 forge deploy デプロイ先は develop、staging、production の3つが用意されており、--environment (-e) オプションで指定できます (デフォルトは development)。今回は development にします。 続いて、以下のコマンドを実行し、デプロイしたアプリを自分が管理するテナントにインストールします。実行後にテナントの ドメイン を聞かれるので入力してください。 forge install これで、Atlassianのサービスからアプリを使えるようになりました。確認してみましょう。 今回は Jira Service Management のリク エス ト閲覧画面にボタンを追加するモジュールを使ったので、当該画面を見に行きます。 すると、以下のようにボタンが設置されており、 クリックするとモーダルが開いて「 Hello World !」と表示されます。 ここまで 1 ミリもコーディングをしていません。素晴らしいですね。 ⑤ TypeScript への対応 本ステップは任意ですが、UI kit では簡単に対応できるので、ぜひやっておきましょう。 以下の 4 つの作業を行います。 (1) tsconfig. json の作成 今回は以下のように設定しました。 // tsconfig.json { "compilerOptions": { "target": "es2020", "jsx": "react", "module": "commonjs", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true } } (2) ソースファイルの拡張子を変更 js(x) → ts(x) (3) TypeScript の プリプロセッサ を package. json に追加 必要な プリプロセッサ があれば追加しましょう。 ついでに、React がないと index. tsx で エラーが表示されてしまうので、react および対応する プリプロセッサ を npm からインストールするとともに、index. tsx でインポートしておきます。 // src/index.tsx import React from "react" なお、TypeScript の コンパイル は、前述した forge deploy コマンド実行時に一緒に実行してくれます。 ⑥ Atlassian REST API と UI kit フックを使い、必要な情報を取得してみる 続いて、編集に必要な情報を取得して、モーダルに表示してみます。 アプリと Atlassian サービスとのやり取りは、基本的には Atlassian が提供する REST API を介して行われます。加えて、現在閲覧しているリク エス トの ID など、UI kit のフックを介して取得する情報も一部あります。 Forgeでは、 REST API の認可は OAuth2.0 で行われており、権限を移譲されたアプリがユーザーに代わって API 呼び出しを行います。そのため、開発者は トーク ンなどの機密情報を管理する必要がなく、簡潔なコードで API を呼び出せます。 併せて、アプリにどの程度の権限(スコープ)を与えるかを、 マニフェスト ファイル (manifest.yml) に記述する必要があり、このスコープによって扱える API の範囲が決まります。 利用できる Atlassian REST API は以下から確認してください。その API に必要なスコープなどの情報も記載されています。 https://developer.atlassian.com/platform/forge/product-rest-api-reference/ 今回は、現在閲覧しているリク エス トの ID をUI kit の フック で取得し、その ID をもとに、リク エス トの設問文や問い合わせ内容を REST API で取得してみます。 現在閲覧しているページの情報は、useProductContext という UI kit フックで取得します。なお、本フックの返り値の型として ProductContext 型が提供されていますが、Jira のみに対応していて、Jira Service Management には対応していません。ですので、今回は ProductContextForJsm 型を自身で定義しています。 // src/index.tsx // ProductContextForJsm の型定義 (省略。「コード全文を見る」から確認できます。) const productContext = useProductContext() as ProductContextForJsm; const requestId = productContext.extensionContext.request.key; 次に、欲しい情報を REST API で取得します。まずは @forge/ api モジュールを npm からインストールするとともに、index. tsx でインポートします。 // src/index.tsx import api, { Route, route } from "@forge/api"; リク エス ト情報の取得には「Get customer request by id or key」という API を用います。この API を呼びだす関数を定義しましょう。 // src/index.tsx // ResponseJson の型定義 (省略。「コード全文を見る」から確認できます。) const fetchRequest = async (requestKey: string): Promise<ResponseJson> => { const response = await api .asApp() .requestJira(route`/rest/servicedeskapi/request/${requestKey}`, { headers: { Accept: "application/json", }, }); return await response.json(); }; API 呼び出しの関数は、useEffect 内で呼び出し、返り値は state に保持するのが良いでしょう。 state を追加し、 // src/index.tsx const [responseJson, setResponseJson] = useState<ResponseJson>({ requestFieldValues: [], }); useEffect 内で API を呼び出して、返り値を state にセットします。 // src/index.tsx useEffect(async () => { const responseJson = await fetchRequest(requestId); setResponseJson(responseJson); }, []); 最後に、使用する API を呼びだすために必要なスコープを、 マニフェスト ファイルに追加します。 // manifest.yml permissions: scopes: - "read:servicedesk-request" - "read:jira-work" コード全文を見る // src/index.tsx import api, { Route, route } from "@forge/api"; import ForgeUI, { render, Text, PortalRequestViewAction, ModalDialog, useState, useProductContext, useEffect, } from "@forge/ui"; import { ExtensionContext, ProductContext } from "@forge/ui/out/types"; import React from "react"; interface ProductContextForJsm extends ProductContext { extensionContext: ExtensionContextForJsm; } interface ExtensionContextForJsm extends ExtensionContext { request: { key: string }; } interface ResponseJson { requestFieldValues: Request[]; } interface Request { fieldId: string; label: string; value: string; } const App = () => { const [isOpen, setOpen] = useState(true); const [responseJson, setResponseJson] = useState<ResponseJson>({ requestFieldValues: [], }); useEffect(async () => { const responseJson = await fetchRequest(requestId); setResponseJson(responseJson); }, []); const productContext = useProductContext() as ProductContextForJsm; const requestId = productContext.extensionContext.request.key; const fetchRequest = async (requestKey: string): Promise<ResponseJson> => { const response = await api .asApp() .requestJira(route`/rest/servicedeskapi/request/${requestKey}`, { headers: { Accept: "application/json", }, }); return await response.json(); }; if (!isOpen) { return null; } return ( <ModalDialog header="Hello" onClose={() => setOpen(false)}> <Text>Hello world!</Text> </ModalDialog> ); }; export const run = render( <PortalRequestViewAction> <App /> </PortalRequestViewAction> ); ⑦ 取得した値を表示する 続いて、先ほど取得した値を画面に表示してみましょう。Jira Service Management では多様な設問タイプ(テキストボックス、 チェックボックス 、 ラジオボタン ・・・)がありますが、これら全てに対応しようとすると、本記事では収まりきりません。今回は簡易的に、リク エス トの「要約」欄のみを表示してみることにします。 これです。 Form、TextField という UI kit コンポーネント を使用するので、まずはインポートを追加します。Text コンポーネント はもう使わないので、消しておきます。 // src/index.tsx import ForgeUI, { render, PortalRequestViewAction, ModalDialog, useState, useProductContext, useEffect, Form, TextField, } from "@forge/ui"; 続いて、 REST API で受け取ったリク エス トの情報から要約欄の情報だけを抜き出し、 TextField コンポーネント に変換する関数を定義します。 // src/index.tsx const makeTextField = ( responseJson: ResponseJson ): JSX.Element | undefined => { const summary = responseJson.requestFieldValues.find( (request) => request.fieldId === "summary" ); if (!summary) { return; } return ( <TextField label={summary.label} name={summary.fieldId} defaultValue={summary.value} ></TextField> ); }; 最後に、先ほど作成した関数から返される TextField コンポーネント を Form コンポーネント で囲み、App 関数で返すようにします。現在「 Hello World 」を返している部分を変更します。 // src/index.tsx return ( <ModalDialog header="Edit" onClose={() => setOpen(false)}> <Form onSubmit={() => setOpen(false)}>{makeTextField(responseJson)}</Form> </ModalDialog> ); Form コンポーネント は onSubmit プロパティ(画面で submit ボタンが押された際に走る処理)の指定が必須ですが、一旦は、画面を閉じる処理を入れておきます。 それでは、画面で確認してみましょう、、、 の前に、アプリのスコープを変更したので、アップグレードが必要です。以下のコマンドでアップグレードを実行します。 forge install --upgrade これでアプリを利用可能になりました。 確認してみると、以下のように、要約欄の情報を取得できるようになっていると思います。 コード全文を見る // src/index.tsx import api, { Route, route } from "@forge/api"; import ForgeUI, { render, PortalRequestViewAction, ModalDialog, useState, useProductContext, useEffect, Form, TextField, } from "@forge/ui"; import { ExtensionContext, ProductContext } from "@forge/ui/out/types"; import React from "react"; interface ProductContextForJsm extends ProductContext { extensionContext: ExtensionContextForJsm; } interface ExtensionContextForJsm extends ExtensionContext { request: { key: string }; } interface ResponseJson { requestFieldValues: Request[]; } interface Request { fieldId: string; label: string; value: string; } const App = () => { const [isOpen, setOpen] = useState(true); const [responseJson, setResponseJson] = useState<ResponseJson>({ requestFieldValues: [], }); useEffect(async () => { const responseJson = await fetchRequest(requestId); setResponseJson(responseJson); }, []); const productContext = useProductContext() as ProductContextForJsm; const requestId = productContext.extensionContext.request.key; const fetchRequest = async (requestKey: string): Promise<ResponseJson> => { const response = await api .asApp() .requestJira(route`/rest/servicedeskapi/request/${requestKey}`, { headers: { Accept: "application/json", }, }); return await response.json(); }; const makeTextField = ( responseJson: ResponseJson ): JSX.Element | undefined => { const summary = responseJson.requestFieldValues.find( (request) => request.fieldId === "summary" ); if (!summary) { return; } return ( <TextField label={summary.label} name={summary.fieldId} defaultValue={summary.value} ></TextField> ); }; if (!isOpen) { return null; } return ( <ModalDialog header="Edit" onClose={() => setOpen(false)}> <Form onSubmit={() => setOpen(false)}>{makeTextField(responseJson)}</Form> </ModalDialog> ); }; export const run = render( <PortalRequestViewAction> <App /> </PortalRequestViewAction> ); ⑧ 編集内容を提出できるようにする それでは最後に、編集した内容を提出し、変更を反映できるようにします。 編集内容の確定には「Edit issue」という API を使用します。この API を呼びだす関数を作成します。この際、リク エス トボディには、submit ボタンが押された際に送られてくるデータをもとに作成した JSON を指定します。 // src/index.tsx const execEdit = async (submitted: {summary: string}) => { await api.asApp().requestJira(route`/rest/api/3/issue/${requestId}`, { method: "PUT", headers: { Accept: "application/json", "Content-Type": "application/json", }, body: `{"fields":{"summary":"${submitted["summary"]}"}}`, }); setOpen(false); }; そして、この関数を Form コンポーネント の onSubmit プロパティで指定します。 // src/index.tsx return ( <ModalDialog header="Edit" onClose={() => setOpen(false)}> <Form onSubmit={execEdit}>{makeTextField(responseJson)}</Form> </ModalDialog> ); これでOKです。 画面で確認する前に、新たに API を追加したので、書き込み権限をアプリに与えてアップグレードしましょう。 // manifest.yml permissions: scopes: - "read:servicedesk-request" - "read:jira-work" - "write:jira-work" 以上で全ての工程が完了です。実際に使ってみます。 要約欄を編集し、submit を押して…リロードすると… 反映されています! コード全文を見る // src/index.tsx import api, { Route, route } from "@forge/api"; import ForgeUI, { render, PortalRequestViewAction, ModalDialog, useState, useProductContext, useEffect, Form, TextField, } from "@forge/ui"; import { ExtensionContext, ProductContext } from "@forge/ui/out/types"; import React from "react"; interface ProductContextForJsm extends ProductContext { extensionContext: ExtensionContextForJsm; } interface ExtensionContextForJsm extends ExtensionContext { request: { key: string }; } interface ResponseJson { requestFieldValues: Request[]; } interface Request { fieldId: string; label: string; value: string; } const App = () => { const [isOpen, setOpen] = useState(true); const [responseJson, setResponseJson] = useState<ResponseJson>({ requestFieldValues: [], }); useEffect(async () => { const responseJson = await fetchRequest(requestId); setResponseJson(responseJson); }, []); const productContext = useProductContext() as ProductContextForJsm; const requestId = productContext.extensionContext.request.key; const fetchRequest = async (requestKey: string): Promise<ResponseJson> => { const response = await api .asApp() .requestJira(route`/rest/servicedeskapi/request/${requestKey}`, { headers: { Accept: "application/json", }, }); return await response.json(); }; const makeTextField = ( responseJson: ResponseJson ): JSX.Element | undefined => { const summary = responseJson.requestFieldValues.find( (request) => request.fieldId === "summary" ); if (!summary) { return; } return ( <TextField label={summary.label} name={summary.fieldId} defaultValue={summary.value} ></TextField> ); }; const execEdit = async (submitted: { summary: string }) => { await api.asApp().requestJira(route`/rest/api/3/issue/${requestId}`, { method: "PUT", headers: { Accept: "application/json", "Content-Type": "application/json", }, body: `{"fields":{"summary":"${submitted["summary"]}"}}`, }); setOpen(false); }; if (!isOpen) { return null; } return ( <ModalDialog header="Edit" onClose={() => setOpen(false)}> <Form onSubmit={execEdit}>{makeTextField(responseJson)}</Form> </ModalDialog> ); }; export const run = render( <PortalRequestViewAction> <App /> </PortalRequestViewAction> ); 開発を便利にする Tips 開発手順の紹介は以上ですが、開発を便利にする Tips も紹介しておきます。 ① ログの出し方 ログを出したい箇所に console.log("出したい内容") を入れ、 forge logs を実行することで、ログが確認できます。 いちいち上記コマンドを打つのは面倒ですが、次に紹介する Tunnel モードを組み合わせることで解消されます。 ② Tunnel モード ファイル保存時に自動的に再ビルドを実行してくれる機能です。ただし、development 環境へアプリをデプロイしている場合に限定されます。いちいち forge deploy コマンドを打つ手間が省けるほか、前述したログもリアルタイムで出してくれるため、非常に便利です。 使い方は以下を確認してください。 https://developer.atlassian.com/platform/forge/tunneling/ 注意点として、たまに、何のエラーも出ていないにもかかわらず変更が反映されない場合がありました。把握しておかないと永遠に時間を溶かすことになるので気をつけてください。 ③ モジュールの追加方法 forge create コマンドでプロジェクトを作成した直後はモジュールは1つだけですが、当然複数モジュールを扱うこともできます。 マニフェスト ファイル (manifest.yml) に追加すればOKです。以下のようなイメージ。 // manifest.yml modules: jiraServiceManagement:portalRequestViewAction: - key: module1 function: func1 title: モジュール1 jiraServiceManagement:queuePage: - key: module2 function: func2 title: モジュール2 function: - key: func1 handler: index.run1 - key: func2 handler: index.run2 表示するボタンの表記やアイコン、ボタンを押した際に最初に呼び出される関数などを変更したい場合も マニフェスト ファイルをいじります。 UI kit を使ってみた感想 本記事では UI kit を使って開発を進めてきましたが、メリット・デメリットともに強く感じたため、感想を記しておきます(おおむねコンセプト通りの感想ですが)。 メリット とにかく開発が速くて簡単です。デザインのことは何も考えなくて良いです(考える余地がないとも言う)。 コンポーネント の種類も割と豊富で、シンプルな機能を追加するだけであればあまり困らないと思います。 デメリット 想像以上に自由がききませんでした。現状だと、文字の色やサイズ、 コンポーネント 間の間隔などのちょっとしたところも変えられません。また、今回紹介した編集機能に関して言うと、フィールドに対応した コンポーネント が提供されておらず、工夫してもどうしようもない場合がありました(リッチテキストで入力する欄など)。 レンダリング 速度が遅いです。毎回バックエンドを経由しているので仕方ないのですが、入力値のバリデーションなどでは特に気になります。 機能が複雑化してくるとどうしても UI kit では物足りない場面があるため、今後は Custom UI を使用した開発にも取り組んでいきたいと思います。 おわりに 今回は、Atlassian Forge の概要と実際の開発手順を説明しました。データの取得・画面表示・登録と、基本的な動作をカバーしたつもりです。ニッチなツールではありますが、本記事が誰かのお役に立てば幸いです。 私たちは同じチームで働いてくれる仲間を探しています。今回のエントリで紹介したような仕事に興味のある方、ご応募お待ちしています。 ソリューションアーキテクト 執筆: @matsu ( Shodo で執筆されました )
アバター
こんにちは!金融ソリューション事業部の山下です。 本記事では、 オープンソース の3DCGソフト Blender を用いて、現実の地形や建物を模した3DCGモデルを生成する方法を紹介します。 今回は Blender GIS という プラグイン を用いて、衛星画像、建物データ( OSM )、標高データ(SRTM)を利用してモデルを制作しました。 実施環境/ツール 実施手順 1. BlenderにBlenderGISプラグインをインストールする 2. 衛星画像データを取得する 3. 標高データ(SRTM)の取得、反映 4. 建物、その他データ(OSM)の取得、反映 5. 建物上部へテクスチャをマッピング 6. 建物側面へのマテリアルの適用 完成イメージ 所感 参考 実施環境/ツール OS: Windows 11 pro GPU : NVIDIA GeForce RTX 3070Ti Laptop DCC: Blender 3.3.1 Plugin: Blender GIS 2.2.8 実施手順 Blender にBlenderGIS プラグイン をインストールする 衛星画像データを取得する 標高データ(SRTM)の取得、反映 建物、その他データ( OSM )の取得、反映 建物上部へテクスチャを マッピング 建物側面へのマテリアルの適用 1. Blender にBlenderGIS プラグイン をインストールする 以下の GitHub リポジトリ をダウンロードします。 Blender   GIS プラグイン https://github.com/domlysz/BlenderGIS プラグイン のインストールは、メニューのEdit > Preferences > Add-onsを選択して、ダウンロードしたZIPファイルを読み込むことで完了します。完了後、Add onの一覧にBlenderGISが追加され、チェックがつきます。 2. 衛星画像データを取得する BlenderGIS プラグイン をインストールすると、以下のように3D viewport内に「 GIS 」というメニューが表示されます。 GIS > Web geodata > Basemapを選択します。 設定はそのままで、OKを押下します。 世界地図が表示されます。 Gボタン押下で検索バーが表示されるので、表示させたい地名で検索します。 今回は「Shinagawa Station」、Zoom Levelは「16」と入力します。 品川駅周辺の地図が表示されました。 Eボタン押下で、Basemapの2D画像がExportできます。 3. 標高データ(SRTM)の取得、反映 2D画像を選択した状態で、 GIS > Web geodata > Get SRTMを選択します。 ちなみにSRTMはShuttle Radar Topography Missionの略で、 スペースシャトル に搭載したレーダーで取得した地球の標高データです。 取得にはopnetopography.orgの API Keyが必要になります。以下のサイトから API keyが無料で取得可能です。 https://portal.opentopography.org/ API Keyを入力してOKボタンを押下。 先ほどの2D画像にSRTMの標高データが反映されます。 起伏には乏しい地域ですが、高輪のグ ランドプリンス ホテル周辺や御殿山周辺は比較的標高が高いことが分かります。 4. 建物、その他データ( OSM )の取得、反映 2D画像を選択した状態で、 GIS > Web geodata > Get OSM を選択します。 ちなみに OSM は OpenStreetMap の略で、 オープンソース で運営されている地図データのプロジェクトです。 取得するデータ種類を選択します。今回は全て選択します。 また、手順3.で標高データの マッピング を行っている為、Elevation from objectのチェックをONにします。 OKボタンを押下すると、建物データや線路データなどが反映されます。 5. 建物上部へテクスチャを マッピング 4.で取得した建物データ(Areas;building)を選択した状態で、Tabキー押下でEditModeに切り替えます。 ModeをFace Selectに変更します。 どれか一つの建物上部の面を選択した状態で、Select > Select Similar > Normalにより、同じ向きの面を全て選択します。 建物上部の面が選択されている状態 次に、Edit Modeから、UV Editor Modeに切り替えます。 上部メニューから、 マッピング させる画像(EXPORT_ GOOGLE _SAT_WM)を選択します。 選択した面と、画像が一致するように、位置を修正します。 Gボタン + マウスカーソルで移動、Sボタン+マウスカーソルでスケールの調整が可能です。 ランドマークとなる建物にフォーカスすると調整しやすいです。 位置が決まったら、マテリアルを設定します。 まず、3D View右下のMaterial Propertyメニューを選択して、+ボタンでマテリアルを作成します。 今回は、2つのマテリアルスロットを作成しました(「Material 001」と「roof」)。 Assignボタン押下で、Edit Modeで選択している面に、これらのマテリアルを適用します。 roofマテリアルを選択した状態で、UV Editor ModeからShader Editor Modeに切り替えます。 Shift + Aで検索バーを出して、Image Textureノードを追加します。 インプットイメージに、先ほどUV Editorで マッピング させた画像(EXPORT_ GOOGLE _SAT_WM)を選択して、ColorをPrincipled BSDFノードのBase Colorに接続します。 建物上部の面に、衛星画像が マッピング されました。 6. 建物側面へのマテリアルの適用 続いて、建物側面のマテリアルを作成します。 建物側面のテクスチャは衛星データやオープンデータで提供されていないので、一般的なビルに近しいテクスチャを使用します。 以下のテクスチャを使用します(フリーアカウントでも商用フリーの画像がダウンロード可能です)。 Size MのAlbedo, Height, Normal, Roughness, Metalic, AO, Emissiveの7種類の画像をダウンロードします。 https://www.textures.com/download/PBR0537/138572 先ほど作成したマテリアル(Material 001)を選択した状態で、UV Editingメニューを開きます。 Principle BSDFを選択した状態で、Ctrl + Shift + Tを押下します。 Blender File Viewが表示されるので、先ほどダウンロードした画像を全て選択して、「Principled Texture Setup」を押下します。 建物の側面にもマテリアルが適用されました。 完成イメージ 完成イメージはこちらです。夜景にしてみました。 本記事ではライティング、 レンダリング のプロセスは割愛します。 所感 今回は GIS を用いてビル群を作成しました。 ハイクオリティなビルを作成するアプローチは他にも色々とありますが、 GIS を使うと大規模な都市や地形がほぼ一瞬で生成できる為、高精細なクオリティが不要な遠景等で用いる分には有用だと感じました。また、山など起伏のある地形のモデルを制作する場合は 国土地理院 地図などもう少し正確な標高データの活用も有用だと思います。 現在ISIDは web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! ISID採用ページ(Web3/メタバース/AI) 参考 https://www.esrij.com/gis-guide/gis-datamodel/gis-datamodel/ https://www.youtube.com/watch?v=YNtKnmRXVlo&t=283s https://www.youtube.com/watch?v=uk404c43pRY 執筆: @yamashita.yuki 、レビュー: @wakamoto.ryosuke ( Shodo で執筆されました )
アバター
こんにちは!金融ソリューション事業部の山下です。 本記事では、 オープンソース の3DCGソフト Blender を用いて、現実の地形や建物を模した3DCGモデルを生成する方法を紹介します。 今回は Blender GIS という プラグイン を用いて、衛星画像、建物データ( OSM )、標高データ(SRTM)を利用してモデルを制作しました。 実施環境/ツール 実施手順 1. BlenderにBlenderGISプラグインをインストールする 2. 衛星画像データを取得する 3. 標高データ(SRTM)の取得、反映 4. 建物、その他データ(OSM)の取得、反映 5. 建物上部へテクスチャをマッピング 6. 建物側面へのマテリアルの適用 完成イメージ 所感 参考 実施環境/ツール OS: Windows 11 pro GPU : NVIDIA GeForce RTX 3070Ti Laptop DCC: Blender 3.3.1 Plugin: Blender GIS 2.2.8 実施手順 Blender にBlenderGIS プラグイン をインストールする 衛星画像データを取得する 標高データ(SRTM)の取得、反映 建物、その他データ( OSM )の取得、反映 建物上部へテクスチャを マッピング 建物側面へのマテリアルの適用 1. Blender にBlenderGIS プラグイン をインストールする 以下の GitHub リポジトリ をダウンロードします。 Blender   GIS プラグイン https://github.com/domlysz/BlenderGIS プラグイン のインストールは、メニューのEdit > Preferences > Add-onsを選択して、ダウンロードしたZIPファイルを読み込むことで完了します。完了後、Add onの一覧にBlenderGISが追加され、チェックがつきます。 2. 衛星画像データを取得する BlenderGIS プラグイン をインストールすると、以下のように3D viewport内に「 GIS 」というメニューが表示されます。 GIS > Web geodata > Basemapを選択します。 設定はそのままで、OKを押下します。 世界地図が表示されます。 Gボタン押下で検索バーが表示されるので、表示させたい地名で検索します。 今回は「Shinagawa Station」、Zoom Levelは「16」と入力します。 品川駅周辺の地図が表示されました。 Eボタン押下で、Basemapの2D画像がExportできます。 3. 標高データ(SRTM)の取得、反映 2D画像を選択した状態で、 GIS > Web geodata > Get SRTMを選択します。 ちなみにSRTMはShuttle Radar Topography Missionの略で、 スペースシャトル に搭載したレーダーで取得した地球の標高データです。 取得にはopnetopography.orgの API Keyが必要になります。以下のサイトから API keyが無料で取得可能です。 https://portal.opentopography.org/ API Keyを入力してOKボタンを押下。 先ほどの2D画像にSRTMの標高データが反映されます。 起伏には乏しい地域ですが、高輪のグ ランドプリンス ホテル周辺や御殿山周辺は比較的標高が高いことが分かります。 4. 建物、その他データ( OSM )の取得、反映 2D画像を選択した状態で、 GIS > Web geodata > Get OSM を選択します。 ちなみに OSM は OpenStreetMap の略で、 オープンソース で運営されている地図データのプロジェクトです。 取得するデータ種類を選択します。今回は全て選択します。 また、手順3.で標高データの マッピング を行っている為、Elevation from objectのチェックをONにします。 OKボタンを押下すると、建物データや線路データなどが反映されます。 5. 建物上部へテクスチャを マッピング 4.で取得した建物データ(Areas;building)を選択した状態で、Tabキー押下でEditModeに切り替えます。 ModeをFace Selectに変更します。 どれか一つの建物上部の面を選択した状態で、Select > Select Similar > Normalにより、同じ向きの面を全て選択します。 建物上部の面が選択されている状態 次に、Edit Modeから、UV Editor Modeに切り替えます。 上部メニューから、 マッピング させる画像(EXPORT_ GOOGLE _SAT_WM)を選択します。 選択した面と、画像が一致するように、位置を修正します。 Gボタン + マウスカーソルで移動、Sボタン+マウスカーソルでスケールの調整が可能です。 ランドマークとなる建物にフォーカスすると調整しやすいです。 位置が決まったら、マテリアルを設定します。 まず、3D View右下のMaterial Propertyメニューを選択して、+ボタンでマテリアルを作成します。 今回は、2つのマテリアルスロットを作成しました(「Material 001」と「roof」)。 Assignボタン押下で、Edit Modeで選択している面に、これらのマテリアルを適用します。 roofマテリアルを選択した状態で、UV Editor ModeからShader Editor Modeに切り替えます。 Shift + Aで検索バーを出して、Image Textureノードを追加します。 インプットイメージに、先ほどUV Editorで マッピング させた画像(EXPORT_ GOOGLE _SAT_WM)を選択して、ColorをPrincipled BSDFノードのBase Colorに接続します。 建物上部の面に、衛星画像が マッピング されました。 6. 建物側面へのマテリアルの適用 続いて、建物側面のマテリアルを作成します。 建物側面のテクスチャは衛星データやオープンデータで提供されていないので、一般的なビルに近しいテクスチャを使用します。 以下のテクスチャを使用します(フリーアカウントでも商用フリーの画像がダウンロード可能です)。 Size MのAlbedo, Height, Normal, Roughness, Metalic, AO, Emissiveの7種類の画像をダウンロードします。 https://www.textures.com/download/PBR0537/138572 先ほど作成したマテリアル(Material 001)を選択した状態で、UV Editingメニューを開きます。 Principle BSDFを選択した状態で、Ctrl + Shift + Tを押下します。 Blender File Viewが表示されるので、先ほどダウンロードした画像を全て選択して、「Principled Texture Setup」を押下します。 建物の側面にもマテリアルが適用されました。 完成イメージ 完成イメージはこちらです。夜景にしてみました。 本記事ではライティング、 レンダリング のプロセスは割愛します。 所感 今回は GIS を用いてビル群を作成しました。 ハイクオリティなビルを作成するアプローチは他にも色々とありますが、 GIS を使うと大規模な都市や地形がほぼ一瞬で生成できる為、高精細なクオリティが不要な遠景等で用いる分には有用だと感じました。また、山など起伏のある地形のモデルを制作する場合は 国土地理院 地図などもう少し正確な標高データの活用も有用だと思います。 現在ISIDは web3領域のグループ横断組織 を立ち上げ、Web3および メタバース 領域のR&Dを行っております(カテゴリー「3DCG」の記事は こちら )。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください! 私たちと同じチームで働いてくれる仲間を、是非お待ちしております! ISID採用ページ(Web3/メタバース/AI) 参考 https://www.esrij.com/gis-guide/gis-datamodel/gis-datamodel/ https://www.youtube.com/watch?v=YNtKnmRXVlo&t=283s https://www.youtube.com/watch?v=uk404c43pRY 執筆: @yamashita.yuki 、レビュー: @wakamoto.ryosuke ( Shodo で執筆されました )
アバター
こんにちは。X イノベーション 本部ソフトウェアデザインセンターの陳です。 この記事ではNext.jsの getServerSideProps の利用でハマったことについて話します。 Server-side Rendering( SSR )とgetServerSideProps Server-side Renderingはリク エス トごとにサーバー側で レンダリング を行い、HTMLページを生成する機能です。 頻繁に変更されるデータを画面に表示させたい時はServer-side Renderingを利用します。Server-side Renderingを実現するには、サーバー側からデータを受け取る処理を行う getServerSideProps 関数を使います。 Next.jsの 公式ドキュメント では以下の例を掲載しています。 function Page({ data }) { // Render data... } // This gets called on every request export async function getServerSideProps() { // Fetch data from external API const res = await fetch(`https://.../data`) const data = await res.json() // Pass data to the page via props return { props: { data } } } export default Page Blue/Greenデプロイでハマったこと 今回はNext.jsの API routes を使ってバックエンド処理の API を作成しました。 例えば、以下はデータベースからarticle名のリストを取得する処理を行う API routeです。 // pages/api/articles.ts export default function handler(_req: NextApiRequest, res: NextApiResponse) { const articles: Article[] = await getArticlesFromDB(); const articleNames = articles.map((article) => article.name ); res.status(200).json(articleNames); } getServerSideProps で API からデータを受け取って ブラウザー に渡します。Axiosの baseURL を 環境変数 で指定していました。 // pages/index.tsx function Page({ articleNames }) { // Render articles data... } export async function getServerSideProps() { const baseURL = process.env.SERVER ? process.env.SERVER : "http://localhost:3000"; const res = await Axios.get<string[]>(`{baseURL}/api/articles`) return { props: { articleNames: res.data } } } export default Page このコードは一見問題なさそうですが、Blue/GreenデプロイでAxiosの baseURL を正しく指定しないと問題が起きます(Fetch API を利用する場合も同様です)。 Blue/Greenデプロイは、本番環境と検証環境を交互に入れ替えることにより、ダウンタイムを最小にするデプロイ手法です。 本来であれば、 API のパスはウェブサーバーと同一環境のものにならないといけません。例えば、本番環境のパスは https://example.com であれば、 API のパスは https://example.com/api/articles になります。 今回はAxiosのbaseURLを本番環境のパスに指定しましたため、検証環境ウェブサーバーにアクセスしても、叩いた API は本番環境のものでした。 Blue/Greenデプロイはルーティング制御により、本番環境と検証環境の入れ替えが行われるため、 API のホスト名も環境と共に切り替える必要があります。 アクセスした環境は本番環境か検証環境かを識別し、 API のパスを変換する仕組みが必要ですが、なかなか難しいです。 解決方法 そもそも getServerSideProps と API routes は両方ともサーバー側で実行されるため、 getServerSideProps で API routes を呼びだすのは蛇足ですね。データの取得などのバックエンド処理は関数で作成し、 getServerSideProps で呼び出せばいいです。 修正後のコードはこちらです。 API routesの作成をやめて、データベースの処理を関数にしました。 // logic/articles.ts export async function fetchArticleNames(): Promise<string[]> { const articles: Article[] = await getArticlesFromDB(); return articles.map((article) => article.name ); } 続いて、 getServerSideProps で api/articles を介さずに、 fetchArticleNames() 関数でarticle名のリストを取得するように修正しました。 // pages/index.tsx function Page({ articleNames }) { // Render article names... } export async function getServerSideProps() { const articleNames = await fetchArticleNames(); return { props: { articleNames } } } export default Page これで、Blue/Greenデプロイの API パス問題を回避し、コードもすっきりしました。 まとめ この記事では、Next.jsの getServerSideProps を使ってBlue/Greenデプロイでハマったことについてまとめました。 getServerSideProps はサーバー側で実行されますので、データの取得処理は API routes を介さずに関数で呼び出しましょう。 私たちは同じチームで働いてくれる仲間を探しています。今回のエントリで紹介したような仕事に興味のある方、ご応募お待ちしています。 - ソリューションアーキテクト 執筆: @chen.xinying 、レビュー: @sato.taichi ( Shodo で執筆されました )
アバター
こんにちは。X イノベーション 本部ソフトウェアデザインセンターの陳です。 この記事ではNext.jsの getServerSideProps の利用でハマったことについて話します。 Server-side Rendering( SSR )とgetServerSideProps Server-side Renderingはリク エス トごとにサーバー側で レンダリング を行い、HTMLページを生成する機能です。 頻繁に変更されるデータを画面に表示させたい時はServer-side Renderingを利用します。Server-side Renderingを実現するには、サーバー側からデータを受け取る処理を行う getServerSideProps 関数を使います。 Next.jsの 公式ドキュメント では以下の例を掲載しています。 function Page({ data }) { // Render data... } // This gets called on every request export async function getServerSideProps() { // Fetch data from external API const res = await fetch(`https://.../data`) const data = await res.json() // Pass data to the page via props return { props: { data } } } export default Page Blue/Greenデプロイでハマったこと 今回はNext.jsの API routes を使ってバックエンド処理の API を作成しました。 例えば、以下はデータベースからarticle名のリストを取得する処理を行う API routeです。 // pages/api/articles.ts export default function handler(_req: NextApiRequest, res: NextApiResponse) { const articles: Article[] = await getArticlesFromDB(); const articleNames = articles.map((article) => article.name ); res.status(200).json(articleNames); } getServerSideProps で API からデータを受け取って ブラウザー に渡します。Axiosの baseURL を 環境変数 で指定していました。 // pages/index.tsx function Page({ articleNames }) { // Render articles data... } export async function getServerSideProps() { const baseURL = process.env.SERVER ? process.env.SERVER : "http://localhost:3000"; const res = await Axios.get<string[]>(`{baseURL}/api/articles`) return { props: { articleNames: res.data } } } export default Page このコードは一見問題なさそうですが、Blue/GreenデプロイでAxiosの baseURL を正しく指定しないと問題が起きます(Fetch API を利用する場合も同様です)。 Blue/Greenデプロイは、本番環境と検証環境を交互に入れ替えることにより、ダウンタイムを最小にするデプロイ手法です。 本来であれば、 API のパスはウェブサーバーと同一環境のものにならないといけません。例えば、本番環境のパスは https://example.com であれば、 API のパスは https://example.com/api/articles になります。 今回はAxiosのbaseURLを本番環境のパスに指定しましたため、検証環境ウェブサーバーにアクセスしても、叩いた API は本番環境のものでした。 Blue/Greenデプロイはルーティング制御により、本番環境と検証環境の入れ替えが行われるため、 API のホスト名も環境と共に切り替える必要があります。 アクセスした環境は本番環境か検証環境かを識別し、 API のパスを変換する仕組みが必要ですが、なかなか難しいです。 解決方法 そもそも getServerSideProps と API routes は両方ともサーバー側で実行されるため、 getServerSideProps で API routes を呼びだすのは蛇足ですね。データの取得などのバックエンド処理は関数で作成し、 getServerSideProps で呼び出せばいいです。 修正後のコードはこちらです。 API routesの作成をやめて、データベースの処理を関数にしました。 // logic/articles.ts export async function fetchArticleNames(): Promise<string[]> { const articles: Article[] = await getArticlesFromDB(); return articles.map((article) => article.name ); } 続いて、 getServerSideProps で api/articles を介さずに、 fetchArticleNames() 関数でarticle名のリストを取得するように修正しました。 // pages/index.tsx function Page({ articleNames }) { // Render article names... } export async function getServerSideProps() { const articleNames = await fetchArticleNames(); return { props: { articleNames } } } export default Page これで、Blue/Greenデプロイの API パス問題を回避し、コードもすっきりしました。 まとめ この記事では、Next.jsの getServerSideProps を使ってBlue/Greenデプロイでハマったことについてまとめました。 getServerSideProps はサーバー側で実行されますので、データの取得処理は API routes を介さずに関数で呼び出しましょう。 私たちは同じチームで働いてくれる仲間を探しています。今回のエントリで紹介したような仕事に興味のある方、ご応募お待ちしています。 - ソリューションアーキテクト 執筆: @chen.xinying 、レビュー: @sato.taichi ( Shodo で執筆されました )
アバター
皆さん、こんにちは。イノラボ小林です。 今回の記事では1月3日~1月8日にラスベガスで開催されたCES2023について取材をしてきたので、その中でも気になったクイックインタビューを数点紹介させて頂きます。 尚、その他の動画については、下記リンクから飛んでいただけるとまとめて視聴可能です。 https://www.tiktok.com/@tyoreporter 名称:CES®(世界最大級のテックイベント) 主催:Consumer Technology Association ( CTA ) 日程:Media Days:2023.1.3 – 4 ( 2日間 )    CES2023 :2023.1.5 – 8 ( 4日間 ) ■ マイクロソフト (カテゴリー:XR) Hololens2を活用した展示解説ツアー @tyoreporter #ces2023 #ces #AR #HoloLens #マイクロソフト ♬ オリジナル楽曲 - TYO-REPORTER ■ BMW (カテゴリー:モビリティ) 32色に変色する車体パネル @tyoreporter #ces2023 #ces #車 #bmw ♬ オリジナル楽曲 - TYO-REPORTER ■ ARHT(カテゴリー:コミュニケーション) リアルタイムで1/1を映し出す遠隔コミュニケーションディスプレイ @tyoreporter #ディスプレイ #メタバース #デジタルツイン #サイネージ #ces #ces2023 #マーケティング ♬ オリジナル楽曲 - TYO-REPORTER PPT資料(PDF)もあるので、閲覧/配布希望の方はご連絡下さい。 連絡先:kobayashi.kentaro@isid.co.jp 執筆: @tyoreporter 、レビュー: @sato.taichi ( Shodo で執筆されました )
アバター
皆さん、こんにちは。イノラボ小林です。 今回の記事では1月3日~1月8日にラスベガスで開催されたCES2023について取材をしてきたので、その中でも気になったクイックインタビューを数点紹介させて頂きます。 尚、その他の動画については、下記リンクから飛んでいただけるとまとめて視聴可能です。 https://www.tiktok.com/@tyoreporter 名称:CES®(世界最大級のテックイベント) 主催:Consumer Technology Association ( CTA ) 日程:Media Days:2023.1.3 – 4 ( 2日間 )    CES2023 :2023.1.5 – 8 ( 4日間 ) ■ マイクロソフト (カテゴリー:XR) Hololens2を活用した展示解説ツアー @tyoreporter #ces2023 #ces #AR #HoloLens #マイクロソフト ♬ オリジナル楽曲 - TYO-REPORTER ■ BMW (カテゴリー:モビリティ) 32色に変色する車体パネル @tyoreporter #ces2023 #ces #車 #bmw ♬ オリジナル楽曲 - TYO-REPORTER ■ ARHT(カテゴリー:コミュニケーション) リアルタイムで1/1を映し出す遠隔コミュニケーションディスプレイ @tyoreporter #ディスプレイ #メタバース #デジタルツイン #サイネージ #ces #ces2023 #マーケティング ♬ オリジナル楽曲 - TYO-REPORTER PPT資料(PDF)もあるので、閲覧/配布希望の方はご連絡下さい。 連絡先:kobayashi.kentaro@isid.co.jp 執筆: @tyoreporter 、レビュー: @sato.taichi ( Shodo で執筆されました )
アバター
おはようございます!こんにちは!こんばんは! 電通国際情報サービス  X(クロス) イノベーション 本部 デジタルエンゲージメントセンター 1年目社員の根本康平です。 今回は「 Salesforce 」が一体どういうものなのかを簡単な言葉だけで紹介する記事です。 難しい言葉は一切使いません! 中高生でも分かるように紹介します。 『 Salesforce 聞いたことあるけど何ができるか分からない』という方向けの記事です。『業務で Salesforce を使う機会無いから・・』という方も是非ご覧ください。課題解決の手段の1つとして知っておくに越したことはないと思います!IT業界に興味のある「就活生」の方々もぜひご覧ください! Salesforceの概要 Salesforceがどのように使われるのか?(ネット通販編) Salesforceを使うとどのようなメリットがある? まとめ Salesforce の概要 まずは「 Salesforce 」という単語について。 Salesforce とは アメリ カに本社を置くITの会社のことです。 Salesforce が提供しているサービスは世界中の様々な会社で使われています。 では「 Salesforce が提供しているサービス」とはどういうものでしょうか。今回はイメージしやすいように我々に身近な「ネット通販」で Salesforce が利用されたことを想定して紹介します。 Salesforce がどのように使われるのか?(ネット通販編) まずは登場人物・登場する会社についてです。「A会社」が「A通販」を運営しています。A通販を利用するお客さんが「佐藤さん」と「田中さん」です。A会社に勤めている社員は「兵藤社員」と「 利根川 社員」です。 では「 Salesforce 」の説明に戻ります。 Salesforce が提供しているサービスは「お客さんの情報を一元管理・社員の営業活動を支援するシステム」です。 「お客さんの情報」というのは佐藤さんや田中さんの「名前」「住所」「性別」「年齢」「購入した商品」「A会社への問い合わせ履歴」・・・などの情報を指します。 「社員の営業活動」というのは兵藤社員や 利根川 社員が行った「会議の記録」や「取引情報」・・などを指します。 これらの情報を1か所にまとめて管理(一元管理)するシステムを提供しているのが「 Salesforce 」です。 一元管理されたお客さんの情報を社員が見るとこのような感じで確認できます。 情報は一元管理されていますので、佐藤さん・田中さんに関する情報は全てここに集約されます!全ての情報が一か所に集まっていてスムーズに情報を閲覧できることは非常に大事なことです。 ここまで理解できれば Salesforce の7割を理解できたといっても過言ではないと思います! Salesforce を使うとどのようなメリットがある? 情報共有が容易 例えば、佐藤さんの「プリンターインクの不備について」という問い合わせ対応を 利根川 社員が担当したとしましょう。 利根川 社員が調べた結果、その問い合わせの情報・やり取りを別部署の兵藤社員に報告する必要が発生しました。こういう時に情報を一元管理していると、佐藤さんの氏名や住所、購入履歴や問い合わせ履歴を簡単に兵藤社員に共有できます。 このようにいろいろな部署や部門間でスムーズな情報共有ができます。 情報分析とニーズの予測 例えば、兵藤社員から 利根川 社員に「A通販の食料品の売り上げに関するレポートを作ってください」と依頼があったとしましょう。 利根川 社員はどうすればいいでしょうか? なんと、 Salesforce を使うと数クリックの簡単な操作だけで食料品に関するレポートを作ることができるんです!月ごとの売り上げグラフを作ることも、食品別の売り上げ割合もとても簡単に作ることができます!これも Salesforce で情報を一元管理しているおかげです。 兵藤社員からの依頼を超スピーディーにやり遂げることができれば 利根川 社員の評価もうなぎ登りですね。 営業活動の効率化 Salesforce は社員の情報も管理できます。 例えば、 利根川 社員は兵藤社員から「A通販は洋服商品が全然ないから、洋服を売っている色々な会社に『A通販でも販売しませんか?』とお願いしてきてください」と依頼されたとしましょう。 利根川 社員は洋服を売っている色々な会社に行って、打ち合わせや取引の手続きをします。その打ち合わせの情報やお客さんの会社の情報を Salesforce に入力して記録を残します。 そうすると、 Salesforce で 利根川 社員の活動状況について、画像のように分かりやすく確認できます。兵藤社員は Salesforce で 利根川 社員の活動状況を確認して「11/29 青木洋服に声かけてからすごく時間が経っているけど大丈夫?」「契約できそうなのが2件あるんだね!順調だね!」などコメントできます。また、全営業パーソンの状況を確認すれば、今期の契約数の予測も簡単に出せそうですね! このように、 Salesforce を用いると営業活動の効率化が期待できます。 アプリケーション開発が容易にできる Salesforce ではアプリケーション開発が簡単にできます。例えば、「A会社独自のスケジュールアプリが必要」という要望も Salesforce を使って独自に開発できます。イメージとして、 Apple Store や Google Play ストアにあるような便利アプリを Salesforce で作れる! Salesforce で利用できる!というような感じです。 その他 他にも様々なメリットがあります。セキュリティ対策がある程度できている・マウスをクリックするだけでほとんどのことができる・ iPhone /PCなど様々な端末に対応しやすい・簡単にWebサイトや問い合わせサイトを作れる・・・まだまだ出てきそうです! まとめ 何となく Salesforce が何か、何ができるのかをご理解いただけたでしょうか? Salesforce は「お客さんの情報を一元管理・社員の営業活動を支援するシステム」ができるものでしたね! 少しでも Salesforce に興味を持っていただけていたら嬉しいです。 Salesforce を導入してみたい・話を聞いてみたい!という方は 電通国際情報サービス (ISID) デジタルエンゲージメントセンター までお問い合わせください! 興味を持ってくださった就活生の方々、ISIDは新卒採用も積極的に行っています!ぜひ下のリンクから確認してみてください! 私たちは一緒に働いてくれる仲間を募集しています! 【全社集約】CRMソリューションコンサルタント IT業界に興味のある就活生の皆さま、ぜひご応募ください! 【新卒採用】ISID リクルートサイト 執筆: @nemoto.kouhei 、レビュー: @yamashita.tsuyoshi ( Shodo で執筆されました )
アバター
おはようございます!こんにちは!こんばんは! 電通国際情報サービス  X(クロス) イノベーション 本部 デジタルエンゲージメントセンター 1年目社員の根本康平です。 今回は「 Salesforce 」が一体どういうものなのかを簡単な言葉だけで紹介する記事です。 難しい言葉は一切使いません! 中高生でも分かるように紹介します。 『 Salesforce 聞いたことあるけど何ができるか分からない』という方向けの記事です。『業務で Salesforce を使う機会無いから・・』という方も是非ご覧ください。課題解決の手段の1つとして知っておくに越したことはないと思います!IT業界に興味のある「就活生」の方々もぜひご覧ください! Salesforceの概要 Salesforceがどのように使われるのか?(ネット通販編) Salesforceを使うとどのようなメリットがある? まとめ Salesforce の概要 まずは「 Salesforce 」という単語について。 Salesforce とは アメリ カに本社を置くITの会社のことです。 Salesforce が提供しているサービスは世界中の様々な会社で使われています。 では「 Salesforce が提供しているサービス」とはどういうものでしょうか。今回はイメージしやすいように我々に身近な「ネット通販」で Salesforce が利用されたことを想定して紹介します。 Salesforce がどのように使われるのか?(ネット通販編) まずは登場人物・登場する会社についてです。「A会社」が「A通販」を運営しています。A通販を利用するお客さんが「佐藤さん」と「田中さん」です。A会社に勤めている社員は「兵藤社員」と「 利根川 社員」です。 では「 Salesforce 」の説明に戻ります。 Salesforce が提供しているサービスは「お客さんの情報を一元管理・社員の営業活動を支援するシステム」です。 「お客さんの情報」というのは佐藤さんや田中さんの「名前」「住所」「性別」「年齢」「購入した商品」「A会社への問い合わせ履歴」・・・などの情報を指します。 「社員の営業活動」というのは兵藤社員や 利根川 社員が行った「会議の記録」や「取引情報」・・などを指します。 これらの情報を1か所にまとめて管理(一元管理)するシステムを提供しているのが「 Salesforce 」です。 一元管理されたお客さんの情報を社員が見るとこのような感じで確認できます。 情報は一元管理されていますので、佐藤さん・田中さんに関する情報は全てここに集約されます!全ての情報が一か所に集まっていてスムーズに情報を閲覧できることは非常に大事なことです。 ここまで理解できれば Salesforce の7割を理解できたといっても過言ではないと思います! Salesforce を使うとどのようなメリットがある? 情報共有が容易 例えば、佐藤さんの「プリンターインクの不備について」という問い合わせ対応を 利根川 社員が担当したとしましょう。 利根川 社員が調べた結果、その問い合わせの情報・やり取りを別部署の兵藤社員に報告する必要が発生しました。こういう時に情報を一元管理していると、佐藤さんの氏名や住所、購入履歴や問い合わせ履歴を簡単に兵藤社員に共有できます。 このようにいろいろな部署や部門間でスムーズな情報共有ができます。 情報分析とニーズの予測 例えば、兵藤社員から 利根川 社員に「A通販の食料品の売り上げに関するレポートを作ってください」と依頼があったとしましょう。 利根川 社員はどうすればいいでしょうか? なんと、 Salesforce を使うと数クリックの簡単な操作だけで食料品に関するレポートを作ることができるんです!月ごとの売り上げグラフを作ることも、食品別の売り上げ割合もとても簡単に作ることができます!これも Salesforce で情報を一元管理しているおかげです。 兵藤社員からの依頼を超スピーディーにやり遂げることができれば 利根川 社員の評価もうなぎ登りですね。 営業活動の効率化 Salesforce は社員の情報も管理できます。 例えば、 利根川 社員は兵藤社員から「A通販は洋服商品が全然ないから、洋服を売っている色々な会社に『A通販でも販売しませんか?』とお願いしてきてください」と依頼されたとしましょう。 利根川 社員は洋服を売っている色々な会社に行って、打ち合わせや取引の手続きをします。その打ち合わせの情報やお客さんの会社の情報を Salesforce に入力して記録を残します。 そうすると、 Salesforce で 利根川 社員の活動状況について、画像のように分かりやすく確認できます。兵藤社員は Salesforce で 利根川 社員の活動状況を確認して「11/29 青木洋服に声かけてからすごく時間が経っているけど大丈夫?」「契約できそうなのが2件あるんだね!順調だね!」などコメントできます。また、全営業パーソンの状況を確認すれば、今期の契約数の予測も簡単に出せそうですね! このように、 Salesforce を用いると営業活動の効率化が期待できます。 アプリケーション開発が容易にできる Salesforce ではアプリケーション開発が簡単にできます。例えば、「A会社独自のスケジュールアプリが必要」という要望も Salesforce を使って独自に開発できます。イメージとして、 Apple Store や Google Play ストアにあるような便利アプリを Salesforce で作れる! Salesforce で利用できる!というような感じです。 その他 他にも様々なメリットがあります。セキュリティ対策がある程度できている・マウスをクリックするだけでほとんどのことができる・ iPhone /PCなど様々な端末に対応しやすい・簡単にWebサイトや問い合わせサイトを作れる・・・まだまだ出てきそうです! まとめ 何となく Salesforce が何か、何ができるのかをご理解いただけたでしょうか? Salesforce は「お客さんの情報を一元管理・社員の営業活動を支援するシステム」ができるものでしたね! 少しでも Salesforce に興味を持っていただけていたら嬉しいです。 Salesforce を導入してみたい・話を聞いてみたい!という方は 電通国際情報サービス (ISID) デジタルエンゲージメントセンター までお問い合わせください! 興味を持ってくださった就活生の方々、ISIDは新卒採用も積極的に行っています!ぜひ下のリンクから確認してみてください! 私たちは一緒に働いてくれる仲間を募集しています! 【全社集約】CRMソリューションコンサルタント IT業界に興味のある就活生の皆さま、ぜひご応募ください! 【新卒採用】ISID リクルートサイト 執筆: @nemoto.kouhei 、レビュー: @yamashita.tsuyoshi ( Shodo で執筆されました )
アバター
こんにちは。X(クロス) イノベーション 本部 ソフトウェアデザインセンター セキュリティグループの耿です。 CI/CD環境として GitHub Actions を使っているときに、コンテナをビルド、プッシュする前に distroless ベースイメージの署名を検証する方法について紹介します。(自前でビルドしたイメージの署名やその検証については、この記事では扱いません) (2023/8/21追記) Cosign 2.0 よりKeyless Signingが推奨されるようになっているため、記事の一部を更新しました。 はじめに Cosignとは 署名の検証で期待したいこと cosign verify コマンド cosign verify は何をやっているのか 追加の検証をするべき状況 署名を検証するワークフロー 署名の検証にかかる時間 はじめに コンテナを利用する場合、軽量で最小限のファイルのみを含んでいる distroless イメージをベースイメージとして使うことが推奨されます。 distroless イメージは Cosign で署名されており、 GitHub リポジトリ の README でも、イメージの使用前に 署名を検証することが推奨されています : All distroless images are signed by cosign. We recommend verifying any distroless image you use before building your image. Cosignとは Cosign はコンテナなどを署名するためのライブラリであり、 OSS コミュニティである Sigstore が提供するツールの1つです。Sigstore は 2022年10月にGAを発表 しました。 distroless イメージは全て Cosign の Keyless signing で署名されています。 署名の検証で期待したいこと コンテナのベースイメージの署名を検証することで、イメージが改ざんされておらず(完全性)、検証時に指定する公開鍵とペアの 秘密鍵 で署名されていること(真正性)が期待できます。イメージが不正に差し替えられた場合には、それを利用する前に検知できるようになります。 一方、署名用の 秘密鍵 への正当なアクセスを持つユーザーや、アクセス権を入手した攻撃者がベースイメージを置き換えた場合、署名も書き換えることが可能になるので、署名の検証による防御の対象外になります。また 秘密鍵 にアクセスできなくても、検証に使用する公開鍵を差し替えできてしまう場合も、署名の検証による防御の対象外です。 このような攻撃の成立を難しくするために、 Keyless signing が推奨されています。Keyless signing の流れは次の通りです。 署名者が OIDC ( Google や GitHub など)で認証する 署名者は発行されたOIDCのID トーク ンを利用し、署名に利用する鍵ペアを生成する 生成した公開鍵とID トーク ンをSigstoreの Fulcio に送信する FulcioはID トーク ンを検証し、短命の証明書(公開鍵が含まれる)を発行する 署名者は署名を行い、証明書と署名をSigstore の Rekor という透明性ログサービスに記録する 秘密鍵 は破棄される 署名検証時では、利用者はRekorから証明書およびそれに含まれる公開鍵を取得し、証明書が有効だった期間に署名が行われたことを検証する この仕組みにより、公開鍵を自前で公開する手段を用意する必要はなく、 秘密鍵 も短命のものを利用し署名後は破棄されるため、盗まれるリスクも大幅に低減されます。また仮にOIDCの認証が突破されたとしても署名の記録はRekorにタイムスタンプ付きで公開されるため、仕組みとしては身に覚えのない署名を検知できるようになっています。 なお署名の流れが複雑に見えますが、そのほとんどはCosignの CLI コマンドで自動化されます。 cosign verify コマンド さて cosign verify コマンドで署名者のEメールアドレスと、署名者が利用したOIDCプロバイダーを指定すると署名の検証ができ、例えば distroless/nodejs:18 イメージに対しては次のように出力されます $ cosign verify gcr.io/distroless/nodejs:18 --certificate-oidc-issuer https://accounts.google.com --certificate-identity keyless@distroless.iam.gserviceaccount.com Verification for gcr.io/distroless/nodejs:18 -- The following checks were performed on each of these signatures: - The cosign claims were validated - Existence of the claims in the transparency log was verified offline - The code-signing certificate was verified using trusted certificate authority certificates (以下略) Cosign による署名と検証について こちらの記事 で非常に詳しく説明されています。 cosign verify は何をやっているのか cosign による署名と検証について、自分なりの理解をまとめると次のようになります。 コンテナイメージを保管する OCI(Open Container Initiative) Registry には、Manifest、Config、Blob の3種の Artifact を保存できる Manifest ファイルには Config や Blob(コンテナのレイヤー)への参照がダイジェストで記載されており、従ってコンテナレイヤーが変わるとイメージの Manifest ファイルも変わる Cosign による署名はイメージの Manifest ファイルに対して作成される Cosign でコンテナイメージを署名すると、イメージの Manifest ファイルのダイジェストを Signature Claim という JSON ファイルに埋め込む Signature Claim も Artifact として OCI Registry にアップロードされる。この署名の Manifest ファイルには、Signature Claim のダイジェストを 秘密鍵 で暗号化したものが signature として記載されている 署名の検証は、まずイメージの Manifest ファイルから特定の規則に従い、署名の Manifest ファイルの格納場所を算出する。これにはイメージの Manifest ファイルのダイジェストが利用される 署名の Manifest ファイルに書かれた signature を公開鍵で復号し、Signature Claim のダイジェストと一致しているかを検証する 検知したいと思っている「コンテナイメージをすり替えたが、署名と公開鍵をすり替えることができない」状況を想像します。イメージレイヤーが変わるとイメージの Manifest ファイルも変更になり、 Manifest ファイルのダイジェストから算出される署名の格納場所も変わります。しかし攻撃者はその場所に従来の 秘密鍵 で署名を格納することができない前提で考えているため、結果としてコンテナイメージのすり替えを検知できます。 追加の検証をするべき状況 cosign verify だけでは十分ではなく、追加の検証をした方が良い状況も存在します。それは「イメージ署名の検証」と「実際のイメージのpull」に時間的な差分があるときです。 cosign verify はコンテナのダイジェストを直接検証に使用するわけではないため、 cosign verify する時点の OCI Registry にあるイメージと、pull してきたイメージが同一ではない可能性が無視できない場合、 docker inspect コマンドで pull してきたイメージのダイジェストと署名に記載されたダイジェストの比較が必要です。 署名を検証するワークフロー さて前置きが長くなりましたが、ここからはイメージ署名の検証とイメージ pull の時間差を無視して良く、追加の検証を行わない場合を考えます。 GitHub Actions でコンテナのビルドやプッシュを行う前に、以下のステップを追加することで distroless ベースイメージの署名を検証できます。 steps : # 自身のリポジトリのチェックアウト。署名の検証とは無関係 - name : Checkout uses : actions/checkout@v3 # 1. Cosign のインストール - name : Install cosign uses : sigstore/cosign-installer@6e04d228eb30da1757ee4e1dd75a0ec73a653e06 #v3.1.1 # 2. Cosign による検証。例として nodejs:18 イメージのものを対象としている - name : cosign verify run : cosign verify gcr.io/distroless/nodejs:18 --certificate-oidc-issuer https://accounts.google.com --certificate-identity keyless@distroless.iam.gserviceaccount.com # これ以降コンテナのビルドやプッシュを行う 説明: sigstore/cosign-installer アクションを利用し、cosign をインストールする ここではさらに安全性を高めるためにアクションをバージョン指定ではなく、 コミットハッシュでコードをピン留め している --certificate-oidc-issuer と --certificate-identity オプションを指定し、Keyless署名されたコンテナイメージを検証する ここでは gcr.io/distroless/nodejs:18 イメージを検証している 署名の検証にかかる時間 cosign のインストールと署名の検証はいずれも数秒以内に終わるため、特にCI/CDの妨げにはならない印象です。 数行のアクションを追加するだけでベースイメージが不正にすり替えられていないか検証できるため、万が一に備えて distroless イメージの署名検証をやってみてはいかがでしょうか。 私たちは同じチームで働いてくれる仲間を大募集しています!たくさんのご応募をお待ちしています。 セキュリティエンジニア(セキュリティ設計) 執筆: @kou.kinyo 、レビュー: 寺山 輝 (@terayama.akira) ( Shodo で執筆されました )
アバター
こんにちは。X(クロス) イノベーション 本部 ソフトウェアデザインセンター セキュリティグループの耿です。 CI/CD環境として GitHub Actions を使っているときに、コンテナをビルド、プッシュする前に distroless ベースイメージの署名を検証する方法について紹介します。(自前でビルドしたイメージの署名やその検証については、この記事では扱いません) (2023/8/21追記) Cosign 2.0 よりKeyless Signingが推奨されるようになっているため、記事の一部を更新しました。 はじめに Cosignとは 署名の検証で期待したいこと cosign verify コマンド cosign verify は何をやっているのか 追加の検証をするべき状況 署名を検証するワークフロー 署名の検証にかかる時間 はじめに コンテナを利用する場合、軽量で最小限のファイルのみを含んでいる distroless イメージをベースイメージとして使うことが推奨されます。 distroless イメージは Cosign で署名されており、 GitHub リポジトリ の README でも、イメージの使用前に 署名を検証することが推奨されています : All distroless images are signed by cosign. We recommend verifying any distroless image you use before building your image. Cosignとは Cosign はコンテナなどを署名するためのライブラリであり、 OSS コミュニティである Sigstore が提供するツールの1つです。Sigstore は 2022年10月にGAを発表 しました。 distroless イメージは全て Cosign の Keyless signing で署名されています。 署名の検証で期待したいこと コンテナのベースイメージの署名を検証することで、イメージが改ざんされておらず(完全性)、検証時に指定する公開鍵とペアの 秘密鍵 で署名されていること(真正性)が期待できます。イメージが不正に差し替えられた場合には、それを利用する前に検知できるようになります。 一方、署名用の 秘密鍵 への正当なアクセスを持つユーザーや、アクセス権を入手した攻撃者がベースイメージを置き換えた場合、署名も書き換えることが可能になるので、署名の検証による防御の対象外になります。また 秘密鍵 にアクセスできなくても、検証に使用する公開鍵を差し替えできてしまう場合も、署名の検証による防御の対象外です。 このような攻撃の成立を難しくするために、 Keyless signing が推奨されています。Keyless signing の流れは次の通りです。 署名者が OIDC ( Google や GitHub など)で認証する 署名者は発行されたOIDCのID トーク ンを利用し、署名に利用する鍵ペアを生成する 生成した公開鍵とID トーク ンをSigstoreの Fulcio に送信する FulcioはID トーク ンを検証し、短命の証明書(公開鍵が含まれる)を発行する 署名者は署名を行い、証明書と署名をSigstore の Rekor という透明性ログサービスに記録する 秘密鍵 は破棄される 署名検証時では、利用者はRekorから証明書およびそれに含まれる公開鍵を取得し、証明書が有効だった期間に署名が行われたことを検証する この仕組みにより、公開鍵を自前で公開する手段を用意する必要はなく、 秘密鍵 も短命のものを利用し署名後は破棄されるため、盗まれるリスクも大幅に低減されます。また仮にOIDCの認証が突破されたとしても署名の記録はRekorにタイムスタンプ付きで公開されるため、仕組みとしては身に覚えのない署名を検知できるようになっています。 なお署名の流れが複雑に見えますが、そのほとんどはCosignの CLI コマンドで自動化されます。 cosign verify コマンド さて cosign verify コマンドで署名者のEメールアドレスと、署名者が利用したOIDCプロバイダーを指定すると署名の検証ができ、例えば distroless/nodejs:18 イメージに対しては次のように出力されます $ cosign verify gcr.io/distroless/nodejs:18 --certificate-oidc-issuer https://accounts.google.com --certificate-identity keyless@distroless.iam.gserviceaccount.com Verification for gcr.io/distroless/nodejs:18 -- The following checks were performed on each of these signatures: - The cosign claims were validated - Existence of the claims in the transparency log was verified offline - The code-signing certificate was verified using trusted certificate authority certificates (以下略) Cosign による署名と検証について こちらの記事 で非常に詳しく説明されています。 cosign verify は何をやっているのか cosign による署名と検証について、自分なりの理解をまとめると次のようになります。 コンテナイメージを保管する OCI(Open Container Initiative) Registry には、Manifest、Config、Blob の3種の Artifact を保存できる Manifest ファイルには Config や Blob(コンテナのレイヤー)への参照がダイジェストで記載されており、従ってコンテナレイヤーが変わるとイメージの Manifest ファイルも変わる Cosign による署名はイメージの Manifest ファイルに対して作成される Cosign でコンテナイメージを署名すると、イメージの Manifest ファイルのダイジェストを Signature Claim という JSON ファイルに埋め込む Signature Claim も Artifact として OCI Registry にアップロードされる。この署名の Manifest ファイルには、Signature Claim のダイジェストを 秘密鍵 で暗号化したものが signature として記載されている 署名の検証は、まずイメージの Manifest ファイルから特定の規則に従い、署名の Manifest ファイルの格納場所を算出する。これにはイメージの Manifest ファイルのダイジェストが利用される 署名の Manifest ファイルに書かれた signature を公開鍵で復号し、Signature Claim のダイジェストと一致しているかを検証する 検知したいと思っている「コンテナイメージをすり替えたが、署名と公開鍵をすり替えることができない」状況を想像します。イメージレイヤーが変わるとイメージの Manifest ファイルも変更になり、 Manifest ファイルのダイジェストから算出される署名の格納場所も変わります。しかし攻撃者はその場所に従来の 秘密鍵 で署名を格納することができない前提で考えているため、結果としてコンテナイメージのすり替えを検知できます。 追加の検証をするべき状況 cosign verify だけでは十分ではなく、追加の検証をした方が良い状況も存在します。それは「イメージ署名の検証」と「実際のイメージのpull」に時間的な差分があるときです。 cosign verify はコンテナのダイジェストを直接検証に使用するわけではないため、 cosign verify する時点の OCI Registry にあるイメージと、pull してきたイメージが同一ではない可能性が無視できない場合、 docker inspect コマンドで pull してきたイメージのダイジェストと署名に記載されたダイジェストの比較が必要です。 署名を検証するワークフロー さて前置きが長くなりましたが、ここからはイメージ署名の検証とイメージ pull の時間差を無視して良く、追加の検証を行わない場合を考えます。 GitHub Actions でコンテナのビルドやプッシュを行う前に、以下のステップを追加することで distroless ベースイメージの署名を検証できます。 steps : # 自身のリポジトリのチェックアウト。署名の検証とは無関係 - name : Checkout uses : actions/checkout@v3 # 1. Cosign のインストール - name : Install cosign uses : sigstore/cosign-installer@6e04d228eb30da1757ee4e1dd75a0ec73a653e06 #v3.1.1 # 2. Cosign による検証。例として nodejs:18 イメージのものを対象としている - name : cosign verify run : cosign verify gcr.io/distroless/nodejs:18 --certificate-oidc-issuer https://accounts.google.com --certificate-identity keyless@distroless.iam.gserviceaccount.com # これ以降コンテナのビルドやプッシュを行う 説明: sigstore/cosign-installer アクションを利用し、cosign をインストールする ここではさらに安全性を高めるためにアクションをバージョン指定ではなく、 コミットハッシュでコードをピン留め している --certificate-oidc-issuer と --certificate-identity オプションを指定し、Keyless署名されたコンテナイメージを検証する ここでは gcr.io/distroless/nodejs:18 イメージを検証している 署名の検証にかかる時間 cosign のインストールと署名の検証はいずれも数秒以内に終わるため、特にCI/CDの妨げにはならない印象です。 数行のアクションを追加するだけでベースイメージが不正にすり替えられていないか検証できるため、万が一に備えて distroless イメージの署名検証をやってみてはいかがでしょうか。 私たちは同じチームで働いてくれる仲間を大募集しています!たくさんのご応募をお待ちしています。 セキュリティエンジニア(セキュリティ設計) 執筆: @kou.kinyo 、レビュー: 寺山 輝 (@terayama.akira) ( Shodo で執筆されました )
アバター