斜めの縞模様(グレーティング)が運動(ドリフト)するデモです。
はじめに次の内容を理解をしておいてください。
numFrames=12; % temporal period, in frames, of the drifting gratingコメントに書いてあるように、グレーティング刺激のフレーム数なのですが、重要なのは1サイクルする(sin波が一周する)間のフレーム数だということです。つまり、1フレーム目の状態と、13フレーム目の状態は同じということになります。
for i=1:numFrames phase=(i/numFrames)*2*pi; % grating [x,y]=meshgrid(-300:300,-300:300); angle=30*pi/180; % 30 deg orientation. f=0.05*2*pi; % cycles/pixel a=cos(angle)*f; b=sin(angle)*f; m=sin(a*x+b*y+phase); %m=exp(-((x/90).^2)-((y/90).^2)).*sin(a*x+b*y+phase); tex(i)=Screen('MakeTexture', w, gray+inc*m); %#ok<AGROW>endm=exp(-((x/90).^2)-((y/90).^2)).*sin(a*x+b*y+phase);
という行がありますが、話を簡単にするために
m=sin(a*x+b*y+phase);
にしたいと思います。
変更前の状態では、グレーティングにガウスフィルター(?)がかかっています。周辺がぼけて呈示されます。ドリフトとは本質的に関係がありませんので、とりあえずここではフィルターをかけないことにします。
tex(textureの略でしょう)はScreen('MakeTexture')によって12個作られます。grayの値は文字通り灰色(黒と白の中間)のデータです。白を数字で表すと255、黒を数字で表すと0になります。grayは128になります。
さて、grayに、inc×mの値を足していますが、incは white - gray なので、127になります。mの値については、前述のようにsinの値に変更しました(フィルターを取り除きました)ので、-1から+1の値をとります。つまり、
gray+inc*mは1から255の値をとります。grayとincは定数で、mがsin状に変化しますので、上の数式の結果もsin状に変化することになります。したがってtexが表すテクスチャがsin波の縞模様になるわけです。
次に x, y の2次元平面状で考えてみましょう。このときmはz方向(xy平面に垂直な方向)になります。
刺激として呈示されるsin波を黒の太線で表しています(3本しかありませんが、実際にはもっとたくさん縞模様があります)。このsin波の周波数をfとします(プログラム上でも f となっています)。周期をtとすると波の性質から t = 1 / f となります。周期tは、sin波が1サイクルするときのピクセル数になります。
(ちなみに、刺激呈示画面ではyの正方向は上ではなく、下方向になります)
ここで、x軸方向(またはy軸方向)でsin波を包丁でざっくりと切ってその断面を見るようなことを考えてみましょう。実はその断面もsin波になっているのです。重要なのは、刺激として呈示されるsin波は、x方向の成分とy方向の成分に分けることができるということです。sin(x+y)のグラフのページも参考にしてみてください。
sin波のx方向の成分に着目して、その周波数をfx, 周期をtxとします。同様にy方向についても、周波数をfy, 周期をtyとします。すると、次の数式が成り立ちます。
t / tx = cos(θ)
t / ty = sin(θ)
この2つの式は次のように変形できます。
fx / f = cos(θ)
fy / f = sin(θ)
つまり、fx = f * cos(θ)、fy = f * sin(θ) となります。プログラムでは a = fx, b = fy となっているのです。
周波数は、1ピクセルあたりに何サイクルするのか、を表していますから、ある点(x, y)のz方向の値、つまりmは
m=sin(a*x+b*y)と表すことができます。
プログラム内では、さらにphaseが足し合わされています。
phaseは「2×pi(piは円周率)= 1サイクル」の 1/12, 2/12, 3/12・・・12/12 倍になります。12枚のグレーティングのテクスチャにおいて、1/12ずつ縞模様がずれるようにするためにphaseを足し合わせます。
m=sin(a*x+b*y+phase);は次のような図で表すことができるでしょう。
movieDurationSecs=5;グレーティングがドリフトする時間です。単位は秒です。frameRate=Screen('FrameRate',screenNumber);ディスプレイのフレームレートを返します。単位はHzです。
movieDurationFrames=round(movieDurationSecs * frameRate);ドリフトしている時間のフレーム数を計算します。
movieFrameIndices=mod(0:(movieDurationFrames-1), numFrames) + 1;Screen('DrawTexture', w, tex(movieFrameIndices(i)));を見ると分かるように、movieFrameIndicesは呈示するテクスチャが、何番目のテクスチャかを表すために用いられます。
具体的に考えてみましょう。frameRateが60Hzだったとします。movieDurationFramesは5secs × 60Hzで300になります。modコマンドの結果は次のようになります。
0, 1, 2, 3, ・・・10, 11, 0, 1, 2, 3, ・・・10, 11, ,・・・
このように0から11の組が繰り返されるベクトルができます。このベクトルの要素数はmovieDurationFrames = 300 になります。さらに最後に1を足していることに注意してください。これはテクスチャを表すtexが0から11ではなく、1から12で表されているからです。
for i=1:movieDurationFrames Screen('DrawTexture', w, tex(movieFrameIndices(i))); Screen('Flip', w);endScreen('Flip')は自動的にリフレッシュレートに合わせてテクスチャを画面に呈示します。例えばリフレッシュレートが60Hzだとすると、16.7 ms 毎にテクスチャが切り替わります。言い換えればグレーティングのドリフト速度はここで決まるということになります。