LearnOpenGL:Shader(下)写shader

  • Post author:
  • Post category:其他


我们使用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);



版权声明:本文为Moreland0424原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。