If there are errors in the code, terminology, etc. in the text, please correct me
Article Directory
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
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
- And the OpenGLShader header file #include <glad/glad3.h> file
- The current application is in a Sandbox project
- And this Sandbox project does not contain the file #include <glad/glad3.h>, so an error will be reported .
And since I #include<GLFW/glfw3.h> header file in the OpenGlShader.h file, the following error will also be reported
-
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>
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>
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
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"));