【GLSL教程】(二)在OpenGL中使用GLSL
設定GLSL
這一節講述在OpenGL中配置GLSL,假設你已經寫好了頂點shader和畫素shader。如果你還沒有準備好,可以從如下網址獲得相關內容:
http://www.3dshaders.com/home/
http://www.opengl.org/sdk/tools/ShaderDesigner/
http://developer.amd.com/archive/gpu/rendermonkey/pages/default.aspx
在OpenGL中,GLSL的shader使用的流程與C語言相似,每個shader類似一個C模組,首先需要單獨編譯(compile),然後一組編譯好的shader連線(link)成一個完整程式。
這裡將忽略ARB擴充套件,只列舉OpenGL2.0的程式碼。建議使用GLEW庫:
下面的程式碼檢查OpenGL 2.0是否可用:
- #include <GL/glew.h>
- #include <GL/glut.h>
- void main(int argc, char **argv)
- {
- glutInit(&argc, argv);
- ...
- glewInit();
- if (glewIsSupported("GL_VERSION_2_0"))
- printf("Ready for OpenGL 2.0\n");
- else
- {
- printf("OpenGL 2.0 not supported\n");
- exit(1);
- }
- setShaders();
- glutMainLoop();
- }
#include <GL/glew.h>
#include <GL/glut.h>
void main(int argc, char **argv)
{
glutInit(&argc, argv);
...
glewInit();
if (glewIsSupported("GL_VERSION_2_0"))
printf("Ready for OpenGL 2.0\n");
else
{
printf("OpenGL 2.0 not supported\n");
exit(1);
}
setShaders();
glutMainLoop();
}
下圖顯示了建立shader的必要步驟,函式的具體使用方法將在下面各小結描述:
建立shader
下圖顯示了建立shader的步驟:
首先建立一個物件作為shader的容器,這個建立函式將返回容器的控制程式碼。
- GLuint glCreateShader(GLenum shaderType);
- 引數:
- ·shaderType – GL_VERTEX_SHADER or GL_FRAGMENT_SHADER.
GLuint glCreateShader(GLenum shaderType);
引數:
·shaderType – GL_VERTEX_SHADER or GL_FRAGMENT_SHADER.
你可以建立許多shader,但記住所有的頂點shader只能有一個main函式,所有畫素shader也一樣。
下一步將新增原始碼。shader的原始碼是一個字串陣列,新增的語法如下:
- void glShaderSource(GLuint shader, int numOfStrings, const char **strings, int *lenOfStrings);
- 引數:
- ·shader – the handler to the shader.
- ·numOfStrings – the number of strings in the array.
- ·strings – the array of strings.
- ·lenOfStrings – an array with the length of each string, or NULL, meaning that the strings are NULL terminated.
void glShaderSource(GLuint shader, int numOfStrings, const char **strings, int *lenOfStrings);
引數:
·shader – the handler to the shader.
·numOfStrings – the number of strings in the array.
·strings – the array of strings.
·lenOfStrings – an array with the length of each string, or NULL, meaning that the strings are NULL terminated.
最後編譯shader:
- void glCompileShader(GLuint shader);
- 引數:
- •shader – the handler to the shader.
void glCompileShader(GLuint shader);
引數:
•shader – the handler to the shader.
建立程式
下圖顯示了獲得一個可以執行的shader程式的步驟:
首先建立一個物件,作為程式的容器。此函式返回容器的控制程式碼。
- GLuint glCreateProgram(void);
GLuint glCreateProgram(void);
你可以建立任意多個程式,在渲染時,可以在不同程式中切換,甚至在某幀返回固定功能流水線。比如你想用折射和反射shader繪製一個茶杯,然後回到固定功能生成立方體環境貼圖(cube map)顯示背景。
下面將把上一節編譯的shader附加到剛剛建立的程式中。方法如下:
- void glAttachShader(GLuint program, GLuint shader);
- 引數:
- ·program – the handler to the program.
- ·shader – the handler to the shader you want to attach.
void glAttachShader(GLuint program, GLuint shader);
引數:
·program – the handler to the program.
·shader – the handler to the shader you want to attach.
如果同時有頂點shader和片斷shader,你需要把它們都附加到程式中。你可以把多個相同型別(頂點或畫素)的shader附加到一個程式中,如同一個C程式可以有多個模組一樣,但它們只能有一個main函式。
你也可以把一個shader附加到多個程式,比如你想在不同程式中使用某個相同的shader。
最後一步是連線程式。方法如下:
- void glLinkProgram(GLuint program);
- 引數:
- ·program – the handler to the program.
void glLinkProgram(GLuint program);
引數:
·program – the handler to the program.
在連線操作之後,shader的原始碼可以被修改並重編譯,並不會影響到整個程式。
程式連線後,可以呼叫glUseProgram來使用程式。每個程式都分配了一個控制程式碼,你可以事先連線多個程式以備使用。
- void glUseProgram(GLuint prog);
- 引數:
- ·prog – the handler to the program you want to use, or zero to return to fixed functionality.
void glUseProgram(GLuint prog);
引數:
·prog – the handler to the program you want to use, or zero to return to fixed functionality.
當一個程式被使用後,如果被再次連線,它將被自動替換並投入使用,所以沒有必要再次呼叫上面這個函式。如果使用的引數為0,表示將使用固定功能流水線。
例子
下面的程式碼包含了上面描述的所有步驟,引數p,f,v是全域性的GLuint型變數。
- void setShaders()
- {
- char *vs,*fs;
- v = glCreateShader(GL_VERTEX_SHADER);
- f = glCreateShader(GL_FRAGMENT_SHADER);
- vs = textFileRead("toon.vert");
- fs = textFileRead("toon.frag");
- const char *vv = vs;
- const char *ff = fs;
- glShaderSource(v, 1, &vv, NULL);
- glShaderSource(f, 1, &ff, NULL);
- free(vs);free(fs);
- glCompileShader(v);
- glCompileShader(f);
- p = glCreateProgram();
- glAttachShader(p, v);
- glAttachShader(p, f);
- glLinkProgram(p);
- glUseProgram(p);
- }
void setShaders()
{
char *vs,*fs;
v = glCreateShader(GL_VERTEX_SHADER);
f = glCreateShader(GL_FRAGMENT_SHADER);
vs = textFileRead("toon.vert");
fs = textFileRead("toon.frag");
const char *vv = vs;
const char *ff = fs;
glShaderSource(v, 1, &vv, NULL);
glShaderSource(f, 1, &ff, NULL);
free(vs);free(fs);
glCompileShader(v);
glCompileShader(f);
p = glCreateProgram();
glAttachShader(p, v);
glAttachShader(p, f);
glLinkProgram(p);
glUseProgram(p);
}
GLUT版的完整例子如下:
http://lighthouse3d.com/wptest/wp-content/uploads/2011/03/glutglsl_2.0.zip
完整例子中包含了shader程式碼及文字檔案讀入程式。
錯誤處理
除錯shader是很困難的。目前還沒有像printf這樣的東西,雖然未來可能出現有除錯功能的開發工具。
編譯階段的狀態可以用如下函式獲得:
- void glGetShaderiv(GLuint object, GLenum type, int *param);
- 引數:
- ·object – the handler to the object. Either a shader or a program
- ·type – GL_COMPILE_STATUS.
- ·param – the return value, GL_TRUE if OK, GL_FALSE otherwise.
void glGetShaderiv(GLuint object, GLenum type, int *param);
引數:
·object – the handler to the object. Either a shader or a program
·type – GL_COMPILE_STATUS.
·param – the return value, GL_TRUE if OK, GL_FALSE otherwise.
連線階段的狀態可以用如下函式獲得:
- void glGetProgramiv(GLuint object, GLenum type, int *param);
- 引數:
- ·object – the handler to the object. Either a shader or a program
- ·type – GL_LINK_STATUS.
- ·param – the return value, GL_TRUE if OK, GL_FALSE otherwise.
void glGetProgramiv(GLuint object, GLenum type, int *param);
引數:
·object – the handler to the object. Either a shader or a program
·type – GL_LINK_STATUS.
·param – the return value, GL_TRUE if OK, GL_FALSE otherwise.
如果發生錯誤,就需要從InfoLog中找到更多的資訊。這個日誌儲存了最後一次操作的資訊,比如編譯時的警告、錯誤,連線時發生的各種問題。這個日誌甚至可以告訴你硬體是否支援你的shader。不幸的是InfoLog沒有一個規範,所以不同的驅動/硬體可能產生不同的日誌資訊。
為了獲得特定shader或程式的日誌,可以使用如下程式:
- void glGetShaderInfoLog(GLuint object, int maxLen, int *len, char *log);
- void glGetProgramInfoLog(GLuint object, int maxLen, int *len, char *log);
- 引數:
- ·object – the handler to the object. Either a shader or a program
- ·maxLen – The maximum number of chars to retrieve from the InfoLog.
- ·len – returns the actual length of the retrieved InfoLog.
- ·log – The log itself.
void glGetShaderInfoLog(GLuint object, int maxLen, int *len, char *log);
void glGetProgramInfoLog(GLuint object, int maxLen, int *len, char *log);
引數:
·object – the handler to the object. Either a shader or a program
·maxLen – The maximum number of chars to retrieve from the InfoLog.
·len – returns the actual length of the retrieved InfoLog.
·log – The log itself.
GLSL規範有必要在這裡進行一些改進:你必須知道接收InfoLog的長度。為了找到這個準確的值,使用下面的函式:
- void glGetShaderiv(GLuint object, GLenum type, int *param);
- void glGetProgramiv(GLuint object, GLenum type, int *param);
- 引數:
- ·object – the handler to the object. Either a shader or a program
- ·type – GL_INFO_LOG_LENGTH.
- ·param – the return value, the length of the InfoLog.
void glGetShaderiv(GLuint object, GLenum type, int *param);
void glGetProgramiv(GLuint object, GLenum type, int *param);
引數:
·object – the handler to the object. Either a shader or a program
·type – GL_INFO_LOG_LENGTH.
·param – the return value, the length of the InfoLog.
下面的函式可以用來列印InfoLog的內容:
- void printShaderInfoLog(GLuint obj)
- {
- int infologLength = 0;
- int charsWritten = 0;
- char *infoLog;
- glGetShaderiv(obj, GL_INFO_LOG_LENGTH,&infologLength);
- if (infologLength > 0)
- {
- infoLog = (char *)malloc(infologLength);
- glGetShaderInfoLog(obj, infologLength, &charsWritten, infoLog);
- printf("%s\n",infoLog);
- free(infoLog);
- }
- }
- void printProgramInfoLog(GLuint obj)
- {
- int infologLength = 0;
- int charsWritten = 0;
- char *infoLog;
- glGetProgramiv(obj, GL_INFO_LOG_LENGTH,&infologLength);
- if (infologLength > 0)
- {
- infoLog = (char *)malloc(infologLength);
- glGetProgramInfoLog(obj, infologLength, &charsWritten, infoLog);
- printf("%s\n",infoLog);
- free(infoLog);
- }
- }
void printShaderInfoLog(GLuint obj)
{
int infologLength = 0;
int charsWritten = 0;
char *infoLog;
glGetShaderiv(obj, GL_INFO_LOG_LENGTH,&infologLength);
if (infologLength > 0)
{
infoLog = (char *)malloc(infologLength);
glGetShaderInfoLog(obj, infologLength, &charsWritten, infoLog);
printf("%s\n",infoLog);
free(infoLog);
}
}
void printProgramInfoLog(GLuint obj)
{
int infologLength = 0;
int charsWritten = 0;
char *infoLog;
glGetProgramiv(obj, GL_INFO_LOG_LENGTH,&infologLength);
if (infologLength > 0)
{
infoLog = (char *)malloc(infologLength);
glGetProgramInfoLog(obj, infologLength, &charsWritten, infoLog);
printf("%s\n",infoLog);
free(infoLog);
}
}
清理
前面的小節講到了附加一個shader到一個程式中,這裡的呼叫是將shader從程式中分離:
- void glDetachShader(GLuint program, GLuint shader);
- 引數:
- ·program – The program to detach from.
- ·shader – The shader to detach.
void glDetachShader(GLuint program, GLuint shader);
引數:
·program – The program to detach from.
·shader – The shader to detach.
注意,只有沒有附加到任何程式的shader可以被刪除,刪除shader和程式的呼叫如下:
- void glDeleteShader(GLuint id);
- void glDeleteProgram(GLuint id);
- 引數:
- ·id – The hanuler of the shader or program to delete.
void glDeleteShader(GLuint id);
void glDeleteProgram(GLuint id);
引數:
·id – The hanuler of the shader or program to delete.
如果一個shader還附加在某個程式中,這個shader並不能真正刪除,只能標記為刪除。當這個shader從所有程式中分離之後,才會被最終刪除。
相關文章
- 【GLSL教程】(三)在OpenGL中向shader傳遞資訊
- OpenGL 4.0 GLSL 採用平行光照模型模型
- 【GLSL教程】(五)卡通著色
- 在 iOS 中使用 GLSL 實現抖音特效iOS特效
- 【GLSL教程】(一)圖形流水線
- 【GLSL教程】(四)shder的簡單示例
- 【GLSL教程】(六)逐頂點的光照
- 在WebGL中使用GLSL實現光線追蹤Web
- OpenGL 和 GLSL 在頂點著色器中動態調整裁剪平面引數的簡單程式碼示例
- GLSL學習_高斯濾波
- Tessellation Shader的GLSL入門實現: 平面
- three.js 著色器材質之glsl內建函式JS函式
- Shader學習筆記,通過GLSL實現(一)筆記
- 關於Vulkan應用程式執行時編譯GLSL Shader檔案的方法編譯
- OpenGL系列教程之一:OpenGL(轉)
- OpenGL著色器教程
- 如何使用python中的opengl?Python
- Mobx在Flutter中的使用教程Flutter
- 在學習opengl前
- Retrofit使用教程(二)
- Android OpenGL ES 開發(二): OpenGL ES 環境搭建Android
- OpenGL基礎圖形程式設計(二)OpenGL概念建立程式設計
- OpenGL 紋理取樣 在GPU中哪個部件完成GPU
- 在OpenGL中實現視角切換插值過渡動畫動畫
- cmake使用教程(二)-新增庫
- OpenGL中的畫素操作
- opengl 教程(5) shader(2) uniform變數ORM變數
- NeHe的opengl教程delphi版(9)----星星 (轉)
- Android開發教程 - 使用Data Binding(四)在Fragment中的使用AndroidFragment
- Android開發教程 - 使用Data Binding(三)在Activity中的使用Android
- sqlalchemy在python中的使用(關於查詢)二SQLPython
- 淺談二維陣列在傳參中的使用陣列
- 在.NET Core 3.0中的WPF中使用IOC圖文教程
- Docker框架的使用系列教程(二)Docker框架
- OpenGL/OpenGL ES 初探
- NeHe的opengl教程delphi版(7)----濾波 (轉)
- 短視訊原始碼,在Android 中opengl es實現燈光效果原始碼Android
- Mac OS X下如何使用OpenGLMac