我们使用C++
文件流(file stream)
读取着色器内容,储存到几个
string
对象里
右键源文件 新建 添加 新建项 选择C++类 名字改为Shader
这样之后,会出现Shader.h和Shader.cpp两个文件
在c++中 如果不宣告并定义的话 你将无法在main中使用这个函数
现在这个 .h*与 .cpp*的分页的意义是 把所有的宣告 全部都放入到.h*的文档里 .cpp*里只用写程式码不用考虑是否有宣告过
首先来看一下shader 的宣告
- 在h文件中,最开始会是这样,因为今天的内容很简单 不需要释放内存 所以只用使用 构造函数就可以 (文中shader 代表构造和析构函数)
-
构造函数 ,是一种特殊的方法。主要用来在
创建对象时初始化对象
, 即为对象
成员变量
赋初始值,总与new
运算符
一起使用在创建对象的语句中。特别的一个类可以有多个构造函数 ,可根据其参数个数的不同或参数类型的不同来区分它们 即构造函数的
重载
。
-
析构函数(destructor) 与构造函数相反,当对象结束其
生命周期
,如对象所在的
函数
已
调用
完毕时,系统自动执行析构函数。析构函数往往用来做“清理善后” 的工作(例如在建立对象时用new开辟了一片内存空间,delete会自动调用析构函数后
释放
内存
)。
-
#pragma once class Shader { public: Shader(); //~Shader(); };
先传入档名
-
Shader(const char* vertexPath, const char* fragmentPath)
char* 读取档名
-
const 是因为 下面要是用“ 档名”的形式 写死 所以要使用const修饰 不然容易报错
-
写完之后 需要复制到 .cpp中
-
Shader ::Shader(const char* vertexPath, const char* fragmentPath)
为什么 .cpp*中是 Shader ::Shader
-
这是命名空间的关系 因为你在写c++的时候 你并不知道你现在是什么命名空间 所以你在cpp中宣告的是 在Shander空间中的Shader函数
-
.cpp*代码
#include "Shader.h" #include <iostream> //构造函数 Shader ::Shader(const char* vertexPath, const char* fragmentPath) { } void Shader::test() { printf("LI DOU DOU DOU"); } //析构函数 // Shader :: ~Shader() // { // }
.h*代码
-
#pragma once class Shader { public: Shader(const char* vertexPath, const char* fragmentPath); //~Shader(); void test(); };
全局代码
#include <iostream> #define GLEW_STATIC #include <GL/glew.h> #include <GLFW/glfw3.h> #include "Shader.h" //由于是要渲染一个三角形,所以一共要指定三个顶点,每个顶点都有一个3D位置。 //我们将它们以标准化设备坐标的形式定义为一个float数组。 float vertices[] = { 0.5f, 0.5f, 0.0f, 1.0f, 0, 0, // 右上角 0.5f, -0.5f, 0.0f, 0, 1.0f, 0, // 右下角 -0.5f, -0.5f, 0.0f, 0, 0, 1.0f, // 左下角 -0.5f, 0.5f, 0.0f , 1.0f, 0, 1.0f // 左上角 }; unsigned int indices[] = { // 注意索引从0开始! 0, 1, 3, // 第一个三角形 1, 2, 3 // 第二个三角形 }; const char* vertexShaderSource = "#version 330 core \n " "layout(location = 0) in vec3 aPos ; \n " "layout(location = 1) in vec3 aColor ; \n " "out vec4 vertexColor ; \n " //夹塞一个颜色vec4 输出 "void main() { \n " " gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0); \n " " vertexColor = vec4 (aColor.x , aColor.y , aColor.z, 1.0 ); } \n "; const char* fragmentShaderSource = "#version 330 core \n " "in vec4 vertexColor ; \n " "uniform vec4 ourColor ; \n " "out vec4 FragColor; \n " "void main() { \n " " FragColor = vertexColor ;} \n "; void processInput(GLFWwindow* window) { if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) { glfwSetWindowShouldClose(window, true); } } int main() { Shader *testShader = new Shader("vertexSource.txt", "fragmentSource.txt"); testShader -> test(); glfwInit(); //初始化glfw函数 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); //主版本号 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); //次版本号 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL); if (window == NULL) { printf("open window faild"); glfwTerminate(); return -1; } glfwMakeContextCurrent(window); //Init GLEW glewExperimental = true; if (glewInit() != GLEW_OK) { printf("Init GLEW failed."); glfwTerminate(); return -1; } glViewport(0, 0, 800, 600); //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); unsigned int VAO; glGenVertexArrays(1, &VAO); glBindVertexArray(VAO); //VBO输出的模型的数据信息VAO unsigned int VBO; glGenBuffers(1, &VBO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); unsigned int EBO; glGenBuffers(1, &EBO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); //vertex shader unsigned int vertexShader; vertexShader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertexShader, 1, &vertexShaderSource, NULL); glCompileShader(vertexShader); //fragment shader unsigned int fragmentShader; fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL); glCompileShader(fragmentShader); //接下来要把这两个shader组装成一个program才能拿出来用 unsigned int shaderProgram; shaderProgram = glCreateProgram(); glAttachShader(shaderProgram, vertexShader); glAttachShader(shaderProgram, fragmentShader); glLinkProgram(shaderProgram); //教vao如何从vbo中挖资料 // 位置属性 先挖顶点 从起始点0开始挖 每挖完3个float 就跳6个float 到下一个顶点 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0); //程序需要选中0号栏位开始放入,把我们送进去的这些数值,每三个当成一份资料,它们都是float,不需要正规化,每隔3*float个长度去挖下一个,不需要偏移量,然后再激活对外开放0号栏位。 glEnableVertexAttribArray(0); // 颜色属性 再挖颜色的值 第一个颜色 是在xyz的值后面 所以从3个float后开始挖 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float))); glEnableVertexAttribArray(1); while (!glfwWindowShouldClose(window)) { // 输入 processInput(window); // 渲染指令 glClearColor(1.0f, 0.5f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); //清除buffer float timeValue = glfwGetTime(); float greenValue = sin(timeValue) / 2.0f + 0.5f; int vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor"); glUseProgram(shaderProgram); //fragment shader 一定要送出任意一个4维向量的东西到Tests and blending glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f); glBindVertexArray(VAO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); //glDrawArrays(GL_TRIANGLES, 0, 6); //用这个program去画这个VAO 从第0个数值开始画 画3个数值 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); // 检查并调用事件,交换缓冲 glfwPollEvents(); glfwSwapBuffers(window); } glfwTerminate(); return 0; }
结果
什么是文件流( C++ filestreams )
一个在右上角磁盘上的shader 想要被cpu接受 需要进行一些列的转化
先从硬盘调取大量文件到memory,但是memory不能直接读取数据的 所以需要在memory中开辟一个FIleBuffer来做缓存,但是这些文件 有各种各样的格式,如何操作我们是不知道的 所以用StringBuffer来对FileBuffer来做一个诠释
但是Opengl是不直接吃 stringbuffer的,所以 我们要用string把string buffer里面的东西做一个转化,转化为char数组,然后 char数组才能够被opengl所识别
所以这个千辛万苦 西天取经一样的流程代码 就是我们使用C++文件流读取shader内容 储存到几个string里面的代码
首先第一件事情,从硬盘拿去的东西我们需要用一个FileBuffer储存好 ifstream ( input file stream) (Filebuffer=ifstream 所有的buffer 在库里面都叫 stream) 然后我们需要引用 file stream这个库(#include <fstream>)
std
:: 是个名称空间标示符,
C++
标准库
中
的函数或者对象都是在命名空间
std中
定义的,所以我们要使用标准函数库
中
的函数或对象都要使用
std
来限定。 对象count是标准函数库所提供的对象,而标准库在名字空间
中
被指定为
std
,所以在使用cout的时候要加上
std
::。
不过 你也可以 在前面声明 using namespace std;来避免每一次都要在前面声明 std:: (可能会出现报错)
现在来开文档 并设置 如果开档失败 马上报错 open file error
#include "Shader.h"
#include <iostream>
#include <fstream>
using namespace std;
//构造函数
Shader ::Shader(const char* vertexPath, const char* fragmentPath)
{
ifstream vertexFile;
ifstream fragmentFile;
vertexFile.open(vertexPath);
fragmentFile.open(fragmentPath);
try
{
if (!vertexFile.is_open() || !fragmentFile.is_open() )
{
throw exception("open file error");
}
}
catch (const std::exception& ex)
{
printf (ex.what());
}
}
由于现在的代码中 没有这两个档案所以会直接报错
下面要在项目中新增这两个档案 右键项目 ——添加——新增项目——实用文件——文本文件
添加 vertexSource 和 fragmentSource 两个文本文件 加入这两个文档之后 就不会报错了
下面还要加入 逻辑上开不了文档和 真实上开不了文档(档案有问题) 查知道有问题 报异常
vertexFile.exceptions(ifstream::fail || ifstream::badbit);
fragmentFile.exceptions(ifstream::fail || ifstream::badbit);