Hazel game engine (044) loads Shader files and compiles

If there are errors in the code, terminology, etc. in the text, please correct me

foreword

  • of this program

    Since the previous shader code was written in Sandbox, it is hard-coded, and it is not easy to expand, write, and look good.

    I want to design it to load shader code from a file, so I need to write relevant code to read the content of the file , and finally get the corresponding shader code

  • How to achieve

    • Using object-oriented processing, the Shader code is loaded as the behavior of the Shader class.

    • Put the shader code in the assert folder to become a resource, load and parse the file with the function code of the Shader class (use the fstream class of C++ to read the file content)

    • Get the shader code string and then compile, link, delete,

key code

  • The string reading of OpenGlShader is placed in the string, and the parsing result is placed in the value of the map

    std::string OpenGLShader::ReadFile(const std::string& filepath)
    {
          
          
        std::string result;
        std::ifstream in(filepath, std::ios::in, std::ios::binary);// 二进制读取?为什么还是保持字符串的形式?
        if (in) {
          
          
            in.seekg(0, std::ios::end);			// 将指针放在最后面
            result.resize(in.tellg());			// 初始化string的大小, in.tellg()返回位置
            in.seekg(0, std::ios::beg);			// in指回头部
            in.read(&result[0], result.size());	// in读入放在result指向的内存中
        }
        else {
          
          
            HZ_CORE_ERROR("不能打开文件:{0}", filepath);
        }
        return result;
    }
    std::unordered_map<GLenum, std::string> OpenGLShader::PreProcess(const std::string& source)
    {
          
          
        std::unordered_map<GLenum, std::string> shaderSources;
    
        std::string typeToken = "#type";
        size_t typeTokenLen = typeToken.size();
        size_t findCurPos = source.find(typeToken, 0);
        size_t findNextPos = findCurPos;
        while (findNextPos != std::string::npos) {
          
          
            size_t curlineEndPos = source.find_first_of("\r\n", findCurPos);///r/n写错为/r/n
            HZ_CORE_ASSERT(curlineEndPos != std::string::npos, "解析shader失败" );
            size_t begin = findCurPos + typeTokenLen + 1;
    
            std::string type = source.substr(begin, curlineEndPos - begin);// 获取到是vertex还是fragment
            HZ_CORE_ASSERT(ShaderTypeFromString(type), "无效的shader的类型	");
    
            size_t nextLinePos = source.find_first_not_of("\r\n", curlineEndPos);
            findNextPos = source.find(typeToken, nextLinePos);
            // 获取到具体的shader代码
            shaderSources[ShaderTypeFromString(type)] = source.substr(nextLinePos, findNextPos - (nextLinePos == std::string::npos ? source.size() - 1 : nextLinePos));
    
            findCurPos = findNextPos;
        }
        return shaderSources;
        /*
    			用find,而不是find_firtst_of,因为
    			find返回完全匹配的字符串的的位置;
    			find_first_of返回 被查 匹配字符串中某个字符的第一次出现位置。
    
    			std::string::npos是一个非常大的数
    			source.substr(0, source.size() + 10000)截取到从头到末尾,不会报错
    		*/
    }
    
  • Diagram explaining the various positions of the parsing code

    Please add a picture description

error encountered

  • error message

    Since the OpenGlShader subclass has its own Unpload function, but the parent Shader does not, it is necessary to force the dynamic pointer to convert the pointer type to a derived class pointer pointing to a derived class.

    In the SandboxApp code

    std::dynamic_pointer_cast<Hazel::OpenGLShader>(m_SquareTexCoordShader);
    

    If the dynamic pointer is used , the generic type is OpenGLShader, then SandboxApp needs to import OpenGLShader.h

    1. And the OpenGLShader header file #include <glad/glad3.h> file
    2. The current application is in a Sandbox project
    3. And this Sandbox project does not contain the file #include <glad/glad3.h>, so an error will be reported .


    Please add a picture description

    And since I #include<GLFW/glfw3.h> header file in the OpenGlShader.h file, the following error will also be reported

    bug1_2

  • Error analysis

    See the Premake.lua file

    These three items are referenced by the GameEngineLightWeight project and are not provided to the Sandbox project .

  • Handling Errors: Workarounds

    • Solve cannot include glfw/glfw3.h

      Delete glfw/glfw3.h in OpenGLShader.h, but put it in OpenGLShader.cpp file #include <glad/glad.h>

      Please add a picture description

      Because the cpp file is compiled obj, the OpenGLShader.cpp file is not included in the SandboxApp code, and therefore glad/glad.h is not included.

    • Solve the failure to include glad/glad.h

      OpenGlShader#include <glad/glad.h> is because the declaration of Glenum is needed , so define Glenum in this file and delete #include <glad/glad.h>

      Please add a picture description

      In this way, the Sandbox cannot include the glad/glad.h file

  • diff glm.hpp

    When OpenGLShader.h imports the <glm/glm.hpp> math library file, Sandbox does not report an error because

    • GameEngineLightWeight is set in premake to include this glm math library file.
    • And the Sandbox references the GameEngineLightWeight project, so the glm math library file can be directly included in the Sandbox

    The glad.h file belongs to the GLAD project and is referenced by GameEngineLightWeight, so it can be included by GameEngineLightWeight, but the Sandbox project does not reference the GLAD project, so the Sandbox cannot include glad.h

    Please add a picture description

    Please add a picture description

code modification

  • OpenGLShader.cpp

    #include "hzpch.h"
    #include "OpenGLShader.h"
    #include <fstream>
    #include <glad/glad.h>
    #include <glm/gtc/type_ptr.hpp>
    namespace Hazel {
          
          
    	static GLenum ShaderTypeFromString(const std::string& type) {
          
          
    		if (type == "vertex") {
          
          
    			return GL_VERTEX_SHADER;
    		}
    		if (type == "fragment" || type == "pixel") {
          
          
    			return GL_FRAGMENT_SHADER;
    		}
    		HZ_CORE_ASSERT(false, "不知道的shader类型");
    		return 0;
    	}
    	OpenGLShader::OpenGLShader(const std::string& filepath)
    	{
          
          
    		std::string source = ReadFile(filepath);
    		HZ_CORE_ASSERT(source.size(), "GLSL读取的字符串为空");
    		auto shaderSources = PreProcess(source);
    		Compile(shaderSources);
    	}
    	OpenGLShader::OpenGLShader(const std::string& vertexSrc, const std::string& fragmentSrc)
    	{
          
          
    		std::unordered_map<GLenum, std::string> shaderSources;
    		shaderSources[GL_VERTEX_SHADER] = vertexSrc;
    		shaderSources[GL_FRAGMENT_SHADER] = fragmentSrc;
    		Compile(shaderSources);
    	}
    	std::string OpenGLShader::ReadFile(const std::string& filepath)
    	{
          
          
    		std::string result;
    		std::ifstream in(filepath, std::ios::in, std::ios::binary);// 二进制读取?为什么还是保持字符串的形式?
    		if (in) {
          
          
    			in.seekg(0, std::ios::end);			// 将指针放在最后面
    			result.resize(in.tellg());			// 初始化string的大小, in.tellg()返回位置
    			in.seekg(0, std::ios::beg);			// in指回头部
    			in.read(&result[0], result.size());	// in读入放在result指向的内存中
    		}
    		else {
          
          
    			HZ_CORE_ERROR("不能打开文件:{0}", filepath);
    		}
    		return result;
    	}
    	std::unordered_map<GLenum, std::string> OpenGLShader::PreProcess(const std::string& source)
    	{
          
          
    		std::unordered_map<GLenum, std::string> shaderSources;
    
    		std::string typeToken = "#type";
    		size_t typeTokenLen = typeToken.size();
    		size_t findCurPos = source.find(typeToken, 0);
    		size_t findNextPos = findCurPos;
    		while (findNextPos != std::string::npos) {
          
          
    			size_t curlineEndPos = source.find_first_of("\r\n", findCurPos);///r/n写错为/r/n
    			HZ_CORE_ASSERT(curlineEndPos != std::string::npos, "解析shader失败" );
    			size_t begin = findCurPos + typeTokenLen + 1;
    
    			std::string type = source.substr(begin, curlineEndPos - begin);// 获取到是vertex还是fragment
    			HZ_CORE_ASSERT(ShaderTypeFromString(type), "无效的shader的类型	");
    
    			size_t nextLinePos = source.find_first_not_of("\r\n", curlineEndPos);
    			findNextPos = source.find(typeToken, nextLinePos);
    			// 获取到具体的shader代码
    			shaderSources[ShaderTypeFromString(type)] = source.substr(nextLinePos, findNextPos - (nextLinePos == std::string::npos ? source.size() - 1 : nextLinePos));
    
    			findCurPos = findNextPos;
    		}
    		return shaderSources;
    		/*
    			用find,而不是find_firtst_of,因为
    			find返回完全匹配的字符串的的位置;
    			find_first_of返回被查匹配字符串中某个字符的第一次出现位置。
    
    			std::string::npos是一个非常大的数
    			source.substr(0, source.size() + 10000)截取到从头到末尾,不会报错
    		*/
    	}
    	void OpenGLShader::Compile(const std::unordered_map<GLenum, std::string>& shaderSources)
    	{
          
          
    		GLuint program = glCreateProgram();
    		std::vector<GLenum> glShaderIDs(shaderSources.size());
    		for (auto &kv : shaderSources) {
          
          
    			GLenum type = kv.first;
    			const std::string& source = kv.second;
    			// Create an empty vertex shader handle
    			GLuint shader = glCreateShader(type);
    			// Send the vertex shader source code to GL
    			// Note that std::string's .c_str is NULL character terminated.
    			const GLchar* sourceCStr = source.c_str();
    			glShaderSource(shader, 1, &sourceCStr, 0);
    			// Compile the vertex shader
    			glCompileShader(shader);
    			GLint isCompiled = 0;
    			glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);
    			if (isCompiled == GL_FALSE)
    			{
          
          
    				GLint maxLength = 0;
    				glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
    				// The maxLength includes the NULL character
    				std::vector<GLchar> infoLog(maxLength);
    				glGetShaderInfoLog(shader, maxLength, &maxLength, &infoLog[0]);
    				// We don't need the shader anymore.
    				glDeleteShader(shader);
    				// Use the infoLog as you see fit.
    				// In this simple program, we'll just leave
    				HZ_CORE_ERROR("{0} ", infoLog.data());
    				HZ_CORE_ASSERT(false, "shader 编译失败!");
    				break;
    			}			
    			// Attach our shaders to our program
    			glAttachShader(program, shader);
    			glShaderIDs.push_back(shader);
    		}
    		m_RendererID = program;
    		// Link our program
    		glLinkProgram(program);
    		// Note the different functions here: glGetProgram* instead of glGetShader*.
    		GLint isLinked = 0;
    		glGetProgramiv(program, GL_LINK_STATUS, (int*)&isLinked);
    		if (isLinked == GL_FALSE)
    		{
          
          
    			GLint maxLength = 0;
    			glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength);
    			// The maxLength includes the NULL character
    			std::vector<GLchar> infoLog(maxLength);
    			glGetProgramInfoLog(program, maxLength, &maxLength, &infoLog[0]);
    			// We don't need the program anymore.
    			glDeleteProgram(program);
    			// Don't leak shaders either.
    			for (auto id : glShaderIDs) {
          
          
    				glDeleteShader(id);
    			}
    			// Use the infoLog as you see fit.
    			// In this simple program, we'll just leave
    			HZ_CORE_ERROR("{0} ", infoLog.data());
    			HZ_CORE_ASSERT(false, "shader link failure!");
    			return;
    		}
    		// Always detach shaders after a successful link.
    		for (auto id : glShaderIDs) {
          
          
    			glDetachShader(program, id);
    		}
    	}
    ......
    }
    
  • SandboxApp.cpp

    //m_SquareTexCoordShader.reset(Hazel::Shader::Create(squareTexCoordShaderVertexSrc, squareTexCoordShaderfragmentSrc));
    m_SquareTexCoordShader.reset(Hazel::Shader::Create("asserts/shaders/Texture.glsl"));
    

Guess you like

Origin blog.csdn.net/qq_34060370/article/details/131505431