OpenGL学习笔记(一)

1. 顶点

OpenGL本质是一个状态机,点作为表示图形最基本的元素,如何告诉OpenGL点的基本信息就是我们要做的事情

1.1VertexArray
VertexArray是所有顶点的集合,我们可以将VertexBuffer分成很多组,每组采取不同的着色方式
之后再解释Addlayout()

class VertexArray
{
public:
	VertexArray()
	{
		//创建一个VertexArray
		glGenVertexArrays(1, &m_vao);
		//绑定
		glBindVertexArray(m_vao);
	}
	~VertexArray()
	{
		glDeleteVertexArrays(1, &m_vao);
	}

	void Addlayout(const VertexBuffer& vb, const VertexBufferLayout& layout);
	void Bind()const;
	void UnBind()const;
private:
	unsigned int m_vao;
};

void VertexArray::Addlayout(const VertexBuffer& vb, const VertexBufferLayout& layout)
{
	vb.Bind();
	const auto& elements = layout.GetElements();

	unsigned int offset = 0;

	for (int i = 0; i < elements.size(); i++)
	{
		const auto& element = elements[i];
		//之后再解释
		glVertexAttribPointer(i, element.size, element.type, element.normalized, element.stride, (const void*)offset);
		glEnableVertexAttribArray(i);

		offset += VertexBufferElement::GetSizeofType(element.type) * element.size;
	}
}

void VertexArray::Bind()const
{
	glBindVertexArray(m_vao);

}
void VertexArray::UnBind()const
{
	glBindVertexArray(0);
}

1.2VertexBuffer
VertexBuffer就是将所有需要的点的数据进行缓存,但OpenGL仅仅只是知道了所有的数据,多少个字节表示一个点,OpenGL还是不知道

在这里插入图片描述
就像上图,整个大长方形表示我们所有点的内存总和,每个小长方形表示每一个点占的内存,这时OpenGL只是知道了存储这些点需要多少内存,但不知道有多少个点,每个点的每个字节的意义

class VertexBuffer
{
public:
	VertexBuffer(const void* data,unsigned int size);
	~VertexBuffer();
	
	void Bind() const;
	void Unbind() const;

private:
	unsigned int m_RendererID;
};

VertexBuffer::VertexBuffer(const void* data, unsigned int size)
{
    glGenBuffers(1, &m_RendererID);
    glBindBuffer(GL_ARRAY_BUFFER, m_RendererID);
    glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
}
VertexBuffer::~VertexBuffer()
{
    glDeleteBuffers(1, &m_RendererID);
}

void VertexBuffer::Bind()const
{
    glBindBuffer(GL_ARRAY_BUFFER, m_RendererID);
}
void VertexBuffer::Unbind()const
{
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}

1.3VertexBufferLayout

VertexBufferLayout描述VertexBuffer中每个点占的字节数多少和每个点内存的划分是怎样的

在这里插入图片描述
例如这个点,一共4个字节,我需要前两个字节表示坐标,后两个字节表示颜色,VertexBufferLayout就可分配字节,前两个字节为一组,后两个字节为一组,如果要具体的表示坐标与颜色后文会介绍

struct VertexBufferElement
{
	int size;
	int type;
	unsigned char normalized;
	static unsigned int stride;

	static unsigned int GetSizeofType(int type)
	{
		switch (type)
		{
			case GL_FLOAT:
			{
				return 4;
			}
			case GL_UNSIGNED_INT:
			{
				return 4;
			}
			case GL_UNSIGNED_BYTE:
			{
				return 1;
			}
			case GL_BYTE:
			{
				return 1;
			}
			default:
			{
				break;

			}
		}
		return false;

	}
};
unsigned int VertexBufferElement::stride = 0;

class VertexBufferLayout
{
public:
	template<class T>
	void Push(int size)
	{
		static_assert(false);
	}

	template<>
	void Push<float>(int size)
	{
		m_Elements.push_back({ size,GL_FLOAT,GL_FALSE });
		VertexBufferElement::stride += VertexBufferElement::GetSizeofType(GL_FLOAT) * size;
	}
	template<>
	void Push<unsigned int>(int size)
	{
		m_Elements.push_back({ size,GL_UNSIGNED_INT,GL_FALSE });
		VertexBufferElement::stride += VertexBufferElement::GetSizeofType(GL_UNSIGNED_INT) * size;
	}

	template<>
	void Push<char>(int size)
	{
		m_Elements.push_back({ size,GL_BYTE,GL_FALSE });
		VertexBufferElement::stride += VertexBufferElement::GetSizeofType(GL_BYTE) * size;
	}

	template<>
	void Push<unsigned char>(int size)
	{
		m_Elements.push_back({ size,GL_UNSIGNED_BYTE,GL_FALSE });
		VertexBufferElement::stride += VertexBufferElement::GetSizeofType(GL_UNSIGNED_BYTE) * size;
	}
	inline const std::vector<VertexBufferElement>& GetElements() const { return m_Elements; }

private:
	std::vector<VertexBufferElement> m_Elements;
};

1.4解释1.1中的Addlayout
解释之前理解下glVertexAttribPointer

glVertexAttribPointer 是一个 OpenGL 函数,用于将当前的顶点属性与顶点缓冲对象(VBO)关联起来。它的原型如下:

void glVertexAttribPointer(GLuint index, GLint size, GLenum type,
GLboolean normalized, GLsizei stride, const GLvoid *pointer);

index 指定要配置的顶点属性的编号。 size 指定每个顶点属性的分量数(1、2、3 或 4,就像向量的维度一样)。
type指定每个分量的数据类型,可以是GL_BYTE、GL_UNSIGNED_BYTE、GL_SHORT、GL_UNSIGNED_SHORT、GL_INT、GL_UNSIGNED_INT、GL_FLOAT或 GL_DOUBLE。 normalized1 指定是否将数据归一化到 [0,1] 或 [-1,1] 范围内。
stride(步长)指定连续两个顶点属性间的字节数。如果为 0,则表示顶点属性是紧密排列的。 pointer
指向缓冲对象中第一个顶点属性的第一个分量的地址。(offset的作用)

知道了这个之后再看看Addlayout通过VertexBufferLayout将每个顶点的布局进行加载

void VertexArray::Addlayout(const VertexBuffer& vb, const VertexBufferLayout& layout)
{
	vb.Bind();
	const auto& elements = layout.GetElements();

	unsigned int offset = 0;

	for (int i = 0; i < elements.size(); i++)
	{
		const auto& element = elements[i];

		glVertexAttribPointer(i, element.size, element.type, element.normalized, element.stride, (const void*)offset);
		glEnableVertexAttribArray(i);

		offset += VertexBufferElement::GetSizeofType(element.type) * element.size;
	}
}

2. 着色器(Shader)

我们已经知道了顶点的信息,但目前而言,顶点的信息只是一串数,让OpenGL如何解析这些数,就是Shader要做的事情

OpenGL需要在另一个文件单独写一个着色方法,因此本文只介绍怎么创建着色器

struct ShaderProgramSource
{
	std::string Vertex;
	std::string Fragment;
};

class Shader
{
public:
	Shader(const std::string& filepath);
	~Shader();

	void SetUniform4f(const std::string& name, float v1, float v2, float v3, float v4);
	void SetUniform1i(const std::string& name, int v1);

	void Bind()const;
	void Unbind() const;

private:
	int GetUniformLocation(const std::string& name);

	ShaderProgramSource ParseShader(const std::string& filepath);

	unsigned int ComplieShader(const std::string& source, unsigned int type);
	
	unsigned int CreateShader(const std::string& vertexShader, const std::string& fragmentShader);

private:
	unsigned int m_program;
	std::string m_filepath;
	std::unordered_map<std::string,int> m_UniformLocations;
};

Shader::Shader(const std::string& filepath)
	:m_filepath(filepath)
	,m_program(0)
{
    ShaderProgramSource shaderProgram = ParseShader(m_filepath);

    m_program = CreateShader(shaderProgram.Vertex, shaderProgram.Fragment);
    glUseProgram(m_program);
}

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

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


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

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

    GLCall(int location = glGetUniformLocation(m_program, name.c_str()));

    if (location == -1)
    {
        std::cout << "fail glGetUniformLocation" << std::endl;
        return 0;
    }
    else
    {
        m_UniformLocations[name] = location;
        return location;
    }

}


ShaderProgramSource Shader::ParseShader(const std::string& filepath)
{
    enum ShaderTypeEnum
    {
        NONE = -1,
        VERTEX = 0,
        FRAGMENT = 1
    };

    std::ifstream stream(filepath);
    std::string line;
    std::string ss[2];

    ShaderTypeEnum type = NONE;
    while (std::getline(stream, line))
    {
        if (line.find("#shader") != std::string::npos)
        {
            if (line.find("vertex") != std::string::npos)
            {
                type = VERTEX;
            }
            else
            {
                type = FRAGMENT;
            }
        }
        //没有#shader
        else
        {
            ss[type] += line;
            ss[type] += '\n';
        }
    }
    return { ss[0],ss[1] };
}

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

    int result;
    glGetShaderiv(id, GL_COMPILE_STATUS, &result);

    if (result == false)
    {
        int length;
        glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
        char* message = new char[length];
        glGetShaderInfoLog(id, length, &length, message);
        std::cout << "Failed to complie:" << ((type == GL_VERTEX_SHADER) ? "vertex" : "fragment") << std::endl;
        std::cout << message << std::endl;
        glDeleteShader(id);
        return 0;
    }

    return id;
}

unsigned int Shader::CreateShader(const std::string& vertexShader, const std::string& fragmentShader)
{
    unsigned int program = glCreateProgram();
    unsigned int vs = ComplieShader(vertexShader, GL_VERTEX_SHADER);
    unsigned int fs = ComplieShader(fragmentShader, GL_FRAGMENT_SHADER);

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

    glDeleteShader(vs);
    glDeleteShader(fs);

    return program;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值