OpenGL编译用户着色器shader

shader 相信很多朋友们都听说过,shader 就是运行再GPU 上的程序。虽然是这么说,但是我们发现,很多IDE 开发工具比如说visual studio 没有办法直接去运行shader 代码。这是因为,许多编译器不会自动将shader 文件编译成可执行的代码然后发送给GPUshader 代码的编译需要开发人员手动动用GPUshader 编译接口,将对应的代码编译成可执行的程序。在这里,笔者就为大家介绍,如何使用OpenGL 提供的API 编译用户自己的shader程序。

Shader

OpenGL渲染管线

这里先为大家介绍一下OpenGL渲染管线如下图所示

事实上在一众着色器当中,只有顶点着色器和片元着色器是必须的,其他都是可选的。具体想要了解更多,可以去看一些资料或者书籍,比如笔者手上的这本《OpenGL编程指南》

OpenGL shader

OpenGL 支持的 shader 语法是 OpenGL shader language 简称是glsl 。这个shader 语法和**C++**基本上是大同小异,很快就可以轻松上手。

话说到这里,让我们回顾一下上一篇文章OpenGL渲染结果移至ImGui窗口上,有细心的本有不难发现,我并没有去编译用户着色器,而且必要的顶点着色器和片元着色器都没有进行编写,但是我们仍然得到我们想要的渲染结果。其原因是,OpenGL 自带默认的着色器,所以对于初学者来说,可以尽可能使用少的代码去实现自己想要的结果,这就是为什么图形接口的学习一般都是从OpenGL开始学起。

OpenGL着色器编译

Shader 类

这里笔者写了一个Shader类,整体的代码如下

Shader.h

cpp 复制代码
#pragma once

#include<unordered_map>
typedef unsigned int GLenum;

class Shader {
public:
	Shader(const std::string& filePath);
	~Shader();
	void Bind();
	void UBind();

	void UploadUniformFloat4(const std::string& name, float* value);

private:
	std::string ReadFile(const std::string& filePath);
	std::unordered_map<GLenum, std::string> PreProcess(const std::string& source);
	void Compile(const std::unordered_map<GLenum, std::string>& shaderSources);
private:
	uint32_t m_ShaderID;
	std::string m_Name;
};

Shader.cpp

cpp 复制代码
#include<glad/glad.h>
#include<string>
#include<array>
#include<fstream>
#include<iostream>

#include"Shader.h"

static GLenum ShaderTypeFromString(const std::string& type) {
	if (type == "vertex")
		return GL_VERTEX_SHADER;
	else if (type == "fragment" || type == "pixel")
		return GL_FRAGMENT_SHADER;

	std::cout << "Unknown shader type" << std::endl;
	return 0;
}

Shader::Shader(const std::string& filePath) :m_ShaderID(0) {
	std::string source = ReadFile(filePath);
	auto shaderSource = PreProcess(source);
	Compile(shaderSource);

	auto lastSlash = filePath.find_last_of("/\\");
	lastSlash = lastSlash == std::string::npos ? 0 : lastSlash + 1;
	auto lastDot = filePath.rfind('.');
	auto count = lastDot == std::string::npos ? filePath.size() - lastSlash : lastDot - lastSlash;
	m_Name = filePath.substr(lastSlash, count);
}

Shader::~Shader() {
	glDeleteProgram(m_ShaderID);
}

void Shader::Bind(){
	glUseProgram(m_ShaderID);
}

void Shader::UBind(){
	glUseProgram(0);
}

void Shader::UploadUniformFloat4(const std::string& name, float* value) {
	int location = glGetUniformLocation(m_ShaderID, name.c_str());
	glUniform4f(location, value[0], value[1], value[2], value[3]);
}

std::string Shader::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 {
		std::cout << "着色器文件没有正常打开" << std::endl;
		__debugbreak();
	}

	return result;
}

std::unordered_map<GLenum, std::string> Shader::PreProcess(const std::string& source) {
	std::unordered_map<GLenum, std::string> shaderSources;

	const char* typeToken = "#type";
	size_t typeTokenLength = strlen(typeToken);
	size_t pos = source.find(typeToken,0);

	while (pos != std::string::npos) {
		size_t eol = source.find_first_of("\r\n", pos);
		if (eol == std::string::npos) {
			std::cout << "着色器语法出错" << std::endl;
			__debugbreak();
		}

		size_t begin = pos + typeTokenLength + 1;
		std::string type = source.substr(begin, eol - begin);

		if (!ShaderTypeFromString(type)) {
			std::cout << "这是一个不合法的着色器类型" << std::endl;
			__debugbreak();
		}

		size_t nextLinePos = source.find_first_of("\r\n", eol);
		pos = source.find(typeToken, nextLinePos);
		shaderSources[ShaderTypeFromString(type)] = source.substr(nextLinePos, pos - (nextLinePos == std::string::npos ? source.size() - 1 : nextLinePos));
	}

	return shaderSources;
}

void Shader::Compile(const std::unordered_map<GLenum, std::string>& shaderSources) {
	unsigned int program = glCreateProgram();
	//一次性至多编译两种着色器
	if (shaderSources.size() < 2) {
		std::cout << "一次性至多编译两种着色器" << std::endl;
		__debugbreak();
	}

	std::array<GLenum, 2> glShaderIDs;
	int glShaderIDIndex = 0;
	for (auto& kv : shaderSources) {
		GLenum type = kv.first;
		const std::string& source = kv.second;

		unsigned int shader = glCreateShader(type);

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

		glCompileShader(shader);

		int isCompiled = 0;
		glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);

		if (isCompiled == GL_FALSE) {
			int maxLength = 0;
			glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);

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

			glDeleteShader(shader);

			std::cout << "着色器编译出错:" << infoLog.data() << std::endl;
			__debugbreak();
			break;
		}

		glAttachShader(program, shader);
		glShaderIDs[glShaderIDIndex++] = shader;
	}

	m_ShaderID = program;

	// Link our program
	glLinkProgram(program);

	// Note the different functions here: glGetProgram* instead of glGetShader*.
	int isLinked = 0;
	glGetProgramiv(program, GL_LINK_STATUS, (int*)&isLinked);

	if (isLinked == GL_FALSE) {
		int maxLength = 0;
		glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength);

		// The maxLength includes the NULL character
		std::vector<char> infoLog(maxLength);
		glGetProgramInfoLog(program, maxLength, &maxLength, &infoLog[0]);

		// We don't need the program anymore.
		glDeleteProgram(program);

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

		std::cout << "用户着色器链接失败:" << infoLog.data() << std::endl;
		__debugbreak();
		return;
	}

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

着色器代码

TextureShader.glsl

cpp 复制代码
#type vertex
#version 450 core
//标记为0的内存位置输入一个有两个分量的向量,这是顶点的位置
layout(location = 0) in vec2 v_Position;

void main(){
    //顶点位置的数据进行赋值,需要转换为齐次向量
	gl_Position = vec4(v_Position,0.0f,1.0f);
}

#type fragment
#version 450 core
//标记为0的内存位置输出一个有四个分量的向量,这是像素的颜色
layout(location = 0) out vec4 o_Color;

void main(){
	o_Color = vec4(0.8f,0.2f,0.3f,1.0f);
}

着色器介绍

上面虽然是一个着色器文件,其实这里面写了两个着色器,一个是顶点着色器,一个是片元着色器。#type vertex 下面的是顶点着色器,#type fragment下面的是片元着色器。为什么这两个要一起写了?前面也介绍了,这两个着色器是必需要有的,所以笔者推荐这个着色器最好就是一起写。顶点着色器必须要有输入数据,片元着色器必须要有输出数据,不然屏幕上就看不到任何东西。

着色器编译

笔者将其分成了3个步骤进行

1、读取对应的文件内容

cpp 复制代码
std::string Shader::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 {
		std::cout << "着色器文件没有正常打开" << std::endl;
		__debugbreak();
	}

	return result;
}

TextureShader.glsl 当中的文本信息全部转换成一个string类型当中进行存储。

2、确定着色器的类型,以及每个着色器的代码

cpp 复制代码
std::unordered_map<GLenum, std::string> Shader::PreProcess(const std::string& source) {
	std::unordered_map<GLenum, std::string> shaderSources;

	const char* typeToken = "#type";
	size_t typeTokenLength = strlen(typeToken);
	size_t pos = source.find(typeToken,0);

	while (pos != std::string::npos) {
		size_t eol = source.find_first_of("\r\n", pos);
		if (eol == std::string::npos) {
			std::cout << "着色器语法出错" << std::endl;
			__debugbreak();
		}

		size_t begin = pos + typeTokenLength + 1;
		std::string type = source.substr(begin, eol - begin);

		if (!ShaderTypeFromString(type)) {
			std::cout << "这是一个不合法的着色器类型" << std::endl;
			__debugbreak();
		}

		size_t nextLinePos = source.find_first_of("\r\n", eol);
		pos = source.find(typeToken, nextLinePos);
		shaderSources[ShaderTypeFromString(type)] = source.substr(nextLinePos, pos - (nextLinePos == std::string::npos ? source.size() - 1 : nextLinePos));
	}

	return shaderSources;
}

对于OpenGL 来说我们不光要告诉它需要编译的代码,还要告诉它编译的着色器代码是什么类型的着色器代码。在前面可以看到TextureShader.glsl 当中有 #type vertex 这样的语句,这个并不是glsl语法,我们在进行文本处理的时候需要省略掉才行,不然的话编译会失败,这个只是用来告诉程序下面着色器代码是什么类型着色器的,所以这里选择返回了一个字典,用来存储着色器的类型和需要编译的程序。能够编译的是下面两段

cpp 复制代码
#version 450 core
//标记为0的内存位置输入一个有两个分量的向量,这是顶点的位置
layout(location = 0) in vec2 v_Position;

void main(){
    //顶点位置的数据进行赋值,需要转换为齐次向量
	gl_Position = vec4(v_Position,0.0f,1.0f);
}
cpp 复制代码
#version 450 core
//标记为0的内存位置输出一个有四个分量的向量,这是像素的颜色
layout(location = 0) out vec4 o_Color;

void main(){
	o_Color = vec4(0.8f,0.2f,0.3f,1.0f);
}

他们已经被分开存储了。

3、编译链接着色器

cpp 复制代码
void Shader::Compile(const std::unordered_map<GLenum, std::string>& shaderSources) {
	//注册使用下面两个着色器的程序号
    unsigned int program = glCreateProgram();
	//一次性至多编译两种着色器
	if (shaderSources.size() < 2) {
		std::cout << "一次性至多编译两种着色器" << std::endl;
		__debugbreak();
	}

	std::array<GLenum, 2> glShaderIDs;
	int glShaderIDIndex = 0;
	for (auto& kv : shaderSources) {
		GLenum type = kv.first;
		const std::string& source = kv.second;
        //注册对饮类型的着色器
		unsigned int shader = glCreateShader(type);

		const char* sourceCStr = source.c_str();
		glShaderSource(shader, 1, &sourceCStr, 0);
        //编译着色器源码
		glCompileShader(shader);

		int isCompiled = 0;
		glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);
        //检查着色器是否编译失败
		if (isCompiled == GL_FALSE) {
			int maxLength = 0;
			glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);

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

			glDeleteShader(shader);

			std::cout << "着色器编译出错:" << infoLog.data() << std::endl;
			__debugbreak();
			break;
		}
        //将着色器加入到这个程序当中
		glAttachShader(program, shader);
		glShaderIDs[glShaderIDIndex++] = shader;
	}

	m_ShaderID = program;

	// Link our program
	glLinkProgram(program);

	// Note the different functions here: glGetProgram* instead of glGetShader*.
	int isLinked = 0;
	glGetProgramiv(program, GL_LINK_STATUS, (int*)&isLinked);
    //检查程序是否能够链接成功
	if (isLinked == GL_FALSE) {
		int maxLength = 0;
		glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength);

		// The maxLength includes the NULL character
		std::vector<char> infoLog(maxLength);
		glGetProgramInfoLog(program, maxLength, &maxLength, &infoLog[0]);

		// We don't need the program anymore.
		glDeleteProgram(program);

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

		std::cout << "用户着色器链接失败:" << infoLog.data() << std::endl;
		__debugbreak();
		return;
	}

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

上面大致流程就是,注册程序的编号,创建对应类型的着色器,根据下面的代码

cpp 复制代码
static GLenum ShaderTypeFromString(const std::string& type) {
	if (type == "vertex")
		return GL_VERTEX_SHADER;
	else if (type == "fragment" || type == "pixel")
		return GL_FRAGMENT_SHADER;

	std::cout << "Unknown shader type" << std::endl;
	return 0;
}

可以知道 #type vertex 对应的着色器类型就是GL_VERTEX_SHADER#type fragment 对应的着色器类型就是GL_FRAGMENT_SHADER。创建了对应的着色器类型过后就是对源码进行编译,放入到程序当中,检查这个程序能否顺利接入管线当中,隔离开然后等待被调用。

使用用户自定义着色器

着色器使用,主函数代码如下

cpp 复制代码
#include<glad/glad.h>
#include<GLFW/glfw3.h>

#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"

#include<iostream>

#include"FrameBuffer.h"
#include"Shader.h"

int main() {
	glfwInit();

	GLFWwindow* window = glfwCreateWindow(640, 480, "Triangles", NULL, NULL);

	glfwMakeContextCurrent(window);
	glfwSwapInterval(1); // Enable vsync

	// Setup Dear ImGui context
	IMGUI_CHECKVERSION();
	ImGui::CreateContext();
	ImGuiIO& io = ImGui::GetIO(); (void)io;
	io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;     // Enable Keyboard Controls
	io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;      // Enable Gamepad Controls
	io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;         // Enable Docking
	io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;       // Enable Multi-Viewport / Platform Windows
	//io.ConfigViewportsNoAutoMerge = true;
	//io.ConfigViewportsNoTaskBarIcon = true;
	
	// Setup Dear ImGui style
	ImGui::StyleColorsDark();
	//ImGui::StyleColorsLight();

	// When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones.
	ImGuiStyle& style = ImGui::GetStyle();
	if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
	{
		style.WindowRounding = 0.0f;
		style.Colors[ImGuiCol_WindowBg].w = 1.0f;
	}

	// Setup Platform/Renderer backends
	ImGui_ImplGlfw_InitForOpenGL(window, true);
	ImGui_ImplOpenGL3_Init("#version 130");

	//需要初始化GLAD
	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
		std::cout << "Failed to initialize GLAD" << std::endl;
		return -1;
	}

	float positions[6] = {
		-0.5f, -0.5,
		0.0f, 0.5f,
		0.5f, -0.5f
	};

	GLuint buffer = 0;

	glGenBuffers(1, &buffer);
	glBindBuffer(GL_ARRAY_BUFFER, buffer);
	glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);

	glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), NULL);
	glEnableVertexAttribArray(0);

	bool show_demo_window = true;
	ImVec2 viewPortSize(640,480);
	float colorEditor[4] = {1.0f, 1.0f, 1.0f, 1.0f};

	FrameBuffer *pFrameBuffer = new FrameBuffer(640, 480);
	Shader* pShader = new Shader("assets/shaders/TextureShader.glsl");
	pShader->UBind();

	while (!glfwWindowShouldClose(window)) {
		pFrameBuffer->Bind();
		pShader->Bind();
		glClear(GL_COLOR_BUFFER_BIT);

		glDrawArrays(GL_TRIANGLES, 0, 3);
		pFrameBuffer->UBind();

		// Start the Dear ImGui frame
		ImGui_ImplOpenGL3_NewFrame();
		ImGui_ImplGlfw_NewFrame();
		ImGui::NewFrame();

		ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport());

		ImGui::Begin("ViewPort");
		viewPortSize = ImGui::GetContentRegionAvail();
		if (viewPortSize.x * viewPortSize.y > 0 && (viewPortSize.x != pFrameBuffer->GetWidth() || viewPortSize.y != pFrameBuffer->GetHeight())) {
			pFrameBuffer->Resize(viewPortSize.x, viewPortSize.y);
			glViewport(0, 0, viewPortSize.x, viewPortSize.y);
		}
		uint32_t textureID = pFrameBuffer->GetColorAttachment();
		ImGui::Image(reinterpret_cast<void*>(textureID), viewPortSize, { 0,1 }, { 1,0 });
		ImGui::End();

		ImGui::Begin("ColorEditor");
		ImGui::ColorEdit4("##colorEditor", colorEditor);
		ImGui::End();

		/*if(show_demo_window)
			ImGui::ShowDemoWindow(&show_demo_window);*/

		// Rendering
		ImGui::Render();
		
		ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());

		if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
		{
			GLFWwindow* backup_current_context = glfwGetCurrentContext();
			ImGui::UpdatePlatformWindows();
			ImGui::RenderPlatformWindowsDefault();
			glfwMakeContextCurrent(backup_current_context);
		}

		glfwSwapBuffers(window);

		glfwPollEvents();
	}

	// Cleanup
	ImGui_ImplOpenGL3_Shutdown();
	ImGui_ImplGlfw_Shutdown();
	ImGui::DestroyContext();

	delete pFrameBuffer;
	delete pShader;

	glfwDestroyWindow(window);
	glfwTerminate();
}

得到的结果是

三角形被顺利染成了红色,有人可能会说这也有点费了这么大的劲,就把颜色改成了红色,实在是有点无聊,那让我们来做一些比较Cool的事。

我们修改一下着色器

cpp 复制代码
#type vertex
#version 450 core

layout(location = 0) in vec2 v_Position;

void main(){
	gl_Position = vec4(v_Position,0.0f,1.0f);
}

#type fragment
#version 450 core

layout(location = 0) out vec4 o_Color;
//增加的片段
uniform vec4 u_Color;

void main(){
	o_Color = u_Color;
}

主函数也修改一下

cpp 复制代码
pShader->UBind();

	while (!glfwWindowShouldClose(window)) {
		pFrameBuffer->Bind();
		pShader->Bind();
        //新增片段
		pShader->UploadUniformFloat4("u_Color", colorEditor);
		glClear(GL_COLOR_BUFFER_BIT);

展示一下结果

我们现在可以通过ImGui 上面的控件对三角形的颜色进行实时修改了,不用去改动程序,是不是很棒了。下面还是把整个主函数放出来,如果对里面的FrameBuffer 类不了解的可以看笔者的OpenGL渲染结果移至ImGui窗口上这篇文章,同样有源代码,希望对大家能有帮助。

cpp 复制代码
#include<glad/glad.h>
#include<GLFW/glfw3.h>

#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"

#include<iostream>

#include"FrameBuffer.h"
#include"Shader.h"

int main() {
	glfwInit();

	GLFWwindow* window = glfwCreateWindow(640, 480, "Triangles", NULL, NULL);

	glfwMakeContextCurrent(window);
	glfwSwapInterval(1); // Enable vsync

	// Setup Dear ImGui context
	IMGUI_CHECKVERSION();
	ImGui::CreateContext();
	ImGuiIO& io = ImGui::GetIO(); (void)io;
	io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;     // Enable Keyboard Controls
	io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;      // Enable Gamepad Controls
	io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;         // Enable Docking
	io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;       // Enable Multi-Viewport / Platform Windows
	//io.ConfigViewportsNoAutoMerge = true;
	//io.ConfigViewportsNoTaskBarIcon = true;
	
	// Setup Dear ImGui style
	ImGui::StyleColorsDark();
	//ImGui::StyleColorsLight();

	// When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones.
	ImGuiStyle& style = ImGui::GetStyle();
	if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
	{
		style.WindowRounding = 0.0f;
		style.Colors[ImGuiCol_WindowBg].w = 1.0f;
	}

	// Setup Platform/Renderer backends
	ImGui_ImplGlfw_InitForOpenGL(window, true);
	ImGui_ImplOpenGL3_Init("#version 130");

	//需要初始化GLAD
	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
		std::cout << "Failed to initialize GLAD" << std::endl;
		return -1;
	}

	float positions[6] = {
		-0.5f, -0.5,
		0.0f, 0.5f,
		0.5f, -0.5f
	};

	GLuint buffer = 0;

	glGenBuffers(1, &buffer);
	glBindBuffer(GL_ARRAY_BUFFER, buffer);
	glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);

	glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), NULL);
	glEnableVertexAttribArray(0);

	bool show_demo_window = true;
	ImVec2 viewPortSize(640,480);
	float colorEditor[4] = {1.0f, 1.0f, 1.0f, 1.0f};

	FrameBuffer *pFrameBuffer = new FrameBuffer(640, 480);
	Shader* pShader = new Shader("assets/shaders/TextureShader.glsl");
	pShader->UBind();

	while (!glfwWindowShouldClose(window)) {
		pFrameBuffer->Bind();
		pShader->Bind();
		pShader->UploadUniformFloat4("u_Color", colorEditor);
		glClear(GL_COLOR_BUFFER_BIT);

		glDrawArrays(GL_TRIANGLES, 0, 3);
		pFrameBuffer->UBind();

		// Start the Dear ImGui frame
		ImGui_ImplOpenGL3_NewFrame();
		ImGui_ImplGlfw_NewFrame();
		ImGui::NewFrame();

		ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport());

		ImGui::Begin("ViewPort");
		viewPortSize = ImGui::GetContentRegionAvail();
		if (viewPortSize.x * viewPortSize.y > 0 && (viewPortSize.x != pFrameBuffer->GetWidth() || viewPortSize.y != pFrameBuffer->GetHeight())) {
			pFrameBuffer->Resize(viewPortSize.x, viewPortSize.y);
			glViewport(0, 0, viewPortSize.x, viewPortSize.y);
		}
		uint32_t textureID = pFrameBuffer->GetColorAttachment();
		ImGui::Image(reinterpret_cast<void*>(textureID), viewPortSize, { 0,1 }, { 1,0 });
		ImGui::End();

		ImGui::Begin("ColorEditor");
		ImGui::ColorEdit4("##colorEditor", colorEditor);
		ImGui::End();

		/*if(show_demo_window)
			ImGui::ShowDemoWindow(&show_demo_window);*/

		// Rendering
		ImGui::Render();
		
		ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());

		if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
		{
			GLFWwindow* backup_current_context = glfwGetCurrentContext();
			ImGui::UpdatePlatformWindows();
			ImGui::RenderPlatformWindowsDefault();
			glfwMakeContextCurrent(backup_current_context);
		}

		glfwSwapBuffers(window);

		glfwPollEvents();
	}

	// Cleanup
	ImGui_ImplOpenGL3_Shutdown();
	ImGui_ImplGlfw_Shutdown();
	ImGui::DestroyContext();

	delete pFrameBuffer;
	delete pShader;

	glfwDestroyWindow(window);
	glfwTerminate();
}
相关推荐
浅陌sss3 小时前
UnityShaderLab 实现黑白着色器效果
着色器
程序员正茂14 天前
Unity着色器Shader根据到某点的距离显示不同颜色
unity·shader·着色器
小春熙子20 天前
Unity图形学之着色器之间传递参数
unity·游戏引擎·技术美术·着色器
优雅永不过时·20 天前
three.js实现地球 外部扫描的着色器
前端·javascript·webgl·three.js·着色器
踏实探索22 天前
OpenLayers教程12_WebGL自定义着色器:实现高级渲染效果
前端·arcgis·vue·webgl·着色器
Padid1 个月前
SRP 实现 Cook-Torrance BRDF
c++·笔记·unity·游戏程序·图形渲染·着色器
UTwelve1 个月前
【UE5】在材质中实现球形法线技术,常用于改善植物等表面的渲染效果
ue5·材质·虚幻引擎·着色器·虚幻4
UTwelve1 个月前
【UE5】一种老派的假反射做法,可以用于移动端,或对反射的速度、清晰度有需求的地方
ue5·虚幻引擎·着色器·虚幻4
Padid1 个月前
Unity SRP学习笔记(二)
笔记·学习·unity·游戏引擎·图形渲染·着色器