OpenGL Vision | OpenGL Shading Language Version |
OpenGL 4.0 | GLSL 4.0 |
OpenGL 3.3 | GLGL 3.3 |
OpenGL 3.2 | GLSL 1.5 |
OpenGL 3.1 | GLSL 1.4 |
OpenGL 3.0 | GLSL 1.3 |
OpenGL 2.1 | GLSL 1.2 |
OpenGL 2.0 | GLSL 1.1 |
现在第一个例子:绘制三角形
- Initial OpenGL Canvas
- Load 绘制所需的資料 / 建立儲存資料的資料結構
- Initial OpenGL state 與 各種 OpenGL buffer objects
- Draw and Display
- 接 受各種事件,根據事件變更資料結構的內容、OpenGL State 或是 OpenGL buffer objects
- 反覆流程 4 與流程 5 直至程式結束
首先是流程 2,在這個例子裡只要準備三角形的資料、Vertex Shdaer 程式碼與 Fragment Shader 程式碼,因為都直接寫死放在程式的變數內,所以只需要有變數的宣告就可以把Init () 省略。
首先三角形的資料很簡單,使用一個 Array 儲存三角形頂點資料即可。
GLfloat afVertex[][3]= { {-0.75,-0.5,0.0}, {0.75,-0.5,0.0}, {0.0,0.75,0.0} }; | 三個三角形頂點資料 |
而 Vertex Shader 與 Fragment Shader,也簡單使用 char pointer 來存。
Vertex Shader 程式碼 |
const char* cpVShader = { #version 150 in vec3 vPos; void main() { gl_Position = vec4(vPos,1); } }; |
"in vec3 vPos;" 表示 Vertex Shader 會接受 三個 float 的 Vertex Attribute 「Vpos」變數輸入。
然後 Vertex Shader 的預設 Vertex 輸出為內建的變數 gl_Position,Type 為 4個 float 的 Vector 。
因為只有要畫單純的三角形,並沒有要對三角形多做處理,所以在Vertex Shader 裡面 直接把輸入的 vPos 變成 homogenious coordinate 也就是後面再加個 1,放到 gl_Position 裡 。
Fragment Shader 程式碼 |
const char* cpFShader = { #version 150 out vec4 fColor; void main() { fColor = vec4( 1, 1, 1, 1 ); ;} }; |
"out vec4 fColor;" 宣告輸出的 Fragment 變數名稱。
然後設定三角形上面的每個 Fragment 用 ( 1, 1, 1, 1) 的白色輸出。
接下來資料都準備好了,開始進行流程 3 撰寫 initGL 的內容。
void initGL() { | |
glGenBuffers(1,&uVbo ); glBindBuffer(GL_ARRAY_BUFFER,uVbo); glBufferData(GL_ARRAY_BUFFER,sizeof(afVertex), afVertex, GL_STATIC_DRAW ); | 產生一個 Vertex Buffer Object ,並且把GL_ARRAY_BUFFER 設定為 uVbo ,然後將Array的頂點資料存到這個 GL_ARRAY_BUFFER 設定的 uVbo 這個buffer object裡。 |
uProgram = glCreateProgram(); AttachProgram( uProgram, cpVShader , GL_VERTEX_SHADER ); AttachProgram( uProgram, cpFShader , GL_FRAGMENT_SHADER ); glLinkProgram( uProgram ); | 產生一個 Shader Program,然後分別把Vertex Shader、Fragment Shader attach 至program上,最後 Link 起來可以在 Draw時使用這個 Shader Program。 |
uPos = glGetAttribLocation( uProgram, "vPos" ); | 取得 Shader Program 裡面 vPos 這個變數的位置。 |
glClearColor( 0.0, 0.0, 1.0, 1.0 ); | 設 定OpenGL Canvus 背景顏色。 |
} |
這邊 InitGL() 主要作了四件事,分別是產生一個 Vertex Buffer Object ,一個 Shader Program ,取得 Shader Program 裡面 vPos 變數的輸入位置。
最後就是顯示的部分 Display 這個 Function:
glUseProgram(uProgram); | 設定現在使用的 Shader Program為 uProgram |
glBindBuffer( GL_ARRAY_BUFFER, uVbo ); | 設定現在使用的 GL_ARRAY_BUFFER 為uVbo |
glVertexAttribPointer( uPos, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0) ); | 設定 GL_ARRAY_BUFFER 裡面 Vertex 資料的格式,並且設定資料是當作 Shader Program 內 uPos 位置的變數內容 |
glEnableVertexAttribArray( uPos ); | Enable uPos 這個 Vertex attribute array |
glDrawArrays( GL_TRIANGLES, 0, 3 ); | 最後在 Vertex Array 裡從第 0 個開始取三個 Vertex 當成三角形來畫 |
這樣就可以畫出一個白色的三角形了。
一些概念
OpenGL 的運作是一個 State Machine,所以程式執行是根據 OpenGL 內部各種 State 的狀況,因此常見的模式就是設定某些 State 狀態,呼叫 Function 針對現在的 State 執行,透過如此的反覆進行完成一個 OpenGL程式。
代码如下:
/**
* OpenGL 3.x with GLEW and GLUT - Example 02
*
* author Po-Ying Li bylee@nchc.org.tw
* version 1.0
*
* Homepage: http://viml.nchc.org.tw
*
* Copyright Po-Ying Li
*/
#include<stdio.h>
#include<stdlib.h>
#include<GL/glew.h>
#ifdef MACOSX
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif
#define BUFFER_OFFSET(offset) ((GLvoid*)offset)
GLfloat afVertex[][3]=
{
{-0.75,-0.5,0.0},
{0.75,-0.5,0.0},
{0.0,0.75,0.0}
};
const char* cpVShader = {
"#version 150\n"
""
"in vec3 vPos;"
""
"void main() {"
" gl_Position = vec4(vPos,1);"
"}"
};
const char* cpFShader = {
"#version 150\n"
""
"out vec4 fColor;"
""
"void main() {"
" fColor = vec4( 1, 1, 1, 1 );"
"}"
};
GLuint uVbo;
GLuint uPos;
GLuint uProgram;
bool AttachProgram( GLuint uProgram, const char* cpShader, GLenum eShaderType )
{
GLuint uShader;
GLint iCompleted;
uShader = glCreateShader( eShaderType );
glShaderSource( uShader, 1, &cpShader, NULL );
glCompileShader( uShader );
glGetShaderiv( uShader, GL_COMPILE_STATUS, &iCompleted );
if ( !iCompleted )
{
GLint len;
char* msg;
glGetShaderiv( uShader, GL_INFO_LOG_LENGTH, &len );
msg = (char*) malloc( len );
glGetShaderInfoLog( uShader, len, &len, msg );
fprintf( stderr, "Vertex shader compilation failure:\n%s\n", msg );
free( msg );
return false;
}else
{
glAttachShader( uProgram, uShader );
return true;
}
}
//‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
void initGL()
{
glGenBuffers(1,&uVbo );
glBindBuffer(GL_ARRAY_BUFFER,uVbo);
glBufferData(GL_ARRAY_BUFFER,sizeof(afVertex), afVertex, GL_STATIC_DRAW );
uProgram = glCreateProgram();
AttachProgram( uProgram, cpVShader , GL_VERTEX_SHADER );
AttachProgram( uProgram, cpFShader , GL_FRAGMENT_SHADER );
glLinkProgram( uProgram );
uPos = glGetAttribLocation( uProgram, "vPos" );
glClearColor( 0.0, 0.0, 1.0, 1.0 );
}
//‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
void display()
{
glClear( GL_COLOR_BUFFER_BIT );
glUseProgram(uProgram);
glBindBuffer( GL_ARRAY_BUFFER, uVbo );
glVertexAttribPointer( uPos, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0) );
glEnableVertexAttribArray( uPos );
glDrawArrays( GL_TRIANGLES, 0, 3 );
glutSwapBuffers();
}
//‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
void reshape( int width, int height )
{
glViewport( 0, 0, width, height );
}
//‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
void keyboard( unsigned char key, int x, int y )
{
switch( key ) {
case 033: // Escape Key
exit( EXIT_SUCCESS );
break;
}
glutPostRedisplay();
}
//‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
int main( int argc, char* argv[] )
{
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE );
glutCreateWindow( argv[0] );
glewInit();
initGL();
glutDisplayFunc( display );
glutReshapeFunc( reshape );
glutKeyboardFunc( keyboard );
glutMainLoop();
}