スポンサーリンク

GLSLを試す (7) シェーディング

この記事の概要

モダンOpenGLではシェーディングは光源の座標から何から全部自分でUniform変数として転送した上で、反射角から何から何まで全部自分で計算しなければならない。わかる。わかるがそれは3DCGライブラリとして見たときどうなんだと・・・。


以下の記事の中のシェーダをコピペしてシェーディングを行う。

The Basics of GLSL 4.0 Shaders
https://www.gamedev.net/articles/programming/graphics/the-basics-of-glsl-40-shaders-r2861

この記事はADS シェーディング(= Ambient Diffuse Specular Shading)のソースコードが載っているのでほぼそのままコピペで使える。

Implementing per-vertex ambient, diffuse, and specular (ADS) shading
基本のADSシェーディングのコード
Using functions in shaders
基本のを関数化したもの
Implementing two-sided shading
裏面も表示できるようにしたもの
Implementing flat shading
フラットシェーディング。つまり面法線で反射計算するようなやつ。

今回はこの中でUsing Functions in Shadersの項目にあるシェーダを使う。

ただし以下私が書いたCのソースでは各面ごとに頂点を用意してそれぞれの頂点に同じ面法線を用意しているのでスムーズシェーディングのシェーダなんだけれどフラットシェーディングと同じ効果になっている。

ソースコード

C++

#pragma warning(disable:4996)

#pragma comment(lib,"glew32.lib")


#include <gl/glew.h>
#include <GL/glut.h>

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


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

void timer(int value) {
  glutPostRedisplay();
  glutTimerFunc(20, timer, 0);
}

int width, height;
void resize(int w, int h) {
  width = w;
  height = h;
}
int main(int argc, char* argv[])
{
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_RGBA);
  glutCreateWindow(argv[0]);
  glutDisplayFunc(display);
  glutReshapeFunc(resize);
  glutTimerFunc(20, timer, 0);
  glewInit(); // glewの初期化
  init();
  glutMainLoop();
  return 0;
}

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

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

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

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

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

}
GLuint VertexShaderID;
void prepare_vertex_shader() { ////////////////////////////////////////////// // シェーダを作ります VertexShaderID = glCreateShader(GL_VERTEX_SHADER); //////////////////////////////////////////// // ファイルから頂点シェーダを読み込みます。 const char* vertex_file_path = "..\\vertex.shader"; 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 = "..\\fragment.shader"; 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 set(std::vector<GLfloat>& v, GLfloat x, GLfloat y, GLfloat z) { v.push_back(x); v.push_back(y); v.push_back(z); }
 

//////////////////////////////////////////////////
// バッファ名 /////////////////////////////////// GLuint buf_v_name; GLuint buf_c_name; GLuint buf_n_name;
/////////////////////////////////////////////////// // ライトのLocation /////////////////////////////// GLint m_loc_Light_Position; GLint m_loc_Light_La; GLint m_loc_Light_Ld; GLint m_loc_Light_Ls;
///////////////////////////////////////////////////
// マテリアルのLocation /////////////////////////// GLint m_loc_Material_Ka; GLint m_loc_Material_Kd; GLint m_loc_Material_Ks; GLint m_loc_Material_Shiniess;
///////////////////////////////////////////////////
// 変換行列のLocation ///////////////////////////// GLint m_loc_ModelViewMatrix; GLint m_loc_ProjectionMatrix;
 
////////////////////////////////////////////////// // バッファ作成 ////////////////////////////////// void prepare_buffers() { std::vector<GLfloat> vtx; std::vector<GLfloat> col; std::vector<GLfloat> nrm;//法線 double size = 1; double m = -size / 2; double p = size / 2; double xp = p; double yp = p; double zp = p; double xm = m; double ym = m; double zm = m; //////////////////////////////////
  // 頂点 //////////////////////////
  //(A)
set(vtx, xp, yp, zm); set(vtx, xp, ym, zm); set(vtx, xm, yp, zm); set(vtx, xm, ym, zm); //(B) set(vtx, xp, yp, zp); set(vtx, xp, ym, zp); set(vtx, xp, yp, zm); set(vtx, xp, ym, zm); //(C) (Aの反対) set(vtx, xm, ym, zp); set(vtx, xp, ym, zp); set(vtx, xm, yp, zp); set(vtx, xp, yp, zp); //(D)(Bの反対) set(vtx, xm, ym, zm); set(vtx, xm, ym, zp); set(vtx, xm, yp, zm); set(vtx, xm, yp, zp); //上 set(vtx, xp, yp, zm); set(vtx, xm, yp, zm); set(vtx, xp, yp, zp); set(vtx, xm, yp, zp); //下 set(vtx, xp, ym, zp); set(vtx, xm, ym, zp); set(vtx, xp, ym, zm); set(vtx, xm, ym, zm); //////////////////////////////////
  // 頂点の色 //////////////////////
//(A) set(col, 1.0, 0.0, 0.0); set(col, 1.0, 0.0, 0.0); set(col, 1.0, 0.0, 0.0); set(col, 1.0, 0.0, 0.0); //(B) set(col, 0.0, 1.0, 0.0); set(col, 0.0, 1.0, 0.0); set(col, 0.0, 1.0, 0.0); set(col, 0.0, 1.0, 0.0); //(C) (Aの反対) set(col, 0.0, 0.0, 1.0); set(col, 0.0, 0.0, 1.0); set(col, 0.0, 0.0, 1.0); set(col, 0.0, 0.0, 1.0); //(D)(Bの反対) set(col, 1.0, 1.0, 0.0); set(col, 1.0, 1.0, 0.0); set(col, 1.0, 1.0, 0.0); set(col, 1.0, 1.0, 0.0); //上 set(col, 0.0, 1.0, 1.0); set(col, 0.0, 1.0, 1.0); set(col, 0.0, 1.0, 1.0); set(col, 0.0, 1.0, 1.0); //下 set(col, 1.0, 0.0, 1.0); set(col, 1.0, 0.0, 1.0); set(col, 1.0, 0.0, 1.0); set(col, 1.0, 0.0, 1.0); ////////////////////////////////// // 頂点法線 ////////////////////// //(A) set(nrm, 0, 0, -1); set(nrm, 0, 0, -1); set(nrm, 0, 0, -1); set(nrm, 0, 0, -1); //(B) set(nrm, 1, 0, 0); set(nrm, 1, 0, 0); set(nrm, 1, 0, 0); set(nrm, 1, 0, 0); //(C) (Aの反対) set(nrm, 0, 0, 1); set(nrm, 0, 0, 1); set(nrm, 0, 0, 1); set(nrm, 0, 0, 1); //(D)(Bの反対) set(nrm, -1, 0, 0); set(nrm, -1, 0, 0); set(nrm, -1, 0, 0); set(nrm, -1, 0, 0); //上 set(nrm, 0, 1, 0); set(nrm, 0, 1, 0); set(nrm, 0, 1, 0); set(nrm, 0, 1, 0); //下 set(nrm, 0, -1, 0); set(nrm, 0, -1, 0); set(nrm, 0, -1, 0); set(nrm, 0, -1, 0); glGenBuffers(1, &buf_v_name); glBindBuffer(GL_ARRAY_BUFFER, buf_v_name); glBufferData( GL_ARRAY_BUFFER, 4 * 6 * 3 * sizeof(GLfloat), &vtx[0], GL_STATIC_DRAW ); glGenBuffers(1, &buf_c_name); glBindBuffer(GL_ARRAY_BUFFER, buf_c_name); glBufferData( GL_ARRAY_BUFFER, 4 * 6 * 3 * sizeof(GLfloat), &col[0], GL_STATIC_DRAW ); glGenBuffers(1, &buf_n_name); glBindBuffer(GL_ARRAY_BUFFER, buf_n_name); glBufferData( GL_ARRAY_BUFFER, 4 * 6 * 3 * sizeof(GLfloat), &nrm[0], GL_STATIC_DRAW ); glUseProgram(ProgramID); m_loc_Light_Position = glGetUniformLocation(ProgramID, "Light.Position"); m_loc_Light_La = glGetUniformLocation(ProgramID, "Light.La"); m_loc_Light_Ld = glGetUniformLocation(ProgramID, "Light.Ld"); m_loc_Light_Ls = glGetUniformLocation(ProgramID, "Light.Ls"); m_loc_Material_Ka = glGetUniformLocation(ProgramID, "Material.Ka"); m_loc_Material_Kd = glGetUniformLocation(ProgramID, "Material.Kd"); m_loc_Material_Ks = glGetUniformLocation(ProgramID, "Material.Ks"); m_loc_Material_Shiniess = glGetUniformLocation(ProgramID, "Material.Shininess"); m_loc_ModelViewMatrix = glGetUniformLocation(ProgramID, "ModelViewMatrix"); m_loc_ProjectionMatrix = glGetUniformLocation(ProgramID, "ProjectionMatrix"); }
//////////////////////////////////////////////////////
//かなり強引に各種行列を求める
//本当はこんなことをしてはいけない
void calc_matrixes(GLfloat* proj, GLfloat* model) { double asp = width / (double)height; static int r1 = 0; static int r2 = 0; r1 += 1; r2 += 1; glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslated(0, 0, -5); glRotatef(r1, 1, 0, 0); glRotatef(r2, 0, 1, 0); glGetFloatv(GL_MODELVIEW_MATRIX, model); glLoadIdentity(); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45, asp, 0.1, 10); glGetFloatv(GL_PROJECTION_MATRIX, proj); glLoadIdentity(); }
//////////////////////////////////////////////////////
//表示
void display(void) { glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); // シェーダを使う glUseProgram(ProgramID); // 頂点バッファ:頂点 glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, buf_v_name); glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0 ); // カラーバッファを有効にする glEnableVertexAttribArray(1); glBindBuffer(GL_ARRAY_BUFFER, buf_c_name); glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE, 0, (void*)0 ); // 法線バッファを有効にする glEnableVertexAttribArray(2); glBindBuffer(GL_ARRAY_BUFFER, buf_n_name); glVertexAttribPointer( 2, 3, GL_FLOAT, GL_FALSE, 0, (void*)0 ); glViewport(0, 0, width, height); GLfloat mt_projection[16]; GLfloat mt_modelview[16]; //行列を作成 calc_matrixes(mt_projection, mt_modelview); glUniformMatrix4fv(m_loc_ProjectionMatrix, 1, GL_FALSE, mt_projection); glUniformMatrix4fv(m_loc_ModelViewMatrix, 1, GL_FALSE, mt_modelview); glUniform4f(m_loc_Light_Position, 1, 1, 2, 1); glUniform3f(m_loc_Light_La, 0.3, 0.3, 0.3); glUniform3f(m_loc_Light_Ld, 1.0, 1.0, 1.0); glUniform3f(m_loc_Light_Ls, 1.0, 1.0, 1.0); glUniform3f(m_loc_Material_Ka, 1.0, 1.0, 1.0); glUniform3f(m_loc_Material_Kd, 1.0, 1.0, 1.0); glUniform3f(m_loc_Material_Ks, 1.0, 1.0, 1.0); glUniform1f(m_loc_Material_Shiniess, 100.0); // 三角形を描きます! glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDrawArrays(GL_TRIANGLE_STRIP, 4, 4); glDrawArrays(GL_TRIANGLE_STRIP, 8, 4); glDrawArrays(GL_TRIANGLE_STRIP, 12, 4); glDrawArrays(GL_TRIANGLE_STRIP, 16, 4); glDrawArrays(GL_TRIANGLE_STRIP, 20, 4); glDisableVertexAttribArray(0); glDisableVertexAttribArray(1); glDisableVertexAttribArray(2); glFlush(); }

頂点シェーダ

#version 460 core

//Using functions in shaders

layout (location = 0) in vec3 VertexPosition;
layout (location = 1) in vec3 VertexColor;
layout (location = 2) in vec3 VertexNormal;


// CPU側から転送する
uniform mat4 ModelViewMatrix;
uniform mat4 ProjectionMatrix;

//この二つはコピペ元ではuniform変数になっているが、面倒なのでGPU側で計算する。
mat3 NormalMatrix;
mat4 MVP;


out vec3 LightIntensity;

struct LightInfo {
  vec4 Position;// Light position in eye coords.
  vec3 La;      // Ambient light intensity
  vec3 Ld;      // Diffuse light intensity
  vec3 Ls;      // Specular light intensity
};
uniform LightInfo Light;

struct MaterialInfo {
  vec3 Ka;         // Ambient reflectivity
  vec3 Kd;         // Diffuse reflectivity
  vec3 Ks;         // Specular reflectivity
  float Shininess; // Specular shininess factor
};
uniform MaterialInfo Material;


void getEyeSpace( out vec3 norm, out vec4 position )
{
  norm = normalize( NormalMatrix * VertexNormal);
  position = ModelViewMatrix * vec4(VertexPosition,1.0);
}

vec3 phongModel( vec4 position, vec3 norm )
{
  vec3 s = normalize(vec3(Light.Position - position));
  vec3 v = normalize(-position.xyz);
  vec3 r = reflect( -s, norm );
  vec3 ambient = Light.La * Material.Ka;
  float sDotN = max( dot(s,norm), 0.0 );
  vec3 diffuse = Light.Ld * Material.Kd * sDotN;
  vec3 spec = vec3(0.0);
  if( sDotN > 0.0 )
	  spec = Light.Ls * Material.Ks *
			 pow( max( dot(r,v), 0.0 ), Material.Shininess );
  return ambient + diffuse + spec;
}

void main()
{

  //MVPは本来GPUにやらせるような作業じゃないけれど、
  //CPU側のソースコードが無駄に膨れるのを避けるためここで計算する
  MVP = ProjectionMatrix * ModelViewMatrix;
  
  // モデルビュー行列の上3x3の逆行列の転置行列
  // これも多分CPU側でやったほうがいい
  NormalMatrix = transpose(inverse(mat3(ModelViewMatrix)));


  vec3 eyeNorm;
  vec4 eyePosition;
  // Get the position and normal in eye space
  getEyeSpace(eyeNorm, eyePosition);

  // Evaluate the lighting equation.
  LightIntensity = phongModel( eyePosition, eyeNorm );

  gl_Position = MVP * vec4(VertexPosition,1.0);
}	

フラグメントシェーダ

#version 460 core

out vec4 FragColor;

in vec3 LightIntensity;

void main() {
	FragColor = vec4(LightIntensity, 1.0);
}

実行結果1

色がついていないように見えるのはMaterialで渡したのがグレイスケールなのでそれがそのまま反映されている。オブジェクト単位で同じ色を使うなら素直な方法だが頂点ごとに色指定したい場合はどうやってVertexColorを反映させるか・・・。

頂点シェーダ(色反映版)

マテリアルに VertexColor をかけてみた。C++側から渡したMaterialはどれもグレースケールなのでそのまま重みとして使うことができる。

#version 460 core

//Using functions in shaders

layout (location = 0) in vec3 VertexPosition;
layout (location = 1) in vec3 VertexColor;
layout (location = 2) in vec3 VertexNormal;


// CPU側から転送する
uniform mat4 ModelViewMatrix;
uniform mat4 ProjectionMatrix;

//この二つはコピペ元ではuniform変数になっているが、面倒なのでGPU側で計算する。
mat3 NormalMatrix;
mat4 MVP;


out vec3 LightIntensity;

struct LightInfo {
  vec4 Position;// Light position in eye coords.
  vec3 La;      // Ambient light intensity
  vec3 Ld;      // Diffuse light intensity
  vec3 Ls;      // Specular light intensity
};
uniform LightInfo Light;

struct MaterialInfo {
  vec3 Ka;         // Ambient reflectivity
  vec3 Kd;         // Diffuse reflectivity
  vec3 Ks;         // Specular reflectivity
  float Shininess; // Specular shininess factor
};
uniform MaterialInfo Material;


void getEyeSpace( out vec3 norm, out vec4 position )
{
  norm = normalize( NormalMatrix * VertexNormal);
  position = ModelViewMatrix * vec4(VertexPosition,1.0);
}

vec3 phongModel( vec4 position, vec3 norm )
{
  vec3 s = normalize(vec3(Light.Position - position));
  vec3 v = normalize(-position.xyz);
  vec3 r = reflect( -s, norm );
  vec3 ambient = Light.La * Material.Ka * VertexColor;
  float sDotN = max( dot(s,norm), 0.0 );
  vec3 diffuse = Light.Ld * Material.Kd * VertexColor * sDotN;
  vec3 spec = vec3(0.0);
  if( sDotN > 0.0 )
	  spec = Light.Ls * Material.Ks *
			 pow( max( dot(r,v), 0.0 ), Material.Shininess );
  return ambient + diffuse + spec;
}

void main()
{

  //MVPは本来GPUにやらせるような作業じゃないけれど、
  //CPU側のソースコードが無駄に膨れるのを避けるためここで計算する
  MVP = ProjectionMatrix * ModelViewMatrix;
  
  // モデルビュー行列の上3x3の逆行列の転置行列
  // これも多分CPU側でやったほうがいい
  NormalMatrix = transpose(inverse(mat3(ModelViewMatrix)));


  vec3 eyeNorm;
  vec4 eyePosition;
  // Get the position and normal in eye space
  getEyeSpace(eyeNorm, eyePosition);

  // Evaluate the lighting equation.
  LightIntensity = phongModel( eyePosition, eyeNorm );

  gl_Position = MVP * vec4(VertexPosition,1.0);
}	

コメントを残す

メールアドレスが公開されることはありません。

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


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