#Shader Vertex
#version 330 core
layout(location = 0) in vec2 position;
layout(location = 1) in vec3 aColor;
out vec3 vertexColor;
void main()
{
gl_Position = vec4(position,0.0,1.0);
vertexColor = aColor;
}
#Shader fragment
#version 330 core
in vec3 vertexColor;
out vec4 FragColor;
void main()
{
FragColor = vec4(vertexColor, 1.0);
}
我的理解是在顶点着色器书写完之后,片段着色器就会将这个out的参数传递给片段着色器,然后片段着色器再out出去的函数就是最终的颜色变化
如何在.cpp文件中读取文件
struct ShaderProgramSource {
string VertexSource;
string FragmentSource;
};
static ShaderProgramSource ParseShader(const string& filepath)
{
ifstream stream(filepath);
enum class ShaderType
{
NONE = -1, VERTEX = 0, FRAGMENT = 1
};
string line;
stringstream ss[2];
ShaderType type = ShaderType::NONE;
while (getline(stream, line))
{
if (line.find("#Shader") != string::npos)
{
if (line.find("Vertex") != string::npos)
type = ShaderType::VERTEX;
else if (line.find("fragment") != string::npos)
type = ShaderType::FRAGMENT;
}
else
{
ss[(int)type] << line << '\n';
}
}
return { ss[0].str(),ss[1].str() };
}
基本的逻辑就是读取给定的文件地址,然后定义一个enum用来标记当前读取的字符串是那种类型的文件,然后将读取出来的文件全部传出去就行了
static unsigned int CompileShader(unsigned int type, const string& source)
{
unsigned int id = glCreateShader(type);//创建一个着色器对象,type传入的是着色器的类型
const char* src = source.c_str();
glShaderSource(id, 1, &src, nullptr);//将源代码和着色器链接起来
glCompileShader(id);//编译着色器
int result;
glGetShaderiv(id, GL_COMPILE_STATUS, &result);
if (result == GL_FALSE)
{
int length;
glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
char* message = (char*)alloca(length * sizeof(char));
glGetShaderInfoLog(id, length, &length, message);
cout << "Failed to compile" << (type == GL_VERTEX_SHADER ? "vertex" : "fragment") << "shader!" << endl;
cout << message << endl;
glDeleteShader(id);
return 0;
}
return id;
}
这个就是编译着色器的程序 第一个参数代表编译一个什么类型的程序,是顶点还是片段,这里要先创建一个编译的句柄,用无符号整形
参数说明(glShaderSource)
-
shader
:- 这是要设置源代码的着色器对象的 ID。通常是在调用
glCreateShader
时生成的。
- 这是要设置源代码的着色器对象的 ID。通常是在调用
-
count
:- 这是源代码字符串的数量。可以一次传入多个字符串,这对于大的着色器源代码非常有用。
-
string
:- 这是指向字符串的指针数组,包含着色器的源代码。在你的代码中,这个参数是
&src
,这是一个指向const char*
的指针。
- 这是指向字符串的指针数组,包含着色器的源代码。在你的代码中,这个参数是
-
length
:- 这是一个可选参数,可以用来指定每个字符串的长度。如果这个参数为
nullptr
,OpenGL 将假设字符串以空字符(\0
)结束。
- 这是一个可选参数,可以用来指定每个字符串的长度。如果这个参数为
其实到这里,也就是调用完这个参数后就可以结束了,但是为了检测哪里有错,就要增加一些错误日志输出
static unsigned int CreateShader(const string& vertexShader, const string& fragmentShader)
{
unsigned int program = glCreateProgram();//创建一个着色器程序
unsigned int vs = CompileShader(GL_VERTEX_SHADER, vertexShader);
unsigned int fs = CompileShader(GL_FRAGMENT_SHADER, fragmentShader);
glAttachShader(program, vs);
glAttachShader(program, fs);//将编译好的着色器给到这个着色器程序
glLinkProgram(program);//将这个着色器程序和原来的程序链接
glValidateProgram(program);//验证程序
glDeleteShader(vs);
glDeleteShader(fs);//链接完以后就不再需要了
return program;
}
最后就是先创建一个主着色器程序,将刚刚编译好的着色器都连接到这个上面
然后就是将这个着色器程序作为渲染管线,并且要进行验证,为了资源不被浪费记得删除编译好的着色器文件,只要返回着色器程序就行了
ShaderProgramSource source = ParseShader("Shader.shader");
unsigned int shader = CreateShader(source.VertexSource, source.FragmentSource);
cout << "Vertex" << endl;
cout << source.VertexSource << endl;
cout << "FRAGMENT" << endl;
cout << source.FragmentSource << endl;
glUseProgram(shader);
最后就是运行着色器了glUseProgram(shader)就可以只用渲染管线了,在结尾别忘了删除这个管线
glDeleteProgram(shader);//删除着色器
下面是我的两个文件的代码,里面的Parseshader()里面的路径写自己的
#include<GL/glew.h>
#include <GLFW/glfw3.h>
#include<iostream>
#include<string>
#include<fstream>
#include<sstream>
using namespace std;
//以下三个函数是编译着色器文件的函数
struct ShaderProgramSource {
string VertexSource;
string FragmentSource;
};
static ShaderProgramSource ParseShader(const string& filepath)
{
ifstream stream(filepath);
enum class ShaderType
{
NONE = -1, VERTEX = 0, FRAGMENT = 1
};
string line;
stringstream ss[2];
ShaderType type = ShaderType::NONE;
while (getline(stream, line))
{
if (line.find("#Shader") != string::npos)
{
if (line.find("Vertex") != string::npos)
type = ShaderType::VERTEX;
else if (line.find("fragment") != string::npos)
type = ShaderType::FRAGMENT;
}
else
{
ss[(int)type] << line << '\n';
}
}
return { ss[0].str(),ss[1].str() };
}
static unsigned int CompileShader(unsigned int type, const string& source)
{
unsigned int id = glCreateShader(type);//创建一个着色器对象,type传入的是着色器的类型
const char* src = source.c_str();
glShaderSource(id, 1, &src, nullptr);//将源代码和着色器链接起来
glCompileShader(id);//编译着色器
int result;
glGetShaderiv(id, GL_COMPILE_STATUS, &result);
if (result == GL_FALSE)
{
int length;
glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
char* message = (char*)alloca(length * sizeof(char));
glGetShaderInfoLog(id, length, &length, message);
cout << "Failed to compile" << (type == GL_VERTEX_SHADER ? "vertex" : "fragment") << "shader!" << endl;
cout << message << endl;
glDeleteShader(id);
return 0;
}
return id;
}
static unsigned int CreateShader(const string& vertexShader, const string& fragmentShader)
{
unsigned int program = glCreateProgram();//创建一个着色器程序
unsigned int vs = CompileShader(GL_VERTEX_SHADER, vertexShader);
unsigned int fs = CompileShader(GL_FRAGMENT_SHADER, fragmentShader);
glAttachShader(program, vs);
glAttachShader(program, fs);//将编译好的着色器给到这个着色器程序
glLinkProgram(program);//将这个着色器程序和原来的程序链接
glValidateProgram(program);//验证程序
glDeleteShader(vs);
glDeleteShader(fs);//链接完以后就不再需要了
return program;
}
void setVBOVAO(GLuint& VAO, GLuint& VBO, GLfloat* vertices, GLsizei vertexCount)
{
glGenVertexArrays(1, &VAO);//由传入的参数创建一个VAO
glBindVertexArray(VAO);//绑定到当前的VBO
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER,VBO);
glBufferData(GL_ARRAY_BUFFER, vertexCount * sizeof(GLfloat), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 5*sizeof(GLfloat), (void*)0);//当只有顶点信息的时候stride可以是零
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 5*sizeof(GLfloat), (void*)(2 * sizeof(GLfloat)));
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, 0); //解绑VBO
glBindVertexArray(0); // 解绑VAO
}
int main(void)
{
GLFWwindow* window;
/* Initialize the library */
if (!glfwInit())
return -1;
/* Create a windowed mode window and its OpenGL context */
window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
if (!window)
{
glfwTerminate();
return -1;
}
/* Make the window's context current */
glfwMakeContextCurrent(window);
// Initialize GLEW
if (glewInit() != GLEW_OK) {
return -1;
}
GLuint VAO[4], VBO[4];//分别用来存储点和三角形和直线的坐标
float point[] = {
-0.5f,0.0f
};
float line[] = {
-0.4f,0.8f
};
float Rectangle[] = {
0.4f,0.4f,1.0f,1.0f,1.0f,
0.5f,0.5f,1.0f,1.0f,1.0f,
0.5f,0.3f,1.0f,1.0f,1.0f
};
float rect[] = {
-0.2f,-0.2f, 1.0,1.0,0.0,
0.2f,-0.2f, 1.0,1.0,0.0,
-0.2f, 0.2f, 1.0,1.0,0.0,
-0.2f, 0.2f, 1.0,1.0,0.0,
0.2f, 0.2f, 1.0,1.0,0.0,
0.2f,-0.2f, 1.0,1.0,0.0
};
setVBOVAO(VAO[2], VBO[2], Rectangle, 15);
setVBOVAO(VAO[3], VBO[3], rect, 30);
ShaderProgramSource source = ParseShader("Shader.shader");
unsigned int shader = CreateShader(source.VertexSource, source.FragmentSource);
cout << "Vertex" << endl;
cout << source.VertexSource << endl;
cout << "FRAGMENT" << endl;
cout << source.FragmentSource << endl;
glUseProgram(shader);
/* Loop until the user closes the window */
while (!glfwWindowShouldClose(window))
{
/* Render here */
glClear(GL_COLOR_BUFFER_BIT);
// 绘制三角形
glBindVertexArray(VAO[2]);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(VAO[3]);
glDrawArrays(GL_TRIANGLES, 0, 6);
glfwSwapBuffers(window);
/* Poll for and process events */
glfwPollEvents();
}
glDeleteProgram(shader);//删除着色器
glfwTerminate();
return 0;
}
Shader.shader
#Shader Vertex
#version 330 core
layout(location = 0) in vec2 position;
layout(location = 1) in vec3 aColor;
out vec3 vertexColor;
void main()
{
gl_Position = vec4(position,0.0,1.0);
vertexColor = aColor;
}
#Shader fragment
#version 330 core
in vec3 vertexColor;
out vec4 FragColor;
void main()
{
FragColor = vec4(vertexColor, 1.0);
}