まず以下のように定義する。
struct IntValue ... TraitMyDisp と TraitMyPow 実装
struct FloatValue ... TraitMyPow のみ実装
// これをimplementしているときは表示できる trait TraitMyDisp{ fn disp(&self); } // これをimplementしているときは値を二乗する trait TraitMyPow{ fn get_pow2(&self)->f32; } ////////////////////////////////////////// //////////////////////////////////////////struct IntValue{ value: i32 } impl TraitMyDisp for IntValue{ fn disp(&self){ println!("{}",self.value); } } impl TraitMyPow for IntValue{ fn get_pow2(&self)->f32{ ( self.value*self.value ) as f32 } }////////////////////////////////////////// //////////////////////////////////////////struct FloatValue{ value:f32 } impl TraitMyPow for FloatValue{ fn get_pow2(&self)->f32{ ( self.value*self.value ) as f32 } }
まず、以下のようにただ<T>を渡してもコンパイルは通らない。
// C++だと T に get_pos2があればビルドが通るが // Rustでは valueに対して何をできるのかを指定しなければならない fn call<T>(value:T){ println!("call {}",value.get_pow2()); // NG T にget_pow2が必要なんて聞いてない }
fn main(){ // TraitMyDisp , TraitMyPow の両方が実装済み let i_val = IntValue{value:2}; // TraitMyPow のみが実装済み let f_val = FloatValue{value:2.0}; call(i_val); }
「型Tを取る」ではなく、「get_pow2()を持つ型Tを取る」と記述しなければいけない。
// T は TraitMyPow を実装していなければならない fn call< T : TraitMyPow >(value:T){ println!("call {}",value.get_pow2()); }
トレイトは + で複数指定できる。
T は+で指定されたすべてのトレイトに対応していなければならない。
たとえその関数の中で使っていなくても、実装だけはしていなければならない。
// <T:トレイト + トレイト> のように+で複数指定できる fn call_both<T:TraitMyPow + TraitMyDisp>(value:&T){ // TraitMyDisp::dispは使っていないが、トレイト境界を指定しているので // Tがdispに対応していないならコンパイルできない(たとえ使っていなくても) println!("call {}",value.get_pow2()); } fn main(){ // TraitMyDisp , TraitMyPow の両方が実装済み let i_val = IntValue{value:2}; // TraitMyPow のみが実装済み let f_val = FloatValue{value:2.0}; call_both(&i_val); // OK // call_both(&f_val); // NG FloatValueのためのTraitMyDispはimplしていない }
Tに渡される型が + 演算できるかどうかはわからない。
したがって、例え自分がプリミティブな数値型しか与えるつもりがなくても、コンパイラはそんなことは知らないので「TはAddが可能である」ことを教えなければいけない。
// Addモジュールを使う use std::ops::Add; ///////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////
// add関数の定義 // Tの必須の振る舞いとしてAddを指定。これで operator + が使える fn add< T : Add<Output=T> >( v1:T , v2:T ) -> T { v1 + v2 }
fn main(){ let val:i32 = add( 3,4); }
Rustには継承はないがポリモーフィズムはある。C++やJavaのようなオブジェクト指向ではポリモーフィズムの実現のために継承があるようなイメージがあるので、この関係が奇妙に見える。
継承とは「鷹も燕も"鳥"の特性をすべて持ち、その他に若干の差異がある同種のモノである」という考え方をする。だからどうしても子クラスが肥大化していく。そのあたりを嫌った結果らしい。
// 振る舞い trait Flying{ fn fly(&self); }
// 構造体 鷹 struct Hawk{ weight:f32, speed:f32, } // トレイトのR実装 impl Flying for Hawk{ // メソッド fn fly(&self){ println!("鷹が飛ぶ {} km/h \n",self.speed ); } }
// 構造体 ハチドリ struct Hummingbird{ weight:f32, speed:f32, } // トレイトのR実装 impl Flying for Hummingbird{ // メソッド fn fly(&self){ println!("ハチドリが飛ぶ {} km/h\n", self.speed); } }
fn main() { // スタック上に確保する固定長配列は [T;N]の形をしている // T ... Type , N ... 個数 let ary:[ Box<dyn Flying> ; 2 ] = [ // Boxはヒープにメモリを確保する。 Box::new(Hawk{weight:1100.0,speed:190.0}), // アカオノスリ Box::new(Hummingbird{weight:20.0,speed:98.0}) // ハチドリ ]; ary[0].fly(); ary[1].fly(); }
C++の継承が「Birdの特性を持った鷹を実装」という形であるのに対し、
Rustのtraitは「Flyという振る舞いを、鷹や燕の特性に付与する」というような発想をすると、現時点ではとらえている(下図、現時点の解釈)
C++のオブジェクト指向だと飛べないはずのペンギンもflyを持たざるを得ないが、Rustのtraitであればペンギンにはflyを付与しなければよいだけの話だということになる。
配列にはいくつか種類があるらしい。
・[T;N] ... スタック上に固定長配列
・Vec<T> ヒープ上の可変長配列
・Box<[T;N]>ヒープ上に固定長配列
注意点として、基本的に配列の要素は全て初期化しなければならない。可変長配列でcapacityを指定できるVecはともかく、それ以外をメモリ確保だけして初期化したくない場合には、std::mem::MaybeUninitを使わなければならない。これはunsafeで囲まなければならない。
fn main() { //////////////////////////////////////////// // 固定長配列(スタック)
let mut ary1:[i32;5]=[1,2,3,4,5]; for a1 in ary1{ print!("{} ",a1); } println!("");
// 初期化しないバージョン let mut ary2:[i32;5] = unsafe { std::mem::MaybeUninit::uninit().assume_init() }; for a2 in ary2{ println!("* {} ",a2); } println!("\n");
//////////////////////////////////////////// // 可変長配列
let mut vec1:Vec<&str> = vec!["a", "b", "c"]; vec1.push("d"); for v1 in vec1{ print!("{} ",v1); } println!("");
// 初期化しないバージョン let mut vec2:Vec<&str> = vec![]; vec2.push("d"); for v2 in vec2{ print!("* {} ",v2); } println!("\n");
//////////////////////////////////////////// // 固定長配列(ヒープ)
let mut box1:Box<[f32;5]> = Box::new([10.5 , 11.5 , 12.5 , 13.5 , 14.5]); for b1 in box1.iter(){ print!("{} ",b1); } println!("");
// 初期化しないバージョン let mut box2:Box<[f32;5]> = Box::new(unsafe { std::mem::MaybeUninit::uninit().assume_init() } ); for b2 in box2.iter(){ println!("* {} ",b2); }
}
初期化しなければ不正な値が入っていることを確認できた。
1 2 3 4 5
* 112
* 0
* -580595040
* 367
* -580614544
a b c d
* d
10.5 11.5 12.5 13.5 14.5
* 2
* 0
* -1030514000000000000
* 0.000000000000000000000000000000000000000000514
* 0.000000000000000000000000000000000000000000006
構造体関連で、struct,impl,trait の役割は以下(だと考えられる)
本当はポリモーフィズムをやりたかったがどうしても理解が追い付かなかったので省略。
// #[derive(Debug)] をつけるとprintln!でそのまま表示できるようになる #[derive(Debug)] // 構造体の定義。 struct Rectangle{ width:i32, height:i32, }
fn main() { // 初期化なしでは定義できない //let mut rr = Rectangle{}; let mut rr = Rectangle{width:0,height:0}; // mut をつけているので代入できる rr.width = 20; rr.height = 20; // #[derive(Debug)] をつけているので {:?} で表示できる println!("{:?}",rr); }
// 構造体の定義 struct Rectangle{ width:i32, height:i32, }
// implの中でメソッドや関連関数を定義 impl Rectangle{ // Rectangleのメソッドを定義 メソッドはselfをとる。 selfがないなら関連関数 fn area(&self)->i32{ return self.width * self.height; } }
fn main() { // インスタンス生成 let mut rr = Rectangle{width:0,height:0}; rr.width = 20; rr.height = 20; // メソッド呼び出し println!("{}",rr.area() ); }
implの中に&selfをとらない関数を定義すると関連関数になる。
新規インスタンスを作成する関連関数を定義する場合、名前をnewにするのが一般的らしい。
呼び出しは 構造体名::関連関数名() となる。
// 構造体の定義 struct Rectangle{ width:i32, height:i32, } impl Rectangle{ // 関連関数。 &selfをとらない。 // Rectangle::new で呼び出す。 // 新規インスタンス生成関連関数は「new」にする習慣がある fn new()->Rectangle{ return Rectangle{width:20,height:30}; } // メソッド fn area(&self)->i32{return self.width * self.height;} }
fn main() { // 関連関数でインスタンス生成 let mut rr = Rectangle::new(); println!("{}",rr.area() ); }
implをbehavior_calc_areaの実装とすることで、areaメソッドの実装を強制することができる。
// 振舞いの定義 trait behavior_calc_area{ fn area(&self)->f32; }
// 構造体の定義 struct Rectangle{ width:i32, height:i32, } // トレイトのR実装 impl behavior_calc_area for Rectangle{ // メソッド fn area(&self)->f32{ return (self.width * self.height) as f32; } }
struct Triangle{ width:i32, height:i32, } // トレイトのR実装 impl behavior_calc_area for Triangle{ // メソッド fn area(&self)->f32{ return (self.width * self.height) as f32 / 2.0 ; } }
fn main() { // インスタンス生成 let t:Triangle = Triangle{width:10,height:5}; let r:Rectangle = Rectangle{width:10,height:5}; println!("{}\n",t.area() ); println!("{}\n",r.area() ); }
基本はそんなに複雑ではないのだが、呼び出すときに暗黙型変換してくれないらしく、明示的型変換(as f32) を入れないといけないらしい。
// 関数定義 // 値渡し fn add(a:f32,b:f32)->f32{ return a+b; } fn main() { // 関数呼び出し // 引数に整数を入れてもaddのf32に暗黙変換できないらしいので // as f32 で実数に型変換する let k = add(3 as f32,5 as f32); println!("k= {}",k); }
不変参照で渡すときは渡す側と受ける側で&をつける。
// デフォルトで不変参照。C++でいうところのconst。kの内容を変更できない fn add2(k: &i32)->f64{ // &mutでないときは *k としなくていいらしい return (k + 2) as f64; }
fn main() { let a:i32=3; // mutがついていないので変更不可 println!(" {}", add2(&a) ); // aの定義時にmutをつけていないのでここもただの& }
可変参照は型名の前に&mut をつける。
呼び出す側でも&mutが必要。
さらにこの値を使うときは参照外しとして*をつけなければならない。もっとほかの記号はなかったのか。
// &mut で可変参照 fn swap(i:&mut i32,j:&mut i32){ let k:i32; // &mutの時は頭に*をつけて *i とする k = *i; *i = *j; *j = k; }
fn main() { let mut a:i32=3; let mut b:i32=8; swap(&mut a,&mut b); println!("{} {}",a,b); }
C++のテンプレートのようなgenericsという機能があるが、ただ<T>としただけでは動かない。printlnすらできない。いずれ詳しく調べる。
https://www.koderhq.com/tutorial/rust/generics/
use std::ops::Mul; fn square<T: Mul<Output = T> + Copy >(val:T)->T{ return val*val; }
use std::fmt::Display; fn console_log<T: Display> (x: T) { println!("{}", x); }
fn main() { println!("{}", square(3) ); console_log( 3 ); }
fn main() { for x in 1..=9{ for y in 1..=9{ print!("{} ",x*y); } println!(""); } }
なんとRustのforにはCのforのようにカウンタを使う記述法がない。その手のものが必要な時はwhileを使う。さらにRustにはインクリメント演算子++がない。+=はあるのでそれを使う。
なぜ++がないのかよくわからないが、コンパイラが進化した今inc命令とadd命令をプログラマが区別する必要はないのでそのために言語仕様を複雑にする必要はないとかそんな感じではないだろうか(適当)。
fn main() { let mut k = 0;// 変数はデフォルトで変更不可なのでmutをつける while k < 10{ println!("k=={}",k); k+=1; // Rustには++がない } }
ifはCとかなり近い。ただし()で囲う必要がない。
fn main() { let k = 3; if k == 1{ println!("k==1"); } else if k==2{ println!("k==2"); } else{ println!("other k"); } }
特に違うものとして、Cの三項演算子に該当するものがifで表記するようになっている。
fn main() { let k = 2; let msg = if k%2!=0 {"odd"}else{"even"}; println!("{}",msg); }
いろいろとCのswitchに近いがbreakしないところが特に違う。あとカンマ区切り。
fn main() { let i = 2; // switchに近い。 // ただし、matchの中で、「取りうるすべての可能性について」処理する必要がある match i{ // Cと違いbreakしなくてもいい。ほかの式が実行されることはない 3 => println!("three"), // 複数の指揮を実行するときは{}で括る 4 => { println!("four"); println!("FOUR") }, // 取りうるすべての可能性を記述しなければいけないため // ワイルドカード _ を使ってCのdefaultと同じことをする。 _ => println!("others"), }; }
matchの結果をそのまま変数に渡すこともできる。
fn main() { let i = 3; let msg:&str = match i{ 3 => "three", 4 => "four", _ => "others", }; println!("{}",msg); }
// 入出力を使う use std::io; fn main() { // 変数 buffer を定義 let mut buffer = String::new(); // bufferへ文字列入力 io::stdin().read_line(&mut buffer); // bufferを表示 println!("{}",buffer); }
// 入出力を使う use std::io; fn main() { // 変数 buffer を定義 let mut buffer = String::new(); io::stdin().read_line(&mut buffer); //標準入力から入力 // 型指定 :i32 が必要 // read_lineから読み込んだ文字列に対してはtrim()が必要 let val:i32 = buffer.trim().parse().unwrap(); // bufferを表示 println!("{}",val); }
// ファイル入力を使う use std::{fs::File, io::Read}; fn main() { let filename = "test.txt";
// ファイル入力を使う let mut file = File::open(filename).expect("file not found"); // 変数 buffer を定義 let mut buffer = String::new(); // ファイルから入力 file.read_to_string(&mut buffer).expect("something went wrong reading the file"); // ファイルを閉じる方法 // 方法1 スコープを抜けるとdropが呼ばれる(C++のデストラクタのようなもの) // 方法2 明示的にdropを呼び出す drop(file); // 明示的にdropを呼び出す // bufferを表示 println!("{}",buffer); }
use std::fs::File; // write!やwrite_allを使うのに必要 use std::io::Write; // 戻り値は std::io::Result<()> 型 // 最後に OK(()) を返す。 // これを入れないとwrite_allなどが使えない fn main()-> std::io::Result<()> { let filename = "test.txt"; // ファイル入力を使う let mut file = File::create(filename)?; // 末尾の ? は例外処理のようなもの関連 // ただしRustには例外がないのであくまでそれっぽく振る舞うだけ。 // 変数 buffer を定義 let _buffer = "hello".to_string(); // ファイルへ書き出し file.write_all(_buffer.as_bytes())?; // こっちも使える // write!(file,"hello2")?; // ファイルを閉じる drop(file); // std::io::Result<()> 型の場合 Ok(()) }
CargoはRustのビルドシステム及びパッケージマネージャ。
cargo buildはカレントディレクトリにsrcディレクトリがあることを想定する。
デバッグビルド
リリースビルド
Rustではパッケージのことを「クレート」というらしい。Cargo.tomlの[dependencies]にモジュール名とバージョンを記述する。
[package]
name = "myproj"
version = "0.1.0"
edition = "2021"
[dependencies]
regex = "1.6.0"
use regex::Regex; fn main() { let re = Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap(); println!("Did our date match? {}", re.is_match("2014-01-01")); }
cargo buildすると勝手にcrates.ioからクレートを取得するので上記の記述以外にすることはない。
VSCodeでデバッグできるのだが、私の環境では、クレートを使っているとVSCode関連で失敗する。
細かく検証していないが上記regexを使った場合VSCodeからは実行すらできない。PowerShellからは動く。
どれだけ検索しても対策が見つからなかったがcodelldbのバージョンによっては起こる?らしい。
プログラミング言語Rustをやってみる。
Windowsでは、
https://learn.microsoft.com/ja-jp/windows/dev-environment/rust/setup
のいうとおりにやればビルドできるようになる。以下要約。
1.VC++をインストール(必須環境)
2.Rustをインストール(本体)
https://www.rust-lang.org/tools/install
3.VSCodeをインストール(エディタ・開発環境)
https://code.visualstudio.com/
4.VSCodeの拡張機能をインストール
rust-analyzer
CodeLLDB
プロジェクト生成にはGUIがないのでコマンドラインからcargoコマンドを叩く。
まずはプロジェクトを作成するディレクトリへ行き、
cargoコマンドでプロジェクトを作成する。「cargo new プロジェクト名」
作成したプロジェクトのディレクトリを引数にVSCodeを起動するか、VSCodeを開きディレクトリをドラッグ&ドロップする。
コマンドラインから開く場合(VSCodeのパスが通っている場合)
code first_rust_project
VSCodeにD&Dしてもいい
src\main.rs ... ソースコード本体
Cargo.toml ... プロジェクトの設定ファイル
VSCodeで、 [実行]>[デバッグの開始] 。
初回のみエラーメッセージのようなものが出るが、「OK」「はい」として、launch.jsonが出たら何もせず閉じてもう一度デバッグの開始。
以下引用:
CodeLLDB 拡張機能とデバッガーで初めてアプリを実行すると、"起動構成が提供されていないため、デバッグを開始できません" というダイアログ ボックスが表示されます。 [OK] をクリックすると、"このワークスペースで Cargo.toml が検出されました。 ターゲットの起動構成を生成しますか?" という 2 番目のダイアログ ボックスが表示されます。 [はい] をクリックします。 次に、launch.json ファイルを閉じて、もう一度デバッグを開始します。
近い点をより大きく描画することで頂点がカメラに近づく表現をする。
#pragma warning(disable:4996) #pragma comment(lib,"glew32.lib") #include <gl/glew.h> #include <GL/glut.h> #include <fstream> #include <sstream> #include <vector> #include <algorithm> #include <array> typedef std::array<GLfloat,3> point_t; //モデルの座標用 x y z typedef std::array<GLfloat,4> color_t;//テクスチャ座標用 r g b a ////////////////////////////////////////// ////////////////////////////////////////// //////////////////////////////////////////
const char* vshader_program = R"( #version 460 core layout(location = 0) in vec3 vtx; layout(location = 1) in vec4 color; out vec4 vColor; uniform mat4 ModelViewMatrix; uniform mat4 ProjectionMatrix; // カメラ座標 uniform vec4 campos; void main() { gl_Position = ProjectionMatrix * ModelViewMatrix * vec4(vtx, 1.0); // 近いものほど大きく見せる float size = 50 / distance(campos,gl_Position); vColor = color; gl_PointSize = size; // 頂点のサイズ(ピクセル) } )";
////////////////////////////////////////// ////////////////////////////////////////// //////////////////////////////////////////
const char* fshader_program = R"( #version 460 core out vec4 FragColor; in vec4 vColor; void main() { FragColor = vColor; } )";
///////////////////////////////// //バッファとデータ GLuint vertexbufferName; GLuint colorbufferName; const GLuint v_count=100; GLuint VertexShaderID; void prepare_vertex_shader(); GLuint FragmentShaderID; void prepare_fragment_shader(); GLuint ProgramID; void link_program(); void init(void); void display(void); void timer(int value) { // 再描画 glutPostRedisplay(); // 100ミリ秒後にtimer関数を引数0で自分自身を呼び出す glutTimerFunc(100, timer, 0); }
int main(int argc, char* argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGBA); glutCreateWindow(argv[0]); glutDisplayFunc(display); glutReshapeWindow(300, 300); glewInit(); // glewの初期化 init(); glutTimerFunc(100, timer, 0); glutMainLoop(); return 0; } void prepare_buffers(); void init(void) { //頂点データと色情報の作成 prepare_buffers(); //頂点シェーダの準備 prepare_vertex_shader(); //フラグメントシェーダの準備 prepare_fragment_shader(); //プログラムのリンク link_program(); } //////////////////////////////////////////////////
void prepare_buffers() { ////////////////////////////////////////// ////////////////////////////////////////// std::vector< point_t > vertex; std::vector< color_t > color; float scale=50000.0f; for (size_t i = 0; i < v_count; i++){ float x = (rand() % (int)scale) * (1.0f / scale) - 0.5; float y = (rand() % (int)scale) * (1.0f / scale) - 0.5; float z = (rand() % (int)scale) * (1.0f / scale) - 0.5; vertex.emplace_back(point_t{ x*1.5f,y*1.5f,z*50 }); float r = (rand() % (int)scale) * (1.0f / scale); float g = (rand() % (int)scale) * (1.0f / scale); float b = (rand() % (int)scale) * (1.0f / scale); color.emplace_back(color_t{ r,g,b,1 }); } ////////////////////////////////////////// ////////////////////////////////////////// glGenBuffers(1, &vertexbufferName); glBindBuffer(GL_ARRAY_BUFFER, vertexbufferName); glBufferData(GL_ARRAY_BUFFER, 3 * v_count * sizeof(GLfloat), vertex.data(), GL_STATIC_DRAW); glGenBuffers(1, &colorbufferName); glBindBuffer(GL_ARRAY_BUFFER, colorbufferName); glBufferData(GL_ARRAY_BUFFER, 4 * v_count * sizeof(GLfloat), color.data(), GL_STATIC_DRAW); }
void prepare_vertex_shader() { ////////////////////////////////////////////// // シェーダを作ります // (作るといっても宣言みたいなもの) VertexShaderID = glCreateShader(GL_VERTEX_SHADER); //////////////////////////////////////////// // ファイルから頂点シェーダを読み込みます。 // 注意 ここはSTLのifstreamとかstringstreamの使い方の話で、 // OpenGL命令は一つも無い。 std::string VertexShaderCode = vshader_program; //////////////////////////////////////////// // 頂点シェーダをコンパイルします。 //printf("Compiling shader : %s\n", vertex_file_path); char const* VertexSourcePointer = VertexShaderCode.c_str(); glShaderSource(VertexShaderID, 1, &VertexSourcePointer, NULL); glCompileShader(VertexShaderID); //////////////////////////////////////////// // エラーチェック GLint Result = GL_FALSE; int InfoLogLength; // 頂点シェーダをチェックします。 glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result); glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); if (Result == FALSE) { std::vector<char> VertexShaderErrorMessage(InfoLogLength); glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]); fprintf(stdout, "%s\n", &VertexShaderErrorMessage[0]); } }
void prepare_fragment_shader() { ///////////////////////////////////////////// // シェーダを作ります。 FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER); ///////////////////////////////////////////// // ファイルからフラグメントシェーダを読み込みます。 std::string FragmentShaderCode = fshader_program; ///////////////////////////////////////////// // フラグメントシェーダをコンパイルします。 //printf("Compiling shader : %s\n", fragment_file_path); char const* FragmentSourcePointer = FragmentShaderCode.c_str(); glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer, NULL); glCompileShader(FragmentShaderID); GLint Result = GL_FALSE; int InfoLogLength; ///////////////////////////////////////////// // フラグメントシェーダをチェックします。 glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result); glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); if (Result == GL_FALSE) { std::vector<char> FragmentShaderErrorMessage(InfoLogLength); glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]); fprintf(stdout, "%s\n", &FragmentShaderErrorMessage[0]); } }
void link_program() { GLint Result = GL_FALSE; int InfoLogLength; //////////////////////////////////////// // プログラムをリンクします。 fprintf(stdout, "Linking program\n"); ProgramID = glCreateProgram(); glAttachShader(ProgramID, VertexShaderID); glAttachShader(ProgramID, FragmentShaderID); glLinkProgram(ProgramID); //////////////////////////////////////// // プログラムをチェックします。 glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result); glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength); std::vector<char> ProgramErrorMessage((std::max)(InfoLogLength, int(1))); glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]); fprintf(stdout, "%s\n", &ProgramErrorMessage[0]); }
void display(void) { glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); glLoadIdentity(); //glTranslated(0.5, 0.0, 0.0);//平行移動 glUseProgram(ProgramID); GLfloat mfproj[16]; GLfloat mftran[16]; mfproj[0] = 2.26961231; mfproj[1] = 0.0; mfproj[2] = 0.0; mfproj[3] = 0.0; mfproj[4] = 0.0; mfproj[5] = 2.41421366; mfproj[6] = 0.0; mfproj[7] = 0.0; mfproj[8] = 0.0; mfproj[9] = 0.0; mfproj[10] = -1.00020003; mfproj[11] = -1.00000000; mfproj[12] = 0.0; mfproj[13] = 0.0; mfproj[14] = -0.0200020000; mfproj[15] = 0.0; static float z = -10.0; z += 0.2; mftran[0] = 1.0; mftran[1] = 0.0; mftran[2] = 0.0; mftran[3] = 0.0; mftran[4] = 0.0; mftran[5] = 1.0; mftran[6] = 0.0; mftran[7] = 0.0; mftran[8] = 0.0; mftran[9] = 0.0; mftran[10] = 1.0; mftran[11] = 0.0; mftran[12] = 0.0;//x mftran[13] = 0.0;//y mftran[14] = z;//z mftran[15] = 1.0; GLint proj = glGetUniformLocation(ProgramID, "ProjectionMatrix"); GLint modl = glGetUniformLocation(ProgramID, "ModelViewMatrix"); glUniformMatrix4fv(proj, 1, GL_FALSE, mfproj); glUniformMatrix4fv(modl, 1, GL_FALSE, mftran); GLint campos = glGetUniformLocation(ProgramID, "campos"); GLfloat fcp[4] = { 0,0,z }; glUniform3fv(campos,1, fcp); //gl_PointSizeを有効にする glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); // 頂点バッファ glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, vertexbufferName); glVertexAttribPointer( 0, // 属性0 3, // 1要素の要素数 x y z GL_FLOAT, // タイプ GL_FALSE, // 正規化しない(データが整数型の時) 0, // ストライド (void*)0 // 配列バッファオフセット ); // カラーバッファ glEnableVertexAttribArray(1); glBindBuffer(GL_ARRAY_BUFFER, colorbufferName); glVertexAttribPointer( 1, // 属性1 4, // 1要素の要素数 r g b a GL_FLOAT, // タイプ GL_FALSE, // 正規化しない(データが整数型の時) 0, // ストライド (void*)0 // 配列バッファオフセット ); // 5頂点を描く glDrawArrays(GL_POINTS, 0, v_count); glDisableVertexAttribArray(0); glDisableVertexAttribArray(1); glDisableVertexAttribArray(2); //////////////////////////////////////// //////////////////////////////////////// glFlush(); }