着色器
一般編程是頂點着色器。片段着色器。然後把這兩個鏈接一下。
可以寫成一個着色器類
#ifndef SHADER_H
#define SHADER_H
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include <GL/glew.h>
class Shader
{
public:
GLuint Program;
// Constructor generates the shader on the fly
Shader(const GLchar* vertexPath, const GLchar* fragmentPath)
{
// 1. Retrieve the vertex/fragment source code from filePath
std::string vertexCode;
std::string fragmentCode;
std::ifstream vShaderFile;
std::ifstream fShaderFile;
// ensures ifstream objects can throw exceptions:
vShaderFile.exceptions(std::ifstream::badbit);
fShaderFile.exceptions(std::ifstream::badbit);
try
{
// Open files
vShaderFile.open(vertexPath);
fShaderFile.open(fragmentPath);
std::stringstream vShaderStream, fShaderStream;
// Read file's buffer contents into streams
vShaderStream << vShaderFile.rdbuf();
fShaderStream << fShaderFile.rdbuf();
// close file handlers
vShaderFile.close();
fShaderFile.close();
// Convert stream into string
vertexCode = vShaderStream.str();
fragmentCode = fShaderStream.str();
}
catch (std::ifstream::failure e)
{
std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
}
const GLchar* vShaderCode = vertexCode.c_str();
const GLchar * fShaderCode = fragmentCode.c_str();
// 2. Compile shaders
GLuint vertex, fragment;
GLint success;
GLchar infoLog[512];
// Vertex Shader
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vShaderCode, NULL);
glCompileShader(vertex);
// Print compile errors if any
glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertex, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}
// Fragment Shader
fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fShaderCode, NULL);
glCompileShader(fragment);
// Print compile errors if any
glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragment, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
// Shader Program
this->Program = glCreateProgram();
glAttachShader(this->Program, vertex);
glAttachShader(this->Program, fragment);
glLinkProgram(this->Program);
// Print linking errors if any
glGetProgramiv(this->Program, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(this->Program, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
}
// Delete the shaders as they're linked into our program now and no longer necessery
glDeleteShader(vertex);
glDeleteShader(fragment);
}
// Uses the current shader
void Use()
{
glUseProgram(this->Program);
}
};
#endif
使用時類似這樣
Shader ourShader("G:\\LearnOpenGLCN\\Project1\\x64\\Release\\shader.vs", "G:\\LearnOpenGLCN\\Project1\\x64\\Release\\shader.frag");
在gl循環中調用
ourShader.Use();
着色器語言
叫glsl
shader.vs
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 color;
out vec3 ourColor;
void main()
{
gl_Position = vec4(position, 1.0f);
ourColor = color;
}
shader.frag
#version 330 core
in vec3 ourColor;
out vec4 color;
void main()
{
color = vec4(ourColor, 1.0f);
}
例如上面的兩個着色器
#version 330 core
聲明glsl的語言版本,一般要與glfw版本匹配
vecn是一種類型聲明
類型 | 含義 |
---|---|
vecn | 包含n個float分量的默認向量 |
bvecn | 包含n個bool分量的向量 |
ivecn | 包含n個int分量的向量 |
uvecn | 包含n個unsigned int分量的向量 |
dvecn | 包含n個double分量的向量 |
其中n是可變的
着色器之間交流有輸入和輸出
關鍵字是 in 和 out
in代表從外部輸入的
out代表需要輸出的
比如上面ourColor就是從shader.vs傳到shader.frag的 需要注意的是,他們的變量名需要一致
layout (location = 0)設定了輸入變量的位置值,這個位置值是我們規定的,通過glEnableVertexAttribArray();
比如這樣的頂點數組
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 // 頂部
};
前三個是位置,後三個是顏色
GLuint VBO, VAO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
// Bind the Vertex Array Object first, then bind and set vertex buffer(s) and attribute pointer(s).
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
// 顏色屬性
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, 0); // Note that this is allowed, the call to glVertexAttribPointer registered VBO as the currently bound vertex buffer object so afterwards we can safely unbind
glBindVertexArray(0); // Unbind VAO (it's always a good thing to unbind any buffer/array to prevent strange bugs)
glBufferData,將頂點數據初始化到緩衝,後面會有個地方使用glUseProgram正式在着色器中編譯這些數據。glDrawArray正式繪製
通過glVertexAttribPointer分割,通過glEnableVertexAttribArray規定
uniform一個比較特殊的關鍵字
Uniform是一種從CPU中的應用向GPU中的着色器發送數據的方式,但uniform和頂點屬性有些不同。首先,uniform是全局的(Global)。全局意味着uniform變量必須在每個着色器程序對象中都是獨一無二的,而且它可以被着色器程序的任意着色器在任意階段訪問。第二,無論你把uniform值設置成什麼,uniform會一直保存它們的數據,直到它們被重置或更新。
#version 330 core
out vec4 color;
uniform vec4 ourColor; // 在OpenGL程序代碼中設定這個變量
void main()
{
color = ourColor;
}
那代碼中怎麼改變這個值呢
比如這樣隨時間改變顏色
GLfloat timeValue = glfwGetTime();
GLfloat greenValue = (sin(timeValue) / 2) + 0.5;
GLint vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor");
glUseProgram(shaderProgram);
glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);
用glGetUniformLocation在 鏈接頂點着色器和片段的着色器的程序着色器中找到我們定義的uniform,然後給他賦值,需要注意的是一定要在glUseProgram後面給他賦值。