1.着色器概念
着色器是運行在GPU上的小程序,這些小程序爲圖形渲染管線的某個特性部分而運行。着色器是一種把輸入轉化爲輸出的程序。着色器也是一種非常獨立的程序,因爲它們之間不能相互通信,它們之間唯一的溝通只有通過輸入和輸出
2.着色器的結構
着色器的開頭總是要聲明版本,接着是輸入和輸出的變量,uniform和main函數。每個着色器的入口點都是main函數,在main函數中,我們處理所有的輸入變量,並且計算輸出變量
主要的結構:
#version version_number
//輸入變量
in type in_variable_name
in type in_variable_name
//輸出變量
out type out_variable_name
uniform type uniform_name
int main()
{
//處理輸入並進行一些圖形操作
....
//輸出處理過的結果到輸出變量
out_variable_name = weird_stuff_we_processed
}
3.數據類型
GLSL中包含C等其他語言大部分的默認基礎數據類型:int float double uint bool
GLSL包含兩種容器類型 Vector 和 Matrix
4.向量(vector)
類型:
vecn:包含n個float分量
bvecn:包含n個bool分量
ivecn:包含n個int分量
uvecn:包含n個unsigned int 分量
dvecn:n個double分量
注:n代表分量的數量
注:①其中某個分量可以通過vec.x這種方式去獲取,可以分別使用.x、.y、.z、.w來獲取第1、2、3、4個分量,GLSL也可以用rgba,對紋理座標使用stpq訪問相同的分量
注:②向量中的一些有趣的東西,叫重組
例如:
vec2 someVec;
vec4 differentVec = someVec.xyxx;
vec3 anotherVec = differentVec.zyw;
vec4 otherVec = someVec.xxxx+ anotherVec.yxzy
vec2 vect = vec2(0.5, 0.7);
vec4 result = vec4(vect, 0.0, 0.0);
vec4 otherResult = vec4(result.xyz, 1.0)
5.in(輸入)和out(輸出)
GLSL定義了in和out設定輸入和輸出,只要一個輸出變量與下一個着色階段的輸入匹配,就會傳遞下去
頂點着色器接收的是一種特殊形式的輸入,頂點着色器從頂點數據中直接接收輸入,使用location指定輸入變量,頂點着色器需要爲它的輸入提供額外的layout標識,layout(location = 0)
片段着色器,需要一個vec4顏色輸出變量,片段着色器需要生成一個最終輸出的顏色,如果沒有定義輸出顏色,OpenGL會把物體渲染成黑色或者白色
如果從一個着色器向另一個着色器發送數據,必須在發送方着色器中聲明一個輸出,在接收方着色器中聲明一個類似的輸入。當類型和名字都一樣的時候,OpenGL就會把兩個變量鏈接到一起,它們之間就能發送數據了(這是在鏈接程序對象時完成的)
例如:
頂點着色器
#version 330 core
layout (location) in vec3 aPos //位置變量的屬性位置爲0
out vec4 vertexColor; //爲片段着色器指定一個顏色輸出
void main()
{
gl_Position = vec4(aPos, 1.0);
vertexColor = vec4(0.5f, 0.0, 0.0, 1.0)
}
片段着色器
#version 330 core
out vec4 FragColor;
in vec4 vertexColor;
void main()
{
FragColor = vertexColor
}
可以看到我們在頂點着色器中聲明瞭一個vertexColor變量作爲vec4輸出,並在片段着色器中聲明瞭一個類似的vertexColor。由於它們名字相同且類型相同,片段着色器中的vertexColor就和頂點着色器中的vertexColor鏈接了。由於我們在頂點着色器中將顏色設置爲深紅色,最終的片段也是深紅色的。如下圖所示:
詳細代碼見下
#include <GL/glew.h>
#include<gl\glut.h>
#include<iostream>
void MyInit();
void reshape(int w, int h);
void display();
GLuint vboId;
GLuint vaoId;
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowPosition(100, 100);
glutInitWindowSize(512, 512);
glutCreateWindow("SHADER_RECTANGLE");
glewInit();
MyInit();
glutReshapeFunc(&reshape);
glutDisplayFunc(&display);
glutMainLoop();
return 0;
}
void MyInit()
{
glClearColor(0.0,0.0, 0.0,0.0);
const GLfloat vertices[] = {
-0.5f,-0.5f,0.0f,1.0f,
0.5f,-0.5f,0.0f,1.0f,
0.5f,0.5f,0.0f,1.0f,
-0.5f,0.5f,0.0f,1.0f,
};
//創建VAO對象
glGenVertexArrays(1, &vaoId);
glBindVertexArray(vaoId);
//創建VBO對象
glGenBuffers(1, &vboId);
glBindBuffer(GL_ARRAY_BUFFER, vboId);
//傳入VBO數據
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//解除VBO綁定
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void reshape(int w, int h)
{
glViewport(0, 0,(GLsizei)w, (GLsizei)h);
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
const char* vertex_shader =
"#version 330\n"
"in vec3 aPos;"
"out vec4 vertexColor;"
"void main() {"
" gl_Position = vec4(aPos, 1.0);"
"vertexColor =vec4 (0.5, 0.0, 0.0, 1.0);"
"}";
const char* fragment_shader =
"#version 330\n"
"out vec4 FragColor;"
"in vec4 vertexColor;" //跟頂點着色器中的輸出變量類型,名字一樣
"void main() {"
" FragColor = vertexColor;"
"}";
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertex_shader, NULL);
glCompileShader(vertexShader);
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragment_shader, NULL);
glCompileShader(fragmentShader);
GLuint shader_programme = glCreateProgram();
glAttachShader(shader_programme, fragmentShader);
glAttachShader(shader_programme, vertexShader);
glLinkProgram(shader_programme);
glUseProgram(shader_programme);
glDeleteShader(fragmentShader);
glDeleteShader(vertexShader);
//綁定VBO
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glEnableVertexAttribArray(0);
//解釋頂點數據方式
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
//繪製模型
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDisableVertexAttribArray(0);
glutSwapBuffers();
}
6.Uniform
Uniform是一種從CPU中的應用向GPU中的着色器發送數據的方式,但uniform和頂點屬性有些不同。
①uniform是全局的(Global)。全局意味着uniform變量必須在每個着色器程序對象中都是獨一無二的,而且它可以被着色器程序的任意着色器在任意階段訪問;
②無論你把uniform值設置成什麼,uniform會一直保存它們的數據,直到它們被重置或更新;
我們可以在着色器中添加uniform關鍵字
#version 330 core
out vec4 FragColor;
uniform vec4 myColor; //在OpenGL程序中設定這個變量
void main()
{
FragColor = myColor;
}
可以看到我們在片段着色器中聲明瞭一個uniform vec4 的myColor,並且把着色器的輸出顏色設置爲uniform的內容,因爲uniform變量是全局變量,我們可以在任何着色器中定義
注:如果聲明瞭一個uniform,但是在GLSL中沒用過,編譯器會靜默移除這個變量,導致編譯出的版本並不會包含它。
現在uniform還是空的,我們沒有添加數據,步驟:
(1)找到着色器中uniform屬性的索引/位置值
(2)得到uniform的索引/位置值後,可以更新值
int vertexColorLocation = glGetUniformLocation(shaderProgram, "myColor");
glUseProgram(shaderProgram)
glUniform4f(vertexColorLocation, 0.5f, 0.5f, 0.0f, 1.0f);
用glGetUniformLocation查詢uniform myColor的位置值,如果返回-1就代表沒有找到這個位置
用glUniform4f函數設置uniform值
注意:查詢uniform地址不要求你之前使用過着色器程序,但是更新一個uniform之前必須先使用程序(調用glUseProgram)
代碼見下:
#include <GL/glew.h>
#include<gl\glut.h>
#include<iostream>
void MyInit();
void reshape(int w, int h);
void display();
GLuint vboId;
GLuint vaoId;
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowPosition(100, 100);
glutInitWindowSize(512, 512);
glutCreateWindow("SHADER_RECTANGLE");
glewInit();
MyInit();
glutReshapeFunc(&reshape);
glutDisplayFunc(&display);
glutMainLoop();
return 0;
}
void MyInit()
{
glClearColor(0.0,0.0, 0.0,0.0);
const GLfloat vertices[] = {
-0.5f,-0.5f,0.0f,1.0f,
0.5f,-0.5f,0.0f,1.0f,
0.5f,0.5f,0.0f,1.0f,
-0.5f,0.5f,0.0f,1.0f,
};
//創建VAO對象
glGenVertexArrays(1, &vaoId);
glBindVertexArray(vaoId);
//創建VBO對象
glGenBuffers(1, &vboId);
glBindBuffer(GL_ARRAY_BUFFER, vboId);
//傳入VBO數據
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//解除VBO綁定
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void reshape(int w, int h)
{
glViewport(0, 0,(GLsizei)w, (GLsizei)h);
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
const char* vertex_shader =
"#version 330\n"
"in vec3 aPos;"
"void main() {"
" gl_Position = vec4(aPos, 1.0);"
"}";
const char* fragment_shader =
"#version 330\n"
"out vec4 FragColor;"
"uniform vec4 myColor;" //定義uniform變量
"void main() {"
" FragColor = myColor;"
"}";
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertex_shader, NULL);
glCompileShader(vertexShader);
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragment_shader, NULL);
glCompileShader(fragmentShader);
GLuint shader_programme = glCreateProgram();
glAttachShader(shader_programme, fragmentShader);
glAttachShader(shader_programme, vertexShader);
glLinkProgram(shader_programme);
glUseProgram(shader_programme);
//更新uniform顏色
int vertexColorLocation = glGetUniformLocation(shader_programme, "myColor");
glUniform4f(vertexColorLocation, 0.5f, 0.5f, 0.0f, 1.0f);
glDeleteShader(fragmentShader);
glDeleteShader(vertexShader);
//綁定VBO
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glEnableVertexAttribArray(0);
//解釋頂點數據方式
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
//繪製模型
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDisableVertexAttribArray(0);
glutSwapBuffers();
}
擴展:
我們進行擴展,增加更多的屬性,畫一個三角形,三個角指定爲不同的顏色
float vertices[] = {
// 位置 // 顏色
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // 右下
-0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // 左下
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f // 頂部
}
頂點着色器,因爲有更多的屬性發送到頂點着色器,需要增加屬性
#version 330 core
layout (location = 0) in vec3 aPos; // 位置變量的屬性位置值爲 0
layout (location = 1) in vec3 aColor; // 顏色變量的屬性位置值爲 1
out vec3 ourColor; // 向片段着色器輸出一個顏色
void main()
{
gl_Position = vec4(aPos, 1.0);
ourColor = aColor; // 將ourColor設置爲我們從頂點數據那裏得到的輸入顏色
}
片段着色器
#version 330 core
out vec4 FragColor;
in vec3 ourColor;
void main()
{
FragColor = vec4(ourColor, 1.0);
}
使用glVertexAttribPointer函數更新頂點格式
// 位置屬性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// 顏色屬性
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3* sizeof(float)));
glEnableVertexAttribArray(1);
由於有兩個頂點屬性,必須要重新計算步長,獲得數據隊列的下一個屬性值,即下一個頂點的屬性,必須向右移動6個float,3個位置值,3個顏色值
同樣的,我們必須指定一個偏移量,頂點位置屬性在前,所以偏移量爲0,顏色屬性在後,所以偏移量是3個float
效果見下:
代碼:
#include <GL/glew.h>
#include<gl\glut.h>
#include<iostream>
void MyInit();
void reshape(int w, int h);
void display();
GLuint vboId;
GLuint vaoId;
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowPosition(100, 100);
glutInitWindowSize(512, 512);
glutCreateWindow("SHADER_RECTANGLE");
glewInit();
MyInit();
glutReshapeFunc(&reshape);
glutDisplayFunc(&display);
glutMainLoop();
return 0;
}
void MyInit()
{
glClearColor(0.0,0.0, 0.0,0.0);
const GLfloat vertices[] = {
// 位置 // 顏色
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // 右下
-0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // 左下
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f // 頂部
};
//創建VAO對象
glGenVertexArrays(1, &vaoId);
glBindVertexArray(vaoId);
//創建VBO對象
glGenBuffers(1, &vboId);
glBindBuffer(GL_ARRAY_BUFFER, vboId);
//傳入VBO數據
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//解除VBO綁定
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void reshape(int w, int h)
{
glViewport(0, 0,(GLsizei)w, (GLsizei)h);
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
const char* vertex_shader =
"#version 330\n"
"layout (location = 0) in vec3 aPos;"// 位置變量的屬性位置值爲 0
"layout (location = 1) in vec3 aColor; "// 顏色變量的屬性位置值爲 1
"out vec3 OurColor;"//向片段着色器輸出一個顏色
"void main() {"
" gl_Position = vec4(aPos, 1.0);"
"OurColor = aColor;"
"}";
const char* fragment_shader =
"#version 330\n"
"out vec4 FragColor;"
"in vec3 OurColor;"
"void main() {"
" FragColor = vec4(OurColor, 1.0f);"
"}";
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertex_shader, NULL);
glCompileShader(vertexShader);
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragment_shader, NULL);
glCompileShader(fragmentShader);
GLuint shader_programme = glCreateProgram();
glAttachShader(shader_programme, fragmentShader);
glAttachShader(shader_programme, vertexShader);
glLinkProgram(shader_programme);
glUseProgram(shader_programme);
glDeleteShader(fragmentShader);
glDeleteShader(vertexShader);
//綁定VBO
glBindBuffer(GL_ARRAY_BUFFER, vboId);
//解釋頂點數據方式
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), 0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*) (3 * sizeof(float)));
glEnableVertexAttribArray(1);
//繪製模型
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glutSwapBuffers();
}