LearnOpenGL:Shader(下)写shader

LearnOpenGL:Shader(下)写shader,第1张

我们使用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 
    //构造函数
    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 
    
    #define GLEW_STATIC
    #include 
    #include 
    
    #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

std:: 是个名称空间标示符,C++标准库中的函数或者对象都是在命名空间std中定义的,所以我们要使用标准函数库中的函数或对象都要使用std来限定。

对象count是标准函数库所提供的对象,而标准库在名字空间中被指定为std,所以在使用cout的时候要加上std::。

不过 你也可以 在前面声明 using namespace std;来避免每一次都要在前面声明 std:: (可能会出现报错)

现在来开文档 并设置 如果开档失败 马上报错 open file error

#include "Shader.h"

#include 
#include 

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);

 

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/662753.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-18
下一篇 2022-04-18

发表评论

登录后才能评论

评论列表(0条)

保存