OpenGL的一个简单shader

这篇博客介绍了初学者如何在OpenGL中编写并整合vertex和fragment shader,为图形渲染打下基础。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

学习OpenGL的第一步从写shader开始。

注:我是将vertex和fragment的内容都放在一个文件中。

以下是shader.h的源码。

#pragma once

#include <string>
#include <unordered_map>

struct ShaderProgramSource
{  /*结构体,代表顶点着色器和片段着色器*/
	std::string VertexSource;
	std::string FragmentSource;
};

class Shader
{
private:
	std::string m_FilePath;/*文件路径*/
	unsigned int m_RendererID;/*shader的id*/
	std::unordered_map<std::string, int> m_UniformLocationCache;/*把所有uniform储存在一个表中,方便查询*/
public:
	Shader(const std::string& filepath);/*调用private里面的前三个函数*/
	~Shader();

	void Use() const;
	void Unbind() const;
	unsigned int GetRenderID() const;

	// uniforms 封装,可自行再添加,调用了GetUniformLocation
	void SetUniform1i(const std::string& name, int value);
	void SetUniform1f(const std::string& name, float value);
	void SetUniform4f(const std::string& name, float v0, float v1, float v2, float v3);
private:
	ShaderProgramSource ParseShader(const std::string& filepath);
	unsigned int CompileShader(unsigned int type, const std::string& source);
	unsigned int CreateShader(const std::string& vertexShader, const std::string& fragmentShader);
	int GetUniformLocation(const std::string& name);
};

 以下是shader.cpp的源码。

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

Shader::Shader(const std::string& filepath)
	: m_FilePath(filepath), m_RendererID(0)
{/*通过ParseShader获取文件内容,再通过CreateShader创建shader*/
    ShaderProgramSource source = ParseShader(filepath);
    m_RendererID = CreateShader(source.VertexSource, source.FragmentSource);
}

Shader::~Shader()
{//用完自动删除shader
    GLCall(glDeleteProgram(m_RendererID));
}

void Shader::Use () const
{//使用shader
    GLCall(glUseProgram(m_RendererID));

}

void Shader::Unbind() const
{//解绑shader
    GLCall(glUseProgram(0));
}


unsigned int Shader::GetRenderID() const
{//返回shaderid
    return m_RendererID;
}

ShaderProgramSource Shader::ParseShader(const std::string& filepath)
{
    std::ifstream stream(filepath);//获取文件内容

    enum class ShaderType//设定三种模式
    {
        NONE = -1, VERTEX = 0, FRAGMENT = 1
    };

    std::string line;
    std::stringstream ss[2];//存放文件内容
    ShaderType type = ShaderType::NONE;
    while (std::getline(stream, line))//读取字符内容
    {
        if (line.find("#shader") != std::string::npos)
        {
            if (line.find("vertex") != std::string::npos)
            {
                type = ShaderType::VERTEX;//当找到vertex,转换状态
            }
            else if (line.find("fragment") != std::string::npos)
            {
                type = ShaderType::FRAGMENT;//当找到fragment,转换状态
            }
        }
        else
        {
            // 其余情况,根据状态分别存放vertex和fragment的字符内容
            ss[(int)type] << line << '\n';
        }
    }

    return { ss[0].str(), ss[1].str() };
}
unsigned int Shader::CreateShader(const std::string& vertexShader, const std::string& fragmentShader)
{/*通过CompileShader创建vs和fs,再与program链接*/
	unsigned int program = glCreateProgram();
	unsigned int vs = CompileShader(GL_VERTEX_SHADER, vertexShader);
	unsigned int fs = CompileShader(GL_FRAGMENT_SHADER, fragmentShader);

	glAttachShader(program, vs);
	glAttachShader(program, fs);
	glLinkProgram(program);

	// 查阅文档我们知道,验证的状态会被存储为程序对象状态的一部分你可以调用,比如用 glGetProgram 查询实际结果是什么之类的
	glValidateProgram(program);

	glDeleteShader(vs);
	glDeleteShader(fs);

	return program;
}

unsigned int Shader::CompileShader(unsigned int type, const std::string& source) // 第二个参数是因为 GLenum 是 unsigned int 的
{   /*根据类型来创建shader*/
    unsigned int id = glCreateShader(type);
    const char* src = source.c_str(); // 返回的是一个指向 string 内部的指针,因此这个 string 必须存在才有效
    glShaderSource(id, 1, &src, nullptr);
    glCompileShader(id);

    // Error handling
    int result;
    glGetShaderiv(id, GL_COMPILE_STATUS, &result); // iv: integer, vector
    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 << "Failed to compile " << (type == GL_VERTEX_SHADER ? "vertex" : "fragment") << " shader!" << std::endl;
        std::cout << message << std::endl;
        glDeleteShader(id);
        return 0;
    }

    return id;
}

// 从文件中获取,然后编译、链接,生成buffer id,用于之后的绑定


void Shader::SetUniform1i(const std::string& name, int value)
{
    GLCall(glUniform1i(GetUniformLocation(name), value));
}

void Shader::SetUniform1f(const std::string& name, float value)
{
    GLCall(glUniform1f(GetUniformLocation(name), value));
}

void Shader::SetUniform4f(const std::string& name, float v0, float v1, float v2, float v3)
{
    GLCall(glUniform4f(GetUniformLocation(name), v0, v1, v2, v3));
}

int Shader::GetUniformLocation(const std::string& name)
{   /*在表中存放uniform*/
    if (m_UniformLocationCache.find(name) != m_UniformLocationCache.end())
        return m_UniformLocationCache[name];

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值