GLSL:マルチテクスチャ

マルチテクスチャです。
今回のプログラムは地球の地表テクスチャと雲のテクスチャを合成し、
地表よりも雲を遅れて回転させています。
マウスの左ドラッグで視点を変更できます。
画像はNASAがフリーで配布している物を縮小して使用しています。

earth.png

cloud.png

vertex.shader

varying vec3 P;
varying vec3 N;

void main(void)
{
  P = vec3(gl_ModelViewMatrix * gl_Vertex);
  N = normalize(gl_NormalMatrix * gl_Normal).xyz;
  gl_TexCoord[0] = gl_MultiTexCoord0;
  gl_TexCoord[1] = gl_TextureMatrix[1] * gl_MultiTexCoord0;
  gl_Position = ftransform();
}

flagment.shader

varying vec3 P;
varying vec3 N;
uniform sampler2D sampler0;
uniform sampler2D sampler1;
uniform float mixFactor;//混合率

void main(void)
{
  vec3 L = normalize(gl_LightSource[0].position.xyz - P);
  N = normalize(N);
  
  vec4 ambient = gl_FrontLightProduct[0].ambient;
  float dotNL = dot(N, L);//max(0.0, dot(N, L));
  vec4 diffuse = gl_FrontLightProduct[0].diffuse * max(0.0, dotNL);
    vec3 V = normalize(-P);
    vec3 H = normalize(L + V);
    float powNH = pow(max(dot(N, H), 0.0), gl_FrontMaterial.shininess);
    if(dotNL <= 0.0) powNH = 0.0;
    vec4 specular = gl_FrontLightProduct[0].specular * powNH;
  //テクスチャの色
  vec4 texColor0 = texture2D(sampler0, gl_TexCoord[0].st);
  vec4 texColor1 = texture2D(sampler1, gl_TexCoord[1].st);
  vec4 texColor = mix(texColor0, texColor1, mixFactor);
//vec4 texColor = clamp(texColor0 + texColor1, vec4(0.0), vec4(1.0));
  
    //GL_MODULATEモード
    gl_FragColor = (ambient + diffuse) * texColor + specular;
    //任意の混合比
//    gl_FragColor = mix(ambient + diffuse, texColor, 0.5) + specular;    
}

GLSL.h

#pragma once
 #include <stdio.h>
 
//GLSLクラス
 class GLSL{
 public:
  GLuint ShaderProg;
  GLuint VertexShader, FragmentShader;
  void ReadShaderCompile(GLuint Shader, const char *File);//shader fileを読み込みコンパイルする
  void Link( GLuint Prog );//リンクする
  void InitGLSL(const char *VertexFile);//GLSLの初期化
  void InitGLSL(const char *VertexFile, const char *FragmentFile);//GLSLの初期化
  void ON();//シェーダー描画に切り替え
  void OFF();//シェーダー解除
  ~GLSL();
 };
 
 void GLSL::ReadShaderCompile(GLuint Shader, const char *File){
    FILE *fp;
    char *buf;
    GLsizei size, len;
    GLint compiled;
 
    fopen_s(&fp,File, "rb");
    if(!fp) printf("ファイルを開くことができません %s\n", File);
 
    fseek(fp, 0, SEEK_END);
    size = ftell(fp);
 
   buf = (GLchar *)malloc(size);
    if (buf == NULL) {
      printf("メモリが確保できませんでした \n");
    }
 
    fseek(fp, 0, SEEK_SET);
    fread(buf, 1, size, fp);
    glShaderSource(Shader, 1, (const GLchar **)&buf, &size);
    free(buf);
    fclose(fp);
 
    glCompileShader(Shader);
    glGetShaderiv( Shader, GL_COMPILE_STATUS, &compiled );
 
   if ( compiled == GL_FALSE )
    {
     printf( "コンパイルできませんでした!!: %s \n ", File);
     glGetProgramiv( Shader, GL_INFO_LOG_LENGTH, &size );
     if ( size > 0 )
     {
      buf = (char *)malloc(size);
      glGetShaderInfoLog( Shader, size, &len, buf);
      printf(buf);
      free(buf);
     }
    }
  }
 
 void GLSL::Link( GLuint Prog ){
    GLsizei size, len;
    GLint linked;
    char *infoLog ;
 
   glLinkProgram( Prog );
 
   glGetProgramiv( Prog, GL_LINK_STATUS, &linked );
 
   if ( linked == GL_FALSE ){
    printf("リンクできませんでした!! \n");
    
    glGetProgramiv( Prog, GL_INFO_LOG_LENGTH, &size );
    if ( size > 0 ){
     infoLog = (char *)malloc(size);
     glGetProgramInfoLog( Prog, size, &len, infoLog );
     printf(infoLog);
     free(infoLog);
    }
   }
 }
 
void GLSL::InitGLSL(const char *VertexFile){
    GLenum err = glewInit();
    if (err != GLEW_OK)
    {
     printf("Error: %s\n", glewGetErrorString(err));
    }
    printf("VENDOR= %s \n", glGetString(GL_VENDOR));
    printf("GPU= %s \n", glGetString(GL_RENDERER));
    printf("OpenGL= %s \n", glGetString(GL_VERSION));
    printf("GLSL= %s \n", glGetString(GL_SHADING_LANGUAGE_VERSION));
    VertexShader = glCreateShader(GL_VERTEX_SHADER);
    ReadShaderCompile(VertexShader, VertexFile);
    ShaderProg = glCreateProgram();
    glAttachShader(ShaderProg, VertexShader);
    glDeleteShader(VertexShader);
    Link(ShaderProg);
  }
 
 
void GLSL::InitGLSL(const char *VertexFile, const char *FragmentFile){
    GLenum err = glewInit();
    if (err != GLEW_OK)
    {
     printf("Error: %s\n", glewGetErrorString(err));
    }
    printf("VENDOR= %s \n", glGetString(GL_VENDOR));
    printf("GPU= %s \n", glGetString(GL_RENDERER));
    printf("OpenGL= %s \n", glGetString(GL_VERSION));
    printf("GLSL= %s \n", glGetString(GL_SHADING_LANGUAGE_VERSION));
 
   VertexShader = glCreateShader(GL_VERTEX_SHADER);
    FragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
 
   ReadShaderCompile(VertexShader, VertexFile);
    ReadShaderCompile(FragmentShader, FragmentFile);
 
   ShaderProg = glCreateProgram();
 
    glAttachShader(ShaderProg, VertexShader);
    glAttachShader(ShaderProg, FragmentShader);
 
    glDeleteShader(VertexShader);
    glDeleteShader(FragmentShader);
    Link(ShaderProg);
  }
 
void GLSL::ON(){
  glUseProgram(ShaderProg);
 }
 
void GLSL::OFF(){
  glUseProgram(0);
 }
 
GLSL::~GLSL(){
  glDeleteProgram(ShaderProg);
 }
 
 

PNG.h

 #pragma once
 #include "lodepng.h"
 
//テクスチャクラス
 class TEXTURE{
 protected:
  LodePNG_Decoder decoder;//デコーダ
  unsigned char* buffer;//バッファ
  size_t buffersize, imagesize;//サイズ
 public:
  TEXTURE();
  TEXTURE(const char* FileName);//コンストラクタ
  void LOAD_PNG(const char* FileName);//PNG読み込み
  unsigned char* image;//イメージポインタ
  unsigned int Width,Height;//画像サイズ
 };
 TEXTURE::TEXTURE(){
 }
 TEXTURE::TEXTURE(const char* FileName){
  LOAD_PNG(FileName);
 }
 void TEXTURE::LOAD_PNG(const char* FileName){
  LodePNG_Decoder_init(&decoder);
  //ロード
  LodePNG_loadFile(&buffer, &buffersize, FileName);
  //デコード
  LodePNG_decode(&decoder, &image, &imagesize, buffer, buffersize);
  //幅,高さ
  Width = decoder.infoPng.width;Height = decoder.infoPng.height;
 }
 

main.cpp

#pragma comment(linker, "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup")
#pragma comment(lib, "glew32.lib")

#include <windows.h>
#include <GL/glew.h>
#include "GLSL.h"
#include <math.h>
#include <GL/freeglut/freeglut.h>
#include "PNG.h"

float lightPos[] = {10.0f, 15.0f, 10.0f, 1.0f};//光源位置
float shadowDiffuse[] =  {0.0f,0.0f,0.0f,0.3f};//影の拡散光
float shadowSpecular[] = {0.0f,0.0f,0.0f,1.0f};//影の鏡面光

struct View{
  float pos[3];//位置(視点)
  float cnt[3];//注視点
  float dist;  //注視点から視点までの距離
  float theta; //仰角(水平面との偏角)
  float phi;   //方位角
  float fovY;  //視野角
  float nearZ; //前方クリップ面(近平面)
  float farZ;  //後方クリップ面(遠平面)
};
View view = {
 0.0f, 0.0f, 0.0f,//pos
 0.0f, 1.0f, 0.0f,//cnt
 7.0f, 30.0f, 20.0f,//dist, theta, phi
 30.0f, 1.0f, 100.0f//fovY,nearZ, farZ
};
View view0 = view;

GLSL glsl;
TEXTURE *texture[2];

float cloudpos = 0.0f;

#define PAI 3.141592f
int width = 640;
int height = 480;
int xStart, yStart;
bool flagMouse = false;
bool flagShadow = false;
int objNo = 0;
float ang = 0.0f;
float dang = 0.1f;//角度増分
GLuint texName[2];
float mixFactor = 0.5f;//テクスチャ混合率


void drawSphere(double radius, int nSlice, int nStack){
 int i, j;
 double r0, r1, th0, th1, phi;
 double p[2][3];

 for(j = 0; j < nStack; j++){
  th0 = PAI * (double)j / (double)nStack;
  th1 = PAI * (double)(j+1) / (double)nStack;
  r0 = radius * sin(th0);
  r1 = radius * sin(th1);
  p[0][2] = radius * cos(th0);
  p[1][2] = radius * cos(th1);

  glBegin(GL_QUAD_STRIP);
  for(i = 0; i <= nSlice; i++){
   phi = 2.0 * PAI * (double)i / (double)nSlice;
   p[0][0] =   r0 * sin(phi);
   p[0][1] = - r0 * cos(phi);
   p[1][0] =   r1 * sin(phi);
   p[1][1] = - r1 * cos(phi);

   glNormal3dv(p[0]);
   glVertex3dv(p[0]);

   glNormal3dv(p[1]);
   glVertex3dv(p[1]);   
  }
  glEnd();
 }
}
void drawFloor(float widthX, float widthZ, int nx, int nz){
  int i, j;
  //Floor1枚当たりの幅
  float wX = widthX / (float)nx;
  float wZ = widthZ / (float)nz;

  float diffuse[][4] = {{ 0.7f, 0.7f, 0.7f, 1.0f}, { 0.1f, 0.1f, 0.1f, 1.0f} };
  float ambient[] = { 0.2f, 0.2f, 0.2f, 1.0f};
  float specular[]= { 0.5f, 0.5f, 0.5f, 1.0f};
  glMaterialfv(GL_FRONT,GL_AMBIENT,ambient);
  glMaterialfv(GL_FRONT,GL_SPECULAR,specular);
  glMaterialf(GL_FRONT,GL_SHININESS,100);

  glNormal3f(0.0f, 1.0f, 0.0f);
  glPushMatrix();
  for (j = 0; j < nz; j++) {
    float z1 = -widthZ / 2.0f + wZ * j; float z2 = z1 + wZ;
    for (i = 0; i < nx; i++) {
      float x1 = -widthX / 2.0f + wX * i; float x2 = x1 + wX;

      glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse[(i + j) & 1]);
   glBegin(GL_QUADS);
      glVertex3f(x1, 0.0f, z1);
      glVertex3f(x1, 0.0f, z2);
      glVertex3f(x2, 0.0f, z2);
      glVertex3f(x2, 0.0f, z1);
   glEnd();
    }
  }
  glPopMatrix();
}

void drawTexSphere(double radius, int nSlice, int nStack, int nRepeatS, int nRepeatT){
 int i, j;
 double s, t0, t1, r0, r1, th0, th1, phi;
 double p[2][3];

 for(j = 0; j < nStack; j++){
  t0 = (double)j / (double)nStack;
  t1 = (double)(j+1) / (double)nStack;
  th0 = PAI * t0;
  th1 = PAI * t1;
  r0 = radius * sin(th0);
  r1 = radius * sin(th1);
  p[0][2] = radius * cos(th0);
  p[1][2] = radius * cos(th1);

  t0 = (1.0 - t0) * nRepeatT;
  t1 = (1.0 - t1) * nRepeatT;

  glEnable(GL_TEXTURE_2D);
  glBegin(GL_QUAD_STRIP);
  for(i = 0; i <= nSlice; i++){
   s = (double)i / (double)nSlice;
   phi = 2.0 * PAI * s;
   p[0][0] = - r0 * cos(phi);
   p[0][1] = - r0 * sin(phi);
   p[1][0] = - r1 * cos(phi);
   p[1][1] = - r1 * sin(phi);

   s *= nRepeatS;

   glTexCoord2d(s, t0);
   glNormal3dv(p[0]);
   glVertex3dv(p[0]);

   glTexCoord2d(s, t1);
   glNormal3dv(p[1]);
   glVertex3dv(p[1]);
  }
  glEnd();
  glDisable(GL_TEXTURE_2D);
 }
}

void resize(int w, int h){
  glViewport(0, 0, w, h);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(view.fovY, (double)w/(double)h, view.nearZ, view.farZ);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  width = w;
  height = h;
}
void idle(void){
  //再描画
  glutPostRedisplay();
}

void TexCreate(int NO,char* FileName){
  glGenTextures(1, &texName[NO]);
  texture[NO] = new TEXTURE(FileName);
  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  //テクスチャオブジェクトの作成
  glBindTexture(GL_TEXTURE_2D, texName[NO]);
  //テクスチャの指定
  glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,texture[NO]->Width,texture[NO]->Height,0,GL_RGBA,GL_UNSIGNED_BYTE,texture[NO]->image);
  //テクスチャの繰り返し方法の指定
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);//GL_CLAMP);
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);//GL_CLAMP);
  //テクスチャを拡大・縮小する方法の指定
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);//NEAREST);
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);//NEAREST);
  glBindTexture(GL_TEXTURE_2D, 0);
}
void setLight(){
  float lightAmbient0[] = {0.5, 0.5, 0.5, 1.0}; //環境光
  float lightDiffuse0[] = {1.0, 1.0, 1.0, 1.0}; //拡散光
  float lightSpecular0[] = {1.0, 1.0, 1.0, 1.0};//鏡面光
  glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmbient0);
  glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiffuse0);
  glLightfv(GL_LIGHT0, GL_SPECULAR, lightSpecular0);
  glLightfv(GL_LIGHT0, GL_POSITION, lightPos);

  glEnable(GL_LIGHT0);
  glEnable(GL_LIGHTING);
}
void setCamera(){
  float pp = PAI / 180.0f;
  view.pos[2] = view.cnt[2] + view.dist * cos(pp * view.theta) * cos(pp * view.phi);//z
  view.pos[0] = view.cnt[0] + view.dist * cos(pp * view.theta) * sin(pp * view.phi);//x
  view.pos[1] = view.cnt[1] + view.dist * sin(pp * view.theta);//y
 resize(width, height);
}
void draw_earth(void){
  float ambient[] = { 1.0f, 1.0f, 1.0f, 1.0f};
  float diffuse[] = { 0.8f, 0.8f, 0.8f, 1.0f};
  float specular[]= { 0.8f, 0.8f, 0.8f, 1.0f};

  if(flagShadow){
   glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,shadowDiffuse);
   glMaterialfv(GL_FRONT,GL_SPECULAR,shadowSpecular);
  }else{ 
   glMaterialfv(GL_FRONT,GL_AMBIENT,ambient);
   glMaterialfv(GL_FRONT,GL_DIFFUSE,diffuse);
   glMaterialfv(GL_FRONT,GL_SPECULAR,specular);
   glMaterialf(GL_FRONT,GL_SHININESS,100);
  }

  glPushMatrix();
  glTranslatef(0, 1.5, 0);
  glRotatef(ang, 0.0, 1.0, 0.0);
  glRotatef(90, 1.0, 0.0, 0.0);

  if(!flagShadow){
   drawTexSphere(0.5, 20, 20, 1, 1);
  }else{
   drawSphere(0.5, 20, 20);
  }
  glPopMatrix();
}
void CalcShadowMat(int ID, float* mat){
  float ex, ey, ez;//光源の方向
  float a, b, c, d;//床の面のパラメータ
  float s; //object中心から光源までの距離
  float x, y, z;

  x = lightPos[0];
  y = lightPos[1];
  z = lightPos[2];

  //光源の方向ベクトル
  s = sqrt(x * x + y * y + z * z);
  ex = x / s;
  ey = y / s;
  ez = z / s;

  //フロアの方向ベクトル(y方向)
  a = 0.0f;
  b = 1.0f;
  c = 0.0f;
  d = -0.001f; //フロアと影の干渉を防ぐため
 //shadow matrix
  mat[0] = b * ey + c * ez;
  mat[1] = -a * ey;
  mat[2] = -a * ez;
  mat[3] = 0.0f;
  mat[4] = -b * ex;
  mat[5] = a * ex + c * ez;
  mat[6] = -b * ez;
  mat[7] = 0.0f;
  mat[8] = -c * ex;
  mat[9] = -c * ey;
  mat[10] = a * ex + b * ey;
  mat[11] = 0.0f;
  mat[12] = -d * ex;
  mat[13] = -d * ey;
  mat[14] = -d * ez;
  mat[15] = a * ex + b * ey + c * ez;
}
void drawShadow(){
  float mat[16];

  flagShadow = true;
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);

  glDepthMask(GL_FALSE);
  //影の描画
  CalcShadowMat(1, mat);
  glPushMatrix();
   glMultMatrixf(mat);
   draw_earth();
  glPopMatrix();

  glDepthMask(GL_TRUE);
  glDisable(GL_BLEND);
  flagShadow = false;
}
void Init(void){
  glsl.InitGLSL("vertex.shader","flagment.shader");
  glClearColor(0.4f, 0.4f, 0.4f, 1.0f);

  setCamera();//視点を求める
  setLight(); //光源設定 
  glEnable(GL_DEPTH_TEST);
  glEnable(GL_NORMALIZE);
  TexCreate(0,"earth.png");
  TexCreate(1,"cloud.png");
}

void display(void){
  //テクスチャの回転
  glActiveTexture(GL_TEXTURE1);
  glMatrixMode(GL_TEXTURE);
  glLoadIdentity();
  glTranslatef(cloudpos, 0, 0);
  glMatrixMode(GL_MODELVIEW); 

  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glLoadIdentity();
  if(cos(PAI * view.theta /180.0) >= 0.0){
   gluLookAt(view.pos[0], view.pos[1]-3, view.pos[2], view.cnt[0], view.cnt[1], view.cnt[2], 0.0, 1.0, 0.0);
  }else{
   gluLookAt(view.pos[0], view.pos[1]-3, view.pos[2], view.cnt[0], view.cnt[1], view.cnt[2], 0.0, -1.0, 0.0);
  }

  glLightfv(GL_LIGHT0, GL_POSITION, lightPos);

  glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);

  //texture unit と texture object の関連付け
  glActiveTexture(GL_TEXTURE0);
  glBindTexture(GL_TEXTURE_2D, texName[0]);
  glActiveTexture(GL_TEXTURE1);
  glBindTexture(GL_TEXTURE_2D, texName[1]);

  glsl.ON();
  //fragment shaderのユニフォーム変数mixFactorのインデックスを取得
  GLint mixLoc = glGetUniformLocation(glsl.ShaderProg, "mixFactor");
  glUniform1f(mixLoc, mixFactor);
  //テクスチャユニットをシェーダ側のサンプラに関連付ける
  GLint samplerLoc0 = glGetUniformLocation(glsl.ShaderProg, "sampler0");
  GLint samplerLoc1 = glGetUniformLocation(glsl.ShaderProg, "sampler1");
  glUniform1i(samplerLoc0, 0);//GL_TEXTURE0を適用
  glUniform1i(samplerLoc1, 1);//GL_TEXTURE1を適用
  draw_earth();

  glsl.OFF();

  drawFloor(20.0, 20.0, 20, 20);
  drawShadow();

  glutSwapBuffers();
}


void mouse(int button, int state, int x, int y){
  float pp = PAI / 180.0f;

  if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN){
   xStart = x; yStart = y;
   flagMouse = true;
  }else if(button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN){
   if(x > width/4 && x < 3*width/4 && y > height/4 && y < 3*height/4){
   }else if(( x < width/4 || x > 3*width/4) && (y > height/4 && y < 3*height/4)){
    if(x < width/4 ) view.phi -= 1.0;
    else view.phi += 1.0;
    view.cnt[2] = view.pos[2] - view.dist * cos(pp * view.phi) * cos(pp * view.theta);
    view.cnt[0] = view.pos[0] - view.dist * sin(pp * view.phi) * cos(pp * view.theta);
   }else if((x > width/4 && x < 3*width/4) && (y < height/4 || y > 3*height/4)){
    if( y < height/4){
     view.theta += 1.0;
    }else{
     view.theta -= 1.0;
    }
    view.cnt[2] = view.pos[2] - view.dist * cos(pp * view.theta) * cos(pp * view.phi);
    view.cnt[0] = view.pos[0] - view.dist * cos(pp * view.theta) * sin(pp * view.phi);
    view.cnt[1] = view.pos[1] - view.dist * sin(pp * view.theta);
   }else if(x < width/8 && y > 7*height/8) view.fovY -= 1.0;//zoom in
   else if(x > 7*width/8 && y > 7*height/8) view.fovY += 1.0;//zoom out
  }else flagMouse = false;
  if(state == GLUT_DOWN) setCamera();
}

void motion(int x, int y){
  if(!flagMouse) return;
  if(cos(PAI * view.theta /180.0f) >= 0.0f){
 view.phi -= 0.5f * (float)(x - xStart) ;//tumble
  }else{
 view.phi += 0.5f * (float)(x - xStart) ;//tumble
  }
  view.theta += 0.5f * (float)(y - yStart) ;//crane

  setCamera();
  xStart = x;
  yStart = y;
}


 //タイマー
 void timer(int value){
  cloudpos-=0.0001f;
  ang += dang;
  if(ang >= 360.0f)ang = 0.0f;
  glutTimerFunc(10 , timer , 0);
 }

 int main(int argc, char *argv[])
 {
   glutInitWindowPosition(100, 100);
   glutInitWindowSize(width, height);
   glutInit(&argc, argv);
   glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
   glutCreateWindow("マルチテクスチャ");
   glutDisplayFunc(display);
   glutIdleFunc(idle);
   glutMouseFunc(mouse);
   glutMotionFunc(motion);
   glutReshapeFunc(resize);
   glutTimerFunc(10 , timer , 0);
   Init();
   glutMainLoop();
   return 0;
 }
 

 

 

 

 

 

 

最終更新:2014年02月19日 22:27
添付ファイル