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.