[OpenGL]OpenGL着色器基礎知識及應用

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();
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章