視覚心理学実験プログラミングの基礎[1]

2009/3/31 光藤宏行

 

1

 

C言語について

 C言語はアメリカのベル研究所で1972年頃、計算プログラム記述のために作られた言語である。現在のパソコンの基本ソフト(オペレーティングシステム、OS)自体はC言語によって書かれていることが多いため、C言語でプログラムを書くことができれば、パソコンの動作および処理能力を手軽に、最大限に近いところで使うことができる。

 

OpenGLによる視覚刺激呈示

 OpenGLとは簡単に言えば、画像を高速に、効率的に描画するための命令関数群(ライブラリ)のことである。具体的には、四角を描く関数、点を描く関数などがある。これをGLUTと呼ばれるOpenGLのための補助的な関数群と組み合わせて使用することにより、画像をフルスクリーンで表示したり、ちらつきなく動画を表示したり、キーボードからの入力を受け付けたり、反応時間を測定することができる。

 通常のOpenGLは、いかにも三次元的なコンピュータグラフィクス(CG)を作成するために利用する場合が多い。しかしここでは、主に視覚心理学の実験や記憶心理学の実験で使用する比較的単純な(動)画像刺激を、OpenGLの基礎的な機能を利用して2次元的に描画し、呈示するための基礎を説明する。

 基本的にはOpenGLの関数は、C言語で書かれたプログラムの中に記述して使用する。従って、 自分でプログラムを作成したり、すでにあるものを改良する場合には、C言語の基礎を理解しておくことが必要となる。具体的には、演習の23回目では基本的にはOpenGLを使わず、文字の入力と表示を行うコンソールアプリケーションを作ることで、C言語の基本的な概念と文法を習得することを目的とする。

 

プログラミングの概略               

 C言語も自然言語と同じく、文法がある。基本的な文法は数は少なく、100もない。基本的な関数が、自然言語でいう単語に相当する。ライブラリ関数と呼ばれる、あらかじめ動作が決まっている関数は数百、それ以上あるが、普段使用するものは数十である。(そのうちのまとまりにOpenGLライブラリと呼ばれるものがある。)関数に従って、コンピュータは特定の操作を実行するので、関数およびその組み合わせは命令とも呼ばれる。関数より小さい単位を考えることは実用上意味はない。通常、関数は変数と呼ばれる値(数や文字列であることが多い)を代入する。関数を変えるとコンピュータの振る舞いが変わるのと同様に、変数を変えてもコンピュータの振る舞いは変わる。

 プログラムのもととなるソースコード(単にコードともいう)は通常は、テキストファイルとして書く。それを、コンパイラと呼ばれる特殊なアプリケーション(ソフトウェア)で開くと、書類が開かれる代わりに、アプリケーションが作成される。コンパイラは正確無比なので、一カ所でも文法に間違いがあれば作成できないし、気を利かせて余計なことをすることは原則としてない。テキストファイルに書かれている内容の通り、実行する[2]

 プログラミングによって、画面に文字を呈示したり、四角を書いたり、音を出したり、キーボードのどのキーがいつ押されたかを記録したり、インターネットを通して他のパソコンに情報を送ったりできる。関数、変数、文法の組み合わせを複雑にすることにより、複雑な動作および処理を実行できる。これらを組み合わせて特定の機能を実現する仕組みをアルゴリズムと呼ぶ。すべてのアプリケーションは、誰かがプログラミングして作成したものである(ブラウザ、オフィスなども)。コンパイラは、ウインドウズであれば、visual C++というソフトウェアがマイクロソフトから無償で提供されている[3]Mac OSX環境であれば、Xcodeというコンパイラが付属している。

 

C言語の基本

 基本的な決まり事に、コード内の関数は、上から下に向かって一列ずつ順に実行される、という原則がある(いくつかの例外はあるが、例外の場合には関数の種類が決まっている)。すべてのC言語プログラムの核の部分はmain( )と呼ばれる関数であり、直後の{ }の中身が上から順に実行される。コードの途中に、繰り返しを指定する重要な箇所がある。それらはfor( )while( )である。また、文字は原則的に英数半角文字で書き、大文字と小文字は異なる文字として扱われる。スペースは基本的には無視される(文字列や関数名の中にある場合は無視されない)。コード中の色づけの意味は、青が予約語(あらかじめ意味が決められている単語)、茶色が文字列、緑がコメント部分(プログラム実行に関係がない箇所)である[4]

 

新しいアプリケーションの作り方

 各PCUSERフォルダに、まず自分のフォルダを作る。その中にプロジェクトを作る。Tera Station(これはネットワーク経由だから)には作らない。良いプロジェクト名をつける[5]

 プロジェクトを新規作成すると、ソリューションという上位カテゴリのものが作られる。一つのソリューションには複数の独立したプロジェクトを作ることができる。新しくプロジェクトを作るためには、左端のソリューションエクスプローラのソリューションを右クリックし、追加新しいプロジェクトで、空のプロジェクトを作る。

 

関数、変数、型

 重要な文法に、{  }  ;  (  )  [ ]  " "  ' '  =  for( )  if( )  else if( )  else( ) while( ) がある。これらは、使い方が決められているので、きちんと使い分ける必要がある。必ずペアにして使う記号に、括弧およびクオーテーションがある。{ }は、関数および命令のまとまりを作るために使い、( ) は関数に代入する変数を指定するとき、または数値計算で使用する。[ ]、配列の添字を指定するときにのみ使う。" "は、文字列を指定するときに使い、' 'は一文字だけ指定するときに使う。

 ; (セミコロン)は、命令の最小単位を区切るために使う。.(ピリオド)は構造体のメンバーを指定するときに使うが、当面は使わない。=は、数学のイコールではなく、左の変数に右側の数字や文字列を代入(コピー)するために使う。

 for( ) は、繰り返し回数を決めて同じ動作を繰り返すときに使用する。while( )は、繰り返しの回数は決めずに、( )内の条件が真である限り繰り返す。これらの関数を使用する際に注意することは、( )内の条件が必ず終了するように記述することである。誤って終了できないプログラムを作成してしまった場合は、ウインドウを閉じる。それでも終了しなければ、ctrlaltdeleteを同時に押してタスクマネージャを起動し、それらしいプログラム(たいていはCPU使用率が一番高い)を強制終了する。if ( )は、( )の中身が真のときに、直後の命令が実行される。else if ( ) および elseは、if ( )を使った後に、使う。

 変数には、数値と文字列がある。数値は型というものがあり、大まかには整数と実数がある。変数の名前は、基本的に好きな名前を付けることができる。名前を定義するときは、整数型および実数型を指定するために、int およびdoubleと書いた直後に変数の名前を書く。文字列、たとえば"hiroyuki"などは、charという変数の型を使う。正確には文字列は、配列と呼ばれる、文字を並べたものからなっている。

 よく使う関数に、printf( ), sin( ), rand( )などがある。これらの関数はコンピュータがあらかじめ用意しているもので、書式が決まっている。例えば、printf( )は画面に文字列を出力する。例えばprintf("こんにちは\n"); とコード中に書けば、画面に「こんにちは」と表示される。sin( )は数学の正弦波と同じで、( )内に角度を記入するとサインの値が得られる(角度の単位はラジアン、360°)。

a=sin(PI/4.0); などのようにすると、変数aにルート2分の1という値が代入される。printf("%f\n", a);と書くとaの値が画面に表示される。PIという文字に3.141592を割り当てるためには、コードの最初の部分に#define PI 3.141592と書いておく必要がある。(\%#などの記号には特別な意味がある。)aを実数型の変数として使うためには、そこで使われている関数の中のはじめの方にdouble a;と記入する必要がある。

rand( )は、乱数を発生させるときに使用する。a=(double)rand()/(double)RAND_MAX;とすると、a0から1の範囲の実数が代入される。(double)rand()(double)は、型変換と呼ばれる特殊な操作であり、直後の値(ここでは、rand())を強制的にdouble 型に変換する。

 

コメントアウト

 テキストファイルに書かれた内容は基本的に、すべてコンパイラによって実行される。例外は二種類あり、//とスラッシュを二つ重ねた後に書いた文字は、リターンが打たれたところまでの内容は無視される。もう一つは/* */と書き、アスタリスクに囲まれた文字は、何が書かれていようともコンパイラによって無視される。この機能によって、プログラムの説明を書き入れたり、一時的に必要のない命令を実行させないことができる。

 

命名規約

 変数名はアルファベットと数字を使ってほぼ自由につけることができる。したがって、自分で覚えやすい名前をつけられる一方、規則を作っておかないとそのうち訳が分からなくなる。最低限の規約として、この演習では、各関数内で定義した変数(ローカル変数)の最初にはaをつけ(慣例の例外として、アルファベット小文字一文字の場合も)、関数外で定義するグローバル変数にはgをつけ、関数の引数にはpをつける。定数はkをつける。また、gCountNumberのように、単語の最初だけを大文字にしてつなげていくこととする(マイナス記号は使わない)。コメントと文字列のみは、日本語表記が可能である(GLUT環境では、日本語文字列は表示できない)。

 

自作関数の定義

 通常のプログラムは処理を繰り返すことが多いため、その特定の処理を繰り返すためには自分で関数を定義して、それを呼び出すようにすると、読みやすく効率的なプログラムを作ることができる。関数の名前は(すでに定義されている名前ではない限り)何でもよく、

 

     int myOriginalFunction( ) {

       printf( );

       return 0;

     }

 

というように、myOriginalFunction( ) { }という書式で書く。他の関数の中にmyOriginalFunction( );と書くと{ }の中身が自動的に実行される。return 0は当面は無視する。(この関数を呼び出す他の関数は、テキストファイルの中でこの関数より下に書く必要がある。従って、関数を定義するときはmain( )を一番下に書く。)

 

時間測定

 ウインドウズ環境でGLUTを使う場合、glutGet(GLUT_ELAPSED_TIME)という関数を使うと簡単にミリ秒単位で時間が計測できる。この関数は、実行されたときの時間を取得する関数であり、時間間隔を測定するためには必ず複数回実行する必要がある。上述のファイルでは、文字列の入力を受け取る関数gets( )を使い、文字列を入力する間待機するという仕組みになっている。

 

課題

・アプリケーションvisual C++を使ってプロジェクトを新規作成し、コンソール画面に「こんにちは!」と表示する

・入力した数字が100より大きいかどうかで表示する文字を変える(if文を使う)

for を使って繰り返す

・数学関数と変数を使って、数値を計算し、表示する

・関数を定義する

・数字を並び替える

 

 

C言語の文法に関する書籍

河西朝雄 入門ソフトウェアシリーズ1 C言語、ナツメ社

 基本的な表示のための関数、数学関数、文字列処理関数の易しい解説。

B.W.カーニハン・D.M.リッチー(石田晴久訳)プログラミング言語C第二版、共立出版

 C言語を開発した人によるC言語の基本的な文法の詳細な解説。

 


第2回

 

 今回も引き続き、C言語の基礎の続きを行う。今回は、前回のプログラムの詳細、配列というデータの種類、および複雑な制御構造について理解を深める[6]

 

処理の制御構造

前回のプログラムで重要なのは、do { } while ( 論理式 );という基本構造である。これは、論理式が真である限り、文の中身をずっと実行する、という制御構造である。別の言い方をすれば、論理式が偽になれば文の繰り返しが終わるということである。この論理式は、==で「等しい」、!=が「等しくない」、<、や>=なども使える。また、論理式をつないで、&&は「かつ」、||は「または」という複雑な論理式を作ることができる。式の解釈があいまいになりそうなときには、((a==b) && (c>d)) || (c<=d)のように、()を使う(括弧はペアにして使う)。

 他によく使う制御構造として、決められた文を決められた回数だけ実行する、for (開始条件; 終了条件; 繰り返し条件) { }という制御構造がある。典型的には、for (i=0; i<5; i++) { }という形式を使う。ここではiint型の変数であり、iは順に0, 1, 2, 3, 4と代入したときの文をそれぞれその順番に実行する。++はインクリメント演算子と呼ばれ、直前の変数(ここではi)を1ずつ増加させる。i+=1またはi=i+1と書いても同じ動作をする。i--とすれば1ずつ減少する。()の中身が、終了するような条件となっていることに注意して実行する[7]

 

標準ライブラリ関数

printf(“文字列”, 変数)という関数も重要である。文字列中の%dは、文字列中に変数の値を出力するために使う。%dint型(整数型)の変数を出力するときに使い、%lfdouble型(実数型)の変数を表示したいときに使う。一つの文字列の中に複数の変数を出力したいときは、文字列中に複数の%dを書き、かつ複数の変数を順に並べてカンマで区切る。文字列と変数の順番は前から順に自動的に対応がとられる。具体的には、printf(“文字列”, 変数1, 変数2)のように書く。文字列中の\nは改行記号である。注意:int型の数値は、ピリオドをつけて少数点表記にすることはできない。また、double型は逆であり、ちょうど整数になる場合には、必ずピリオドゼロとする。

 

配列

 配列は、複数の変数をまとめて記述し扱うために用いる。たとえばint aVariable[4];とすると、整数型の変数を4つ並べたものを定義することになる。使い方は、まずaVariable[0]=3;のように書き、配列それぞれに数値を代入する。その後例えばx=aVariable[0];とすると、変数xaVariable[0]の値が代入される。aVariable[1]aVariable[3]などについても同様に操作を行う。配列を使う際に重要なのは、配列の括弧の中身の番号は0から始まり、定義した数(この例では4)より1小さい数の配列番号までしか使えないということである。定義した配列の長さ以上の数の番号を指定して使おうとすると、それだけでプログラムは動かなくなるので注意が必要である。多次元配列を使いたいときはaVariable[4][3]のように括弧をつなげて宣言し、使用する。OpenGLを使って画像を表示するときには、3次元配列(縦座標、横座標、RGB値)を使う。

 C言語では、文字列はchar型(アルファベットなど)データの一次元配列として扱う必要がある。半角英数文字では1文字が1つの長さに対応し、日本語文字では1文字が2つの長さに対応する[8]。文字列としてchar型の変数を定義する場合は、使用予定の文字列の長さより余裕を持たせ、例えば数行の場合でも[1000]程度の長さにしておくのが安全である。ちなみに、文字列と変数は見かけは似ていても、全く異なることに注意する必要がある。例えば、”aVariable”は文字列であるが、aVariableは変数である。変数の中身は、定義時の宣言、およびその後の処理を調べる必要がある。

 

課題

(1)     前回の数字を入力するプログラムに変更を加える。具体的には、入力した数字が10より大きいかどうかで、表示する文を変える。

(2)     10個の要素をもつdouble型配列を作り、それぞれに0から1までの範囲の乱数を代入する。9までの数字を入力するとそれぞれに割り当てた乱数を表示するプログラムを作成する。乱数をプログラムを起動するたびに変更するには、rand()を使う前に、main関数の中で、srand((unsigned)time(NULL));と一度だけ記述する(加えて、#include <time.h>とソースコードの上部に記述する)[9]

(3)     0から9までの数字をランダムに並び替えて、表示するプログラムを作る。for文を使う。

 

Visual C++の便利な使い方

・日本語変数名も使える(Macでは出来ない)。

・フォントを大きくするには、メニューバーの、ツールオプション環境フォントおよび色、で変更する。

 


3

 

 前回の課題2-3が独力で作成できれば、C言語のプログラムを独力で書く基本的な技術が身に付いていると判断できる。特に、乱数、配列、for文を組み合わせる課題3は、実験実施の際の試行順序のランダム化には欠かせないので、取り組む価値がある[10]

 

 

プロジェクトの追加方法

開いたプロジェクトに、新たにプロジェクトを追加することができる。プロジェクトファイルに新たにプロジェクトを追加する方法は、配布資料2ページを参考にすること。作成したプロジェクトを実行するためには、画面左のウインドウで実行したいプロジェクトファイル(ソースコードではない)を選択して右クリックし、「スタートアッププロジェクトに設定」を選ぶ。正しく選択されているならば、プロジェクト名が太字になる。

 

プログラム実行時の注意

ところで、C言語を使ったプログラムについて、一般的な注意点がある。いくつかの班では、正しく走っているプログラムを終了するときにいきなりウインドウを閉じる人がいた。これは、学習用プログラムとしてはあまりよろしくない終了のさせ方であり、正しくは、(前回のサンプルプログラムであれば、数字以外を入力した後、)黒い画面に「続行するには何かキーを押してください...」と表示され、そこで何でもよいからキーを押す、というのが正しい終了の仕方である。というのは、このような終了の方法は、プログラムを途中で終了しているということであり、実際に最後までエラーなく進んでいるかどうかが分からないからである。また、最後の表示の文字がきちんと出ないのにいきなり終了するプログラムは、実はどこか致命的なバグがあって不正終了している。不正終了したプログラムは、ほかのプログラムにまで悪影響を及ぼしかねない。プログラムは、終了の仕方まできちんと書くことが大事である。

 

複雑な制御構造

 if文、while文、for文などは制御構造を示しているが、これらを入れ子にしたり組み合わせたりすることにより、複雑な処理を明解に記述することができる。入れ子とは、一つの中にほかのものをさらに記述することである。例えば、

 

int i, j;

for (i=1; i<4; i++) {

for (j=1;j<4;j++) {

printf(“遠足%d日目の%d回目のおやつは%d円までです。\n”, i, j, i*j*100);

}

}

 

という風に記述すると、プログラムの実行結果は

 

 

遠足1日目の1回目のおやつは100円までです。

遠足1日目の2回目のおやつは200円までです。

遠足1日目の3回目のおやつは300円までです。

遠足2日目の1回目のおやつは200円までです。

遠足2日目の2回目のおやつは400円までです。

遠足2日目の3回目のおやつは600円までです。

遠足3日目の1回目のおやつは300円までです。

遠足3日目の2回目のおやつは600円までです。

遠足3日目の3回目のおやつは900円までです。

 

となる。

 

課題:前回の課題3main関数の中身をA4紙に書いて班で一枚提出する。課題2が済んでいない班はそちらを先に提出する。紙の上部に班とプログラムのタイトルを書くこと。

 


4

 

数字を並べ替えるプログラム

 数字を並べ替えるプログラムは、プログラムのアルゴリズムを考える良い訓練になる。まず、10個の要素を持つint型の配列を作り、それに0から9までの数字を代入する。配列の名前をaRandArrayとする場合は、aRandArray[0]=0;  aRandArray[1]=1;  aRandArray[9]=9;というように定義する。これはfor文を使うと簡潔に書くことができる。その後、x番目のaRandArray配列の値を、0から9までの間でランダムに選んだ整数番目のaRandArrayの値と入れ替える。x0から9まで順に行えば、aRandArray のそれぞれの値は、0から9までの数字がランダムに入ることになる。最後にそれをprintf関数を使って画面に表示する。

 

文字を入力する時間を測定するプログラム

 今回で、基本的なC言語の文法を終わりたいと思う。C言語に関する最後の課題4では、キーボードからの文字入力を行うためにかかった時間を測定するプログラムを作る。まず、前回のH20-shinri-enshuプロジェクトを開き、新しいプロジェクトを作る。新しいプロジェクトの追加の仕方は前回までに説明した。ここでは反応時間を測定するので、measure-time-projという名前のプロジェクトを作ることにする。画面左のウインドウの中の特定のプロジェクトを実行したい場合の方法は前回のプリントを参考にすること。

 今回作りたいプログラムは、文字列を入力して、打ち込むのにかかった時間を秒単位で表示するというものである。反応時間というのはある時間と別の時間の差分であるから、関数が呼び出されたときの時間を出力する関数glutGet(GLUT_ELAPSED_TIME);を二回呼び出し(この関数はp. 4で簡単に説明している)、その差分を求めると入力にかかった時間を知ることができる。入力を待つ関数はgets( )であり、括弧の中身に文字配列(文字列)の変数を指定する。文字列の定義の方法は(たとえば変数の名前をaStringとすると)、

 

char aString[1000]=””;

 

のように書く。文の右端はダブルクオーテーションが二つ並んでいる。gets関数で取得した文字列をaStringに代入したい場合は、gets(aString);と書く。最初は、プログラムを一度実行すると一度だけ時間を測定し、その結果を表示するプログラムを作ることを目的とする。glutGet関数を使うためには、(main関数の外の)プログラム上部に#include <glut.h>と記述する。

 それができたら、quitと打ち込んだ時に終了し、それ以外は実行し続けるというようにプログラムを変更してみる。処理を繰り返すためには、do{処理}while(繰り返し条件);を使う。これを行うためには、二つの文字列を比較し、同じかどうかを判断する関数strcmp( )を用いる。関数の引数(括弧の中身)は、カンマで区切って比較を行いたい文字列変数を記述する。もし二つの文字列が同じならば、strcmp自体が0という整数を出力する(専門用語で「返す」という)。

 

 


5

 

OpenGLを使うプログラム

 OpenGLOpen Graphics Libraryの略)は、コンピュータ上で画像を簡単に高速に表示するための関数群のことである。現在のOSWindows XP, VistaMac OSXなど)にはあらかじめOpenGLがインストールされている。それらの関数を使うためには、今まで出てきたようなprintfなどの関数と同様に、OpenGL関数を記述するだけである。それらの関数は、すべて初めにglという小文字がつき、よく使う関数の種類は十数種類である。OpenGLは、GLUTOpenGL Utility Toolkit)という、OpenGLの関数を使うための付属のような関数群も同時に使うのが便利である。GLUT関数はものはすべて始めにglutという文字がつく。OpenGLの関数もGLUTの関数も、printf( ), sin( )などの普通の関数と同様に、使い方(書式)が決まっており、ソースコードの中に混ぜて使うことができる。GLUT関数のいくつかは、上から下への処理の流れが適用できない特殊な種類の制御方法を使う(これらは専門用語でコールバック関数と呼ばれる)。

 OpenGL関係でよく使うコールバック関数はglutKeyboardFunc( )glutSpecialFunc( )glutDisplayFunc( )glutReshapeFunc( )glutTimerFunc( )の5つである。glutKeyboardFuncはキーを押したときに何らかの関数を実行する関数、glutSpecialFuncは矢印キーなど特殊なキーを押したときに何らかの関数を実行する関数、glutDisplayFuncはウインドウに呈示するものを定義する関数、glutReshapeFuncはウインドウ自体の設定に用いる関数である。最後のglutTimerFunc( )は、一定時間ごとに自作関数を呼び出すという関数であり、画像を一定時間表示したいときに用いる。

 ここでいう自作関数とは、ソースコード中でmain関数より上に定義されている関数のことであり、それぞれが、printfなどのようなすでに定義されているわけではない関数を指す(p. 3も参照)。また、ソースコード内で関数を記述する順番は実は重要であり、自分で定義した関数を使う場合には、その関数の定義は、呼び出す側の関数より前に行う必要がある。

 

OpenGLを使ったサンプルプログラム

 各PCH20-shinri-enshuのプロジェクトファイルを開き、新たにプロジェクトを追加する。プロジェクトの名前はopengl-projなどのようにする。そのプロジェクトのソースファイルを右クリックして、新しい項目を作成し、simple-glというような名前の空のソースコードファイルを作成する。新しく作ったそのファイルは当然空である[11]

 スタートアッププロジェクトを変更し、実行してみる。スペースキーを押すと、黒背景のフルスクリーン画面になり、画面中央上部の図形が左右に5秒動く。止まった時にもう一度スペースキーを押すと動き出す。sまたはlキーを押すとそれぞれ縮小あるいは拡大する。このプログラムを終了させる場合には、エスケープキー(キーボード左上)を押すと黒いコンソールウインドウに戻るので、そこで何かキーを押してプロジェクトに戻ることができる。

 以上の動作は、すべてソースコードに記述されている。前回までのソースコードとの一番の外見上の違いは、main関数より前にいろいろな(自作)関数が記述されているということである。それぞれが異なる役割を担っており、それらの関数の名前はすべてmain関数の中で(または、main関数から呼び出される別の関数を経由して)使われている。つまり、自分で作った関数は、何かしらの形でmain関数をつながっていない限り勝手に実行されることはないことに注意する。

 

 

視覚刺激の呈示

 画面上に視覚刺激を呈示したいとき、大まかに分けて二つの方法がある。一つは、頂点を指定して線画を描いたり面を塗りつぶす方法である。ここではDisplay関数の中身はこの方法で図形を描画している。基本的には、glBegin( )glEnd( )という関数の間に、glVertex2d( )という関数を挟む。glVertex2d( )の中にはx座標とy座標の値を記入し、座標の位置を変えて、複数書くことで多角形が作成できる。これらの数値の単位はピクセル(画素)である[12]。ウインドウの中心が原点であり、右および縦方向がそれぞれの軸の正の方向である。点が二つだと多角形ではなく線分となる。多角形は重ねて書くことができるので、特定の色をもつ背景上の線画などは容易に描くことができる[13]。色や線幅の指定は、glBegin関数より前に書いておく(それぞれの具体的な使い方は、ソースコードの中身を見ること)。一度書くと、それは次に変更するまで保たれる。面を描きたい場合はglBegin()の引数をGL_POLYGONとし、線でつなぎたい場合はGL_LINE_LOOPとする。点を描きたいときは、GL_POINTSとする。

 画像を表示するもう一つの方法は、画面の2次元ピクセル配列を一つずつ指定して描画する方法である[14]。風景写真などの画像ファイルを読み込んで画面に呈示したい場合には、この方法を用いる。ここでは概略を述べる。カラーの複雑な画像を描くためには、色次元を加えた三次元配列を用意し、その配列をglDrawPixels( )を使って描画する。呈示したい画像の場所を指定するために、glRasterPos2i( )という関数の括弧の中に座標値を指定する。まず、フォトショップなどで.rawの拡張子を持つ画像ファイルを作成しておく。fopen( )の関数を使用して画像をraw形式で開く。その次にfread( )を使って3次元配列にコピーする。その際に画像のピクセルサイズの情報が必要になるが、それは画像作成時に各自で覚えておく。その配列を、glDrawPixels( )を使って画面に転送する。配列の値は数字であり、数字を計算し直すことにより、画像を一部書き換えたり全体的な色を変化させることができる。ビットマップファイルを読み込みたい場合には、programmingフォルダの情報を参考にする。

 

キー押しに対する反応

 キー押しに対する反応は、サンプルプログラムではKeyboard関数によってなされている。前回まではgets関数やscanf関数を使ってキーボードからの入力を取得したが、それらはここでは使えない。それは、gets関数などを使うとその間他の処理ができなくなるからである。コールバック関数glutKeyboardFuncを使うことにより、キー(やマウス)など、いつ処理を行うべきか分からないことに効率的に対応することができる。Keyboard関数の中身は基本的には単純であり、キーに対応する文字変数ごとにif文などで処理を定義し、そこに記述する。サンプルプログラムでは、sまたはlキーを押すと、gMagnifyというグローバル変数[15]の値が増減するような記述を行っている。グローバル変数とは、どの自作関数からでも参照できる値であり、ソースコードの上部で定義される。この値を(同じくコールバック関数から呼び出される)Display関数が参照するようになっているため、画面の内容が自動的に書き換わる仕掛けとなっている。矢印キー押し、またはマウスクリックに対応させてプログラムの動作を変えたい場合には、それぞれ別のコールバック関数を使う。

 

一定時間画像を表示する

 上述のKeyboard関数の例は、キー押しに応じて画面を更新するものであるが、自動的に表示を変えたいこともよくある(刺激の呈示時間を一定にしたい場合など)。その場合には、glutTimerFuncという別のコールバック関数を用いる。サンプルプログラムの例では、この関数の二番目の引数がIdleという関数であり、glutTimerFunc(0,Idle,0)と記述するとIdle関数が直ちに実行される。この関数は一度呼び出すと一度だけ更新する関数であるから、連続的に動作を行うためには何らかの仕掛けが必要である。ここではIdle関数を自分自身で呼び出すという仕掛けをすることにより、連続的に画像を変えることができる。自分自身の呼び出し条件をif文で設定することにより、一定時間のみ画像を更新するという仕組みになっている。

 

課題5

以下に示す動作を行うようにサンプルプログラムのソースコードを改変し、元のソースコードから変更した部分を紙に書いて提出しなさい。

(1)黄色に塗りつぶした三角形を、現在呈示しているものとは別の場所に呈示する。

(2)刺激が動いていない時間は刺激をしないようにする。

二番目の課題のヒントは、刺激が動く時間(5秒以内)にあるかどうかを反映するグローバル変数を作り、その値に応じて画面を書き換える関数の内容を変更することである。刺激が動いている時間は、時間を取得している関数がどこでどのように使われているかをよく見ると分かる。

 

OpenGLに関する情報

GLUTによる「手抜き」OpenGL入門(http://www.wakayama-u.ac.jp/~tokoi/opengl/libglut.html

 OpenGLを使ってCG画像を呈示する際の基礎的事項が勉強できる。

The OpenGL Programming Guide - The redbook (http://www.opengl.org/documentation/red_book/)

 OpenGLの関数群の詳細な説明がある。

OpenGL入門(http://wisdom.sakura.ne.jp/system/opengl/index.html

 アンチエイリアス(画像を滑らかに表示する機能)が載っている。

GLUT API, version 3http://opengl.jp/glut/

 GLUTと呼ばれるOpenGLのための関数群の説明。

 


/****** OpenGLテストプログラム 光藤宏行 *******/

 

//ヘッダファイルの読み込み

#include <stdlib.h>

#include <glut.h>

#include <stdio.h>

#include <math.h>

#include <string.h>

#include <windows.h>

#include <time.h>//vistavc++でランダム化のために必要

 

#define PI 3.141592//PIという定数を円周率として使うための定義(自分で行う)

 

//グローバル変数:これらの変数の値はどの関数からでも呼び出せる

static double gMagnify=1.0, gRotateRad=0.0, gCurrentTime, gStartTime;

 

void Display (void)//画像ウインドウに何を表示するかは、この関数の中身を書き換える

{

    glClear(GL_COLOR_BUFFER_BIT);//Display関数の最初に一度だけ記述する

   

    glLineWidth(2.0);//線幅をピクセル単位で指定

    glColor3d(1.0, 1.0, 1.0);//関数の引数は赤、緑、青に対応(0.0から1.0まで)

   

    glBegin(GL_LINE_LOOP);//閉じた線画図形の描きはじめの宣言

    glVertex2d(0.0+10.0*sin(gRotateRad), 0.0);//x座標、y座標に対応

    glVertex2d(0.0*gMagnify*sin(gRotateRad), 50.0*gMagnify);

    glVertex2d(10.0*gMagnify*sin(gRotateRad), 150.0*gMagnify);

    glVertex2d(120.0*gMagnify*sin(gRotateRad), 0.0*gMagnify);

    glEnd();//図形書き終わりの宣言

   

    //ほかに図形を描きたい場合には、ここにコードを書き足す

   

    glutSwapBuffers();//Display関数の最後に一度だけ記述する

}

 

void Idle (int value)

{

    glutPostRedisplay();//画面書き換えのglut関数

    gCurrentTime=glutGet(GLUT_ELAPSED_TIME);

    gRotateRad+=3.0/360.0*2.0*PI;//PIはプログラム上部の#defineで定義している

    if (gCurrentTime<(gStartTime+5000.0)) {

    glutTimerFunc(0, Idle, 0);//自分自身を呼び出すことにより、自動的に処理を繰り返す

    }

 

}

 

void Keyboard(unsigned char pKey, int x, int y)//中身を必要に応じて変更

{  

    if (pKey=='\033') //escキーを押す

       exit(0);//終了

    else if (pKey=='s')//sキーを押す

       gMagnify-=0.1;//gMagnifyという変数の値を.1減らす

    else if (pKey=='l')//lキーを押す

       gMagnify+=0.1;//gMagnifyという変数の値を.1増やす

    else if (pKey==' ') {//スペースキーを押す

       gStartTime=glutGet(GLUT_ELAPSED_TIME);

//時間を取得し、グローバル変数gStartTimeに代入

       glutTimerFunc(0, Idle, 0);//コールバック関数、Idleという関数を実行

    }

    glutPostRedisplay();//画面を更新するglut関数

}

 

void Resize(int w, int h)//変更しない

{

    glViewport(0, 0, w, h);//set full-window to viewport

    glLoadIdentity();//Initialize transformation matrix

    glOrtho(-w/2.0, w/2.0, -h/2.0, h/2.0, -1.0, 1.0);

}

 

void Init(void)//変更しない

{

    glEnable(GL_LINE_SMOOTH);//for antialias

    glHint(GL_LINE_SMOOTH_HINT , GL_NICEST);//setting for antialias

    glClearColor(0.0, 0.0, 0.0, 1.0);//backgroud color

    glEnable(GL_BLEND);//for antialias

    glBlendFunc(GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA);//for antialias

//   glPixelStorei(GL_UNPACK_ALIGNMENT, 1); //for presenting pixel array

    srand((unsigned)time(NULL));//call once for randomization

}

 

int main(int argc, char *argv[])

{

    glutInit(&argc, argv);//初期化(変更しない)

    glutInitWindowPosition(0, 0);//ウインドウの左上の位置を指定(変更しない)

    glutInitWindowSize(1024, 768);//画像ウインドウのサイズを指定(変更しない)

    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE/**/);//(変更しない)

    glutCreateWindow(argv[0]);//画像ウインドウを表示(変更しない)

    Init();//初期化(変更しない)

    glutFullScreen();//フルスクリーン表示(一度だけ記述)

    glutSetCursor(GLUT_CURSOR_NONE);//カーソルを消す(一度だけ記述)

   

    glutKeyboardFunc(Keyboard);//自作のKeyboard関数を設定

     glutDisplayFunc(Display);//自作のDisplay関数を設定

    glutReshapeFunc(Resize);//Resize関数の設定(変更しない)

    glutMainLoop();//以上の準備を経て、この関数を呼び出すと実際にプログラムが開始する

 

   return 0;

}

 


6

 

課題5(2)「刺激が動いていない時間は刺激を出さないようにする」のヒント

 まず、このプログラムで刺激が動いている原理について解説する。刺激の書き換えはDisplay関数が担っているが、その中のglVertex2dに、変数が使われている。具体的にはgRotateRadgMagnifyという変数であるが、それらの値がどこで変更されているかを調べると、それらの役割がわかる。gRotateRadIdle関数の中で値が更新され、gMagnifyKeyboard関数の中で値が増減されている。Idle関数は5秒間は自動的に実行されるので、自動的に更新されるのはgRotateRadという変数であることが分かる。gRotateRadの値が更新されなければ刺激は(消す関数を書かない限り)そのまま表示されるため、5秒以上経過すれば刺激は静止するという仕組みである。

以上を踏まえて、どのようにコードを変更すればよいか考えてみよう。出来ていた班もあるが、可能ならば全員が以下の手続きも考えてみてほしい。刺激が動いているかどうかは、Idle関数を呼び続けるかどうかで判断できる。Idle関数を呼んでいるのは、Idle関数の中のglutTimerFuncがあるif文の中であるから、そこに、動いているかどうかを反映するグローバル変数(何でもよいが、簡便のため整数型とする)をgStimulusOnと名付け、その変数に動いていることを示す値として1を割り当てる。そして、そうでなければ0となるようにコードを変更する。そして、視覚刺激を表示する部分は、gStimulusOn1の時にのみ実行されるようにすれば、刺激が呈示されていない間は何も描かれない。刺激を表示するのはDisplay関数の中身であり、具体的には刺激を描画している部分をif文でくくると可能になる。

 

課題6

(1)for文とsin関数を使い、縦縞を画面に表示する。整数型変数を、一時的に実数型変数として使う。そのためには、整数型変数をiとすると(double)iと書く(これを、型変換という。型変換は、その場のみ有効である)。可能ならば縞を横に動かしてみる。

 

(2)3種類の刺激を考え、それを順番に呈示する。スペースキーを押すたびに1ずつ増加するような整数型のグローバル変数を用意して、その値に応じてDisplay関数の実行が変わるような仕組みを考えるとよい。便利な小技:このようなグローバル変数の名前をgTrialNumとすると、gTrialNum%3という値はgTrialNum3で割ったときの余り(0,1,2のいずれか)になる。

 

(3)ランダムドット刺激を表示する。点を表示する場合はglBegin( )の中身をGL_POINTSとする。glEndの中でfor文を使うと効率的に点をたくさん定義できる。点の大きさを変えたい場合はglPointSize(実数)という関数を描画前に一度記述する。glVertex2Dの座標値に乱数を代入することでランダムな場所にドットを表示することができる。

Display関数の値を直接書き換えると、随時ドットを書き換えてしまう。そうしたくない場合は、座標値の配列を実数型のグローバル変数として定義して、Display関数以外の関数の中で代入しておく。ヒント:スペースキーを押すたびにランダムドット刺激を変えたい場合は、スペースキーを押したときに実行されるコードが書かれている場所で、rand()を繰り返し使用して、ランダムドットの座標値を決める。

 

(4)スペースキーの代わりに1キーか2キーを押した時に次の刺激が始まるようにする。また、どちらのキーを押したかをprintf関数を使ってコンソール画面に出力する。


7

 

課題6のコメント

 間違いではないが、グローバル変数として使う必要のない場所でグローバル変数を定義して使っている班があった。単一の関数で使う変数、すなわち、キー押しに応じて画像を変えたり、タイマーとして使う変数でない限りは、できるだけローカル変数の方が良い。また、グローバル変数として使う場合には、必ず小文字のgで始める変数名で始めることを心がけることを勧める。小文字のgで始めなくても、関数外で定義すればグローバル変数として扱われ、プログラムとしては動作するけれど、読む人が分かりにくい上に自分でも混乱のもととなるので、なるべく避ける。

 これまた厳密には間違いではないが、glVertex2dの括弧の中はdouble型(実数型)の変数であるので、整数を入力する場合でもできるだけ.0をつける。sin関数も同様であり、この場合に整数を代入すると正しい値は得られなくなるので、可能な限り気をつける。

 また、(double)iという表現がたびたび出てくる。これはiという整数型変数を一時的に実数型として使う型変換(キャストとも呼ばれる)という機能である。この表記が繰り返し出てくると煩瑣なので、新しいdouble型の変数を関数内で定義して、最初に代入して使用すると見た目がすっきりする。定義は、関数の始まりの中括弧{の直後にdouble k;と書いて行う。代入はk=(double)i;と書き、その後は単にkと書くだけで良い。kという変数はここではローカル変数として扱われるので(=関数の中括弧の中で定義されているから)、関数の外ではkという変数を使用することはできない(新しく定義して使うことはできるが、同じ変数名をグローバル変数として使うことはできない。

 

テキストファイルの出力

 課題6(4)ではコンソール(黒い画面)に文字が出力されるが、保存がされないので実験結果を集計するときに困ることになる。そこで、結果をテキストファイルに出力するようにソースコードを変更する。変更のポイントは、(1)テキストファイルを作成するための関数を使ってファイルを作り、(2)そのファイルに文字列を書き、(3)そのファイルを閉じる、ということである。ファイルを作るのはプログラムが起動したときであり、ファイルを閉じるのはプログラムが終了するときである。基本的にこれらの関数はOpenGL関数ではなく、ファイル操作のための基本的な関数を用いる。

 新しいファイルの作成は、main関数の最初の方で行ってみる。

 

    if   ((gTextFile=fopen("result.txt", "a")) == NULL) {

       printf("ファイルresult.txtを開けません。終了します\n");

       exit(0);

    }

    else {

    fprintf(gTextFile,"これから結果が出力されます\n");

以前と同じ

    }

   return 0;

 

fopen( )という関数が、新しくファイルを作るときに使う関数である[16]。最初の引用符の中身が、作りたいファイルの名前であり、”a”は、ファイルの中身をどんどん書き足していく形式                     である。gTextFileは、プログラム内でファイルを識別するための名前であり、ここではグローバル変数として定義する。そのため、ソースコード上部にFILE *gTextFile;と記述する。FILEはファイル型の変数[17]であり、ファイル自体は文字列であるがchar型の配列というわけではないことに注意する。上のコードではif文が使われており、ifの中身は例外的な処理(=まれに生じるエラー、たとえばファイルが破損しているなど)のときに実行される。つまり、正しくファイルを開くことができた場合にはelseの中身が実行される。

elseの中身に、fprintfという関数が使われている。これはprintfと使い方がほぼ同じであるが、コンソールに文字列を出力する代わりに、開いているファイルの中に文字を書き込む。最初の変数がprintfの引数(=括弧の中身)に追加されており、書き込みたいファイル型の変数(ここではgTextFile)の名前を指定する。

プログラムが実行されている間はファイルは開きっぱなしの状態となっているので、ファイルを閉じる処理が必ず必要となる(通常のWordファイルなどと同じである)。ファイルを閉じるのは、ファイルを使う必要がなくなったときに行うのが適切なので、今の場合にはプログラムの終了処理、つまりexit(0);の直前でファイルを閉じる。そのために、終了処理を実行するエスケープキー押しに対する処理が記述されている、Keyboard関数の中を、

 

    if (pKey=='\033') {//escキーを押す

       fprintf(gTextFile, "これからファイルを閉じます\n");//ファイルを閉じる宣言

       fclose(gTextFile);//実際にファイルgTextFileを閉じる

       exit(0);//プログラムの終了

    }

    …その後は同じ…

 

のように書き換える。肝心な部分はfclose関数であり、括弧の中に閉じたいファイル変数名を記述する。これでファイル開閉の基本動作はすべてである。重要な点は、ファイルを閉じた後にファイルを使用すると(=gTextFileを使った操作を行うと)コンピュータは暴走するか、止まるので、fopenfprintffcloseを使うときは、特に気をつける。

 以上のことができたら、とりあえずプログラムを実行してみよう。その後、(インターネットでない)エクスプローラを開き、実行中のプロジェクトが保存されているフォルダを見てみる。そうすると、その中にresult.txtというファイルができているはずなので、それを開く。上記のコードが正しく出来ていれば、中身に「これから結果が出力されます」、「これからファイルを閉じます」の文字がいくつか書かれているはずである。result.txtを開いていてもプログラムは実行できるが、更新された結果を見るためには一度result.txtを閉じて、再度開くことが必要となる。

 

課題7

 押したキーに対応した文字が出力されるように上述のソースコードを変更する。それができたら、試行番号も同じ行に出力できるようにコードを変更する。

 


8

 

エクセルを使ったデータ分析

 課題7のテキストファイル出力では、試行番号および押したキーに対応した文字が出力されるようになっていた。このようなデータをエクセルで集計するための方法を紹介する。まず、ファイルに文字を出力する部分(fprintfの引数)のダブルクオーテーションの部分をなるべく簡略化する。たとえば、1行に出力する文字が2種類であれば、単に二つの数が半角スペースで区切られている状態として出力する(”%d %d\n”のようにする)[18]。数試行実施後、出力ファイルを開き、それをエクセルにコピーして貼り付ける。貼り付けたままの状態では単一の列にすべてデータが書き込まれているので、各数値を各セルに収めるために、ひとつの列が選ばれた状態で、データメニューの、「区切り位置」をクリックし、その後「カンマやタブなどの区切り文字によってフィールドごとに区切られたデータ」を選択して次に進み、「区切り文字」のところの「スペース」にチェックをつける。その後完了ボタンを押すと、単一の列に入っていたデータが複数の列に収納される。

 データの平均値や標準偏差を計算したい場合には、データを条件ごとにまとめてから行うのが便利である。その場合には、並べ変えたいデータ(複数の列)をすべて選択し、特定の列の数値情報を元に並べ替えることができる。その場合、試行番号や条件、反応などがすべて含まれるようにデータを選択し、その後データメニューの「並べ替え」をクリックする。その後、並べ変えに使用したい列を指定すると並べ替えができる。

 平均値の計算は、「関数」と呼ばれる機能を使って行う。まず平均値を表示させたいセルを選び、その状態で数式メニューの「関数の挿入」をクリックする。average関数を選ぶと平均値が計算でき、stdev関数を選択すると標準偏差が計算できる。特定の数字や文字がいくつあるかを計算したい場合にはcountif関数を使う。関数は、セルを選んだ状態で=average(B2:B5)のように半角で文字を直接入力しても使用できる(この場合、B2:B5はデータ範囲)。エクセルは便利であり、関数を記述したセルをコピーして他のセルに貼り付けると、そのセルに対する相対的な位置を保存して再計算が行われる。関数のセルをダブルクリックすれば、現在計算に使用しているデータ範囲が表示される。その状態で色のついた枠の右下をドラッグし、Enterキーを押すとデータ範囲が変更され、再計算が行われる[19]。エクセルでは、相関係数の計算や、t検定などの仮説検定を行うことができ、分散分析なども少しの根気があればできる[20]

 

課題8

残り3回の時間を使い、比較的複雑な実験プログラムを作成する。プログラムは、正弦波の横縞を上または下向きに動かし、運動方向を判断する際の反応時間(類似した先行研究として、Tynan & Sekuler, 1982)を記録するものである。各試行はスペースキーを押すことで開始し、uまたはjキーを押すまで刺激が呈示され、反応時間がミリ秒単位で記録される。縞刺激はスペースキーを押して2秒後に現れるようにする。各試行につき、試行番号、運動方向、運動速度、押したキー、反応時間が出力されるようにする。

プログラムが完成したら、班のメンバー個人を観察者として実験を行い、速度の関数としての反応時間をグラフに描く。速度条件は5水準用意し、計60試行行う。最初の10試行は練習とし、結果の分析には用いない。速度の違いは等間隔である必要はないが、変化幅が大きい方が明快な結果が得られると思う。明らかな外れ値がある場合は、基準を設けて(たとえば、平均値±2標準偏差の範囲に収まらないデータなど)除外しても良い。正しく反応できた試行について、速度条件ごとに平均反応時間を計算する。

課題は、完成したプログラムのソースコードを印刷し、加えて班のメンバーの結果をまとめたものをレジュメとして提出する。ソースコードの中で特に工夫している部分があれば、コメントとして記述してもよい。結果のグラフは、速度の関数としての反応時間のグラフとする。結果の傾向を一言記述する[21]

 

ヒント集

 比較的複雑なプログラムを作成する際の原則は、取り入れるべき内容を一つずつ取り入れてコンパイルし、実際に動くかどうかをこまめに確認しつつ進めることである。ここでは、やりやすい順序の例として、以下のものをあげておく。

 

1.止まっている横縞を表示する

2.スペースキー押しで試行が始まり、uまたはjキー押しで1試行が終了する

3.押したキーおよび反応時間がファイルに出力されるようにする

4.縞を動かし、縞の動く方向が試行ごとにランダムになるようにする

5.縞の動く速度を5水準設定して、低速度の試行から始まり高速度の試行まで順番に実施できるようにする

6.各試行の縞の運動方向、運動速度もファイルに出力できるようにする

7.運動速度の実施順序をランダムにする

8.反応時間、押したキー、運動速度が正しく出力されているかを確認する

9.n試行で自動的に終了するようにし、n=10試行程度でうまく動くようならn=50として実際に実験を行う

 

運動する正弦波の描画

 運動する縞というのは、輝度が画面上の位置と時間の関数として変化する模様である。輝度値Lは、画面上の位置yおよびフレーム数fの関数として以下の式で表すことができる。

L=0.5 sin(2p(y-kf)/l)+0.5

波長lは正弦波の波長(ピクセル単位、100.0程度で良いと思う)、pは円周率、kは運動速度を決める定数でおよそ±5~50の範囲に収まるとちょうど良い速度の変化幅になると思う。とくにkの値の符号は運動方向に対応するので、kは試行ごとにうまく変化するように工夫する必要がある。

 

背景色

 刺激の背景は灰色の方が視覚実験らしくなる[22]ので、背景は灰色にする。背景を灰色に設定する一番容易な方法は、(変更しないと記述している)Init関数の三行目を

 

    glClearColor(0.5, 0.5, 0.5, 1.0);

 

とする(=関数の最初の三つの引数をすべて0.5に設定する)。

 

fprintfの書式

 押すキーはujであるが、ダブルクオーテーションの中身を%cとすると押した文字をそのまま出力できる。ただその場合は、エクセルでの集計が面倒になるので、たとえばuのときには整数の0が、jのときには整数の1が出力されるようにしておくと集計が便利である。運動方向の情報を出力する際も同様であるが、fprintf関数の何番目がどの変数であるかを把握しておく必要がある。

実数型変数の中身が整数の場合、fprintfのダブルクオーテーションの中身を単に%lfとすると小数点以下の0が多くなり出力ファイルが読みにくくなる。そこで、%lfの間に.1というようにピリオドと数字を挟むと、小数点以下の桁数を好きな長さで切り捨てて表示することができる。たとえば%.0lfとすると実質的には整数として出力される。

 

運動速度のランダムな割り当て

あらかじめ準備した要因(ここでは縞の運動速度)の各条件をランダムな順番に実施するためには、配列の要素をランダムに並べかえる関数を使うのが便利である。これはp.6の課題(3)の応用であり、自作することが難しい場合は、(私の自作の)ChangeArray関数というものを使う。この関数を使うと、pMax個の要素をもつ配列pRandArrayの各要素に、0からpMax-1までの整数をランダムに並べ替えたものが代入される。この関数を使用した例は、Tera Stationサーバのprogrammingsource-codesフォルダのrandomizationである。このソースコードの、ChangeArray関数を定義している部分をコピーし、作成中のソースコードの上の方に貼り付けて使用する。

 

引用文献

Tynan, P. D., & Sekuler, R. (1982). Motion processing in peripheral vision: Reaction time and perceived velocity. Vision Research, 22, 61-68.



[1]平成20年度後学期九州大学文学部授業科目「心理学演習I」の配布資料に基づく。

[2]ただし通常のコンパイラは親切でもあるので、実行できない場合にはどこが間違っているか教えてくれる。間違っている個所や実行すると不具合が生じることを、バグという。そのような個所をなくすことはデバッグと呼ばれる。

[3]設定などについては、Tera Stationフォルダのprogrammingを参考にする。以下、脚注では研究室での使用を前提としたことも記述している。

[4]これらはコンパイラによって単に飾られている情報であり、色をつけること自体には特に意味はない。

[5]新しいプロジェクトの作り方は、Tera Stationの、programmingフォルダの中の、c_testを参考にする。

[6]前回のプログラムがない場合は、新規プロジェクトをUSERフォルダに作り、Tera Stationサーバのprogrammingsource-codesif-and-elseの中身をコピーする。

[7]例えば、for(;;)と書くだけでそのプログラムは強制終了するまで止まらない。

[8]英数半角は1バイト文字、日本語などは2バイト文字という言い方をすることもある。

[9]今回は、このソースコードのうちのmain関数の中身を紙に書いて提出する。提出は班で一枚、班のメンバー全員の氏名と課題のタイトルをつけ、日付を書いて授業最後に提出する。

[10]前回のif文とelse文を使ったコードを含むプロジェクトは、Tera Stationサーバのprogrammingフォルダの「H20-shinri-enshu」にある。前回の演習時とはパソコンが違うと思うので、このフォルダを各PCUSERフォルダにコピーし、そのフォルダの「h20-shinri-enshu」の、Microsoft Visual Studio Solutionという種類のファイル(同じ名前の違う種類のファイルがあるので注意する)を開いてから今回の作業を開始する。次回からは同じPCで演習をするので、PCの種類をメモしておくこと。DellPCは同じ型が4台あるので、モニタ上部の番号をあわせてメモする。

[11]中身は、Tera Stationフォルダのprogrammingsource-codesの中のsimple-gl.cppをそのまま開いて(.cppPCによっては見えない場合もある)中身をコピーし、新しく作った先ほどのファイルにコピーする。

[12]実際には、Resize関数の中で、画像中のピクセルと対応がつくような変換が施されている。いかにも3次元CGらしいものを作りたい場合には今の設定を変更する必要がある。

[13]この方法は、画像作成・編集ソフトのイラストレータの描画方法に類似している。

[14]こちらは別の画像編集ソフトであるフォトショップの画像操作方法に類似している。

[15]反対概念はローカル変数であり、特定の関数の中で宣言(定義)される変数のことである。前回までは変数はすべてmain関数の中で定義していたが、これらはすべてローカル変数である。グローバル変数は、同じ名前のものを二つ定義することはできない。それに対しローカル変数は、宣言を、異なる関数内で行う限り何回でも同じ名前を使うことができる。

[16]ファイルを書くだけではなく、読み込むときにも使う。例えば、ビットマップなどの画像ファイルを読むためには、”a”ではなく”rb”を使う。

[17]厳密には変数の中身ではなく、ポインタ変数と呼ばれる、変数がメモリ上に格納される場所を指す変数のことをいう。定義のとき、変数名の前にアスタリスク*をつけるとポインタ変数として定義される。

[18]Visual C++で出力ファイルを確認する簡単な方法は以下の通り。現在実行しているプロジェクトを選んだ状態で、ソリューションブラウザの上のバーのすぐ下にある、「すべてのファイルを表示」と表示されるアイコンをクリックする。そうすると出力ファイルがソリューションブラウザ内のソースコードのアイコンの近くに表示される。それをダブルクリックすれば内容が表示される。これは実験を行うたびにエクスプローラを開く必要がなく、こちらの方が慣れると便利である。

[19]データ範囲が表示されている状態で他の位置をクリックしないように注意する。クリックするとクリックした位置のセルの情報が数式に反映されてしまう。押してしまった場合は、一度Enterを押して、Ctrlzキーを同時に押して一つ前の状態に戻る。

[20]原理をよく知らない場合は、手計算またはエクセルを補助の道具として取り組んでみることを勧める。ただ、因子分析や主成分分析はSPSSSASなどの統計ソフトを使用する方が良いと思う。

[21]ソースコード上部のコメント欄に、班のメンバーの氏名、作成日時を記入しておく。

[22]背景を灰色にする実質的な意味は、白黒の縞の平均輝度は中間的な灰色であるということに関係している。平均輝度を全体的にそろえておき、明るさに対する瞳孔などの反応をなるべく統制しておくという意味がある。


戻る