スポンサーリンク

| キーワード:

glewを使用しないでC++からGLSLを使用

glewのライセンス

https://github.com/nigels-com/glew#copyright-and-licensing

GLEW is originally derived from the EXTGL project by Lev Povalahev. The source code is licensed under the Modified BSD License, the Mesa 3-D License (MIT) and the Khronos License (MIT).

The automatic code generation scripts are released under the GNU GPL.

glewはBSD LicenseかMITライセンス。普通誰も気にしないが、気に入らない場合は使用できない。

けれどやっていることはヘッダファイル側で定数を#defineしているのと、dll側でwglGetProcAddressしているだけ(多分)なので、必要な部分だけ自前で書くことも不可能ではない。

myglinit.hpp

#pragma once

#include <gl/GL.h>

typedef ptrdiff_t GLsizeiptr;
typedef char GLchar;

//サンプルのコンパイルに必要なものだけを取り出したもの


// 関数ポインタ型
using PFNGLGENBUFFERSPROC               = void(_stdcall*) (GLsizei n, GLuint* buffers);
using PFNGLBINDBUFFERPROC               = void(_stdcall*) (GLenum target, GLuint buffer);
using PFNGLBUFFERDATAPROC               = void(_stdcall*) (GLenum target, GLsizeiptr size, const void* data, GLenum usage);
using PFNGLCREATESHADERPROC             = GLuint(_stdcall*) (GLenum type);
using PFNGLSHADERSOURCEPROC             = void(_stdcall*) (GLuint shader, GLsizei count, const GLchar* const* string, const GLint* length);
using PFNGLCOMPILESHADERPROC            = void(_stdcall*) (GLuint shader);
using PFNGLGETSHADERIVPROC              = void(_stdcall*) (GLuint shader, GLenum pname, GLint* param);
using PFNGLGETSHADERINFOLOGPROC         = void(_stdcall*) (GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
using PFNGLCREATEPROGRAMPROC            = GLuint(_stdcall*) (void);
using PFNGLATTACHSHADERPROC             = void(_stdcall*) (GLuint program, GLuint shader);
using PFNGLLINKPROGRAMPROC              = void(_stdcall*) (GLuint program);
using PFNGLGETPROGRAMIVPROC             = void(_stdcall*) (GLuint program, GLenum pname, GLint* param);
using PFNGLGETPROGRAMINFOLOGPROC        = void(_stdcall*) (GLuint program, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
using PFNGLUSEPROGRAMPROC               = void(_stdcall*) (GLuint program);
using PFNGLENABLEVERTEXATTRIBARRAYPROC  = void(_stdcall*) (GLuint index);
using PFNGLVERTEXATTRIBPOINTERPROC      = void(_stdcall*) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* pointer);
using PFNGLDISABLEVERTEXATTRIBARRAYPROC = void(_stdcall*) (GLuint index);

// 定数
#define GL_ARRAY_BUFFER    0x8892
#define GL_STATIC_DRAW     0x88E4
#define GL_VERTEX_SHADER   0x8B31
#define GL_COMPILE_STATUS  0x8B81
#define GL_INFO_LOG_LENGTH 0x8B84
#define GL_FRAGMENT_SHADER 0x8B30
#define GL_LINK_STATUS     0x8B82

// wglGetProcAddressで取得したアドレスを格納する変数(グローバル)
PFNGLGENBUFFERSPROC               glGenBuffers;
PFNGLBINDBUFFERPROC               glBindBuffer;
PFNGLBUFFERDATAPROC               glBufferData;
PFNGLCREATESHADERPROC             glCreateShader;
PFNGLSHADERSOURCEPROC             glShaderSource;
PFNGLCOMPILESHADERPROC            glCompileShader;
PFNGLGETSHADERIVPROC              glGetShaderiv;
PFNGLGETSHADERINFOLOGPROC         glGetShaderInfoLog;
PFNGLCREATEPROGRAMPROC            glCreateProgram;
PFNGLATTACHSHADERPROC             glAttachShader;
PFNGLLINKPROGRAMPROC              glLinkProgram;
PFNGLGETPROGRAMIVPROC             glGetProgramiv;
PFNGLGETPROGRAMINFOLOGPROC        glGetProgramInfoLog;
PFNGLUSEPROGRAMPROC               glUseProgram;
PFNGLENABLEVERTEXATTRIBARRAYPROC  glEnableVertexAttribArray;
PFNGLVERTEXATTRIBPOINTERPROC      glVertexAttribPointer;
PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray;


//関数のアドレスを取得
inline void myglinit() {

  glAttachShader             = (PFNGLATTACHSHADERPROC)wglGetProcAddress("glAttachShader");
  glBindBuffer               = (PFNGLBINDBUFFERPROC)wglGetProcAddress("glBindBuffer");
  glBufferData               = (PFNGLBUFFERDATAPROC)wglGetProcAddress("glBufferData");
  glCompileShader            = (PFNGLCOMPILESHADERPROC)wglGetProcAddress("glCompileShader");
  glCreateProgram            = (PFNGLCREATEPROGRAMPROC)wglGetProcAddress("glCreateProgram");
  glCreateShader             = (PFNGLCREATESHADERPROC)wglGetProcAddress("glCreateShader");
  glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC)wglGetProcAddress("glDisableVertexAttribArray");
  glEnableVertexAttribArray  = (PFNGLENABLEVERTEXATTRIBARRAYPROC)wglGetProcAddress("glEnableVertexAttribArray");
  glGenBuffers               = (PFNGLGENBUFFERSPROC)wglGetProcAddress("glGenBuffers");
  glGetProgramInfoLog        = (PFNGLGETPROGRAMINFOLOGPROC)wglGetProcAddress("glGetProgramInfoLog");
  glGetProgramiv             = (PFNGLGETPROGRAMIVPROC)wglGetProcAddress("glGetProgramiv");
  glGetShaderInfoLog         = (PFNGLGETSHADERINFOLOGPROC)wglGetProcAddress("glGetShaderInfoLog");
  glGetShaderiv              = (PFNGLGETSHADERIVPROC)wglGetProcAddress("glGetShaderiv");
  glLinkProgram              = (PFNGLLINKPROGRAMPROC)wglGetProcAddress("glLinkProgram");
  glShaderSource             = (PFNGLSHADERSOURCEPROC)wglGetProcAddress("glShaderSource");
  glUseProgram               = (PFNGLUSEPROGRAMPROC)wglGetProcAddress("glUseProgram");
  glVertexAttribPointer      = (PFNGLVERTEXATTRIBPOINTERPROC)wglGetProcAddress("glVertexAttribPointer");

}

サンプルコード(本体)

元のソースコード

#include <iostream>

#pragma warning(disable:4996)

//#pragma comment(lib,"glew32.lib")  --- しない


//#include <gl/glew.h>   --- しない
#include <Windows.h>
#include <GL/glut.h>

#include <fstream>
#include <sstream>
#include <vector>
#include <algorithm>

#include "myglinit.hpp"


void init(void);
void display(void);

void link_program();
void prepare_buffers();
void prepare_vertex_shader();
void prepare_fragment_shader();



int main(int argc, char *argv[])
{
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_RGBA);
  glutCreateWindow(argv[0]);
  glutDisplayFunc(display);
  //■ glewInit(); // glewの初期化 --- しない
  myglinit();// glewInit()関数の代わり
  init();
  glutMainLoop();
  return 0;
}

void init(void)
{

  //頂点データと色情報の作成
  prepare_buffers();

  //頂点シェーダの準備
  prepare_vertex_shader();

  //フラグメントシェーダの準備
  prepare_fragment_shader();

  //プログラムのリンク
  link_program();

}

//バッファとデータ
typedef GLfloat points_t[3];
GLuint vertexbuffer;//バッファのIDを格納する変数
GLuint colorbuffer;//バッファのIDを格納する変数
points_t position[3];
points_t color[3];

//////////////////////////////////////////////////
void prepare_buffers() {

  //頂点座標
  position[0][0] = 0;
  position[0][1] = 0.5;
  position[0][2] = 0;

  position[1][0] = 0.5;
  position[1][1] = -0.5;
  position[1][2] = 0;

  position[2][0] = -0.5;
  position[2][1] = -0.5;
  position[2][2] = 0;


  //色
  color[0][0] = 1.0;
  color[0][1] = 0.0;
  color[0][2] = 0.0;

  color[1][0] = 0.0;
  color[1][1] = 1.0;
  color[1][2] = 0.0;

  color[2][0] = 0.0;
  color[2][1] = 0.0;
  color[2][2] = 1.0;

  glGenBuffers(1, &vertexbuffer);
  glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
  glBufferData(
    GL_ARRAY_BUFFER,
    3 * 3 * sizeof(GLfloat),
    position,
    GL_STATIC_DRAW);

  glGenBuffers(1, &colorbuffer);
  glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
  glBufferData(
    GL_ARRAY_BUFFER,
    3 * 3 * sizeof(GLfloat),
    color,
    GL_STATIC_DRAW);

}

GLuint VertexShaderID;

void prepare_vertex_shader() {

  //////////////////////////////////////////////
    // シェーダを作ります
    // (作るといっても宣言みたいなもの)
  VertexShaderID = glCreateShader(GL_VERTEX_SHADER);

  ////////////////////////////////////////////
  // ファイルから頂点シェーダを読み込みます。
  // 注意 ここはSTLのifstreamとかstringstreamの使い方の話で、
  // OpenGL命令は一つも無い。
  const char * vertex_file_path = ".\\verts.sh";
  std::string VertexShaderCode;
  std::ifstream VertexShaderStream(vertex_file_path, std::ios::in);
  if (VertexShaderStream.is_open())
  {
    std::stringstream sstr;
    sstr << VertexShaderStream.rdbuf();
    VertexShaderCode = sstr.str();
    VertexShaderStream.close();
  }
  ////////////////////////////////////////////
  // 頂点シェーダをコンパイルします。
  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]);
  }

}

GLuint FragmentShaderID;

void prepare_fragment_shader() {

  /////////////////////////////////////////////
  // シェーダを作ります。
  FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);

  /////////////////////////////////////////////
  // ファイルからフラグメントシェーダを読み込みます。
  const char * fragment_file_path = ".\\frags.sh";
  std::string FragmentShaderCode;
  std::ifstream FragmentShaderStream(fragment_file_path, std::ios::in);
  if (FragmentShaderStream.is_open()) {
    std::stringstream sstr;
    sstr << FragmentShaderStream.rdbuf();
    FragmentShaderCode = sstr.str();
    FragmentShaderStream.close();
  }

  /////////////////////////////////////////////
  // フラグメントシェーダをコンパイルします。
  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]);
  }
}

GLuint ProgramID;

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);

  glLoadIdentity();
  glTranslated(0.5, 0.0, 0.0);//平行移動

  // シェーダを使う
  glUseProgram(ProgramID);

  // 頂点バッファ:頂点
  glEnableVertexAttribArray(0);
  glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
  glVertexAttribPointer(
    0,                  // シェーダ内のlayoutとあわせる
    3,                  // 1要素の要素数(x,y,z)で3要素
    GL_FLOAT,           // タイプ
    GL_FALSE,           // 正規化しない(データが整数型の時)
    0,                  // ストライド
    (void*)0            // 配列バッファオフセット
  );

  // カラーバッファを有効にする
  glEnableVertexAttribArray(1);
  glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
  glVertexAttribPointer(
    1,                  // シェーダ内のlayoutとあわせる
    3,                  // 1要素の要素数(r,g,b)で3要素
    GL_FLOAT,           // タイプ
    GL_FALSE,           // 正規化しない(データが整数型の時)
    0,                  // ストライド
    (void*)0            // 配列バッファオフセット
  );

  // 三角形を描きます!
  glDrawArrays(GL_TRIANGLES, 0, 3);

  glDisableVertexAttribArray(0);//バッファを無効にする
  glDisableVertexAttribArray(1);

  glFlush();
}

後書き

他にもリンクしちゃいけないときとかもこの方法をとることになると思う。ただその場合はglext.hを使えば自分で#define やtypedefを並べたりしなくて良くなる・・・のか?

「MIT Licenseを気にする状況ってどんな状況?」とか言う人がいそうなのだけれど、個人でならともかく業務でやっていると、自分より上の層で「俺が理解できないものは低品質なガラクタ」とか「俺の知らないものは使えないゴミ」等の謎理論で意味不明な利用制限がかかったりするので、できるかできないかぐらいは把握しておいた方がいい。後は、そういう契約になってるんだ!とか後から言われたり。しないかな?しないよね。

5 件のコメント

  • PFNGLGENBUFFERSPROC glGenBuffers;
    PFNGLBINDBUFFERPROC glBindBuffer;
    PFNGLBUFFERDATAPROC glBufferData;
    PFNGLCREATESHADERPROC glCreateShader;
    PFNGLSHADERSOURCEPROC glShaderSource;
    PFNGLCOMPILESHADERPROC glCompileShader;
    PFNGLGETSHADERIVPROC glGetShaderiv;
    PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog;
    PFNGLCREATEPROGRAMPROC glCreateProgram;
    PFNGLATTACHSHADERPROC glAttachShader;
    PFNGLLINKPROGRAMPROC glLinkProgram;
    PFNGLGETPROGRAMIVPROC glGetProgramiv;
    PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog;
    PFNGLUSEPROGRAMPROC glUseProgram;
    PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray;
    PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer;
    PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray;

    ここらへんって何をしているのですか?

    • こんにちは。

      > PFNGLGENBUFFERSPROC glGenBuffers;
      > …

      > ここらへんって何をしているのですか?

      変数宣言です。
      例えば「int count;」と同じように「型 名前;」となています。

      つまり以下はポインタ型変数glGenBuffersを宣言しています。
      PFNGLGENBUFFERSPROC glGenBuffers;

      ポインタ変数を宣言したら中にアドレスを入れます。それが
      glGenBuffers
      = (PFNGLGENBUFFERSPROC)
      wglGetProcAddress(“glGenBuffers”);

      です。

  • 色々なことを質問したいので、よろしくお願いいたします。
    私もGLEW等のヘルパーツールを使わないでOpenGLの拡張機能を動作させたいと思っています。

    (1) inline void myglinit() の中で、
    glCreateProgram = (PFNGLCREATEPROGRAMPROC)wglGetProcAddress(“glCreateProgram”);

    この行でglCreateProgramに0が代入されてしまいますが、何がいけないのですか。
    (2) OpenGL32.dllにはglStart()などの、旧時の関数のエントリーポイントは入っていますが、glCreateProgram()のエントリーポイントは無いようです。
    glCreateProgram()が実体として入っているdllやlibはどこにあるのでしょうか。

    オカダ・システムエンジニアリング研究所
    イラスト作成支援ソフト(フリーソフト) ぷりスカ(R)の開発者です。

    • こんにちは。

      (1)

      ドライバやDLLが古いなどの環境問題でない場合、OpenGLコンテキストを作成していないか有効化していない可能性が思いつきます。

      この記事のサンプルコードはglutの初期化が終わった時点でコンテキストが作成されて有効になっています。

      GLUTを使わないで、CreateWindowなどでウィンドウを作成するのであれば、
      1. wglCreateContext(HDC) でコンテキストを作成し、
      2. wglMakeCurrent(HDC, HRC)でコンテキストを有効化し、
      3. wglGetProcAddress で関数をロード

      の順番でなければwglGetProcAddressは0を返します。

      (2)

      少し調べたところ、Windows版のopengl32.dllにはOpenGL1.1までの関数しか入っていないようです。
      それ以降の機能はwglGetProcAddressが、「ドライバから」フェッチしてくるようです。

  • 家事が多忙だったので返答が遅れました。申し訳ございません。
    こちらでも多くのことを調査し、理解が深まりました。最終的にwglGetProcAddressによってglCreateProgram()のアドレスを求め、int型のプログラム番号を正常に得ることができました。
    glCreateProgram()の実体のライブラリーは隠蔽されているようですが、アドレスを求める方法がわかったので解決済みとします。御回答まことにありがとうございました。
    現在設計中のプロジェクトは、アプリケーションはVB.netで、ネイティブの各種APIを頻繁に駆使するためにC++/CLIでC++のラッパーを作っています。glCreateProgram = (PFNGLCREATEPROGRAMPROC)wglGetProcAddress(“glCreateProgram”);
    などの箇所はC++/CLIに移植しなければならないのですが、マネージド型の文法がわからず移植が進みません。この辺りについて次回は質問させていただきます。よろしくお願いいたします。

    オカダ・システムエンジニアリング研究所

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)


この記事のトラックバックURL: