以前都是用Cg的,現在改用GLSL,又要重新學,不過兩種語言很多都是相通的。
下面的例子是實現繪制一個三角形的簡單程序。采用了VBO(veretx buffer object)、VAO(vertex array object)等OpenGL的一些新特性。越往后發展,可編程管線肯定是大勢所趨,OpenGL里原來的一些固定管線的內容肯定會被廢棄掉。所以從現在開始寫程序就要養成使用新特性、采用可編程管線技術的好習慣。
一、VAO、VBO介紹
隨著OpenGL狀態和固定管線模式的移除,我們不在用任何glEnable函數調用,而且也不會有glVertex、glColor等函數調用。這就意味著我們需要一種新的方式來將數據傳輸到圖形卡以渲染圖形。我們可以采用VBO,或者是在OpenGL3以上版本引入的新的特性,叫做VAO。通過它,我們可以把頂點數據和顏色存儲在不同的VBO中,但是在同一個VAO中。對于法線數據或者其他的頂點信息也是一樣。
VAO,是這樣一種方式:把對象信息直接存儲在圖形卡中,而不是在當我們需要的時候傳輸到圖形卡。這就是Direct3D所采用得方式,而在OpenGL中只有OpenGL3.X以上的版本中采用。這就意味著我們的應用程序不用將數據傳輸到圖形卡或者是從圖形卡輸出,這樣也就獲得了額外的性能提升。
使用VAO并不難。我們不需要大量的glVertex調用,而是把頂點數據存儲在數組中,然后放進VBO,最后在VAO中存儲相關的狀態。記住:VAO中并沒有存儲頂點的相關屬性數據。OpenGL會在后臺為我們完成其他的功能。
?
使用VAO的步驟:
1、產生VAO
void glGenVertexArrays(GLsizei n, ? GLuint *arrays);
n:要產生的VAO對象的數量。
arrays:存放產生的VAO對象的名稱。
2、綁定VAO
void glBindVertexArray(GLuint array);
array:要綁定的頂點數組的名字。
3、產生VBOs
void glGenBuffers(GLsizei ?n, ? GLuint * ? buffers);
產生緩沖區對象的名稱。
參數含義和glGenVertexArrays類似。
4、綁定VBOs
void glBindBuffer(GLenum ?target, ? GLuint ? buffer);
綁定一個緩沖區對象。
target可能取值是:GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, GL_PIXEL_PACK_BUFFER, or GL_PIXEL_UNPACK_BUFFER.
當進行綁定之后,以前的綁定就失效了。
5、給VBO分配數據:
void glBufferData(?? GLenum? ??????? target,
??????? GLsizeiptr? ??? size,
??????? const GLvoid *? ????? data,
??????? GLenum? ??????? usage);
target可能取值為:GL_ARRAY_BUFFER(表示頂點數據), GL_ELEMENT_ARRAY_BUFFER(表示索引數據),GL_PIXEL_PACK_BUFFER(表示從OpenGL獲取的的像素數據), or GL_PIXEL_UNPACK_BUFFER(表示傳遞給OpenGL的像素數據).
參數含義:
size:緩沖區對象字節數
data:指針:指向用于拷貝到緩沖區對象的數據。或者是NULL,表示暫時不分配數據。
6、定義存放頂點屬性數據的數組:
首先需要啟用VAO中對應的頂點屬性數組:
void glEnableVertexAttribArray(????? GLuint? ? index);
index:指定了需要啟用的頂點屬性數組的索引
注意:它只在OpenGL2.0及其以上版本才有。
7、給對應的頂點屬性數組指定數據:
void glVertexAttribPointer(????? GLuint? ? index,
??????? GLint? ??? size,
??????? GLenum? ??????? type,
??????? GLboolean? ?? normalized,
??????? GLsizei? stride,
??????? const GLvoid *? ????? pointer);
index:要指定數據的頂點屬性數組的索引。
size:每個頂點屬性的數據個數。可能的取值是1、2、3或者4.初始值是4.
type:數組中每個數據的類型。可能的取值是:GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_INT, GL_UNSIGNED_INT, GL_FLOAT, or GL_DOUBLE。初始值是GL_FLOAT。
normalized:指定頂點數在被訪問的時候是否需要被歸一化。
注意:如果有個非零的緩沖對象綁定到GL_ARRAY_BUFFER,那么pointer就是對應的緩沖區對象的偏移量。
?stride:兩個連續頂點的屬性之間的偏移量。
pointer:指向數組中的第一個頂點屬性的第一個數據。
8、然后在進行渲染的時候,只需要綁定對應的VAO即可,操作起來十分方便。
glBindVertexArray(vaoHandle);
9、使用完畢之后需要清除綁定。
glBindVertexArray(0);
VAO參考資料:
VAO wiki
關于VAO的一篇博客
VAO
事實上,在這個簡單的程序中,不用VAO,只用VBO也一樣可以實現。只是采用VAO可以進一步提升性能,而且在較新版本的OpenGL中不用VAO的方式會被逐漸廢棄。
二、GLSL入門
關于GLSL,可以參考紅寶書的附錄,上面介紹了GLSL的入門知識。
這里也有一個很好的入門教程,英文的:
GLSL入門tutorial
三、程序實例
下面貼出源代碼(OpenGL,GLSL實現):
basic.vert:
#version 400 in vec3 VertexPosition; in vec3 VertexColor; out vec3 Color; void main () { Color =VertexColor; gl_Position = vec4(VertexPosition,1.0 ); }
basic.frag:
#version 400 in vec3 Color; out vec4 FragColor; void main () { FragColor = vec4(Color,1.0 ); }
主文件:
main.cpp:
#pragma comment(lib,"glew32.lib" ) #include <GL/glew.h> #include "textfile.h" #include <GL/glut.h> #include <iostream> using namespace std ; GLuint vShader,fShader; float positionData[] = { -0.8f , -0.8f , 0.0f , 0.8f , -0.8f , 0.0f , 0.0f , 0.8f , 0.0f }; float colorData[] = { 1.0f , 0.0f , 0.0f , 0.0f , 1.0f , 0.0f , 0.0f , 0.0f , 1.0f }; GLuint vaoHandle; void initShader (const char *VShaderFile,const char *FShaderFile) { const GLubyte *renderer = glGetString( GL_RENDERER ); const GLubyte *vendor = glGetString( GL_VENDOR ); const GLubyte *version = glGetString( GL_VERSION ); const GLubyte *glslVersion = glGetString( GL_SHADING_LANGUAGE_VERSION ); GLint major, minor; glGetIntegerv(GL_MAJOR_VERSION, &major); glGetIntegerv(GL_MINOR_VERSION, &minor); cout << "GL Vendor :" << vendor << endl ; cout << "GL Renderer : " << renderer << endl ; cout << "GL Version (string) : " << version << endl ; cout << "GL Version (integer) : " << major << "." << minor << endl ; cout << "GLSL Version : " << glslVersion << endl ; vShader = glCreateShader(GL_VERTEX_SHADER); if (0 == vShader) { cerr << "ERROR : Create vertex shader failed" << endl ; exit (1 ); } const GLchar *vShaderCode = textFileRead(VShaderFile); const GLchar *vCodeArray[1 ] = {vShaderCode}; glShaderSource(vShader,1 ,vCodeArray,NULL ); glCompileShader(vShader); GLint compileResult; glGetShaderiv(vShader,GL_COMPILE_STATUS,&compileResult); if (GL_FALSE == compileResult) { GLint logLen; glGetShaderiv(vShader,GL_INFO_LOG_LENGTH,&logLen); if (logLen > 0 ) { char *log = (char *)malloc (logLen); GLsizei written; glGetShaderInfoLog(vShader,logLen,&written,log ); cerr << "vertex shader compile log : " << endl ; cerr << log << endl ; free (log ); } } fShader = glCreateShader(GL_FRAGMENT_SHADER); if (0 == fShader) { cerr << "ERROR : Create fragment shader failed" << endl ; exit (1 ); } const GLchar *fShaderCode = textFileRead(FShaderFile); const GLchar *fCodeArray[1 ] = {fShaderCode}; glShaderSource(fShader,1 ,fCodeArray,NULL ); glCompileShader(fShader); glGetShaderiv(fShader,GL_COMPILE_STATUS,&compileResult); if (GL_FALSE == compileResult) { GLint logLen; glGetShaderiv(fShader,GL_INFO_LOG_LENGTH,&logLen); if (logLen > 0 ) { char *log = (char *)malloc (logLen); GLsizei written; glGetShaderInfoLog(fShader,logLen,&written,log ); cerr << "fragment shader compile log : " << endl ; cerr << log << endl ; free (log ); } } GLuint programHandle = glCreateProgram(); if (!programHandle) { cerr << "ERROR : create program failed" << endl ; exit (1 ); } glAttachShader(programHandle,vShader); glAttachShader(programHandle,fShader); glLinkProgram(programHandle); GLint linkStatus; glGetProgramiv(programHandle,GL_LINK_STATUS,&linkStatus); if (GL_FALSE == linkStatus) { cerr << "ERROR : link shader program failed" << endl ; GLint logLen; glGetProgramiv(programHandle,GL_INFO_LOG_LENGTH, &logLen); if (logLen > 0 ) { char *log = (char *)malloc (logLen); GLsizei written; glGetProgramInfoLog(programHandle,logLen, &written,log ); cerr << "Program log : " << endl ; cerr << log << endl ; } } else { glUseProgram(programHandle); } } void initVBO () { GLuint vboHandles[2 ]; glGenBuffers(2 , vboHandles); GLuint positionBufferHandle = vboHandles[0 ]; GLuint colorBufferHandle = vboHandles[1 ]; glBindBuffer(GL_ARRAY_BUFFER,positionBufferHandle); glBufferData(GL_ARRAY_BUFFER,9 * sizeof (float ), positionData,GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER,colorBufferHandle); glBufferData(GL_ARRAY_BUFFER,9 * sizeof (float ), colorData,GL_STATIC_DRAW); glGenVertexArrays(1 ,&vaoHandle); glBindVertexArray(vaoHandle); glEnableVertexAttribArray(0 ); glEnableVertexAttribArray(1 ); glBindBuffer(GL_ARRAY_BUFFER, positionBufferHandle); glVertexAttribPointer( 0 , 3 , GL_FLOAT, GL_FALSE, 0 , (GLubyte *)NULL ); glBindBuffer(GL_ARRAY_BUFFER, colorBufferHandle); glVertexAttribPointer( 1 , 3 , GL_FLOAT, GL_FALSE, 0 , (GLubyte *)NULL ); } void init () { GLenum err = glewInit(); if ( GLEW_OK != err ) { cout <<"Error initializing GLEW: " << glewGetErrorString(err) << endl ; } initShader("basic.vert" ,"basic.frag" ); initVBO(); glClearColor(0.0 ,0.0 ,0.0 ,0.0 ); } void display () { glClear(GL_COLOR_BUFFER_BIT); glBindVertexArray(vaoHandle); glDrawArrays(GL_TRIANGLES,0 ,3 ); glBindVertexArray(0 ); glutSwapBuffers(); } void keyboard (unsigned char key,int x,int y) { switch (key) { case 27 : glDeleteShader(vShader); glUseProgram(0 ); break ; } } int main (int argc,char ** argv) { glutInit(&argc,argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); glutInitWindowSize(600 ,600 ); glutInitWindowPosition(100 ,100 ); glutCreateWindow("GLSL Test : Draw a triangle" ); init(); glutDisplayFunc(display); glutKeyboardFunc(keyboard); glutMainLoop(); return 0 ; }
讀取shader文件的程序:
textfile.h:
#ifndef TEXTFILE_H #define TEXTFILE_H #include <stdio.h> #include <stdlib.h> #include <string.h> char *textFileRead (const char *fn) ;int textFileWrite (char *fn, char *s) ;unsigned char *readDataFromFile (char *fn) ; #endif
textfile.cpp:
#include "textfile.h" unsigned char * readDataFromFile (char *fn) { FILE *fp; unsigned char *content = NULL ; int count=0 ; if (fn != NULL ) { fp = fopen(fn,"rb" ); if (fp != NULL ) { fseek(fp, 0 , SEEK_END); count = ftell(fp); rewind(fp); if (count > 0 ) { content = (unsigned char *)malloc (sizeof (unsigned char ) * (count+1 )); count = fread(content,sizeof (unsigned char ),count,fp); content[count] = '\0' ; } fclose(fp); } } return content; } char *textFileRead (const char *fn) { FILE *fp; char *content = NULL ; int count=0 ; if (fn != NULL ) { fp = fopen(fn,"rt" ); if (fp != NULL ) { fseek(fp, 0 , SEEK_END); count = ftell(fp); rewind(fp); if (count > 0 ) { content = (char *)malloc (sizeof (char ) * (count+1 )); count = fread(content,sizeof (char ),count,fp); content[count] = '\0' ; } fclose(fp); } } return content; } int textFileWrite (char *fn, char *s) { FILE *fp; int status = 0 ; if (fn != NULL ) { fp = fopen(fn,"w" ); if (fp != NULL ) { if (fwrite(s,sizeof (char ),strlen (s),fp) == strlen (s)) status = 1 ; fclose(fp); } } return (status); } 運行結果:
總結
以上是生活随笔 為你收集整理的【OpenGL4.0】GLSL渲染语言入门与VBO、VAO使用:绘制一个三角形 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。