目录
Shader内的一些关键字
向量
举例:shader之间的数据传输,并实现渐变颜色
举例:C++向shader传输数据的过程
代码整理——shader类的封装
加入颜色信息
索引绘制——EBO
整体代码以及渲染结果
Shader内的一些关键字
- in:上个阶段传来的变量
- out:输出下个阶段的内容
- uniform:在C++程序传入shader的内容
- main 函数:进行一系列操作
向量
操作非常灵活
举例:shader之间的数据传输,并实现渐变颜色
vertexShader.glsl
#version 330 core
layout (location = 0) in vec3 aPos;//由C++代码中VBO绑定的锚定点输入。
out vec4 vertexColor;
void main()
{
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
vertexColor=vec4(0.5,0.0,0.0,1.0);
};
fragmentShader.glsl
#version 330 core
out vec4 FragColor;
in vec4 vertexColor;
void main()
{
//FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
FragColor = vertexColor;
};
vertexShader中的aPos,由
//对哪个锚点进行操作:layout=0的锚点,读3个顶点,类型为float,不需要归一化,每次步长为3个float大小,从0处开始读
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
进行读入;
随后vertexShader输出一个vertexColor传入下一个阶段,也就是fragmentShader。
fragmentShader接受vertexShader的输入(vertexColor),同时再输出一个FragColor。
举例:C++向shader传输数据的过程
直接上代码,注意需要把glUserProgram放在前面,启用shaderProblem这个状态
//渲染
void render() {
glUseProgram(shaderProgram);
float _time = glfwGetTime();
float _green = sin(_time) * 0.5f + 0.5f;
int _location = glGetUniformLocation(shaderProgram, "ourColor");
glUniform4f(_location, 0.0f, _green, 0.0f, 1.0f);
glBindVertexArray(VAO);
//以三角形模式绘制,从第0个顶点开始,起作用的有3个点
glDrawArrays(GL_TRIANGLES, 0, 3);
glUseProgram(0);
}
渲染结果:
代码整理——shader类的封装
创建一个新的Shader类,抽象出一些重合度比较高的代码:
Shader.h
#pragma once
#include "Base.h"
class Shader
{
private:
unsigned int m_shaderProgram;
public:
Shader() {
m_shaderProgram = 0;
}
~Shader() {}
public:
void initShader(const char* _vertexPath, const char* _fragPath);
void start() { glUseProgram(m_shaderProgram); }
void end() { glUseProgram(0); }
};
然后把之前的代码copy进initShader即可。
另外将一些常用的头文件集成到Base.h中:
//Base.h
#pragma once
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
加入颜色信息
我们在vertices中为三个顶点新添加颜色信息:
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
};
同时,添加了这些信息之后,我们就需要为它们新分配一个layout,并且激活这个layout:
这里注意步长变更为6个float,同时颜色的开始地址为第三个float处。
//对哪个锚点进行操作:layout=0的锚点,读3个顶点,类型为float,不需要归一化,每次步长为3个float大小,从0处开始读
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(sizeof(float)*3));
//打开锚点:激活
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
之后,我们既然已经分配了layout=1的GPU空间,并令他为颜色的信息,那么在shader中也应该有所体现,需要读取layout=1处的颜色信息:
//vertexShader.glsl
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
out vec4 outColor;
void main()
{
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
outColor = vec4(aColor, 1.0f);
};
并将颜色信息传入到fragmentShader中:
//fragmentShader.glsl
#version 330 core
out vec4 FragColor;
in vec4 outColor;
void main()
{
FragColor = outColor;
};
渲染结果如下:
索引绘制——EBO
EBO就是用来存储顶点索引的一块区域。
EBO的创建与绑定
与VAO和VBO一样的流程:
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
另外需要注意的是render中需要更换绘制函数:
void render() {
_shader.start();
glBindVertexArray(VAO);
//以三角形模式绘制,从第0个顶点开始,起作用的有3个点
//glDrawArrays(GL_TRIANGLES, 0, 3);
//以三角形模式绘制,用顶点索引
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
_shader.end();
}
整体代码以及渲染结果
#include "Base.h"
#include "Shader.h"
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);
void initModel();
void initShader(const char* _vertexPath, const char* _fragPath);
void render();
unsigned int VBO = 0;
unsigned int VAO = 0;
unsigned int EBO = 0;
Shader _shader;
int main() {
//初始化OpenGL上下文环境,OpenGL是一个状态机,会保存当前状态下的渲染状态以及管线的状态
glfwInit();
//,3版本以上
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
//用OpenGL核心开发模式
glfwWindowHint(GLFW_OPENGL_PROFILE,GLFW_OPENGL_CORE_PROFILE);
//创建窗体
GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGl Core", nullptr, nullptr);
if (window == nullptr) {
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
//把当前上下文绑定至当前窗口
glfwMakeContextCurrent(window);
//通过glad绑定各种函数指针
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
//视口:需要渲染的东西在哪里
glViewport(0, 0, 800, 600);
//当Frame大小变动,调用回调函数调整视口大小
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
initModel();
initShader("vertexShader.glsl", "fragmentShader.glsl");
//防止窗口结束退出
while (!glfwWindowShouldClose(window)) {
processInput(window);
//擦除画布,用定义的颜色填充
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
render();
//双缓冲
glfwSwapBuffers(window);
glfwPollEvents();
}
//结束,释放资源
glfwTerminate();
return 0;
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
glViewport(0, 0, width, height);
}
void processInput(GLFWwindow* window) {
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
glfwSetWindowShouldClose(window, true);
}
}
//渲染
void render() {
_shader.start();
glBindVertexArray(VAO);
//以三角形模式绘制,从第0个顶点开始,起作用的有3个点
//glDrawArrays(GL_TRIANGLES, 0, 3);
//以三角形模式绘制,用顶点索引
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
_shader.end();
}
//构建模型数据:VBO,VAO
void initModel() {
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.5f, -0.5f, 0.0f, 0.0f,0.0f,1.0f,
-0.5f, 0.5f, 0.0f, 0.0f,1.0f,0.0f
};
unsigned int indices[] = {
0,1,3,
1,2,3
};
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
//EBO同样要位于VAO的管理之下
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
//之后的VBO便属于了VAO的管理范围
glGenBuffers(1, &VBO);
//绑定哪一种buffer,
glBindBuffer(GL_ARRAY_BUFFER, VBO);
//分配显存:分配哪种buffer,分配显存大小,分配地址,使用数据的方式
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//对哪个锚点进行操作:layout=0的锚点,读3个顶点,类型为float,不需要归一化,每次步长为3个float大小,从0处开始读
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(sizeof(float)*3));
//打开锚点:激活
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
//解绑
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
//
void initShader(const char* _vertexPath, const char* _fragPath) {
_shader.initShader(_vertexPath, _fragPath);
}
渲染结果