【C++/OpenGL】OpenGL面向对象


在OpenGL提供的基础上,可以对其进行更高级的抽象来方便使用

VertexBuffer(顶点缓冲区):

  • m_RenderID是使用OpenGL的API所分配返回的ID。
  • 构造函数传入需要填入的数据和数据大小,数据大小为字节大小。内部会自动创建绑定缓冲区,并填充数据。当离开对象作用域时,会自动调用析构函数,释放缓冲区内存。
  • Bind和UnBind函数会调用API函数,使用生成的id来绑定指定的缓冲区
//VertexBuffer.h

class VertexBuffer
{
private:
	unsigned int m_RenderID;

public:
	VertexBuffer(const void* data, unsigned int size);
	~VertexBuffer();

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

//VertexBuffer.cpp
#include "VertexBuffer.h"
#include "Renderer.h"

VertexBuffer::VertexBuffer(const void* data, unsigned int size)
{
	GLCall(glGenBuffers(1, &m_RenderID));
	GLCall(glBindBuffer(GL_ARRAY_BUFFER, m_RenderID));
	GLCall(glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW));

	Bind();
}

VertexBuffer::~VertexBuffer()
{
	GLCall(glDeleteBuffers(1, &m_RenderID));
}

void VertexBuffer::Bind() const
{
	GLCall(glBindBuffer(GL_ARRAY_BUFFER, m_RenderID));
}

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

VertexBufferLayout(顶点缓冲区布局):

VertexBufferElement:

  • type:使用如GL_FLOAT,GL_UNSIGNED_INT来为其赋值
  • count:内部元素个数
  • normalized:指定定点数据值应归一化或直接转换为定点值
  • GetSizeOfType:根据type返回单个元素的字节大小

m_Elements:存储顺序紧密排布的元素布局

m_Stride:Layout(所有元素加起来)的长度

Push:泛型函数,向m_Elements推入一个对象,向m_Stride累加大小

//VertexBufferLayout.h

struct VertexBufferElement
{
	unsigned int type;
	unsigned int count;
	unsigned char normalized;

	static unsigned int GetSizeOfType(unsigned int type)
	{
		switch (type)
		{
			case GL_FLOAT:				return 4;
			case GL_UNSIGNED_INT:	return 4;
			case GL_UNSIGNED_BYTE:	return 1;
		}
		ASSERT(false)
		return 0;
	}

	VertexBufferElement(unsigned int type, unsigned int count, unsigned char normalized) 
		: type(type), count(count), normalized(normalized) {}
};

class VertexBufferLayout
{
private:
	std::vector<VertexBufferElement> m_Elements;
	unsigned int m_Stride;

public:
	VertexBufferLayout();

	template<typename T>
	void Push(unsigned  count)
	{
		static_assert(false);
	}

	template<>
	void Push<float>(unsigned int count)
	{
		m_Elements.push_back({ GL_FLOAT, count, GL_FALSE });
		m_Stride += count * VertexBufferElement::GetSizeOfType(GL_FLOAT);
	}

	template<>
	void Push<unsigned int>(unsigned int count)
	{
		m_Elements.push_back({ GL_UNSIGNED_INT, count, GL_FALSE });
		m_Stride += count * VertexBufferElement::GetSizeOfType(GL_UNSIGNED_INT);
	}

	template<>
	void Push<unsigned char>(unsigned int count)
	{
		m_Elements.push_back({ GL_UNSIGNED_BYTE, count, GL_TRUE });
		m_Stride += count * VertexBufferElement::GetSizeOfType(GL_UNSIGNED_BYTE);
	}

	inline const std::vector<VertexBufferElement> GetElements() const { return m_Elements; }
	inline unsigned int GetStride() const { return m_Stride; }
};

//VertexBufferLayout.cpp
VertexBufferLayout::VertexBufferLayout()
{
	m_Stride = 0;
}

Vertex Array Object(顶点数组对象):

AddBuffer:传入缓存区和缓存区布局,根据缓存布局对象内部存储的数据,调用glEnableVertexAttribArray和glVertexAttribPointer来进行实际的布局指定

构造函数:调用glGenVertexArrays生成VAO

Bind,UnBind:调用glBindVertexArray和记录的m_RendererID进行绑定

//VertexArray.h
class VertexBuffer;
class VertexBufferLayout;

class VertexArray
{
private:
	unsigned int m_RendererID;	
public:
	VertexArray();
	~VertexArray();

	void AddBuffer(const VertexBuffer& vb, const VertexBufferLayout& layout);

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

//VertexArray.cpp
#include <GL/glew.h>
#include "VertexArray.h"
#include "VertexBuffer.h"
#include "VertexBufferLayout.h"
#include "Renderer.h"

VertexArray::VertexArray()
{
	GLCall(glGenVertexArrays(1, &m_RendererID));
}

VertexArray::~VertexArray()
{
	GLCall(glDeleteVertexArrays(1, &m_RendererID));
}

void VertexArray::AddBuffer(const VertexBuffer& vb, const VertexBufferLayout& layout)
{
	Bind();
	vb.Bind();

	const auto& elements = layout.GetElements();
	unsigned int offset = 0;
	for (unsigned int i = 0; i < elements.size(); i++)
	{
		const auto& element = elements[i];
		GLCall(glEnableVertexAttribArray(i));
		GLCall(glVertexAttribPointer(i, element.count, element.type, element.normalized, layout.GetStride(), (const void*)offset));
		offset += element.count * VertexBufferElement::GetSizeOfType(element.type);
	}
}

void VertexArray::Bind() const
{
	GLCall(glBindVertexArray(m_RendererID));
}

void VertexArray::Unbind() const
{
	GLCall(glBindVertexArray(0));
}

IndexBuffer(索引缓冲区):

构造函数传入数据和元素个数,内部生成绑定缓冲区,填充数据

Bind,UnBind函数内部绑定缓存的指定缓存区

//IndexBuffer.h 
class IndexBuffer
{
private:
	unsigned int m_RenderID;
	unsigned int m_Count;

public:
	IndexBuffer(const void* data, unsigned int count);
	~IndexBuffer();

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

	inline unsigned int GetCount() const { return m_Count; }
};

//IndexBuffer.cpp
#include "IndexBuffer.h"
#include "Renderer.h"

IndexBuffer::IndexBuffer(const void* data, unsigned int count)
	: m_Count(count)
{
	ASSERT(sizeof(unsigned int) == sizeof(GLuint))

	GLCall(glGenBuffers(1, &m_RenderID));
	GLCall(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_RenderID));
	GLCall(glBufferData(GL_ELEMENT_ARRAY_BUFFER, count * sizeof(unsigned int), data, GL_STATIC_DRAW));
}

IndexBuffer::~IndexBuffer()
{
	GLCall(glDeleteBuffers(1, &m_RenderID));
}

void IndexBuffer::Bind() const
{
	GLCall(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_RenderID));
}

void IndexBuffer::Unbind() const
{
	GLCall(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
}

Shader(着色器):

构造函数传入文件路径,在ParseShader中按自定义规则读取源GLSL代码,在CreateShader中创建并编译顶点和片元着色器

Bind和UnBind函数中调用glUseProgram使用指定的着色器

SetUniform设置着色器变量值,GetUniformLocation获取着色器变量位置,并有缓存机制

//Shader.h
#pragma once
#include <string>
#include <unordered_map>
#include <GL/glew.h>
#include <glm/glm.hpp>

using std::string;
using uint = unsigned int;
using ShaderProgramSource = std::pair<string, string>;
using hash = std::unordered_map<string, int>;


class Shader
{
private:
	string m_FilePath;
	uint m_RendererID;
	hash m_UniformLocationCache;

public:
	Shader(const string& filePath);
	~Shader();

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


	void SetUniform1i(const string& name, int value);

	void SetUniform1f(const string& name, float value);
	void SetUniform4f(const string& name, float v0, float v1, float v2, float v3);

	void SetUniformMat4f(const string& name, const GLfloat* matrixPtr);

private:
	ShaderProgramSource ParseShader(const string& filePath);
	uint CompileShader(uint type, const std::string& source);
	uint CreateShader(const std::string& vertexShader, const std::string& fragmentShader);
	int GetUniformLocation(const string& name);
};


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

using std::ifstream;
using std::cout;
using std::endl;

Shader::Shader(const string& filePath)
	: m_FilePath(filePath), m_RendererID(0)
{
	auto shaderSource = ParseShader(filePath);
	m_RendererID = CreateShader(shaderSource.first, shaderSource.second);
}

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

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

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

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

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

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

void Shader::SetUniformMat4f(const string& name, const GLfloat* matrixPtr)
{
	GLCall(glUniformMatrix4fv(GetUniformLocation(name), 1, GL_FALSE, matrixPtr));
}

int Shader::GetUniformLocation(const string& name)
{
	if (m_UniformLocationCache.contains(name))
		return m_UniformLocationCache[name];

	GLCall(int location = glGetUniformLocation(m_RendererID, name.c_str()));
	if (location == -1)
		cout << "Warning: uniform ' " << name << " ' doesn't exist!" << endl;

	m_UniformLocationCache[name] = location;

	return location;
}


ShaderProgramSource Shader::ParseShader(const string& filePath)
{
	ifstream stream(filePath);

	enum class ShaderType
	{
		NONE = -1, VERTEX = 0, FRAGMENT = 1
	};

	string line;
	std::stringstream ss[2];
	ShaderType type = ShaderType::NONE;
	while (getline(stream, line))
	{
		if (line.find("#shader") != string::npos)
		{
			if (line.find("vertex") != string::npos)
				type = ShaderType::VERTEX;
			else if (line.find("fragment") != string::npos)
				type = ShaderType::FRAGMENT;
		}
		else if (type != ShaderType::NONE)
		{
			ss[(int)type] << line << "\n";
		}
	}

	return { ss[0].str(), ss[1].str() };
}

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

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

		cout << "Failed to compile " << (type == GL_VERTEX_SHADER ? "vertext" : "fragment") << " shader!" << endl;
		cout << message << endl;

		glDeleteShader(id);
		return 0;
	}

	return id;
}

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

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

	//已经使用了的着色器可以清理,节省一点空间但不再能调试了
	glDeleteShader(vs);
	glDeleteShader(fs);

	return program;
}

Renderer(渲染器):

Renderer负责在渲染循环中调用,使用固定的渲染顺序,简化外部使用

外部负责传入vao,ibo或顶点数量,shader来绘制

//Renderer.h
#pragma once
#include <GL/glew.h>

class VertexArray;
class IndexBuffer;
class Shader;

//错误处理
#define ASSERT(x) if (!(x)) __debugbreak();
#define GLCall(x) GLClearError();\
	x;\
	ASSERT(GLLogCall(#x, __FILE__, __LINE__));

void GLClearError();
bool GLLogCall(const char* function, const char* file, int line);

class Renderer
{
public:
	void Draw(const VertexArray& vao, const unsigned int vertexCount, const Shader& shader) const;
	void Draw(const VertexArray& vao, const IndexBuffer& ibo, const Shader& shader) const;
	void Clear();
};

//Renderer.cpp
#include "Renderer.h"
#include <iostream>
#include "VertexArray.h"
#include "IndexBuffer.h"
#include "Shader.h"

using std::cout;
using std::endl;

void GLClearError()
{
	while (glGetError() != GL_NO_ERROR);
}

bool GLLogCall(const char* function, const char* file, int line)
{
	while (GLenum error = glGetError())
	{
		cout << "[OpenGL Error] ( " << error << " ) :\n" << function <<
			"" << file << " : " << line << endl;
		return false;
	}
	return true;
}

void Renderer::Draw(const VertexArray& vao, const unsigned int vertexCount, const Shader& shader) const
{
	shader.Bind();
	vao.Bind();
	GLCall(glDrawArrays(GL_TRIANGLES, 0, vertexCount))
}

void Renderer::Draw(const VertexArray& vao, const IndexBuffer& ibo, const Shader& shader) const
{
	shader.Bind();
	vao.Bind();
	ibo.Bind();
	GLCall(glDrawElements(GL_TRIANGLES, ibo.GetCount(), GL_UNSIGNED_INT, nullptr));
}

void Renderer::Clear()
{
	//清理颜色和深度的缓冲区
	GLCall(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
}

Texture(纹理):

构造函数传入纹理路径,使用自定义的库读取纹理像素。创建绑定纹理对象,设定默认参数,将纹理填入该纹理对象(通过glBindTexture和m_RendererID指定)中

Bind中使用glActiveTexture选择要开启的纹理槽位,glBindTexture将本纹理绑定到该槽位上

//Texture.h
#pragma once
#include <string>

using uint = unsigned int;
using uchar = unsigned char;
using std::string;

class Texture
{
private:
	uint m_RendererID;
	string m_FilePath;
	uchar* m_LocalBuffer;
	int m_Width, m_Height, m_BPP;

public:
	Texture(const string& filePath);
	~Texture();

	void Bind(uint slot = 0) const;
	void Unbind();

	inline int GetWidth() const { return m_Width; }
	inline int GetHeight() const { return m_Height; }
};

//Texture.cpp
#include "Texture.h"
#include "Renderer.h"
#include "stb_image/stb_image.h"

Texture::Texture(const string& filePath)
	: m_RendererID(0), m_FilePath(filePath), m_LocalBuffer(nullptr), m_Width(0), m_Height(0), m_BPP(0)
{
	stbi_set_flip_vertically_on_load(1);
	m_LocalBuffer = stbi_load(filePath.c_str(), &m_Width, &m_Height, &m_BPP, 4);

	GLCall(glGenTextures(1, &m_RendererID));
	GLCall(glBindTexture(GL_TEXTURE_2D, m_RendererID));

	//这四个默认参数不填入,直接给你个黑纹理
	GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
	GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
	GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
	GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));

	GLCall(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, m_Width, m_Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_LocalBuffer));
	GLCall(glBindTexture(GL_TEXTURE_2D, 0));

	if (m_LocalBuffer)
		stbi_image_free(m_LocalBuffer);
}

Texture::~Texture()
{
	GLCall(glDeleteTextures(1, &m_RendererID));
}

void Texture::Bind(uint slot) const
{
	GLCall(glActiveTexture(GL_TEXTURE0 + slot));
	GLCall(glBindTexture(GL_TEXTURE_2D, m_RendererID));
}

void Texture::Unbind()
{
	GLCall(glBindTexture(GL_TEXTURE_2D, 0));
}

Camera(摄像机):

外部调用Process函数来更新变量,并通过GetViewMatrix获取观察矩阵

#pragma once
#include <vector>
#include <GL/glew.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

using glm::vec3;
using glm::radians;
using glm::cross;
using glm::normalize;

enum ECameraMoveDirection
{
	FORWARD, BACKWARD, LEFT, RIGHT
};

// Default camera values
const GLfloat DEF_YAW = -90.0f;
const GLfloat DEF_PITCH = 0.0f;
const GLfloat DEF_SPEED = 3.0f;
const GLfloat DEF_SENSITIVTY = 0.25f;
const GLfloat DEF_ZOOM = 45.0f;

class Camera
{
public:
    // Camera Attributes
    vec3 Position;
    vec3 WorldUp;
    vec3 Front;
    vec3 Right;
    vec3 Up;
    // Eular Angles
    GLfloat Yaw;
    GLfloat Pitch;
    // Camera options
    GLfloat MovementSpeed;
    GLfloat MouseSensitivity;
    GLfloat Zoom;

    Camera(vec3 position = vec3(0, 0, 0), vec3 up = vec3(0, 1, 0), GLfloat yaw = DEF_YAW, GLfloat pitch = DEF_PITCH) :
        Front(vec3(0, 0, -1)), MovementSpeed(DEF_SPEED), MouseSensitivity(DEF_SENSITIVTY), Zoom(DEF_ZOOM),
        Position(position), WorldUp(up), Yaw(yaw), Pitch(pitch)
    {
        UpdateCameraVectors();
    }

    Camera(GLfloat posX, GLfloat posY, GLfloat posZ, GLfloat upX, GLfloat upY, GLfloat upZ, GLfloat yaw, GLfloat pitch) :
        Front(vec3(0, 0, -1)), MovementSpeed(DEF_SPEED), MouseSensitivity(DEF_SENSITIVTY), Zoom(DEF_ZOOM),
        Position(posX, posY, posZ), WorldUp(upX, upY, upZ), Yaw(yaw), Pitch(pitch)
    
    {
        UpdateCameraVectors();
    }

    glm::mat4 GetViewMatrix()
    {
        return glm::lookAt(this->Position, this->Position + this->Front, this->Up);
    }

    void ProcessKeyboard(ECameraMoveDirection direction, GLfloat deltaTime)
    {
        GLfloat velocity = MovementSpeed * deltaTime;
        if (direction == FORWARD)
            Position += Front * velocity;
        if (direction == BACKWARD)
            Position -= Front * velocity;
        if (direction == RIGHT)
            Position += Right * velocity;
        if (direction == LEFT)
            Position -= Right * velocity;
    }

    void ProcessMouseMovement(GLfloat offsetX, GLfloat offsetY, GLboolean constrainPitch = true)
    {
        offsetX *= MouseSensitivity;
        offsetY *= MouseSensitivity;

        Yaw += offsetX;
        Pitch += offsetY;

        if (constrainPitch)
        {
            if (Pitch > 89.0f)
                Pitch = 89.0f;
            if (Pitch < -89.0f)
                Pitch = -89.0f;
        }

        UpdateCameraVectors();
    }

    void ProcessMouseScroll(GLfloat offsetY)
    {
        if (Zoom >= 1 && Zoom <= 45)
            Zoom -= offsetY;
        if (Zoom <= 1)
            Zoom = 1;
        if (Zoom >= 45)
            Zoom = 45;
    }

private:
    void UpdateCameraVectors()
    {
        vec3 front;
        front.x = cos(radians(Yaw)) * cos(radians(Pitch));
        front.y = sin(radians(Pitch));
        front.z = sin(radians(Yaw)) * cos(radians(Pitch));
        Front = normalize(front);
        Right = normalize(cross(Front, WorldUp));
        Up = normalize(cross(Right, Front));
    }
};

示例代码(仅核心部分):

#pragma once
#include <GL/glew.h>
#include <GLFW/glfw3.h>

#include "Renderer.h"
#include "VertexBuffer.h"
#include "IndexBuffer.h"
#include "VertexArray.h"
#include "VertexBufferLayout.h"
#include "Shader.h"
#include "Texture.h"
#include "Camera.h"

#include <iostream>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include "learn1.hpp"

using std::cout;
using namespace glm;


//摄像机变量
Camera camera;
bool keys[1024];
GLfloat lastX = 400, lastY = 300;
bool firstMouse = true;
float deltaTime;


//提前声明
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void DoMovement();


void Learn2(GLFWwindow* window, float targetFrameTime)
{
    deltaTime = targetFrameTime;

    //开启深度测试
    glEnable(GL_DEPTH_TEST);

	//定义顶点和索引数据
    GLfloat vertices[] = {
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
         0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,

        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,

        -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  1.0f, 1.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,

        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f
    };

    // World space positions of our cubes
    glm::vec3 cubePositions[] = {
        glm::vec3(0.0f,  0.0f,  0.0f),
        glm::vec3(2.0f,  5.0f, -15.0f),
        glm::vec3(-1.5f, -2.2f, -2.5f),
        glm::vec3(-3.8f, -2.0f, -12.3f),
        glm::vec3(2.4f, -0.4f, -3.5f),
        glm::vec3(-1.7f,  3.0f, -7.5f),
        glm::vec3(1.3f, -2.0f, -2.5f),
        glm::vec3(1.5f,  2.0f, -2.5f),
        glm::vec3(1.5f,  0.2f, -1.5f),
        glm::vec3(-1.3f,  1.0f, -1.5f)
    };

	//创建顶点数组对象
    VertexArray vao;

	//创建顶点缓存和布局,并链接到顶点数组上
    VertexBuffer vbo(vertices, sizeof(vertices));
    VertexBufferLayout layout;
    layout.Push<float>(3);
    layout.Push<float>(2);

    //缓存和布局绑定到vao上
    vao.AddBuffer(vbo, layout);

	//创建shader并设置参数
    Shader shader("res/shaders/Learn2.shader");
    shader.Bind();

	//创建纹理并启用
    Texture texture("res/textures/awesomeface.png");
    texture.Bind();
    shader.SetUniform1i("u_Texture", 0);

	//创建渲染器
    Renderer renderer;

    //摄像机按键绑定
    glfwSetKeyCallback(window, key_callback);
    glfwSetCursorPosCallback(window, mouse_callback);
    glfwSetScrollCallback(window, scroll_callback);

	//在渲染循环中绘制指定的顶点数组对象,索引缓存,着色器
    while (!glfwWindowShouldClose(window))
    {
        CheckFrameTimeOut(targetFrameTime);
        DoMovement();

        renderer.Clear();

        glm::mat4 view(1), proj(1);

        //投影矩阵(透视参数)
        view = camera.GetViewMatrix();
        shader.SetUniformMat4f("view", glm::value_ptr(view));

        proj = glm::perspective<GLfloat>(45, 640 / 480, .1f, 100.f);
        shader.SetUniformMat4f("projection", glm::value_ptr(proj));

        //模型矩阵(模型位置)
        for (GLuint i = 0; i < 10; i++)
        {
            glm::mat4 model(1);
            model = glm::translate(model, cubePositions[i]);
            GLfloat angle = 20.0f * i;
            model = glm::rotate(model, angle + ((GLfloat)glfwGetTime() * (i % 2 ? 1 : -1)), glm::vec3(1.0f, 0.3f, 0.5f));
            shader.SetUniformMat4f("model", glm::value_ptr(model));

            renderer.Draw(vao, 36, shader);
        }

        glfwSwapBuffers(window);
        glfwPollEvents();
    }
}

// Moves/alters the camera positions based on user input
void DoMovement()
{
    if (keys[GLFW_KEY_W])
        camera.ProcessKeyboard(FORWARD, deltaTime);
    if (keys[GLFW_KEY_S])
        camera.ProcessKeyboard(BACKWARD, deltaTime);
    if (keys[GLFW_KEY_A])
        camera.ProcessKeyboard(LEFT, deltaTime);
    if (keys[GLFW_KEY_D])
        camera.ProcessKeyboard(RIGHT, deltaTime);
}

// Is called whenever a key is pressed/released via GLFW
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);

    if (key >= 0 && key < 1024)
    {
        if (action == GLFW_PRESS)
            keys[key] = true;
        else if (action == GLFW_RELEASE)
            keys[key] = false;
    }
}

void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
    if (firstMouse)
    {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }

    GLfloat xoffset = xpos - lastX;
    GLfloat yoffset = lastY - ypos;  // Reversed since y-coordinates go from bottom to left

    lastX = xpos;
    lastY = ypos;

    camera.ProcessMouseMovement(xoffset, yoffset);
}

void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
    camera.ProcessMouseScroll(yoffset);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值