PythonとKerasによるDeepLearning個人的なまとめ No.01

多クラス分類

データの用意

from keras.datasets import reuters
from keras.utils.np_utils import to_categorical
import numpy as np
import keras

(train_data, train_labels),(test_data, test_labels) = reuters.load_data(num_words=10000)

#こんな感じでハッシュになっている(holleb': 30962)
word_index = reuters.get_word_index()

データ前処理

train_dataには文章が入っていて、その文章を構成されている単語にそれぞれindexが割り振られている.文章をテンソルに変換して、出てきた単語に関しては1を立てておく.(numpy配列は普通の多次元配列みたいにアクセス出来る)

def vectorize_sequences(sequences, dimention=10000):
    result = np.zeros((len(sequences), dimention))
    for i, sequence in enumerate(sequences):
        result[i, sequence] = 1
    return result

# train_dataは8982sample -> 
# 10000語を対象としているのでテンソルの形状は(8982, 10000)
x_train = vectorize_sequences(train_data)
x_test = vectorize_sequences(test_data)

# 正解ラベルも0,1表現にしておく
# sample数が8982でそれら1つ1つのsampleに対して46次元を持つので、
# テンソル形状は(8982, 46)
oh_train_labels = to_categorical(train_labels)
oh_test_labels = to_categorical(test_labels)

modelの作成

今回は初めは(8982,10000)のテンソルが入力となる.その後64次元に落とし最終的には分類するために46次元に落とす.

from keras import models
from keras import layers

model = models.Sequential()
model.add(layers.Dense(64, activation='relu', input_shape=(10000, )))
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(46, activation='softmax'))

model.summary()

model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])

検証

モデルを汎化させるためにも訓練データと検証データはちゃんと分ける

x_val = x_train[:1000]
practical_x_train = x_train[1000:]
y_val = oh_train_labels[:1000]
practical_y_train = oh_train_labels[1000:]

history = model.fit(practical_x_train, practical_y_train, epochs=3, batch_size=512,
validation_data=(x_val, y_val))

print(history.history)
Train on 7982 samples, validate on 1000 samples
Epoch 1/3
2020-01-19 11:08:43.746972: I tensorflow/core/platform/cpu_feature_guard.cc:141] Your CPU supports instructions that this TensorFlow binary was not compiled to use: SSE4.1 SSE4.2 AVX AVX2 FMA
2020-01-19 11:08:43.748935: I tensorflow/core/common_runtime/process_util.cc:71] Creating new thread pool with default inter op setting: 4. Tune using inter_op_parallelism_threads for best performance.
7982/7982 [==============================] - 2s 234us/step - loss: 2.5327 - acc: 0.4957 - val_loss: 1.7231 - val_acc: 0.6110
Epoch 2/3
7982/7982 [==============================] - 1s 105us/step - loss: 1.4493 - acc: 0.6868 - val_loss: 1.3504 - val_acc: 0.7070
Epoch 3/3
7982/7982 [==============================] - 1s 110us/step - loss: 1.0995 - acc: 0.7647 - val_loss: 1.1730 - val_acc: 0.7420
{'acc': [0.49574041605533381, 0.68679528983426075, 0.76472062047219047], 'loss': [2.5327453521581798, 1.4492631792813069, 1.0994816560310339], 'val_acc': [0.61100000095367435, 0.70699999094009403, 0.74200000333786009], 'val_loss': [1.7230872917175293, 1.3503756618499756, 1.1729804239273072]}
(mykeras) M-Takaishi-KeitonoMacBook-Pro-2:Chapter0

訓練データセット、検証データセット、テストデータセットを分ける理由

モデルのハイパーパラメーター(層の数、層のサイズなど)をチューニングするために検証データが必要であり、それを評価するためにテストデータが必要になってくるため元々のデータは3分割する必要がある.またこのチューニングの作業も学習に含まれる.

ガウス・ザイデル法備忘録 [数値解析]

特徴

ガウス・ザイデル法ではk+1回目の反復時の解の算出にk回目に算出された解を用いる. 例えばxの3行目を解を出す時は1,2行目までに算出された解を反復式に代入するイメージ.

f:id:takaishi78:20191108165014j:plain

ヤコビ法との違い

ヤコビ法では右辺の解は更新されてないが,ガウス・ザイデル法では上記の理由で右辺の解が更新されていることが違う点

プログラム

また下のコードだとi=0の時はまだ解(x[0], x[1], x[2])は更新されてない.i=1になるとx[0]がk+1番目の解に更新されている

#Gauss-Seidel
coef_mat = np.array([[3,2,1],[1,4,1],[2,2,5]])
y = np.array([10, 12, 21])
convergence_value = 10 ** -16
dim = len(y)

x = np.zeros(dim)
before_x = np.zeros(dim)
loss_sum = 0
count = 0


while True:
  for i in range(dim):
    before_x[i] = x[i]
    x[i] = (y[i] - coef_mat[i][(i+1)%dim]*x[(i+1)%dim] - coef_mat[i][(i+2)%dim]*x[(i+2)%dim]) / coef_mat[i][i]

  loss_sum = 0.0
  
  
  for i in range(dim):
    loss_sum += abs(before_x[i] - x[i])
  
  if loss_sum < convergence_value:
    break
  
  print("No."+str(count))
  count+=1
  for i in range(dim):
    print(x[i])

具体的なイメージ

数学的というよりかは現実世界での事柄に合わせて自分のイメージでガウス・ザイデル法とヤコビ法の違いを説明する.なにか事件が起きた際に犯人を特定するみたいなことがある.それに対してガウス・ザイデル法では一人の探偵かなんかが怪しい人を調査していってその情報を元に次に怪しいやつを探して最終的に犯人(解)にたどり着くって感じ.それに対してヤコビ法では複数人の探偵で別々に怪しい人を探しいてその日の晩にみんなでミーティングして次の日また怪しいやつを探してみたいな感じで犯人(解)を特定するみたいな感じ.この説明がわかりやすいかわかりにくかは知らない...

ヤコビ法備忘録 [数値解析]

概要

ヤコビ法は、n元の連立一次方程式の連立一次方程式を反復法で解く手法の1。

反復法とは

適当な初期点から出発して反復式 によって点列を生成し最終的に最適解に収束させようとする手法.つまり初めに解を適当な値で初期化して漸化式を用いて解を収束させていく方法

手法

f:id:takaishi78:20191108162725j:plain
ヤコビ法-式

特徴

前の反復の解xを保存しつつ新しい解new_xを算出していく.

プログラム

coef_mat = np.array([[3,2,1],[1,4,1],[2,2,5]])
y = np.array([10, 12, 21])
convergence_value = 10 ** -16
dim = len(y)

x = np.zeros(dim)
new_x = np.zeros(dim)
loss_sum = 0
count = 0


while True:
  for i in range(dim):
    new_x[i] = (y[i] - coef_mat[i][(i+1)%dim]*x[(i+1)%dim] - coef_mat[i][(i+2)%dim]*x[(i+2)%dim]) / coef_mat[i][i]

  loss_sum = 0.0
  
  
  for i in range(dim):
    loss_sum += abs(new_x[i] - x[i])
  
  if loss_sum < convergence_value:
    break
  
  print("No."+str(count))
  count+=1
  for i in range(dim):
    print(new_x[i])
    x[i] = new_x[i]

参考

mmm-ssss.com

http://nkl.cc.u-tokyo.ac.jp/13n/SolverIterative.pdf

ofFboを使わずにオフスクリーンレンダリング

FrameBufferObjectとは

FrameBufferObjectとはColorBuffer, DepthBuffer, StencileBufferを内包しているBufferのことです. これらのbufferに対してTextureやRenderBufferをアタッチしてあげることで利用することが出来ます. この図がわかりやすい→

qiita.com

サンプル

さきにコードを張ります.

GLuint frameBuffer, depthBuffer;
GLuint colorTexture;
void ofApp::createFrameBuffer() {
    //FrameBufferを作成
    glGenFramebuffers(1, & frameBuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
    ofVec2f res = ofVec2f(ofGetWidth(), ofGetHeight());

    glGenTextures(1, &colorTexture);
    glBindTexture(GL_TEXTURE_2D, colorTexture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, res.x, res.y,
        0, GL_RGB, GL_FLOAT, NULL);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    //----FrameBufferにtextureをアタッチ(ColorBufferとしてアタッチされているTextureBuffer)
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0);

    GLuint attachment = GL_COLOR_ATTACHMENT0;
    glDrawBuffers(1, &attachment);

    glGenRenderbuffers(1, &depthBuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer);
    //glTexImage2D的な立ち位置
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, res.x, res.y);
    //attach
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

}

FrameBufferObjectの周り

まず初めにFrameBufferObjectをglGenFramebuffers(1, & frameBuffer)作成します.その後,生成したFrameBufferObjectをbind状態(有効状態)にします.こうすることによって今後する手続きが現在注目しているFrameBufferObjectへ行われることになります.

ColorBuffer

次にColorBufferを作っていきます.これは何も難しいことはなく2DのTextureを生成する手順と変わりはありません.ただ画像から画素の値を読み込むわけではないので最後の引数はnullにしています. Texture周りがよくわからない場合はここら辺を参考に

https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glTexImage2D.xml http://wisdom.sakura.ne.jp/system/opengl/gl23.html http://marina.sys.wakayama-u.ac.jp/~tokoi/?date=20040914

ColorBufferにアタッチするためのTextureが生成出来たらglFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0)でアタッチしていきます.そしてアタッチが出来たら今度はFrameBufferObjectにアタッチするためglBindRenderbuffer(GL_RENDERBUFFER, depthBuffer)を呼んでます.

glDrawBuffersについて説明を飛ばしましたが,これは主にMRT(マルチレンダーターゲット)という技術を使用するときに重要になるます.引数内で指定したバッファにshader内で行ったレンダリング結果が入るようになります.

DepthBuffer

最後にDepthを情報を格納するためのBufferです.これがないとglEnable(GL_DEPTH_TEST)とはを呼んでも参照する深度情報がないので奥行きの関係がおかしくなります. ますはRenderBufferをDepthBufferにアタッチしてみようと思います.まずはRenderBufferObjectの作成glGenRenderbuffers(1, &depthBuffer)そしてbindした後, glRenderbufferStorageという関数を呼びます.これは何をしているかというとTextureでいうTexImage2D的な立ち位置でどんな構成のBufferを作るかっていう設定をしています.今回は深度を格納したいのでGL_DEPTH_COMPONENTを選択します. 最後にこれをFramebufferに関連づけるための関数glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuffer)を呼んでいます.

***追記: 理由ははっきりとわかりませんが,TextureをDepthBufferとしたら描画がされませんでした.

オフスクリーンレンダリング

1Pass目で作成したFrameBufferObjectにレンダリングをしてShader内部でColorBufferへレンダリング結果を書きこんでいます.そして2pass目そのTextureを用いて絵を出しています.今回は2Pass目で簡単に色を反転させてみましたオフスクリーンレンダリングの利点としてこのようなマルチパスがあります.この技術を使うことでぼかしや発光,映り込みなど色々なことが出来るので重要な技術です.

void ofApp::setup(){
    createFrameBuffer();
    mesh = ofSpherePrimitive(100, 16).getMesh();
    glEnable(GL_DEPTH_TEST);
    geometryPass.load("shaders/geom");
    renderPass.load("shaders/render");

    float w = 1.0;
    float h = 1.0;
    quad.setMode(OF_PRIMITIVE_TRIANGLE_FAN);
    quad.addVertex(ofVec3f(w, h, 0.0));
    quad.addTexCoord(ofVec2f(1.0f, 1.0f));
    quad.addVertex(ofVec3f(w, -h, 0.0));
    quad.addTexCoord(ofVec2f(1.0f, 0.0f));
    quad.addVertex(ofVec3f(-w, -h, 0.0));
    quad.addTexCoord(ofVec2f(0.0f, 0.0f));
    quad.addVertex(ofVec3f(-w, h, 0.0));
    quad.addTexCoord(ofVec2f(0.0f, 1.0f));

    cam.lookAt(ofVec3f(0.0, 0.0, 0.0), ofVec3f(0.0, 1.0, 0.0));
}
//--------------------------------------------------------------
void ofApp::draw(){
    glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    geometryPass.begin();
    cam.begin();
    ofMatrix4x4 proj, view, model;
    view = ofGetCurrentViewMatrix();
    proj = cam.getProjectionMatrix();
    cam.end();
    geometryPass.setUniformMatrix4f("model", model);
    geometryPass.setUniformMatrix4f("view", view);
    geometryPass.setUniformMatrix4f("proj", proj);
    mesh.draw();
    geometryPass.end();
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    
    //-----------------------------------------
    renderPass.begin();
    renderPass.setUniformTexture("colorBuffer", GL_TEXTURE_2D, colorTexture, 0);
    quad.draw(OF_MESH_FILL);
    renderPass.end();
}

layout (location = 0) out vec4 colorTextureの部分で先のglDrawBuffersによってレンダリング結果がColoBuffer(Texture)に格納されています.

//vert
#version 330 core
uniform mat4 model;
uniform mat4 view;
uniform mat4 proj;

in vec3 position;
in vec4 color;

out vec4 Color;

void main(){
  gl_Position = proj * view * model * vec4(position, 1.0);
  Color = vec4(1.0, 0.0, 0.0, 1.0);
}

//-------------------------------
//frag
#version 330 core
layout (location = 0) out vec4 colorTexture;

in vec4 Color;

void main(){
  colorTexture = Color;
}
//vert
#version 330 core

in vec3 position;
in vec2 texcoord;

out vec2 TexCoord;
void main(){
  TexCoord = texcoord;
  gl_Position = vec4(position, 1.0);
}
//-----------------------------
//frag
#version 330 core
uniform sampler2D colorBuffer;
in vec2 TexCoord;
out vec4 c;

void main(){
  c = texture(colorBuffer, TexCoord);
  c.rgb = vec3(1.0) - c.rgb;
}

DrawProcedual備忘録

はじめに

DrawProcedualを使用するモチベーションとしては,VertexShaderを用いたインスタンシングである.

ステップ

  • ComputeBufferを作成
 Vector3[] vertices = instancingTargetMesh.triangles
            .Select(i => instancingTargetMesh.vertices[i]).ToArray();


        this.vertexBuffer = new ComputeBuffer
            (this.instancingTargetMesh.triangles.Length, Marshal.SizeOf(typeof(Vector3)));
        this.vertexBuffer.SetData(vertices);
  • Materialにbufferを送信
  • DrawProcedualをする.

ここで頂点数とインスタンシングする数をしてしてあげる.

 Graphics.DrawProceduralNow(MeshTopology.Triangles,
                                this.instancingTargetMesh.triangles.Length,
                                this.numOfInstances);
  • shaderCode
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Custom/Unlit/SimpleInstancing"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }

    SubShader
    {
        Tags
        {
            "RenderType" = "Opaque"
        }

        LOD 100

        Pass
        {
            CGPROGRAM

            #pragma vertex vertexShader
            #pragma fragment fragmentShader
            
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct vertexOutput
            {
                float2 uv : TEXCOORD0;
                half4 color : TEXCOORD1;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST; // need to use TRANSFORM_TEX

            StructuredBuffer<float3> _VertexBuffer;
            
            vertexOutput vertexShader (appdata input,
                                       uint vertexID : SV_VertexID,
                                       uint instanceID : SV_InstanceID)
            {
               
                input.vertex.xyz += _VertexBuffer[vertexID];
                input.vertex.x += (instanceID % 10) + (instanceID % 10) * 0.5;
                input.vertex.y += (instanceID / 10) + (instanceID / 10) * 0.5;

                vertexOutput output;
                output.vertex = UnityObjectToClipPos(input.vertex);
                output.uv = TRANSFORM_TEX(input.uv, _MainTex);
                output.color = half4(1, 0, 0, 1);

                return output;
            }

            fixed4 fragmentShader (vertexOutput input,
                                   uint primitiveID : SV_PrimitiveID) : SV_Target
            {  
                return input.color;
            }

            ENDCG
        }
    }
}

oFでのTextureMapping#02

はじめに

前回の『oFでのTextureMapping#01』では画像を読み込んでTextureMappingを行った.それに今回はテクスチャ内部のピクセルデータを自分で制作しそのテクスチャをShaderへ転送して見ようと思う.そしてそのままMulti Texture Mappngまでこの記事に残して置こうと思う.

Texture Mapping

Texture生成までの流れ

  1. glGenTextures:テクスチャIDを生成
  2. glBindTexture(GLenum target , GLuint texture):指定した名前のテクスチャを有効にする.初めて指定された名前であれば、自動的にテクスチャオブジェクトが作られ、そうでなければ、過去に作られたオブジェクトが再び有効になる
  3. glTexParameteri:テクスチャの細かい設定を行う
  4. glTexImage2D:GPU(VRAM)へデータを転送する(2次元のテクスチャへデータを紐付ける)
//header
#pragma once

#include "ofMain.h"

class ofApp : public ofBaseApp{

    public:
        void setup();
        void update();
        void draw();
    
    
    ofVboMesh mesh;
    ofEasyCam cam;
    ofShader shader;
    unsigned char pixels[256*256*4];
    GLuint texID;
};
void ofApp::setup(){
    ofBackground(0);
    shader.load("Shaders/shader");
    //texture---------------
    int res = 256;
    for(int i = 0; i < res; i++){
        for(int j = 0; j < res; j++){
            int index = (j+i*res)*4;
            pixels[index] = i;
            pixels[index+1] = j;
            pixels[index+2] = (i+j)*0.5;
            pixels[index+3] = 255.0;
        }
    }

    glEnable(GL_TEXTURE_2D);
    glActiveTexture(GL_TEXTURE0);
    glGenTextures(1, &texID);//テクスチャ生成
    cout << "texID : " << texID << endl;
    glBindTexture(GL_TEXTURE_2D , texID);//idを指定してバインド
    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);//テクスチャデータの区切りを設定
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexImage2D(
                 GL_TEXTURE_2D , 0 , GL_RGBA , 256 , 256,
                 0 , GL_RGBA , GL_UNSIGNED_BYTE , pixels
                 );
    ///glDeleteTextures(1 , &texID);
    //----------------------
    mesh.setMode(ofPrimitiveMode::OF_PRIMITIVE_TRIANGLE_FAN);
    float imgSize = 1.0;
   
    mesh.addTexCoord(ofVec2f(0.0, 0.0));
    mesh.addVertex(ofVec3f(-res, res, 0.0));
    mesh.addTexCoord(ofVec2f(0, imgSize));
    mesh.addVertex(ofVec3f(-res, -res, 0.0));
    mesh.addTexCoord(ofVec2f(imgSize, imgSize));
    mesh.addVertex(ofVec3f(res, -res, 0.0));
    mesh.addTexCoord(ofVec2f(imgSize, 0.0));
    mesh.addVertex(ofVec3f(res, res, 0.0));
}
//vertex shader
#version 120
varying vec4 position;
void main(void)
{
  position = gl_ModelViewMatrix * gl_Vertex;

  gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
  gl_Position = ftransform();
}

//fragment shader
#version 120
uniform sampler2D texture;
uniform sampler2D texture2;
void main(void)
{
    vec3 color = texture2D(texture, gl_TexCoord[0].st).xyz;
    gl_FragColor.rgb = color.rgb;
    gl_FragColor.a = 1.0;
}

結果

f:id:takaishi78:20190813012426p:plain

Multi Texture

Texture Unit

複数のテクスチャを使用する場合、Texture Unitという物を意識する必要がある. 初期状態ではテクスチャユニット0が使用可能になっており、1以上を使用する場合はglActiveTexture()を呼ぶ必要がある.

setUniform1i()

textureDataをshader側に転送するにはsetUniform1i()を使用しますが、第2引数に渡す数値はアクティブになっているテクスチャユニットの番号と同じになる.

void ofApp::setup(){
    GLint num_tex_units;
    glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &num_tex_units);
    cout << "Max texture units = " << num_tex_units << endl;
    
    ofBackground(0);
    shader.load("Shaders/shader");
    
    //texture1---------------
    int res = 256;
    for(int i = 0; i < res; i++){
        for(int j = 0; j < res; j++){
            int index = (j+i*res)*4;
            pixels[index] = i;
            pixels[index+1] = j;
            pixels[index+2] = (i+j)*0.5;
            pixels[index+3] = 255.0;
        }
    }

    glEnable(GL_TEXTURE_2D);
    glActiveTexture(GL_TEXTURE0);
    glGenTextures(1, &texID);//テクスチャ生成
    cout << "texID : " << texID << endl;
    glBindTexture(GL_TEXTURE_2D , texID);//idを指定してバインド
    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);//テクスチャデータの区切りを設定
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexImage2D(
                 GL_TEXTURE_2D , 0 , GL_RGBA , 256 , 256,
                 0 , GL_RGBA , GL_UNSIGNED_BYTE , pixels
                 );
    //texture2---------------
    for(int i = 0; i < res; i++){
        for(int j = 0; j < res; j++){
            int index = (j+i*res)*4;
            pixels2[index] = 120;
            pixels2[index+1] = 0;
            pixels2[index+2] = 0;
            pixels2[index+3] = 255.0;
        }
    }
    glEnable(GL_TEXTURE_2D);
    glActiveTexture(GL_TEXTURE1);
    glGenTextures(1, &texID2);//テクスチャ生成
    cout << "texID2 : " << texID2 << endl;
    glBindTexture(GL_TEXTURE_2D , texID2);//idを指定してバインド
    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);//テクスチャデータの区切りを設定
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexImage2D(
                 GL_TEXTURE_2D , 0 , GL_RGBA , 256 , 256,
                 0 , GL_RGBA , GL_UNSIGNED_BYTE , pixels2
                 );
}   

void ofApp::draw(){
    //glEnable(GL_ALPHA_TEST);
    cam.begin();
    shader.begin();
    
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D , texID);
    shader.setUniform1i("texture", 0);
    
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D , texID2);
    shader.setUniform1i("texture2", 1);
    
    mesh.draw();
    shader.end();
    cam.end();
}
#version 120
uniform sampler2D texture;
uniform sampler2D texture2;
void main(void)
{
    vec3 color = texture2D(texture, gl_TexCoord[0].st).xyz;
    vec3 color2 = texture2D(texture2, gl_TexCoord[0].st).xyz;
    gl_FragColor.rgb = color.rgb + color2.rgb;
    gl_FragColor.a = 1.0;
}

結果

たしかに赤の色身が加わってる感じ

f:id:takaishi78:20190813013748p:plain

用語メモ

  • GLenum target : テクスチャの形式でGL_TEXTURE_2Dなど
  • internalformat : テクスチャのフォーマットタイプ. GL_RGBAかGL_RGBが一般的
  • format:読み込むテクスチャメモリブロックのフォーマットタイプ.internalformat同様にGL_RGBAかGL_RGBなどで指定.
  • type:1画素の1成分(RGBAそれぞれ)の型を指定.RGBAならばunsigned charなのでGL_UNSIGNED_BYTEを指定.浮動小数点テクスチャであればGL_FLOAT.

oFでのTextureMapping#01

はじめに

oFで実装されているofTextureなどの関数を使用してというよりかはopenGLレベルでのTextureMappingのやり方のメモを残しておく.テクスチャ系は色々パラメータとか多くて少し触らないと忘れてしまうから..

TextureMappingするまでの手順

Meshの生成

Meshに頂点情報と、それに対になるテクスチャ座標の代入を行う. またここで注意しなければいけないことは読み込まれた画像の座標系では左上が原点でテクスチャ座標空間では左下が原点だということ.

f:id:takaishi78:20190812200318p:plain
座標空間の違い
(↑床井先生のサイトから拝借させていただきました)

addTexCoord関数について

この関数の引数への渡すデータはテクスチャ空間での座標系ではなく画像空間での座標を渡すことに注意.

void ofApp::Setup(){
    shader.load("Shaders/shader");
    img.load("texSample.png");
    
    //VboMesh mesh
    mesh.setMode(ofPrimitiveMode::OF_PRIMITIVE_TRIANGLE_FAN);
    float imgSize = img.getWidth();
    float size = 100.0;
    mesh.addTexCoord(ofVec2f(0.0, 0.0));
    mesh.addVertex(ofVec3f(-size, size, 0.0));
    mesh.addTexCoord(ofVec2f(0, imgSize));
    mesh.addVertex(ofVec3f(-size, -size, 0.0));
    mesh.addTexCoord(ofVec2f(imgSize, imgSize));
    mesh.addVertex(ofVec3f(size, -size, 0.0));
    mesh.addTexCoord(ofVec2f(imgSize, 0.0));
    mesh.addVertex(ofVec3f(size, size, 0.0));
}

TextureをShaderへ転送

img.getTexture().bind()をすることによってshader側で宣言されているsampler2D型で宣言されているuniform変数にテクスチャデータが転送される.

void ofApp::Draw(){
    img.getTexture().bind();
    cam.begin();
    shader.begin();
    mesh.draw();
    shader.end();
    cam.end();
    img.getTexture().unbind();
}
//vertex shader
#version 120
varying vec4 position;
void main(void)
{
  position = gl_ModelViewMatrix * gl_Vertex;

  gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
  gl_Position = ftransform();
}
//fragment shader
#version 120
uniform sampler2D tex0;
void main(void)
{
    // テクスチャ
    vec3 color_tex0 = texture2D(tex0, gl_TexCoord[0].st).xyz;
    gl_FragColor.rgb = color_tex0;
    gl_FragColor.a = 1.0;
}

結果

f:id:takaishi78:20190812202339p:plain

補足(Sampler2D型とSampler2DRect型の違い)

ofDisableArbTex関数を呼ぶことによって画像座標空間が正規化される.そうした場合Shader側で受け取るuniform変数はsampler2D型としなければいけない.

 void ofApp::Setup(){
    ofBackground(0);
    ofDisableArbTex();
    shader.load("Shaders/shader");
    img.load("texture01.jpeg");
    
    mesh.setMode(ofPrimitiveMode::OF_PRIMITIVE_TRIANGLE_FAN);
    float imgSize = 1.0;
    float size = 100.0;
    mesh.addTexCoord(ofVec2f(0.0, 0.0));
    mesh.addVertex(ofVec3f(-size, size, 0.0));
    mesh.addTexCoord(ofVec2f(0, imgSize));
    mesh.addVertex(ofVec3f(-size, -size, 0.0));
    mesh.addTexCoord(ofVec2f(imgSize, imgSize));
    mesh.addVertex(ofVec3f(size, -size, 0.0));
    mesh.addTexCoord(ofVec2f(imgSize, 0.0));
    mesh.addVertex(ofVec3f(size, size, 0.0));
}
 #version 120
//uniform sampler2DRect tex0;
uniform sampler2D tex0;
varying vec2 texCoordVarying;

void main()
{
    //gl_FragColor = vec4(1.0);
    gl_FragColor = texture2DRect(tex0, texCoordVarying);
}

ソースコード

https://github.com/KeitoTakaishi/VolumeRendereringExpt/tree/master/TextureTest01