openGL学习(Shader)
认识Shader
在计算机图形学中,Shader(着色器)是一种运行在 GPU(图形处理单元)上的程序,用于控制图形渲染过程中顶点和像素的处理。着色器是 OpenGL、Direct3D、Vulkan 等图形 API 的核心组成部分,它们允许开发者自定义渲染管线的各个阶段,从而实现高度自定义的视觉效果。
着色器类型
顶点着色器(Vertex Shader):
处理每个顶点的数据。
可以进行顶点位置的变换、纹理坐标的生成、光照计算等。
通常用于实现 3D 变换、动画效果等。
片段着色器(Fragment Shader):
处理每个像素(或片段)的数据。
决定每个像素的颜色和透明度。
用于实现光照、阴影、纹理映射、后处理效果等。
几何着色器(Geometry Shader):
在顶点着色器和片段着色器之间处理整个图元(如点、线、三角形)。
可以生成新的顶点或图元。
用于实现高级的几何变换和效果。
张量着色器(Tessellation Shader):
包括两个阶段:域着色器(Domain Shader)和原语生成着色器(Hull Shader)。
控制细分曲面的生成和处理。
用于实现复杂的曲面细分和建模。
计算着色器(Compute Shader):
执行通用计算任务,不直接参与渲染管线。
可以并行处理大量数据。
用于实现物理模拟、图像处理、AI 计算等。
着色器编写
着色器通常使用 GLSL(OpenGL Shading Language)或 HLSL(High-Level Shader Language)等语言编写。以下是一个简单的 GLSL 顶点着色器示例:
#version 330 core// in代表输入
//vec3代表是一个三维向量(xyz)
// aPos 是我们自己取的名字
// layout(location = n) 代表告诉vertexShaser去vao的第n个属性描述中去取数据。
// gl_Position 是glsl的内置变量,负责向后续阶段输出顶点位置处理的结果。 一般为NDC坐标。
//vec4代表 四维向量。layout(location = 0) in vec3 aPos; // 顶点位置void main() {gl_Position = vec4(aPos, 1.0); // 将顶点位置传递给片段着色器
}
以及一个简单的 GLSL 片段着色器示例:
#version 330 core//out代表输出变量
//vec4代表四维向量,rgba,红绿蓝,透明度
//FragColor 是最终输出的变量
out vec4 FragColor; // 输出颜色void main() {FragColor = vec4(1.0, 0.5, 0.2, 1.0); // 设置像素颜色为橙色
}
着色器编译和链接
在 OpenGL 中,着色器需要经过编译和链接才能使用。以下是一个简化的流程:
编写着色器代码:使用 GLSL 编写顶点着色器和片段着色器代码。
创建着色器对象:使用
glCreateShader
创建着色器对象。编译着色器:使用
glShaderSource
和glCompileShader
编译着色器代码。创建程序对象:使用
glCreateProgram
创建程序对象。附加着色器:使用
glAttachShader
将编译好的着色器附加到程序对象。链接程序:使用
glLinkProgram
链接程序对象。使用程序:使用
glUseProgram
使用链接好的程序对象。
着色器的优势
高度自定义:开发者可以自定义渲染管线的各个阶段,实现复杂的图形效果。
性能优化:着色器在 GPU 上运行,可以利用 GPU 的并行处理能力,提高渲染性能。
跨平台兼容性:着色器语言(如 GLSL)在不同的图形 API 和硬件平台上具有较好的兼容性。
总之,着色器是现代图形渲染中不可或缺的一部分,它们为开发者提供了强大的工具来实现高度自定义的视觉效果。
void prepareShader()
{cout << "prepareShader()" << endl;//1.完成vs和fs,并且装字符串const char* vertexShaderSource ="#version 460 core\n""layout(location = 0) in vec3 aPos;\n""void main()\n""{\n""gl_Position = vec4(aPos.x,aPos.y,aPos.z,1.0);\n""}\n\0";const char* fragmentShaderSource ="#version 330 core\n""out vec4 FragColor;\n""void main()\n""{\n""FragColor = vec4(1.0f,0.5f,0.2f,1.0);\n""}\n\0";//2创建Shader程序(vs和fs)GLuint vertex, fragment;vertex = glCreateShader(GL_VERTEX_SHADER);fragment = glCreateShader(GL_FRAGMENT_SHADER);//3为shader程序输入Shader代码glShaderSource(vertex, 1, &vertexShaderSource, NULL); //字符串用\0进行结尾,不需要告诉他长度。glShaderSource(fragment, 1, &fragmentShaderSource, NULL); //字符串用\0进行结尾,不需要告诉他长度。int success = 0;char infoLog[1024];//4 执行Shader编译glCompileShader(vertex);//5 检查是否正确编译glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);if (!success) // 0有问题,非0没有问题{glGetShaderInfoLog(vertex, 1024, NULL, infoLog); //获取日志信息cout << "Error Vertex Shaser Complie: " << infoLog << endl;}//4 执行fragment编译glCompileShader(fragment);//5 检查是否正确编译glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);if (!success) // 0有问题,非0没有问题{glGetShaderInfoLog(vertex, 1024, NULL, infoLog); //获取日志信息cout << "Error Fragment Shader Complie: " << infoLog << endl;}// 创建Program壳子GLuint program = 0;program = glCreateProgram();//将编译后的结果放进ProgramglAttachShader(program, vertex);glAttachShader(program, fragment);// 执行program链接操作,形成最终的shader程序。glLinkProgram(program);//检查链接错误glGetProgramiv(program, GL_LINK_STATUS, &success);if (!success) // 0有问题,非0没有问题{glGetProgramInfoLog(program, 1024, NULL, infoLog); //获取日志信息cout << "Error Program Link: " << infoLog << endl;}//清理glDeleteShader(vertex);glDeleteShader(fragment);}
#include <iostream>
//#include "thrirdParty/include/GLFW/glfw3.h"
#include "glad/glad.h" //需要先引用glad的头文件。 用于加载 OpenGL 函数指针
#include "GLFW/glfw3.h" // 用于创建窗口和处理输入。
#include <assert.h>
#include "wrapper/checkerror.h"
#include "application/application.h"
using namespace std;/*
* 目标:学习DrawArray进行绘制
* 1.采用GL_TRANGLES进行绘制三角形
* 这里可以缩放窗体,实验NDC坐标的作用。
* prepareVAOForGLTriangles:构建四个顶点的VAO
*
*/GLuint vao, program;void prepareSingleBuffer()
{//1.准备顶点位置数据与颜色数据float positions[] = {-0.5f,-0.5f,0.0f,0.5f,-0.5f,0.0f,0.0f,0.5f,0.0f,};float colors[] = {1.0f,0.0f,0.0f,0.0f,1.0f,0.0f,0.0f,0.0f,1.0f,};//2.为位置&颜色数据各自生成一个VBOGLuint posVbo = 0, colorVbo = 0;GL_CALL(glGenBuffers(1, &posVbo));GL_CALL(glGenBuffers(1, &colorVbo));//3.给两个分开的VBO各自填充数据//positions填充数据GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, posVbo));GL_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW));//colors填充数据GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, colorVbo));GL_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW));//生成VAO并且绑定GLuint vao = 0;glGenVertexArrays(1, &vao);glBindVertexArray(vao);// 分别将数据放入VAO//描述位置属性glBindBuffer(GL_ARRAY_BUFFER, posVbo);//只有绑定了vbo,下面的属性描述才于此有关系glEnableVertexAttribArray(0);glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,3*sizeof(float),0);glBindBuffer(GL_ARRAY_BUFFER, colorVbo);//只有绑定了vbo,下面的属性描述才于此有关系glEnableVertexAttribArray(1);glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);glBindVertexArray(0); //将VAO进行解绑}void prepareInterLeavedBuffer()
{cout << "prepareInterLeavedBuffer()" << endl;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};//2.为位置&颜色数据各自生成一个VBOGLuint vbo = 0;GL_CALL(glGenBuffers(1, &vbo));GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vbo));GL_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW));//生成VAO并且绑定//GLuint vao = 0;glGenVertexArrays(1, &vao);glBindVertexArray(vao);// 分别将数据放入VAO//描述位置属性glBindBuffer(GL_ARRAY_BUFFER, vbo);//只有绑定了vbo,下面的属性描述才于此有关系glEnableVertexAttribArray(0);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);//颜色属性//glBindBuffer(GL_ARRAY_BUFFER, vbo);//只有绑定了vbo,下面的属性描述才于此有关系glEnableVertexAttribArray(1);glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float),(void*)(3*sizeof(float)));glBindVertexArray(0); //将VAO进行解绑
}void prepareVAOForTriangles()
{cout << "prepareVAOForTriangles()" << endl;//目前暂时不用颜色float vertices[] = {-0.5f,-0.5f,0.0f,0.5f,-0.5f,0.0f,0.0f,0.5f,0.0f,0.5f,0.5f,0.0f,0.8f,0.8f,0.0f,0.8f,0.0f,0.0f};//2.生成一个VBOGLuint vbo = 0;GL_CALL(glGenBuffers(1, &vbo));GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vbo));GL_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW));//生成VAO并且绑定glGenVertexArrays(1, &vao);glBindVertexArray(vao);// 分别将数据放入VAO//描述位置属性glBindBuffer(GL_ARRAY_BUFFER, vbo);//只有绑定了vbo,下面的属性描述才于此有关系glEnableVertexAttribArray(0);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);glBindVertexArray(0); //将VAO进行解绑
}void prepareShader()
{cout << "prepareShader()" << endl;//1.完成vs和fs,并且装字符串const char* vertexShaderSource ="#version 460 core\n""layout(location = 0) in vec3 aPos;\n""void main()\n""{\n""gl_Position = vec4(aPos.x,aPos.y,aPos.z,1.0);\n""}\n\0";const char* fragmentShaderSource ="#version 330 core\n""out vec4 FragColor;\n""void main()\n""{\n""FragColor = vec4(1.0f,0.5f,0.2f,1.0);\n""}\n\0";//2创建Shader程序(vs和fs)GLuint vertex, fragment;vertex = glCreateShader(GL_VERTEX_SHADER);fragment = glCreateShader(GL_FRAGMENT_SHADER);//3为shader程序输入Shader代码glShaderSource(vertex, 1, &vertexShaderSource, NULL); //字符串用\0进行结尾,不需要告诉他长度。glShaderSource(fragment, 1, &fragmentShaderSource, NULL); //字符串用\0进行结尾,不需要告诉他长度。int success = 0;char infoLog[1024];//4 执行Shader编译glCompileShader(vertex);//5 检查是否正确编译glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);if (!success) // 0有问题,非0没有问题{glGetShaderInfoLog(vertex, 1024, NULL, infoLog); //获取日志信息cout << "Error Vertex Shaser Complie: " << infoLog << endl;}//4 执行fragment编译glCompileShader(fragment);//5 检查是否正确编译glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);if (!success) // 0有问题,非0没有问题{glGetShaderInfoLog(vertex, 1024, NULL, infoLog); //获取日志信息cout << "Error Fragment Shader Complie: " << infoLog << endl;}// 创建Program壳子//GLuint program = 0;program = glCreateProgram();//将编译后的结果放进ProgramglAttachShader(program, vertex);glAttachShader(program, fragment);// 执行program链接操作,形成最终的shader程序。glLinkProgram(program);//检查链接错误glGetProgramiv(program, GL_LINK_STATUS, &success);if (!success) // 0有问题,非0没有问题{glGetProgramInfoLog(program, 1024, NULL, infoLog); //获取日志信息cout << "Error Program Link: " << infoLog << endl;}//清理glDeleteShader(vertex);glDeleteShader(fragment);}void render()
{//GL_CALL( glClear(1) );//加上gl_call如果产生错误可以打印出来,虽然vs智能提示有问题GL_CALL(glClear(GL_COLOR_BUFFER_BIT));//渲染操作//1. 使用当前的programglUseProgram(program);//绑定当前的vaoglBindVertexArray(vao);//发出绘制指令//绘制三角形// 如果点数不够,就会连到0,0点 (123 和456)绘制两组三角形glDrawArrays(GL_TRIANGLES, 0, 6);//绘制的模式// GL_TRIANGLES 绘制三角形// 默认以三个点绘制一组三角形,不够三个点就不显示// GL_TRIANGLE_STRIP:// 末尾点数为偶数【n-2 n-1 n】,基数【n-1 n-2 n】 n从0开始// 并且会复用之前的点// GL_TRIANGLE_FAN:以扇形序列v0为起点,连接三角形。// GL_LINES:绘制直线// GL_LINE_STRIP// }void onResize(int width,int height)
{GL_CALL(glViewport(0, 0, width, height));cout << "onResize " << endl;
}void frameBufferSizeCallback(GLFWwindow* win, int width, int height)
{std::cout << "窗体的最新大小为:" << width << "高度为:" << height << std::endl;//更新窗体的大小glViewport(0, 0, width, height);}void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods){// key 字母按键码 // scancode 物理按键码 // action:0抬起1按下2长按// mods:是否有shift(1)或ctrl(2) cout << "key = " << key << " scancode = " << scancode << " action = " << action << " mods = " << mods << endl;if (key == GLFW_KEY_W){//按下了w}else if (action == GLFW_PRESS){//按下了}else if (action == GLFW_RELEASE){//抬起}}int main(int argc,char**argv)
{cout << "===================================" << endl;if (app->init(800,600) == -1){return -1;}//设置监听帧缓冲窗口大小回调函数。//glfwSetFramebufferSizeCallback(win, frameBufferSizeCallback);//glfwSetKeyCallback(win, keyCallback);app->setResizeCallBack(onResize);app->setkeyCallBack(keyCallback);//设置OpenGL视口以及清理颜色glViewport(0,0,800,600);glClearColor(0.2f,0.3f,0.3f,1.0f);prepareShader();prepareVAOForTriangles();// 3. 执行窗体循环// while (app->update() == 0 ){render();}// 4. 退出程序前做相关清理app->destroy();cout << "===================================" << endl;//const double M_PI = 3.14159265358979323846;//double radians = M_PI / 2; // 90度,转换为弧度//double sineValue = sin(radians);//std::cout << "sin(" << radians << ") = " << sineValue << std::endl;//system("pause");return 0;
}