当前位置: 首页 > news >正文

HazelEngine 学习记录 - Shader Asset Files

Shader Asset Files

我们之前的所有 Shader 都是在 cpp 文件中进行的,在 Shader 代码比较冗长的时候会让程序变得难以阅读,因此需要将 Shader 单独用文件保存起来,然后使用的时候进行读取和编译。

之前 Shader 的创建是通过传入两个字符串,并对其进行编译之后进行绑定,现在新添加一种创建方式,就是通过文件名来进行读入:

static Shader* Create(const std::string& filepath);
//.cpp
Shader* Shader::Create(const std::string& filepath)
	{
		switch (Renderer::GetAPI())
		{
			case RendererAPI::API::None:    HZ_CORE_ASSERT(false, "RendererAPI::None is currently not supported!"); return nullptr;
			case RendererAPI::API::OpenGL:  return new OpenGLShader(filepath);
		}

		HZ_CORE_ASSERT(false, "Unknown RendererAPI!");
		return nullptr;
	}

同样的,在引擎的 Shader 中我们只是进行 API 的选择,具体的实现放到对应 API 中进行:

在 OpenGLShader 中,需要对传入的文件名进行读取,将文件中的所有字符串保存在一个string 变量当中,这里 Cherno 将 ver 和 frag 的 Shader 放在同一个文件当中,然后添加 type 对字符串进行处理,从而使整个 shader 看起来更加简洁:

// Basic Texture Shader

#type vertex
#version 330 core

layout(location = 0) in vec3 a_Position;
layout(location = 1) in vec2 a_TexCoord;

uniform mat4 u_ViewProjection;
uniform mat4 u_Transform;

out vec2 v_TexCoord;

void main()
{
	v_TexCoord = a_TexCoord;
	gl_Position = u_ViewProjection * u_Transform * vec4(a_Position, 1.0);
}

#type fragment
#version 330 core

layout(location = 0) out vec4 color;

in vec2 v_TexCoord;

uniform sampler2D u_Texture;

void main()
{
	color = texture(u_Texture, v_TexCoord);
}

在读取文件函数中进行如下处理:

首先将所有的字符保存在 string 变量中并返回

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());
			in.seekg(0, std::ios::beg);
			in.read(&result[0], result.size());
			in.close();
;		}
		else
		{
			HZ_CORE_ERROR("Could not open file '{0}'", filepath);
		}

		return result;
	}

然后对该字符串进行处理:

std::unordered_map<GLenum, std::string> OpenGLShader::PreProcess(const std::string& source)
	{
		std::unordered_map<GLenum, std::string> shaderSources; //分别保存两个shader并记录,用哈希表方便我们使用的时候找到

		const char* typeToken = "#type";  //先找到字符串 type 以此来确定是何种 shader
		size_t typeTokenLength = strlen(typeToken);
		size_t pos = source.find(typeToken, 0);  //从起始位置找 type
		while (pos != std::string::npos)  //如果找到了,进入循环,没找到直接返回
		{
			size_t eol = source.find_first_of("\r\n", pos); //从 type 之后找当前行剩下的字符串(vertex/fragment)
			HZ_CORE_ASSERT(eol != std::string::npos, "Syntax error");
			size_t begin = pos + typeTokenLength + 1; 
			std::string type = source.substr(begin, eol - begin);
			HZ_CORE_ASSERT(ShaderTypeFromString(type), "Invalid shader type specified");

			size_t nextLinePos = source.find_first_not_of("\r\n", eol); //确定类型之后,起始位置变为下一行的开始位置
			pos = source.find(typeToken, nextLinePos); //然后找下一个 type token
			shaderSources[ShaderTypeFromString(type)] = source.substr(nextLinePos, pos - (nextLinePos == std::string::npos ? source.size() - 1 : nextLinePos));//存储在哈希表中
		}

		return shaderSources;
	}

按照之前的方法进行编译,只不过这次我们把编译放在一个for 循环里面, 遍历哈希表进行编译:

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;

			GLuint shader = glCreateShader(type);

			const GLchar* sourceCStr = source.c_str();
			glShaderSource(shader, 1, &sourceCStr, 0);

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

				std::vector<GLchar> infoLog(maxLength);
				glGetShaderInfoLog(shader, maxLength, &maxLength, &infoLog[0]);

				glDeleteShader(shader);

				HZ_CORE_ERROR("{0}", infoLog.data());
				HZ_CORE_ASSERT(false, "Shader compilation failure!");
				break;
			}

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

			for (auto id : glShaderIDs)
				glDeleteShader(id);

			HZ_CORE_ERROR("{0}", infoLog.data());
			HZ_CORE_ASSERT(false, "Shader link failure!");
			return;
		}

		for (auto id : glShaderIDs)
			glDetachShader(program, id);
	}

构造函数:

OpenGLShader::OpenGLShader(const std::string& filepath)
	{
		std::string source = ReadFile(filepath);
		auto shaderSources = PreProcess(source);
		Compile(shaderSources);
	}

之后我们添加一个简单的 shader

// Basic Texture Shader

#type vertex
#version 330 core

layout(location = 0) in vec3 a_Position;
layout(location = 1) in vec2 a_TexCoord;

uniform mat4 u_ViewProjection;
uniform mat4 u_Transform;

out vec2 v_TexCoord;

void main()
{
	v_TexCoord = a_TexCoord;
	gl_Position = u_ViewProjection * u_Transform * vec4(a_Position, 1.0);
}

#type fragment
#version 330 core

layout(location = 0) out vec4 color;

in vec2 v_TexCoord;

uniform sampler2D u_Texture;

void main()
{
	color = texture(u_Texture, v_TexCoord);
}

然后进行使用:

m_TextureShader.reset(Hazel::Shader::Create("assets/shaders/Texture.glsl"));

这样我们的 Shader 就可以从外部文件导入了!

相关文章:

  • 网络安全—DDoS攻防
  • 【JavaWeb】之富文本编辑器
  • Synchronized底层核心原理
  • 基于JSP的房屋销售系统设计与实现
  • Arduino UNO 可视化GT-24工业级无线透传
  • 【QT 自研上位机 与 STM32F103下位机联调>>>通信测试-基础样例-联合文章】
  • c语言的三种基本结构——初学者一定要了解哦
  • 无人驾驶:高精地图与定位
  • 【论文笔记】提高超高分辨率图像的语义分割准确性的两种方法:MagNet(CVPR2021)与FCtL(ICCV2021)
  • OGG21C微服务的安装和配置
  • Matlab之多平台雷达检测融合仿真(附源码)
  • 2022年计网《宿舍网有线无线一体化项目》总结
  • 渗透测试基础- - -windows网络安全常用dos命令
  • 【Python】近似熵,样本熵,模糊熵计算高效版
  • Java获取自增主键ID值
  • 【Redis学习笔记】2018-06-28 redis命令源码学习1
  • Django 博客开发教程 8 - 博客文章详情页
  • ES6 ...操作符
  • ES学习笔记(12)--Symbol
  • interface和setter,getter
  • javascript 总结(常用工具类的封装)
  • Java新版本的开发已正式进入轨道,版本号18.3
  • node学习系列之简单文件上传
  • sessionStorage和localStorage
  • webpack4 一点通
  • 包装类对象
  • 程序员最讨厌的9句话,你可有补充?
  • 分享自己折腾多时的一套 vue 组件 --we-vue
  • 湖南卫视:中国白领因网络偷菜成当代最寂寞的人?
  • 前端之Sass/Scss实战笔记
  • 腾讯优测优分享 | Android碎片化问题小结——关于闪光灯的那些事儿
  • 问:在指定的JSON数据中(最外层是数组)根据指定条件拿到匹配到的结果
  • ​sqlite3 --- SQLite 数据库 DB-API 2.0 接口模块​
  • ​人工智能之父图灵诞辰纪念日,一起来看最受读者欢迎的AI技术好书
  • ​一些不规范的GTID使用场景
  • #宝哥教你#查看jquery绑定的事件函数
  • $分析了六十多年间100万字的政府工作报告,我看到了这样的变迁
  • (14)学习笔记:动手深度学习(Pytorch神经网络基础)
  • (32位汇编 五)mov/add/sub/and/or/xor/not
  • (MIT博士)林达华老师-概率模型与计算机视觉”
  • (NO.00004)iOS实现打砖块游戏(九):游戏中小球与反弹棒的碰撞
  • (Redis使用系列) Springboot 使用redis实现接口幂等性拦截 十一
  • (二)正点原子I.MX6ULL u-boot移植
  • (十五)使用Nexus创建Maven私服
  • (详细版)Vary: Scaling up the Vision Vocabulary for Large Vision-Language Models
  • (一) storm的集群安装与配置
  • (原創) 如何動態建立二維陣列(多維陣列)? (.NET) (C#)
  • (转)创业家杂志:UCWEB天使第一步
  • ***linux下安装xampp,XAMPP目录结构(阿里云安装xampp)
  • ... 是什么 ?... 有什么用处?
  • .NET Core Web APi类库如何内嵌运行?
  • .Net Remoting常用部署结构
  • .NET业务框架的构建
  • @Mapper作用
  • [ 攻防演练演示篇 ] 利用通达OA 文件上传漏洞上传webshell获取主机权限