【Cherno的OpenGL视频】Shader abstraction in OpenGL

这篇博客介绍了OpenGL中Shader的重要性和使用方式,包括创建自定义的着色语言,编译成不同渲染API的 shader 代码,并在运行时动态生成和控制。通过Shader类的实现,展示了如何绑定、设置uniform变量。在实际应用中,通过Shader实现颜色的动态变化,最后给出了一个简单的OpenGL程序示例,展示了如何加载和使用Shader。
摘要由CSDN通过智能技术生成

1、Shaders are an incredibly complex topic and they are very very important to graphics programming or any kind of rendering, so they’re tend to be like a tendency to actually make a rather complicated system that is both easy to use but also extremely powerful. In games and game engines typically it’s very common to have a custom shading language which then compiles into shader code for the each kind of rendering API or platform and also is somewhat kind of controllable and extensible through the code at runtime, meaning that it’s very common for games to actually generate new shaders and then have them be used for rendering while the game is actually running or while the game is loading.

In OpenGL when we think about shaders, we just need to write a text in a file or as a string.
main goal here: we just want to be able to create shaders, bind them for use and set uniform.
2、代码实现:
较上篇新增:Shader类
Shader.h
在这里插入图片描述
Shader.cpp

#include "Shader.h"
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include "Renderer.h"

Shader::Shader(const std::string& filepath)
	:m_Filepath(filepath), m_RendererID(0)
{
	ShaderProgramSource source = ParseShader();
	// Use our shader.
	m_RendererID = CreateShader(source.VertexSource, source.FragmentSource);
}

Shader::~Shader()
{
	GLCall(glDeleteProgram(m_RendererID));
}

void Shader::Bind() const
{
	GLCall(glUseProgram(m_RendererID));
}

void Shader::Unbind() const
{
	GLCall(glUseProgram(0));
}

void Shader::SetUniform4f(const std::string& name, float v0, float v1, float v2, float v3)
{
	// after our shader is binded, set u_Color for fragment shader.
	GLCall(glUniform4f(GetUniformLocation(name), v0, v1, v2, v3));
}

ShaderProgramSource Shader::ParseShader()
{
	enum class ShaderType
	{
		NONE = -1, VERTEX = 0, FRAGMENT = 1
	};

	std::ifstream stream(m_Filepath);
	std::string line;
	std::stringstream ss[2];
	ShaderType type = ShaderType::NONE;

	// go through the file line-by-line.
	while (getline(stream, line))
	{
		if (line.find("#shader") != std::string::npos)
		{
			if (line.find("vertex") != std::string::npos)
			{
				// set mode to vertex.
				type = ShaderType::VERTEX;
			}
			else if (line.find("fragment") != std::string::npos)
			{
				// set mode to fragment.
				type = ShaderType::FRAGMENT;
			}
		}
		else
		{
			ss[(int)type] << line << '\n';
		}
	}
	return { ss[0].str(), ss[1].str() };
}

unsigned int Shader::CompileShader(unsigned int type, const std::string& source)
{
	unsigned int id = glCreateShader(type);
	const char* src = source.c_str();
	glShaderSource(id, 1, &src, nullptr);
	glCompileShader(id);

	// error handling.
	int result;
	glGetShaderiv(id, GL_COMPILE_STATUS, &result);
	if (result == GL_FALSE)
	{
		int length;
		glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
		char* message = (char*)alloca(length * sizeof(char));
		glGetShaderInfoLog(id, length, &length, message);
		std::cout << "CompileShader() Failed, " << (type == GL_VERTEX_SHADER ? "vertex" : "fragment") << " shader!" << std::endl;
		std::cout << message << std::endl;
		glDeleteShader(id);
		return 0;
	}

	return id;
}

// Creat a shader.
unsigned int Shader::CreateShader(const std::string& vertexShader, const std::string& fragmentShader)
{
	unsigned int program = glCreateProgram();
	// create our true shader objects.
	unsigned int vs = CompileShader(GL_VERTEX_SHADER, vertexShader);
	unsigned int fs = CompileShader(GL_FRAGMENT_SHADER, fragmentShader);
	// link vs and fs.
	glAttachShader(program, vs);
	glAttachShader(program, fs);
	// link the program.
	glLinkProgram(program);
	glValidateProgram(program);
	// delete it after linked.
	glDeleteShader(vs);
	glDeleteShader(fs);

	return program;
}

int Shader::GetUniformLocation(const std::string& name)
{
	if (m_UniformLocationCache.find(name) != m_UniformLocationCache.end())
	{
		return m_UniformLocationCache[name];
	}

	// 在我们的shader里,每个uniform都会被分配一个ID。
	GLCall(int location = glGetUniformLocation(m_RendererID, name.c_str()));
	if (location == -1)
	{
		std::cout << "Warning uniform " << name << " doesn't exist!" << std::endl;
	}
	m_UniformLocationCache[name] = location;
	return location;
}

Application.cpp

#include <GL/glew.h>// 放在glfw3.h之前,或者任何OpenGL之前。
#include <GLFW/glfw3.h>
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>

#include "Renderer.h"
#include "VertexBuffer.h"
#include "IndexBuffer.h"
#include "VertexArray.h"
#include "Shader.h"

int main(void)
{
	GLFWwindow* window;

	/* Initialize the library */
	if (!glfwInit())
		return -1;

	// 指定用3.3.0版本。
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//3.0
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//0.3  
	// Setting my OpenGL profile to be core!!
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

	/* Create a windowed mode window and its OpenGL context */
	window = glfwCreateWindow(640, 640, "I am Groot", NULL, NULL);
	if (!window)
	{
		glfwTerminate();
		return -1;
	}

	/* Make the window's context current */
	glfwMakeContextCurrent(window);

	glfwSwapInterval(1);

	// after glfwMakeContextCurrent() -> Testing GLEW.
	if (glewInit() != GLEW_OK)
	{
		std::cout << "glewInit() error" << std::endl;
	}
	std::cout << glGetString(GL_VERSION) << std::endl;
	// Testing GLEW.

	{
		// on the CPU.
		float positions[] = {
			-0.5f, -0.5f, //index0
			 0.5f, -0.5f, //index1
			 0.5f,  0.5f, //index2
			-0.5f,  0.5f, //index3
		};

		// on the CPU.
		unsigned int indices[] = {
			0, 1, 2,//index0 1 2
			2, 3, 0 //index2 3 0
		};

		// Create a vertex array object.
		VertexArray VA;

		// Define a vertex buffer: send positions to GPU.
		VertexBuffer VB(positions, 4 * 2 * sizeof(float));

		// set up the layout for it.
		VertexBufferLayout layout;
		layout.Push<float>(2);
		VA.AddBuffer(VB, layout);

		// Define a index buffer: send indices to GPU.
		IndexBuffer IB(indices, 6);

		// Use our shader.
		Shader shader("res/shaders/Basic.shader");
		shader.Bind();
		shader.SetUniform4f("u_Color", 0.8f, 0.3f, 0.8f, 1.0f);

		// Unbinding everything.
		VA.Unbind();
		VB.Unbind();
		IB.Unbind();
		shader.Unbind();

		// sth for fun.
		float r = 0.0f;
		float increment = 0.05f;
		// sth for fun.

		/* Loop until the user closes the window */
		while (!glfwWindowShouldClose(window))
		{
			/* Render here */
			GLCall(glClear(GL_COLOR_BUFFER_BIT));

			shader.Bind();
			shader.SetUniform4f("u_Color", r, 0.3f, 0.8f, 1.0f);

			VA.Bind();
			IB.Bind();

			GLCall(glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr));

			if (r > 1.0f)
			{
				increment = -0.05f;
			}
			else if (r < 0.0f)
			{
				increment = 0.05f;
			}

			r += increment;

			/* Swap front and back buffers */
			glfwSwapBuffers(window);

			/* Poll for and process events */
			glfwPollEvents();
		}

	}

	glfwTerminate();
	return 0;
}

3、运行效果:

Something Fun

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值