yoppa.org


TouchDesigner上級編(4) –VJのためのプロジェクトを作成

いよいよ、ここまで行ってきたTouchDesignerの技術や知識を総動員してVJのためのプロジェクトを作成します! キーボード操作による映像のスイッチングから始めて、最後にはGUIを駆使したVJのためのプロジェクトを構築していきます。

スライド資料

サンプルプログラム

アンケート

本日の講義に参加した方は以下のアンケートに回答してください。


最終課題へ向けて! – Patatapを作る!

Image result for patatap

この講義「データ構造とアルゴリズムでは」変数と定数、反復、乱数、論理式と条件文、アニメーションとベクトル、配列の操作、関数、そしてオブジェクト指向プログラミングと様々なトピックスを扱ってきました。最終課題はこれらの内容を総動員して、Patatap (https://patatap.com/) のようなインタラクティブなオーディオビジュアル作品をp5.jsで制作します。

Patatapはとてもシンプルなインタラクションで構成された作品です。キーボードを操作すると、それぞれのキーにシンプルなアニメーションが割り振られていて、同時にサンプリングされたサウンドが鳴るようになっています。仕組みはとても単純なのですが、キーボードをリズムにあわせてタイピングすることで音と映像が同期して、まるでVJのパフォーマンスをしているような気分を味わえます。キーボードの組み合わせは無限にあるので、いつまでも飽きることがありません。

今回は、最終課題に向け、p5.jsをつかってPatatapを制作するための方法について解説していきます。

映像資料

スライド資料

最終課題提出

最終課題「Patatapを作る」

最終課題制作のオンデマンドの教材を参考にしながら「Patatap」のようにキーボードからのタイピングで音と映像が同期する作品を制作してください。

  • 投稿したOpenProcessingのURLを提出
  • 投稿の際に sfc22final のタグをつけてください!
  • 締切 : 2023年1月11日 (水)
  • 最終課題講評会で発表する方もこちらに回答してください

最終課題提出フォーム

最終課題講評会での発表希望調査

この授業の最終課題の講評会を2022年1月14日と21日の2回に分けて開催します。講評会での発表をした方は一律10点 (100点満点中) 加算します。発表を希望する方は以下に学籍番号と氏名を記入してください。

締切 : 2023年1月11日 (水)

最終課題発表希望登録フォーム

今後のスケジュール

  • 1月13日 : 最終課題講評会 (1)
  • 1月20日 : 最終課題講評会 (2)

サンプルプログラム


TouchDesigner上級編(3) – プロジェクトの構造化とGUI、Container COMP、Widget COMP

TouchDesignerのプロジェクトは階層構造を持っています。作成しているプロジェクトの中に複数のオペレーターをまとめた「サブパッチ」のような構造を作ることが可能です。複数のオペレータをまとめるにはContainer COMPを使用します。Container COMPは、複数の入力と出力を設定したり、他のオペレータの値を参照することが可能です。さらに、このContainerの仕組みを応用してGUI (グラフィクスユーザーインターフェイス) が作成できます。Widgetというユーザーインターフェイスに特化したCOMPを使用してボタンやスライダーといった様々なパーツを組合せて独自のGUIを構築して画面上に配置したり、別ウィンドウで表示することが可能です。

スライド資料

サンプルファイル

アンケート

本日の授業に参加した方は下記のアンケートに答えてください。


TouchDesigner上級編(3) – プロジェクトの構造化とGUI、Container COMP、Widget COMP

TouchDesignerのプロジェクトは階層構造を持っています。作成しているプロジェクトの中に複数のオペレーターをまとめた「サブパッチ」のような構造を作ることが可能です。複数のオペレータをまとめるにはContainer COMPを使用します。Container COMPは、複数の入力と出力を設定したり、他のオペレータの値を参照することが可能です。さらに、このContainerの仕組みを応用してGUI (グラフィクスユーザーインターフェイス) が作成できます。Widgetというユーザーインターフェイスに特化したCOMPを使用してボタンやスライダーといった様々なパーツを組合せて独自のGUIを構築して画面上に配置したり、別ウィンドウで表示することが可能です。

スライド資料

サンプルファイル

アンケート

本日の授業に参加した方は下記のアンケートに答えてください。


TouchDesigner上級編(2) – TouchDesigner + Shader (GLSL) 応用

今回も前回に引き続いてTouchDesignerとGLSLを組合せて高度な表現に挑戦します。前回はフラグメントシェーダー (ピクセルシェーダー) を用いてテクスチャー (TOP) を生成する2次元的な表現に留まっていました。今回はさらにGLSLを応用して3次元の形態(SOP)にGLSLを適用していきます。まず始めにGLSL TOPを用いてバンプマッピング (Bump mapping) をPhongマテリアル(Phong MAT)に適用しレンダリングの際に3Dの形状を変形するという手法を行います。さらに、続いて、GLSL MATを使用して頂点シェーダー (vertex shader) を用いて座標の情報をもとにマテリアルを動的に生成する手法に挑戦します。

スライド資料

サンプルプログラム

アンケート

本日の授業に参加した方は以下のアンケートに答えてください。


p5.soundでサウンドプログラミング

ライブラリー (Library) とは、 汎用性の高い複数のプログラムを再利用可能な形でひとまとまりにしたもので、多くのプログラミング言語で活用されています。p5.js自体もJavaScriptのライブラリーの1つです。p5.jsではさらにライブラリーを活用することで、p5.js単体では実現が難しい様々な機能を追加していくことが可能です。p5.jsのWebサイトでは様々なライブラリーが紹介されていて、すぐに利用することが可能です。

今回は、p5.js Libraryの中から音を扱かうための p5.Soundライブラリーを使用してp5.jsによるサウンドプログラミングに挑戦します。今回の内容の発展が今期の「デザインとプログラミング」の最終課題となります。しっかりと理解して活用できるようになりましょう。

映像資料

スライド資料

本日の課題

p5.soundを用いて、音を使用した表現をしてください。

  • 再生の状態による変化
  • 再生スピード変化
  • フィルター
  • 音量の視覚化 …など
  • 投稿したURLをアンケートから提出
  • 投稿の際に sfc221216 のタグをつけてください!
  • 締切: 2022年12月21日(水)まで!

アンケート

  • 今回も、前回の提出作品の人気投票を行います!
  • 前回のタグ sfc221209 でOpenProcessingで検索 (検索方法をSketches that are tagged にする必用あり)
  • 一番良いと思った作品に1票投票 (自信があれば自分の作品でも可)

サンプルプログラム

音の再生と背景色の変化

マウスのx座標で再生スピードを変更

ローパスフィルター

音量を視覚化


TouchDesigner上級編(2) – TouchDesigner + Shader (GLSL) 応用

今回も前回に引き続いてTouchDesignerとGLSLを組合せて高度な表現に挑戦します。前回はフラグメントシェーダー (ピクセルシェーダー) を用いてテクスチャー (TOP) を生成する2次元的な表現に留まっていました。今回はさらにGLSLを応用して3次元の形態(SOP)にGLSLを適用していきます。まず始めにGLSL TOPを用いてバンプマッピング (Bump mapping) をPhongマテリアル(Phong MAT)に適用しレンダリングの際に3Dの形状を変形するという手法を行います。さらに、続いて、GLSL MATを使用して頂点シェーダー (vertex shader) を用いて座標の情報をもとにマテリアルを動的に生成する手法に挑戦します。

スライド資料

サンプルプログラム

アンケート

本日の授業に参加した方は以下のアンケートに答えてください。


TidalCycles応用 1 – 構造を作る

前回、前々回とTidalCyclesの様々な機能について学んできました。セットアップと起動の方法、パターン生成の基本、パターンを変形する様々な関数、エフェクト、テンポ、乱数、コード、スケールなどTidalCyclesではライブコーディングに関する膨大な機能を使用することが可能です。今回は、これまでの内容を総合して、TidalCyclesで演奏のための構造をつくるにはどのようにすれば良いのか、そのヒントとなる実例をいくつか取り上げます。今回紹介する方法が全てではありませんが、実際のライブコーディングの演奏の際のヒントとなるでしょう。

スライド資料

サンプルコード

do
  d1
    $every 4 (rev)
    $sometimesBy 0.2 (slow 2)
    $sometimesBy 0.8 (jux (iter 8))
    $stack
    [
      sound "ifdrums(3, 8, 0)",
      sound "bd(3, 8, 3)",
      sound "glitch(2, 8)"
    ]
    #n (irand 64)
    #pan (rand)
    #lpf (rangex 800 18000 $slow 4 $sine) #resonance "0.2"
    #delay "0.3" #delaytime "1.125" #delayfeedback "0.5" #lock 1
    #gain "1.3"
  d2
    $sometimesBy 0.3 (jux (rev))
    $sometimesBy 0.2 (slow 2)
    $s "supersaw(3, 8)"
    #note "c4'sus4"
    |+| note "[0, 7][7, 14][14, 21][7, 0]"
    |+| note "[0, 7, 12]"
    |+| note "0"
    #lpf (range 1000 8000 $rand) #resonance "0.2"
    #sustain "0.08"
    #gain "1.0"
    #room "0.1" #size "0.4"</pre>

アンケート

本日のアンケートはこちら。

アンケート


MediaPipeで遊んでみる

MediaPipe – FaceMesh

MediaPipeとは、Googleで開発を主導しているオープンソースの機械学習ライブラリーで、ライブストリーミングでの使用に特化しています。今回はこのMediaPipeのPython版による導入とTouchDesignerでの応用について紹介します。

開発環境の準備

1. Pythonのインストール

今回は後半のTouchDesignerとの連携を意識して、(※2022年12月現在) TouchDesignerにインストールされているPython 3.9系のバージョンをインストールします。以下のリンクから使用している環境にあわせてインストーラーをダウンロードしてインストールしてください。

2. Visual Studio Codeのインストール

今回は開発環境にVisual Studio Codeを使用します。もしまだインストールしていない場合は、下記からダンロードしてインストールしてください。

3. MediaPipeライブラリーのインストール

以下のpipコマンドでMediaPipeのライブラリーをインストールしてください。

python -m pip install --upgrade pip
pip install --upgrade --user mediapipe

MediaPipeのWebで何ができるのか調べる

以下のページの説明を参照してください。

今回試してみるPython版では、以下の機能が利用可能です。

MediaPipeの機能をWebカム入力で試してみる!

以下のコードをVisual Studio Codeに入力して試していきましょう!

Face Detection

#OpenCVとMediaPipeをインポート
import cv2
import mediapipe as mp
#MediaPipeと顔検出と描画のユーティリティを宣言
mp_face_detection = mp.solutions.face_detection
mp_drawing = mp.solutions.drawing_utils
#webカメラから入力
cap = cv2.VideoCapture(0)
#顔検出開始
with mp_face_detection.FaceDetection(
    model_selection=0, min_detection_confidence=0.5) as face_detection:
  while cap.isOpened():
    success, image = cap.read()
    if not success:
      print("Ignoring empty camera frame.")
      continue
    image.flags.writeable = False
    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    results = face_detection.process(image)
    #カメラ映像の上に検出された顔のアノテーションを描画
    image.flags.writeable = True
    image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
    if results.detections:
      for detection in results.detections:
        mp_drawing.draw_detection(image, detection)
    cv2.imshow('MediaPipe Face Detection', cv2.flip(image, 1))
    #escキーで終了
    if cv2.waitKey(5) & 0xFF == 27:
      break
cap.release()

Face Mesh

import cv2
import mediapipe as mp
mp_drawing = mp.solutions.drawing_utils
mp_drawing_styles = mp.solutions.drawing_styles
mp_face_mesh = mp.solutions.face_mesh
# Webカメラから入力
drawing_spec = mp_drawing.DrawingSpec(thickness=1, circle_radius=1)
cap = cv2.VideoCapture(0)
with mp_face_mesh.FaceMesh(
    max_num_faces=1,
    refine_landmarks=True,
    min_detection_confidence=0.5,
    min_tracking_confidence=0.5) as face_mesh:
  while cap.isOpened():
    success, image = cap.read()
    if not success:
      print("Ignoring empty camera frame.")
      continue
    image.flags.writeable = False
    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    results = face_mesh.process(image)
    # 検出された顔のメッシュをカメラ画像の上に描画
    image.flags.writeable = True
    image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
    if results.multi_face_landmarks:
      for face_landmarks in results.multi_face_landmarks:
        mp_drawing.draw_landmarks(
            image=image,
            landmark_list=face_landmarks,
            connections=mp_face_mesh.FACEMESH_TESSELATION,
            landmark_drawing_spec=None,
            connection_drawing_spec=mp_drawing_styles
            .get_default_face_mesh_tesselation_style())
        mp_drawing.draw_landmarks(
            image=image,
            landmark_list=face_landmarks,
            connections=mp_face_mesh.FACEMESH_CONTOURS,
            landmark_drawing_spec=None,
            connection_drawing_spec=mp_drawing_styles
            .get_default_face_mesh_contours_style())
        mp_drawing.draw_landmarks(
            image=image,
            landmark_list=face_landmarks,
            connections=mp_face_mesh.FACEMESH_IRISES,
            landmark_drawing_spec=None,
            connection_drawing_spec=mp_drawing_styles
            .get_default_face_mesh_iris_connections_style())
    cv2.imshow('MediaPipe Face Mesh', cv2.flip(image, 1))
    if cv2.waitKey(5) & 0xFF == 27:
      break
cap.release()

Hands

import cv2
import mediapipe as mp
mp_drawing = mp.solutions.drawing_utils
mp_drawing_styles = mp.solutions.drawing_styles
mp_hands = mp.solutions.hands
# Webカメラから入力
cap = cv2.VideoCapture(0)
with mp_hands.Hands(
    model_complexity=0,
    min_detection_confidence=0.5,
    min_tracking_confidence=0.5) as hands:
  while cap.isOpened():
    success, image = cap.read()
    if not success:
      print("Ignoring empty camera frame.")
      continue
    image.flags.writeable = False
    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    results = hands.process(image)
    # 検出された手の骨格をカメラ画像に重ねて描画
    image.flags.writeable = True
    image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
    if results.multi_hand_landmarks:
      for hand_landmarks in results.multi_hand_landmarks:
        mp_drawing.draw_landmarks(
            image,
            hand_landmarks,
            mp_hands.HAND_CONNECTIONS,
            mp_drawing_styles.get_default_hand_landmarks_style(),
            mp_drawing_styles.get_default_hand_connections_style())
    cv2.imshow('MediaPipe Hands', cv2.flip(image, 1))
    if cv2.waitKey(5) & 0xFF == 27:
      break
cap.release()

Pose

import cv2
import mediapipe as mp
mp_drawing = mp.solutions.drawing_utils
mp_drawing_styles = mp.solutions.drawing_styles
mp_pose = mp.solutions.pose
# Webカメラから入力
cap = cv2.VideoCapture(0)
with mp_pose.Pose(
    min_detection_confidence=0.5,
    min_tracking_confidence=0.5) as pose:
  while cap.isOpened():
    success, image = cap.read()
    if not success:
      print("Ignoring empty camera frame.")
      continue
    image.flags.writeable = False
    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    results = pose.process(image)
    # 検出されたポーズの骨格をカメラ画像に重ねて描画
    image.flags.writeable = True
    image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
    mp_drawing.draw_landmarks(
        image,
        results.pose_landmarks,
        mp_pose.POSE_CONNECTIONS,
        landmark_drawing_spec=mp_drawing_styles.get_default_pose_landmarks_style())
    cv2.imshow('MediaPipe Pose', cv2.flip(image, 1))
    if cv2.waitKey(5) & 0xFF == 27:
      break
cap.release()

Holistic

import cv2
import mediapipe as mp
mp_drawing = mp.solutions.drawing_utils
mp_drawing_styles = mp.solutions.drawing_styles
mp_holistic = mp.solutions.holistic
# Webカメラから入力
cap = cv2.VideoCapture(0)
with mp_holistic.Holistic(
    min_detection_confidence=0.5,
    min_tracking_confidence=0.5) as holistic:
  while cap.isOpened():
    success, image = cap.read()
    if not success:
      print("Ignoring empty camera frame.")
      continue
    image.flags.writeable = False
    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    results = holistic.process(image)
    # 検出されたHolisticのランドマークをカメラ画像に重ねて描画
    image.flags.writeable = True
    image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
    mp_drawing.draw_landmarks(
        image,
        results.face_landmarks,
        mp_holistic.FACEMESH_CONTOURS,
        landmark_drawing_spec=None,
        connection_drawing_spec=mp_drawing_styles
        .get_default_face_mesh_contours_style())
    mp_drawing.draw_landmarks(
        image,
        results.pose_landmarks,
        mp_holistic.POSE_CONNECTIONS,
        landmark_drawing_spec=mp_drawing_styles
        .get_default_pose_landmarks_style())
    cv2.imshow('MediaPipe Holistic', cv2.flip(image, 1))
    if cv2.waitKey(5) & 0xFF == 27:
      break
cap.release()

Segmentation

import cv2
import mediapipe as mp
import numpy as np
mp_drawing = mp.solutions.drawing_utils
mp_selfie_segmentation = mp.solutions.selfie_segmentation
# Webカメラから入力
# 背景色を(B,G,R)で指定
BG_COLOR = (255, 0, 0) # 青
cap = cv2.VideoCapture(0)
with mp_selfie_segmentation.SelfieSegmentation(
    model_selection=1) as selfie_segmentation:
  bg_image = None
  while cap.isOpened():
    success, image = cap.read()
    if not success:
      print("Ignoring empty camera frame.")
      continue
    image = cv2.cvtColor(cv2.flip(image, 1), cv2.COLOR_BGR2RGB)
    image.flags.writeable = False
    results = selfie_segmentation.process(image)
    image.flags.writeable = True
    image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
    # セグメンテーションされた背景を描画
    condition = np.stack(
      (results.segmentation_mask,) * 3, axis=-1) > 0.1
    if bg_image is None:
      bg_image = np.zeros(image.shape, dtype=np.uint8)
      bg_image[:] = BG_COLOR
    output_image = np.where(condition, image, bg_image)
    cv2.imshow('MediaPipe Selfie Segmentation', output_image)
    if cv2.waitKey(5) & 0xFF == 27:
      break
cap.release()

TouchDesignerでMediaPipeを利用する!

TouchDesignerからのMediaPipeの利用は、Bryan Wai-ching CHUNGさんによる素晴しいサンプル集があります! ダウンロードして試してみましょう。Python 3.9系でMediaPipeが正しくインストールされていれば、そのまま利用できるはずです。

応用することで、下記のようにFaceMeshを使用した3Dサーフェスの合成なども可能になります!

いろいろ動かしながら遊んでいきましょう!


p5.js オブジェクト指向プログラミング2 – コンストラクタと、オブジェクトのバリエーション

今回も前回に引き続き、p5.jsによるオブジェクト指向プログラミング (OOP) について学んでいきます。

今回は、オブジェクトを生成する際に実行される初期化のための関数であるコンストラクター (constructor) に注目します。ここまでやってきたようにコンストラクターはオブジェクトが初期化 (インスタンス化) される際に自動的に実行されます。つまりクラスの初期化関数として機能しています。インスタンス化は以下のように行ってきました。

let object = new ClassName();

クラス名の後に () があるところに注目してください。クラス側で設定することによってインスタンス化の際にコンストラクターの引数としてパラメータを受け取ることが可能です。つまり以下のようになります。

let object = new ClassName(arg1, arg2, arg3...);

こうすることで、クラスからオブジェクトを生成する際に引数の値によって様々なバリエーションのオブジェクトを生成することが可能となります。1つの工場 (= クラス) から、様々なバリエーションの車 (= オブジェクト) を生成するイメージです。

実際にp5.jsのコードを動かしながら、インスタンス化とコンストラクターについて理解していきましょう。

映像資料

投稿作品とランキング

投稿作品

おめでとうございます!!

スライド資料

本日の課題

1つのクラスから複数のオブジェクトを生成して表現してください。

  • クラスのコンストラクタの引数からパラメータを読み込み
  • 様々なバリエーションのオブジェクトが生成されるように
  • 本日作成した「Blob」クラスの例を参考にしてみてください!
  • 投稿したURLをアンケートから提出
  • 投稿の際に sfc221209 のタグをつけてください!
  • 締切: 2022年12月14日(水)まで!
  • 今回も、前回の提出作品の人気投票を行います!
  • 前回のタグ sfc221202 でOpenProcessingで検索 (検索方法をSketches that are tagged にする必用あり)
  • 一番良いと思った作品に1票投票 (自信があれば自分の作品でも可)

課題提出フォーム

サンプルコード