例題8-2:直接テクスチャを描きこむ

A: さて、早速プログラム内でテクスチャを描く方法を解説しよう。

B: 今回は無駄な前フリなしですか。

A: さて、さっそくだが今回のサンプルプログラムを見てもらおうか。

B: へ? もうサンプルがあるんですか?

A: 私がVisionEggのプログラムを勉強し始めたころに作ったプログラムを引っ張り出してきたのさ。

B: ずいぶん手回しがいいと思ったら…。

A: そんなわけで、VisionEggのサンプルプログラムを参考に作ったものだから、例題7で解説したVisionEgg.FlowControlを使っている。 例題7を読んでいない人は先に読んでおいてください。

B: またそんな手抜きを…。書き直せばいいのに。

A: ん? そこの君、何か?

B: いや、なんでもないです。

  • 行番号なしのソースファイルをダウンロード→ 08-2.py

 1#!/usr/bin/env python
 2# -*- coding: shift-jis -*-
 3
 4from VisionEgg import *
 5start_default_logging(); watch_exceptions()
 6from VisionEgg.Core import *
 7from VisionEgg.FlowControl import Presentation
 8from VisionEgg.Textures import *
 9import Image, ImageDraw
10import OpenGL.GL
11from VisionEgg.MoreStimuli import *
12from math import *
13
14screen = get_default_screen()
15SX,SY = screen.size
16
17target1 = Target2D(size  = (280.0,10.0),
18                   color = (0.0,0.0,0.0),
19                   orientation = -45.0)
20
21target2 = Target2D(size  = (150.0,10.0),
22                   color = (0.0,0.0,0.0),
23                   orientation = -45.0,
24                   on = False)
25
26target3 = Target2D(size  = (150.0,10.0),
27                   color = (0.0,0.0,0.0),
28                   orientation = -45.0,
29                   on = False)
30
31im_size = (600,400)
32im_texels = Image.new("RGBA",im_size,(224,224,224,255))
33im_draw = ImageDraw.Draw(im_texels)
34im_draw.ellipse((200,100) + (400,300), fill=(0,0,0,0))
35im_texture = Texture(im_texels)
36im_stim = TextureStimulus(texture=im_texture,
37                          internal_format = OpenGL.GL.GL_RGBA,
38                          position = (screen.size[0]/2,screen.size[1]/2),
39                          anchor='center',
40                          size=(im_size[0],im_size[1]),
41                          max_alpha = 1.0
42                          )
43
44def get_target_position1(t):
45    return (SX/2, 80*cos(0.4*2.0*pi*t) + SY/2 )
46
47def get_target_position2(t):
48    return (80*cos(0.4*2.0*pi*t) + SX/2-250, SY/2+250 )
49
50def get_target_position3(t):
51    return (80*cos(0.4*2.0*pi*t) + SX/2+250, SY/2-250 )
52
53target_position_controller1 = FunctionController(during_go_func=get_target_position1)
54target_position_controller2 = FunctionController(during_go_func=get_target_position2)
55target_position_controller3 = FunctionController(during_go_func=get_target_position3)
56
57viewport = Viewport(screen=screen,
58                    stimuli=[target1,target2,target3,im_stim])
59
60p = Presentation(go_duration=(30,'seconds'),
61                 viewports=[viewport])
62
63def every_frame_func(t):
64    if t<5.0:
65        return
66    elif t<10.0:
67        im_stim.parameters.max_alpha = 0.5
68    elif t<15.0:
69        im_stim.parameters.max_alpha = 1.0
70    elif t<20.0:
71        target2.parameters.on = True
72        target3.parameters.on = True
73    elif t<25.0:
74        im_stim.parameters.max_alpha = 0.5
75    else:
76        im_stim.parameters.max_alpha = 1.0
77
78p.add_controller(target1,'position', target_position_controller1 )
79p.add_controller(target2,'position', target_position_controller2 )
80p.add_controller(target3,'position', target_position_controller3 )
81p.add_controller(None, None, FunctionController(during_go_func=every_frame_func))
82
83p.go()

A: まず最初のポイントは9行目。ImageとImageDrawというモジュールをimportしている。 これはPIL、Python Imaging Libraryと呼ばれるパッケージの一部だ。PILはpythonでさまざまな画像処理を行う強力なライブラリで、本当にいろんな機能がある。 VisionEggをインストールするときにPILは必須パッケージとして指定されているので、VisionEggをインストール済みならインストールされているはず。

B: ふむふむ。

A: 実際にテクスチャを作るのは31から35行目だ。まず31行目で作成したいテクスチャのサイズを(横幅,縦幅)のタプルで指定。 続いて32行目のImage.new()でテクスチャを作成する。第1引数の"RGBA"はRGBとαチャンネルがある画像ですよ、という指定。普通のRGBやグレースケールなどいろいろ指定できる。 続いて第2引数だが、これは作成されたテクスチャを塗りつぶす色だ。VisionEggと異なり、0から255の値で指定する。今回のようにαチャンネルを指定する場合は、R、G、B、Aの4つの値をこの順番に指定する。Aってのはαね。

B: ええと、R、G、Bが最大値255で224。明るめの灰色?

A: その通り。そして最後のAだが、値が大きいほど不透明になる。最大値255に対して255だから、完全な不透明ということ。

B: なるほど。

A: 続いて33行目。32行目で作成されたテクスチャはim_texelsという変数に格納されているわけだが、このim_texelsに描画するためのインタフェースをImageDraw()というメソッドを使って作成する。

B: インタフェースを作成する???

A: ふむ。これはちょっとわかりにくいかもしれないな。Image.new()で作られるのはImage.Imageというクラスのインスタンスなんだが、このクラスには 簡単な画像操作のメソッドがあるものの、複雑な図形を描く機能はない。そこで、図形を描く機能をまとめたImageDrawというクラスを通じて図形を描くんだ。Image.Imageが画像ファイルだとしたらImageDrawは 画像編集ソフトだと思えばいい。ImageDraw.Draw()は画像編集ソフトの「ファイルを開く」。そして開いた画像ファイルに対してImageDrawの機能を使って描画していく。

B: はあ、わかったようなわからないような。

A: まあ先へ進むぞ。今回はテクスチャの真ん中に「丸い穴」を描きたい。「丸い穴」を描くためには何色で塗りつぶせばいいかな?

B: へ? 穴を描くなら何色かって? …黒?

A: こらこら。αチャンネルだよ。Aの値が低いほど透明になるんだから、完全に透明にするにはAの値が0の色で塗りつぶせばいい。

B: RGBの値は?

A: Aの値が0なら完全な透明だから、0から255の整数であればなんでもいい。Aに0より大きい値を設定した場合は半透明になるので、この場合はRGBの値が効いてくる。

B: ははあ。なるほど。

A: 別にB君の答えに合わせたわけじゃないが、このサンプルの34行目では黒の透明色、すなわち(0,0,0,0)で塗りつぶしている。 丸く塗りつぶすメソッドはImageDraw.ellipse()だ。名前のとおりこれは楕円を描く関数で、第1引数では楕円にぴったり外接する長方形の右上と左下の座標を指定する。 (200,100)+(400,300)ってのは(600,400)じゃないぞ。タプルやリストに対する+演算子は結合だから、(200,100,400,300)というタプルになる。

B: うは、てっきり(600,400)かと思いました。

A: 例題3-3を復習しておくように。この場合の 座標はVisionEggのスクリーン座標ではなく、今描画中のテクスチャ内の座標 なので注意するように。

B: あのー。

A: ん? どうした?

B: あのー。外接ってなんでしたっけ。

A: あーっ、もう。こういうことだよ。

../_images/08-2-01.png

B: よくわかりました。

A: さて、これで丸い穴があいたテクスチャは完成。あとは例題8-1と同じだね。35行目でim_texelsを引数にVisionEgg.Texture.Textureのインスタンスを生成して、36行目でVisionEgg.Texture.TextureStimulusのインスタンスを生成。 αチャンネルを使っているので忘れずにinternal_format = OpenGL.GL.GL_RGBAを指定しておくこと。

B: 意外と簡単ですね。

A: だろ? ImageDrawのメソッドにはarcとかlineとかpointとかpolygonとかrectangleとかtextとかいろいろあるから調べてみるといい。

B: はーい。

A: さて、後は44行目から76行目だが…。これは実際にプログラムの動作を見てから読み直したらわかるでしょ。 例題7の練習問題! 44行目から76行目の動作を自分で理解しなさい!

B: えーっ、手抜き、手抜き!

A: うるさい。αチャンネル関連でひとつだけ解説しておこう。67、69、74、76行目。これはフレーム毎に呼び出される every_frame_func()関数の中にあるんだが、Presentationが開始されてからの時間に応じてテクスチャの最大透明度を指定するパラメータmax_alphaを変更している。 これによって、プログラムの実行中にテクスチャを半透明にしたり不透明にしたりすることもできる。

B: 完全に透明にも出来るんですか?

A: そりゃ出来るが、その場合はわざわざ透明度を指定しなくてもonパラメータをFalseにすればいいだろ。

B: あ、そうか。

A: ここで注意してほしいことは、32行目は34行目のPILでαチャンネルを指定したときには0から255の整数で指定したけど、67行目などでmax_alphaを指定しているときには0から1の小数で指定していること。 この例に限らずVisionEggではαチャンネルはRGBと同様に0から1で指定する。

B: ややこしいなあ。なんで統一されてないんでしょ。

A: 前回も言ったけどVisionEggはOpenGL上に実装されているからな。OpenGLではRGBAの成分を0から1の小数で指定する。

B: うーん。

A: まあ、間違えてもエラーメッセージが出るか表示が変になるからすぐに気付くよ。さて、これで今回のサンプルも説明終了かな。動かしてみよう。

../_images/08-2-02.png

B: おお、半透明になるのが面白いですね。これ、自分でキーボードを押して半透明を切り替えられたらもっと面白そう。

A: いいこと言うね。それ、練習問題ね。

B: うげ、藪蛇藪蛇。

A: というわけで、αチャンネルの解説はこんなもんで十分かな。次回はテクスチャの描きかえでもやってみるか。

B: あのー、終わる前にひとつ質問が。

A: ん?

B: このプログラムではmax_alphaってパラメータでテクスチャの透明度を変えてますけど、今まで出てきたVisionEggの刺激も同じように透明度を変えられるんでしょうか。

A: おっと、大事なことなのに解説を忘れていたな。ありがとう。VisionEgg.MoreStimuli.Target2DなどのVisionEggの刺激では、colorパラメータで透明度を指定すればいい。 書式はR,G,B,Aの順に0から1の小数で指定する。完全な透明が0、不透明が1だ。例えば(1.0, 0.0, 0.0, 0.1)だとかなり透明な赤になる。今までの例題のようにRGBだけでcolorを指定した場合はA=1を指定したのと同じ結果になる。 プログラム中で透明度を変更したい場合も、colorパラメータを変更すればいい。簡単に試せるから今までのプログラムを適当にいじって試してみたらいい。

B: やってみます。

A: そんなわけで、今回はこれで終了。