Graphics – DECODE https://decode.red/blog data decode, decoder or decoded ... design of code Mon, 15 Dec 2025 06:15:00 +0000 ja hourly 1 https://wordpress.org/?v=4.7.29 Vector Field ../../../202003141086/ Sat, 14 Mar 2020 02:54:22 +0000 ../../../?p=1086 「大きさ」と「向き」の二つの量をもつベクトル。3Dグラフィックを扱うとき必ず出てきますが、もう少し勉強しようと数学のベクトル解析という分野を学び始めました。
しかし数式ばかりでなかなか学んだ感が得られなかったので、ベクトル場という部分について理解を深めるために、Processingでプログラムを作ってみました。

参考) https://medium.com/@vladisluka/vector-fields-in-processing-779516b15141

上が基本的なベクトル関数

→f = (x, y)
の例。
Cellを作り、Cell単位でベクトルを表現しています。
「大きさ」は長さとしてCellに入る程度に丸めています。また「向き」によって色を変えてます。
こうやってみると「向き」というのは情報量が多いことが実感できます。

int len = 16;

void setup () {
  size(640, 640);
  background(0);
  
  colorMode(HSB, 360);
  
  int cols = floor(width / len);
  int rows = floor(height / len);
 
  for (int i = 0; i < cols; i++) {
    for (int j = 0; j < rows; j++) {
      Cell cell = new Cell(i, j);
      
      pushMatrix();
      strokeWeight(2);
      stroke(degrees(cell.arg)+180, 360, 360);
 
      translate((i + 0.5) * len, (j + 0.5) * len);
      rotate(cell.arg);
      line(cell.mag*2, 0, -cell.mag*2, 0);
      translate(len/2, 0);
  
      float a = radians(150);
      float x1 = 5 * cos(a);
      float y1 = 5 * sin(a);
      
      line(0, 0, x1, y1);  
      line(0, 0, x1, -y1);
        
      popMatrix();    
    }
  }
}
class Cell { 
  float arg;
  float mag;
  Cell (int i, int j) {
    float x = (i + 0.5) * len;
    float y = (j + 0.5) * len;   
    x = map(x, 0, width, -3, 3);
    y = map(y, 0, height, -3, 3);
    
    //float u = x;
    //float v = y;
    
    //float u = x * x;
    //float v = 0.5 * y;
    
    float u = cos(x);
    float v = sin(y);
  
    PVector vec = new PVector(u, v);  
    mag = vec.mag(); 
    arg = vec.heading();
  }
}

→f = (x*x, 0.5*y)

→f = (cos(x), sin(y))

様々なデータの可視化にも有用ですが、コンピータグラフィックを使ったアートにも威力を発揮しそうです。

]]>
3D Bezier ../../../202001301070/ Thu, 30 Jan 2020 13:30:49 +0000 ../../../?p=1070 ベジエ曲線というと、3点以上の点を結んだ線分を等分した点を、さらに結んだ線から作る曲線ですが、コンピュータグラフィックによく使われます。平面上に曲線を描くことが多いですが、ここでは3D空間に描き、それをさまざまな角度から眺めてみたいと思います。

環境: Processing 3.3.7 / macOS High Sierra

import peasy.*;
PeasyCam cam;

PVector[] pt = new PVector[5];
int step = 20;
void setup(){
  size(800, 800, P3D);
  colorMode(HSB, 1);
  cam = new PeasyCam(this, 800);
  pt[0] = new PVector(0, 0, 0);
  pt[1] = new PVector(width, 0, 400);
  pt[2] = new PVector(width, height, 0);
  pt[3] = new PVector(0, height, 400);
  pt[4] = new PVector(0, 0, 800);
}
void draw(){
  background(0);
  for(int i=0;i<=step;i++){  
    PVector[] mp = pt;
    while(mp.length > 1){
      PVector[] vt = new PVector[mp.length-1];
      for (int m=0; m<mp.length-1; m++){
        vt[m] = PVector.sub(mp[m+1],mp[m]);
        vt[m].mult(float(i) / float(step));
        vt[m].add(mp[m]);
      }  
      stroke(float(vt.length) / float(pt.length), 1, 1, 1);
      for (int n=0; n<vt.length-1; n++){
        strokeWeight(1);
        line(vt[n].x, vt[n].y, vt[n].z, vt[n+1].x, vt[n+1].y, vt[n+1].z);
      }
      mp = vt;
    }
  }
}



下記書籍のベジエ曲線のコードを参考にpeasyを使って3D化したものです。peasyは最近よく使うのですが、マウスでぐりぐりしながら簡単に3Dオプジェクトをさまざまな角度から見ることができるので、とても便利です。

「数学から作るジェネラティブアート」(技術評論社)

]]>
3D Turtle Graph ../../../201912271064/ Fri, 27 Dec 2019 06:01:50 +0000 ../../../?p=1064 シンプルな手順で複雑なグラフィックが描画できるタートルグラフ。座標を与えてラインを描画する方法でなく、描く主体に指示をして描画します。この主体をタートルと呼び、2Dではその動きが比較的簡単にイメージできるのですが、3Dとなると上下左右の回転を意識する必要があり、なかなか頭を使います。自分主体の回転なので、角度の考え方は以下の過去記事が参考になります。

../../../20171230780/ 「クォータニオン」

ここでは飛行機などの動きでよく用いられる、ロール、ピッチ、ヨーの考え方で動かしてみました。

参考) https://forum.processing.org/two/discussion/20706/#Comment_104904

ロールは、機体の進行方向を軸にした回転、ピッチは上下(Tilt up,down)、ヨーは左右(Pan)で、ここではロールとヨーだけ使います。

import peasy.*;
PeasyCam camera;

PVector v;
int yaw  = 0;
int roll = 0;

void setup() {
  size(1200, 1200, P3D);
  camera = new PeasyCam(this, 0, 0, 0, 1000);
}

void draw() {
  background(111);
  colorMode(HSB);

  v = new PVector(0, 0, 0);
  yaw = 0;
  roll = 0;
  
  pushMatrix();
  for(int i=0;i<255;i++){
    rollAngle(10);
    stroke(i, 255, 255);
    forward(5);
    yawAngle(90);
    forward(200);
    yawAngle(-90);
    forward(5);
    yawAngle(-90);
    forward(200);
    yawAngle(90);
  }
  popMatrix();
}
void yawAngle (float a) {
  yaw += a;
}
void rollAngle (float a) {
  roll += a;
}
 
void forward(  float len  ) {
 
  float psi = radians(yaw);
  float phi = radians(roll);
 
  PVector prev =  v.copy();
 
  v.x =prev.x+ len * cos(phi) * sin(psi);
  v.y =prev.y+ len * sin(phi) * sin(psi); 
  v.z =prev.z+ len * cos(psi);
 
  line (prev.x, prev.y, prev.z, v.x, v.y, v.z) ;
}

ロールで傾けて、ヨーで向きを変えて、これを繰り返していますが、動きをわかりやすくするためラインの色を赤から順に変化させています。

まずは簡単な例でイメージトレーニングでした。

]]>
OpenGL Shading Language (4) ../../../201908161025/ Fri, 16 Aug 2019 07:08:15 +0000 ../../../?p=1025 最近またGLSLを復習する目的で過去のコードを眺めていたところ、ProcessingでGLSLを動かす環境が、WebGLのGLSLSandboxの再現しやいと思い、試してみました。

GLSL Sandbox

上記サイトのコードを使って再現します。

環境: Processing 3.3.7 / Mac
fshader.glsl

uniform float time;
uniform vec2 mouse;
uniform vec2 resolution;
 
const float PI = 3.14159265358979;
 
const float num = 2.0;
 
void main( void ) {
 
	vec2 pos = ( gl_FragCoord.xy / resolution.xy );
	
	vec3 color = vec3(0.05, 0.05, 0.2);
	
	float r, g, b, cl;
 
	cl += 0.5/abs(length(vec2(
		pos.x-0.5+cos(time*0.3)*0.1 - mouse.x * 0.1,
		pos.y-0.5+sin(time*0.3)*0.1 + mouse.y * 0.1
		))-0.3);
	r = cl * color.x;
	g = cl * color.y;
	b = cl * color.z;
 
	gl_FragColor = vec4(r, g, b, 1.0 );
}

mouseの動きで変化をつける部分を追加しました。
このフラグメントシェーダプログラムをProcessingから呼び出します。
このブログラムと同じ階層にdataフォルダを作りその中にシェーダプログラムをおきます。
GraphicsサンプルのLowLevelGLVboSeparateなどが参考になります。
(modes/java/examples/Demos/Graphics/LowLevelGLVboSeparate)

PShader sh;

void setup() {
  size(400, 400, P2D);
  sh = loadShader("fshader.glsl");
}

void draw() {
  sh.set("time", millis() / 1000.0);
  sh.set("resolution", (float)width, (float)height);
  sh.set("mouse", (float)mouseX/(float)width, (float)mouseY/(float)height);
  shader(sh);
  rect(0, 0, width, height);
}

実行結果

当然ですが、GLSL Sandboxと違って、バーテックスシェーダも使えます。
グラフィックアートを表現する方法として、ProcessingではGLSLに頼らなくてもかなりのことはできるのですが、GLSLという並列プログラミングの手法のトレーニング環境として興味深いものと思っています。

]]>
Image J ../../../20190105941/ Sat, 05 Jan 2019 11:23:01 +0000 ../../../?p=941 生物学の世界ではデファクトスタンダードの解析ツールといわれているImage J。

https://imagej.nih.gov/ij/

画像の細胞の数を数えたり専門的な用途向けと思いきや、一般画像処理も意外と簡単にできるようでしたので、テストしてみました。
テストは単純な色違いの図形の数を数えるものです。

テスト画像はScratchでつくりました。(結構こういうの簡単にできるんですよね)

赤、青、緑のそれぞれの図形の数を数えたいと思います。
参考サイト1) で紹介されていたCell Counterというプラグインでテストしようとしたのですが、Macでは、「Gatekeepr Path Randomization」のセキュリティ機能の影響でうまくプラグインを認識してくれませんでした。Linuxではうまくプラグインが認識し動作したのですが、数をカウントできなかったため(0のまま。やり方がまずいのかもしれませんが・・)、この方法を諦めて、2)のサイトを参考に標準機能を使ってテストしました。


Image Jはマクロをサポートしており、操作を記録して二度目からはそのマクロを実行することでカウントできるようになります。そのために、まず’Macros’->’Record’を起動します。
手順
1) 画像ファイルをドラッグ&ドロップ

2) ‘image’->’type’->’8bit’を選択
3) ‘image’->’adjust’->’threshold’ を選択

4) カウントするオブジェクトが選択されるようにスレッショルドを調整
5) ‘Analyze’->’Analyze Particles’で解析実行

実際には30個ですが、重なり部分があるので27個と認識されたようです。

//selectWindow("imgj01.png");
run("8-bit");
setAutoThreshold("Default");
run("Threshold...");
setThreshold(87, 101);
setOption("BlackBackground", false);
run("Convert to Mask");
run("Analyze Particles...", "display clear summarize");

作業を終えると上のように記録されるので、これをベースに編集して次の解析に使います。
イメージファイルをドラッグ&ドロップしてから実行(‘Macros’->’Run’で .ijmファイル読み込み実行)するので、selectWindowはコメントアウトします。
スレッショルドは赤、青、緑でそれぞれ変更します。


冒頭のScratchの画面にあるデータでは丸が重なっていないので正しくし認識されました。
緑も同様なので省略します。

このような簡単なようでなかなかすぐにできるツールはこれ以外知りませんでした。
知っておくと便利ではないでしょうか。

参考:
1) 「imageJで細胞数等を数えるCell Counter 」https://life-science-project.com/1005/
2) 「ImageJ: 細胞核のカウント」http://t-takaya.net/?p=protocol/ImageJ_nuclei_count

]]>
L-system ../../../20180904886/ Tue, 04 Sep 2018 09:30:41 +0000 ../../../?p=886 「形式文法の一種で、植物の成長プロセスを初めとした様々な自然物の構造を記述・表現できるアルゴリズムである。自然物の他にも、反復関数系(Iterated Function System; IFS)のようないわゆる自己相似図形やフラクタル図形を生成する場合にも用いられる。L-System は1968年、ハンガリーユトレヒト大学の理論生物学者にして植物学者であったアリステッド・リンデンマイヤー(Aristid Lindenmayer)により提唱され、発展した。」
https://ja.wikipedia.org/wiki/L-system
説明が難しいのでウィキペデアをそのまま引用させていただきました。

NetLogoの実装がとてもわかりやすかったので、以下サイトのコードを使わせてもらいました。

globals [string rules stack]

to setup
  clear-all
  reset-ticks

  ; 初期文字列(axiom)で初期化
  set string initial-string

  ; 書き換えルール
  ; 空のルールは無視する
  set rules []
  if not empty? rule1 [ set rules lput rule1 rules ]

  ; スタックを初期化
  set stack (list)

  create-turtles 1 [
    hide-turtle
    set color sky
    init
    draw
  ]
end

to step
  set string rewrite string
  clear-output
  output-print string
  clear-drawing
  ask turtles [
    init
    draw
  ]
  tick
end

to init
  ; エージェントのコンテキスト
  pen-up
  set heading 0
  set xcor initial-x
  set ycor initial-y
end

to-report rewrite [old-string]
  let new-string ""
  repeat length old-string [
    ; old-stringの先頭から1文字ずつルールを適用する
    set new-string word new-string apply-rule first old-string
    set old-string but-first old-string
  ]
  report new-string
end

; 1文字に対してルールを適用する
; 適用できるルールがない場合は定数なのでそのまま返す
to-report apply-rule [ch]
  let str ch
  foreach rules [ ?1 ->
    let rule ?1
    let left-side first rule
    if ch = left-side [
      set str get-right-side rule
    ]
  ]
  report str
end

; A -> Bのフォーマットを仮定
; ->の両側に空白が入る
to-report get-right-side [rule]
  let p position "->" rule
  report substring rule (p + 3) length rule
end

; stringに従って描画
; エージェントコンテキスト
to draw
  let local-string string
  repeat length local-string [
    let action first local-string
    set local-string but-first local-string
    ; 線を描画しながら移動
    if action = "F" [
      pen-down
      forward 0.5
    ]
    ; 左折
    if action = "+" [
      left 90
    ]
    ; 右折
    if action = "-" [
      right 90
    ]
  ]
end

これを試してみようと思ったきっかけは、フラクタル図形というものがタートルグラフのコマンド(文字)の置き換えで実現できる点が、とても興味深かったからです。(他にも応用がききそうです)
オリジナルコードは角度を変更したりルールが二つありましたが、それがなくても十分複雑図形を得られるので、初期文字列の変更もなしで、まずは慣れるためにいろいろと触ってみました。

これだけシンプルなルールでも、このようなバリエーションが得られます。
ルールを増やしていけば、いろいろな発見ができそうです。

]]>
Processing 3D ../../../20171122774/ Wed, 22 Nov 2017 14:25:34 +0000 ../../../?p=774 Processingというと、グラフィックの得意な言語、開発環境ですが、3D空間の描画にも便利です。
ただ2Dの拡張のような3Dであるため、座標の感覚がつかみづらく感じました。そこで空間を把握するため3D座標軸とデータをプロットしてみました。(カメラ使用しない)

float rotx = 0;
float roty = 0;

int d1[] = {-1,-3,-5,-7,-3,-4,-8,-4,-5,0};//R
int d2[] = {3,6,3,2,5,7,4,2,1,2};//G
int d3[] = {4,2,2,4,6,7,5,8,4,3};//B
int d4[] = {-5,-2,-3,-4,-7,-5,-6,-1,-3,-2};//Cyan

void setup(){  
  size(600, 600, P3D);
}
void draw(){
  background(240);
  rotateX(rotx);
  rotateY(roty);
  
  for(int i=0;i<60;i++){
    pushMatrix();
    translate(i*10, height/2, height/2);
    noStroke();
    fill(255,0,0);
    sphere(2);
    popMatrix();
  }
  for(int i=0;i<60;i++){
    pushMatrix();
    translate(width/2, i*10, height/2);
    noStroke();
    fill(0,255,0);
    sphere(2);
    popMatrix();
  }
  for(int i=0;i<60;i++){
    pushMatrix();
    translate(width/2, width/2, i*10);
    noStroke();
    fill(0,0,255);
    sphere(2);
    popMatrix();
  }
  
  for(int i=0;i<10;i++){
    pushMatrix();
    translate(width/2 + i*10, height/2 + d1[i]*10, 300 - 100);
    noStroke();
    fill(255, 100, 100, 70);
    sphere(5);
    popMatrix();
  }
  for(int i=0;i<10;i++){
    pushMatrix();
    translate(width/2 + i*10, height/2 + d2[i]*10, 300 - 0);
    noStroke();
    fill(100, 255, 100, 70);
    sphere(5);
    popMatrix();
  }
  for(int i=0;i<10;i++){
    pushMatrix();
    translate(width/2 + i*10, height/2 + d3[i]*10, 300 + 100);
    noStroke();
    fill(100, 100, 255, 70);
    sphere(5);
    popMatrix();
  }
  for(int i=0;i<10;i++){
    pushMatrix();
    translate(width/2 + (-i)*10, height/2 + d4[i]*10, 300 + 180);
    noStroke();
    fill(100, 255, 255, 70);
    sphere(5);
    popMatrix();
  }
}
void mouseDragged() {
  float rate = 0.01;
  rotx += (mouseY - pmouseY) * rate;
  roty += (pmouseX - mouseX) * rate;
}

起動直後、Z軸上から眺めています。(X軸:赤、Y軸:緑、Z軸:青)

マウスでドラッグして視点を視点を変えるとクリッピングされていた手前のデータ(Cyan)が現れます。

さらに変えて、奥から手前にどの間隔でデータを並んでいるか確認してみます。(奥行き600)

カメラ位置や、ローテートのポイントを、使う目的に合わせて設定する必要があります。
Z軸回転のrotateZ()をためしてみましたが、ちょっとイメージと違う動きをしていて、なかなか難しいです。

]]>
Perlin Noise / vvvv ../../../20170702740/ Sun, 02 Jul 2017 00:16:30 +0000 ../../../?p=740 以前ProcessingでPerlin Noiseを扱いましたが、前回使ったvvvvで3D表示してみました。(../../../20141013213/)

参考 : 【vvvv講座】Perlin ノードを使って波を作る

(このvvvv講座、他にも多数の動画があり、とてもわかりやすく素晴らしいです。)

BoxをLinearSpreadを使って多数配置し、位置(高さ)にPerlinNoiseを適用します。


一つ目、二つ目の画像は、Perlinにある以下のパラメータを変化させました。
Octaves 2->4
Persistence 0.5->0.6

次に色とBoxのサイズを変えてみました。

最後にProcessingで実行した2Dと同じものも、作ってみました。

この場合、Texture SourceというPerlinを使います。
ノードには種類があるので、どれを使うかまだまだ悩みます。

]]>
High Level Shading Language ../../../20170619727/ Mon, 19 Jun 2017 12:54:52 +0000 ../../../?p=727 GLSLと同じ用途で使われるMicrosoft DirectXのシェーディング言語。ビジュアル言語vvvvから簡単に使えることを知り、使いたくなりました。GLSLでないのがちょっと残念なのですが、それを上回る魅力があります。しかしこのvvvv、MAX/MSPと見間違うほど似ています。

Live Music Coder M^2 OSC with MAX/MSP

長年愛用しているソフトで、パッチ方式のビジュアル言語として歴史があります。vvvvも含めまた掘り下げたいです。

さてここではGLSLの最初でも取り上げた、ブラーです。WebGLのときも、シェーダプログラムが簡単に走らせると思っていましたが、vvvvはそれ以上でした。ノード(一つ一つの部品。HLSLノードはここの場合NeiPixels。)を右クリックするとソースが変更可能となります。保存するとすぐに反映されます。
IN/OUTを結線して、プログラムします。
HLSLの処理の入力には、素材の画像ファイル、ピクセルのサイズをLFOで揺らしてアニメーションしています。(縦に伸び縮みするようにプレる)こういうことが簡単にできます。

//@author: vvvv group
//@help: This is a very basic template. Use it to start writing your own effects. If you want effects with lighting start from one of the GouraudXXXX or PhongXXXX effects
//@tags: hlsl
//@credits:

// --------------------------------------------------------------------------------------------------
// PARAMETERS:
// --------------------------------------------------------------------------------------------------

//transforms
/***
float4x4 tW: WORLD;        //the models world matrix
float4x4 tV: VIEW;         //view matrix as set via Renderer (EX9)
float4x4 tP: PROJECTION;
float4x4 tWVP: WORLDVIEWPROJECTION;
***/

//texture
texture Tex <string uiname="Texture";>;
sampler Samp = sampler_state    //sampler for doing the texture-lookup
{
    Texture   = (Tex);          //apply a texture to the sampler
    MipFilter = LINEAR;         //sampler states
    MinFilter = LINEAR;
    MagFilter = LINEAR;
};

//texture transformation marked with semantic TEXTUREMATRIX to achieve symmetric transformations

///float4x4 tTex: TEXTUREMATRIX <string uiname="Texture Transform";>;

//the data structure: "vertexshader to pixelshader"
//used as output data with the VS function
//and as input data with the PS function
struct vs2ps
{
    float4 Pos  : POSITION;
    float2 TexCd : TEXCOORD0;
};

// --------------------------------------------------------------------------------------------------
// VERTEXSHADERS
// --------------------------------------------------------------------------------------------------

/***
vs2ps VS(
    float4 PosO  : POSITION,
    float4 TexCd : TEXCOORD0)
{
    //declare output struct
    vs2ps Out;

    //transform position
    Out.Pos = mul(PosO, tWVP);
    
    //transform texturecoordinates
    Out.TexCd = mul(TexCd, tTex);

    return Out;
}
***/

// --------------------------------------------------------------------------------------------------
// PIXELSHADERS:
// --------------------------------------------------------------------------------------------------

///add
float2 PixelSize;
///

float4 PS(vs2ps In): COLOR
{
    /***
	float4 col = tex2D(Samp, In.TexCd);
    return col;
	***/
	
	/// add
    float4 sum = 0;
    int weightSum = 0;
    //the weights of the neighbouring pixels
    int weights[15] = {1, 2, 3, 4, 5, 6, 7, 8, 7, 6, 5, 4, 3, 2, 1};
    //we are taking 15 samples
    for (int i = 0; i < 15; i++)
    {
        //7 upwards, self and 7 downwards
        float2 cord = float2(In.TexCd.x, In.TexCd.y + PixelSize.y * (i-7));
        //the samples are weighed according to their relation to the current pixel
        sum += tex2D(Samp, cord) * weights[i];
        //while going through the loop we are summing up the weights
        weightSum += weights[i];
    }
    sum /= weightSum;
    return float4(sum.rgb, 1);
	
	///
}

// --------------------------------------------------------------------------------------------------
// TECHNIQUES:
// --------------------------------------------------------------------------------------------------

technique TSimpleShader
{
    pass P0
    {
        //Wrap0 = U;  // useful when mesh is round like a sphere
        /// VertexShader = compile vs_1_1 VS();
    	VertexShader = null;
        PixelShader  = compile ps_2_0 PS();
    }
}

/***
technique TFixedFunction
{
    pass P0
    {
        //transforms
        WorldTransform[0]   = (tW);
        ViewTransform       = (tV);
        ProjectionTransform = (tP);

        //texturing
        Sampler[0] = (Samp);
        TextureTransform[0] = (tTex);
        TexCoordIndex[0] = 0;
        TextureTransformFlags[0] = COUNT2;
        //Wrap0 = U;  // useful when mesh is round like a sphere
        
        Lighting       = FALSE;

        //shaders
        VertexShader = NULL;
        PixelShader  = NULL;
    }
}
***/

ソースは自動生成されるテンプレートファイルをベースに、変更部分を///または*** のコメントで記述しています。バーテックスシェーダは使用せず、ピクセルシェーダは、GLSLのフラグメントシェーダに相当します。

参考: https://vvvv.org/documentation/tutorial-effects-neighbouring-pixels

]]>
OpenGL Shading Language (3) ../../../20170613721/ Tue, 13 Jun 2017 12:56:24 +0000 ../../../?p=721 今回はWayland EGLの環境で、前回の四面体を表示してみました。

WaylandはXに変わるディスプレイサーバで、以下の記事でFedora25(デフォルトでサポート)上でテストしました。

Wayland Display Server

ここでは、Ubuntu 16.04を使います。(インストールメモを忘れてしましまたが、下にあるコンパイルオプションからだいたい想像できると思います)

参考 :
http://blog.techlab-xe.net/archives/4637
https://github.com/techlabxe/SimpleWindowWayland/tree/base-1.1

こちらのサイトを参考にさせていただきました。
カメラが自動的移動する処理はそのままで、四面体を表示させ色をつけています。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <cmath>
#include <cstdint>
#include <vector>
#include <GLES2/gl2.h>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>

#include "WaylandCore.h"

#define WIDTH 640
#define HEIGHT 480
#define TITLE  "Wayland EGL Window"

const GLchar* srcVertexShader[] =
{
	"attribute vec4 vPosition;\n"
	"attribute vec3 vColor;\n"
	"varying vec4 color;\n"
	"uniform vec4 matPVW[4];\n"
	"void main() {\n"
		"vec4 pos;\n"
		"pos = matPVW[0] * vPosition.x;\n"
		"pos += matPVW[1]* vPosition.y;\n"
		"pos += matPVW[2]* vPosition.z;\n"
		"pos += matPVW[3]* vPosition.w;\n"
		"gl_Position = pos;\n"
		"color.rgb = vec3(vColor.r, vColor.g, vColor.b);\n"
		"color.a = 1.0;\n"
	"}"
};

const GLchar* srcFragmentShader[] =
{
	"precision mediump float; \n"
	"varying vec4 color;\n"
	"void main() {\n"
		"gl_FragColor = color;\n"
	"}"
};

struct DrawBatch {
	GLuint vb, ib;
	GLuint shader;
	int indexCount;
} drawObj;

struct VertexPosition {
	float x, y, z;
};
struct VertexColor {
	float r, g, b;
};
struct Vertex {
	VertexPosition Position;
	VertexColor   Color;
};

GLint attrPVW;

GLuint createShaderProgram(const char* srcVS[], const char* srcFS[]){
	GLuint shaderVS = glCreateShader(GL_VERTEX_SHADER);
	GLuint shaderFS = glCreateShader(GL_FRAGMENT_SHADER);

	glShaderSource(shaderVS, 1, srcVS, NULL);
	glCompileShader(shaderVS);

	glShaderSource(shaderFS, 1, srcFS, NULL);
	glCompileShader(shaderFS);

	GLuint program;
	program = glCreateProgram();
	glAttachShader(program, shaderVS);
	glAttachShader(program, shaderFS);

	glLinkProgram(program);

	return program;
}

template<typename T> GLuint createBufferObject(std::vector<T>& src, GLuint type)
{
	GLuint vbo;
	glGenBuffers( 1, &vbo );
	glBindBuffer( type, vbo );
	glBufferData( type, sizeof(T) * src.size(), src.data(), GL_STATIC_DRAW );
	return vbo;
}

void createResource() {
	drawObj.shader = createShaderProgram( srcVertexShader, srcFragmentShader );
	GLint attrPos = glGetAttribLocation( drawObj.shader, "vPosition" );
	GLint attrCol = glGetAttribLocation( drawObj.shader, "vColor" );
	
	attrPVW = glGetUniformLocation( drawObj.shader, "matPVW" );

	std::vector<uint16_t> indices = {0,1,2,1,2,3,0,2,3,0,1,3};
	std::vector<Vertex> vertices;

	GLfloat vData[] = { 
		-1.0f, -1.0f, 0.0f, //1
		 1.0f, -1.0f, 0.0f, //2
		 0.0f,  1.0f, 0.0f, //3
		 0.0f,  0.0f, 1.0f, //4
	};
	GLfloat vColor[] = {
		1.0f, 0.0f, 0.0f,
		0.0f, 1.0f, 0.0f,
		0.0f, 0.0f, 1.0f,
		1.0f, 1.0f, 1.0f,
	};
	
	for(int i=0;i<4;i++){
		Vertex v;
		v.Position.x = vData[i*3];
		v.Position.y = vData[i*3+1];
		v.Position.z = vData[i*3+2];
		v.Color.r = vColor[i*3];
		v.Color.g = vColor[i*3+1];
		v.Color.b = vColor[i*3+2];
		vertices.push_back(v);
	}
	drawObj.vb = createBufferObject(vertices, GL_ARRAY_BUFFER);
	drawObj.ib = createBufferObject(indices, GL_ELEMENT_ARRAY_BUFFER);
	drawObj.indexCount = indices.size();

	char* offset = NULL;
	int stride = sizeof(Vertex);
	
	glVertexAttribPointer(attrPos, 3, GL_FLOAT, GL_FALSE, stride, offset);
	offset += sizeof(VertexPosition);
	glVertexAttribPointer(attrCol, 3, GL_FLOAT, GL_FALSE, stride, offset);
	offset += sizeof(VertexColor);
	
	glEnableVertexAttribArray(attrPos);
	glEnableVertexAttribArray(attrCol);
}

void destroyResource() {
	glDeleteBuffers( 1, &drawObj.vb );
	glDeleteBuffers( 1, &drawObj.ib );
	glDeleteProgram( drawObj.shader );
}

void draw()
{
	static double angle = 0.0f;
	angle += .01f;
	if( angle > 3600.0 ) {
		angle -= 3600.0;
	}
	
	glEnable(GL_DEPTH_TEST);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	
	glm::vec3 cameraPos = glm::vec3(0.0, 0.0f, 2.0f);
	glm::mat4 proj = glm::perspective<float>(30.0f, float(WIDTH)/float(HEIGHT), 1.0f, 100.0f);
	glm::mat4 view = glm::lookAt<float>(cameraPos, glm::vec3(0.0f,0.0f,0.0f), glm::vec3(0.0f,1.0f,0.0f));
	glm::mat4 world= glm::rotate( glm::mat4(1.0f), (float)angle, glm::vec3(0.0f,0.0f,1.0f));
	world= glm::rotate(world, (float) angle * 0.5f, glm::vec3(0.0f, 0.0f, 1.0f));
	world= glm::rotate(world, (float) angle * 0.5f, glm::vec3(1.0f, 0.0f, 0.0f));
	glUseProgram(drawObj.shader);

	glm::mat4 pvw = proj * view * world;
	glUniform4fv(attrPVW, 4, glm::value_ptr(pvw));
	glBindBuffer(GL_ARRAY_BUFFER, drawObj.vb);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, drawObj.ib);
	glDrawElements(GL_TRIANGLES, drawObj.indexCount, GL_UNSIGNED_SHORT, NULL);   
}

int main() {
	WaylandCore* core = new WaylandCore(WIDTH, HEIGHT, TITLE);

	fprintf(stderr, "Renderer: %s\n", (char*)glGetString(GL_RENDERER));
	fprintf(stderr, "GL_VERSION: %s\n", (char*)glGetString( GL_VERSION));
	fprintf(stderr, "GL_EXTENSIONS: %s\n", (char*)glGetString(GL_EXTENSIONS));

	createResource();
  
	while( !core->isShouldClose() ) {
		core->pollEvents();
		usleep(100*1000);
		
		glClearColor( 0, 0, 0.25f, 0.75f );
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
		
		draw();
		
		core->swapBuffers();
	}
	
	destroyResource();
	
	delete core;core = NULL;
	return 0;
}

g++ -o WaylandEglTest main.cpp WaylandCore.cpp WaylandCore_registry_handlers.cpp WaylandCore_seat_handlers.cpp -lwayland-client -lwayland-cursor -lwayland-egl -lEGL -lGLESv2 -std=c++11

main.cpp以外のWayland部分は、上記サイトにあるのを使わせていただきました。
(マウスやキーボードのイベントリスナーなども含んでいます)
参考までに、メインループの最初と最後にあるpollEvents()とswapBuffers()をWaylandCore.cppから抜粋。

void WaylandCore::pollEvents() {
  if( mDisplay == NULL || mRegistry == NULL ) {
    mShouldClose = true;
    return;
  }
  pollfd fds[] = {
    { wl_display_get_fd(mDisplay), POLLIN },
  };
  while( wl_display_prepare_read(mDisplay) != 0 )
  {
    wl_display_dispatch_pending(mDisplay);
  }
  wl_display_flush(mDisplay);
  if( poll(fds,1,0) > 0 )
  {
    wl_display_read_events(mDisplay);
    wl_display_dispatch_pending(mDisplay);
  } else {
    wl_display_cancel_read(mDisplay);
  }
}

void WaylandCore::swapBuffers()
{
  eglSwapBuffers( mEglDisplay, mSurface.eglSurface );
}

イベントのポーリングでソケット読み込みしています。IPC(Interprocess Communication)の部分が詳しく書かれています。OpneGLもそうですが、コードボリュームが多くなってしまうのは、このような細かいAPI(wl_xxx)を組みあわせるところから書くからでしょう。意味がわかれば中身を意識せず使えばいいと思われます。
(この部分、Xのようにリモートでアクセスできるかどうか、興味は膨らみます)

ここまで違う環境でシェーダープログラムを試してみると、理解がより深まった気がします。
いろいろな環境で実行できるとわかったところで、GPUの特性を生かした画像処理のシェーダプログラムにもっと比重をおいて学習したいと思います。

Renderer: Gallium 0.4 on llvmpipe(LLVM3.8, 256bits)
GL Version : OpenGL ES 3.0 Mesa 12.0.6
EGL major : 1 minor : 4

]]>