Cherno OpenGL(14 ~ 17)

缓冲区布局抽象

首先我们抽象顶点数组的目的是什么?

对我们来说,顶点数组需要做的是将顶点缓冲区与某种布局联系在一起,所以顶点缓冲区就是有数据的缓冲区,它们没有实际的概念比如前三个浮点数是位置,没有类型或者大小之类的概念,它只是实际数据的普通缓冲区。每个字节是什么、这些顶点有多大等等才是顶点数组真正代表的,它应该把缓冲区和实际布局联系在一起。

顶点数组对象是 OpenGL 存储那种状态的方式,那么当我们考虑创建这个接口时,我们需要做的是需要一些东西来创建一个顶点数组。

顶点数组布局 VertexBufferLayout 类

#pragma once

#include <vector>
#include <GL/glew.h>
#include "Render.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;
	}
};

class VertexBufferLayout {
private:
	std::vector<VertexBufferElement> m_Elements;
	unsigned int m_Stride;
public:
	VertexBufferLayout()
		:m_Stride(0) {}
	
	template<typename T>
	void Push(unsigned int count) {
		//static_assert(false);
	}

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

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

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

	inline const std::vector<VertexBufferElement>& GetElements()const {
		return m_Elements;
	}

	inline unsigned int GetStride() const {
		return m_Stride;
	}
}; 

顶点数组类

.h 

#pragma once

#include "VertexBuffer.h"
#include "VertexBufferLayout.h"

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

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

	void Bind() const;
	void UnBind() const;
};

.cpp

#include "VertexArray.h"

#include "Render.h"

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

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

void VertexArray::AddBuffer(const VertexBuffer& vb, const VertexBufferLayout& layout) {
	Bind();
	vb.Bind();
	const std::vector<VertexBufferElement>& 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_RenderID));
}
void VertexArray::UnBind() const {
	GLCall(glBindVertexArray(0));
}

应用 Application 类

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

#include <iostream>
#include <fstream>
#include <string>
#include <sstream>

#include "Render.h"
#include "IndexBuffer.h"
#include "VertexBuffer.h"
#include "VertexBufferLayout.h"
#include "VertexArray.h"

struct ShaderProgramSource {
    std::string VertexSource;
    std::string FragmentSource;
};

//从文件中将着色器代码读取出来
static ShaderProgramSource 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 (getline(stream, line)) {
        if (line.find("#shader") != std::string::npos) {
            if (line.find("vertex") != std::string::npos) {
                type = ShaderType::VERTEX;
            }
            else if (line.find("fragment") != std::string::npos) {
                type = ShaderType::FRAGMENT;
            }
        }
        else {
            ss[(int)type] << line << '\n';
        }
    }

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

static unsigned int 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); 

    //Syntax 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 << "Failed to compile" << (type == GL_VERTEX_SHADER ? "vertexShader" 
            : "fragmentShader ") << " shader!" << std::endl;
        std::cout << message << std::endl;

        glDeleteShader(id);
        return 0;
    }

    return id;
}

//我们向OpenGL提供我们实际的着色器源代码,我的着色器文本。我们想让OpenGL编译那个程序,将这两个链接到一个
//独立的着色器程序,然后给我们一些那个着色器返回的唯一标识符,我们就可以绑定着色器并使用
static unsigned int CreateShader(const std::string& vertexShader, const std::string& fragmentShader) {
    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);
    glValidateProgram(program);

    //删除一些无用的中间文件
    glDeleteShader(vs);
    glDeleteShader(fs);

    return program;
}

int main(void)
{
    GLFWwindow* window;

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

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);  //指定opengl版本,3
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);  //指定opengl次要版本 0.3  即3.3 
    //glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE); //兼容版本
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 核心版本

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

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

    if (glewInit() != GLEW_OK) {  //should after MakeContextCurrent 
        std::cout << "Error" << std::endl;
    }

    std::cout << glGetString(GL_VERSION) << std::endl;

    {float positions[] = {
        -0.5f, -0.5f, //0
        0.5f, -0.5f, //1
        0.5f, 0.5f, //2
        -0.5f,0.5f, //3
    };

    unsigned int indices[] = {
        0,1,2,
        2,3,0
    };

    VertexArray va;
    // 顶点缓冲区
    VertexBuffer vb(positions, 8 * sizeof(float));
    

    VertexBufferLayout layout;
    layout.Push<float>((unsigned int)2);
    va.AddBuffer(vb,layout);

    //索引缓冲区
    IndexBuffer ib(indices, 6);

    ShaderProgramSource source = ParseShader("res/Shaders/Basic.shader");
    unsigned int shader = CreateShader(source.VertexSource, source.FragmentSource);
    //绑定着色器
    glUseProgram(shader);

    int location = glGetUniformLocation(shader, "u_Color");
    ASSERT(location != -1);

    //将所有绑定解绑
    va.UnBind();
    glUseProgram(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

    float r = 0.0f;
    float increment = 0.05f;
    /* Loop until the user closes the window */
    while (!glfwWindowShouldClose(window))
    {
        /* Render here */
        glClear(GL_COLOR_BUFFER_BIT);

        GLClearError();

        //为了正确绘制图形,重新进行绑定
        glUseProgram(shader);
        //glBindBuffer(GL_ARRAY_BUFFER, buffer);
        //glEnableVertexAttribArray(0);
        //glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 8, 0);

        va.Bind();
        ib.Bind();

        glUniform4f(location, r, 0.0f, 1.0f, 1.0f);

        //Draw call
        GLCall(glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr));

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

        r += increment;

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

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

    //删除着色器
    glDeleteProgram(shader);
}

    glfwTerminate();
    return 0;
}

着色器抽象

着色器需要什么?

第一步首先我们希望能够传递一个文件或者字符串,把它作为着色器来编译;第二步我们希望能够绑定和解绑着色器;第三步则是我们需要能够设置着色器中各种不同的统一变量,这可能就是我们现在正在研究的东西

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_RenderID;
	// caching for uniforms
	std::unordered_map<std::string, int> m_UniformLocationCache;
public:
	Shader(const std::string& filepath);
	~Shader();

	void Bind() const;
	void UnBind() const;

	//Set uniforms
	void SetUniform4f(const std::string& name,float v0,float v1,float v2,float v3);

private:
	unsigned int CompileShader(unsigned int type, const std::string& source);
	ShaderProgramSource ParseShader(const std::string& filepath);
	unsigned int CreateShader(const std::string& vertexShader, const std::string& fragmentShader);
	unsigned int GetUniformLoation(const std::string& name);
};

.cpp

#include "Shader.h"
#include "Render.h"

#include <iostream>
#include <fstream>
#include <string>
#include <sstream>

Shader::Shader(const std::string& filepath) 
	:m_FilePath(filepath),m_RenderID(0) {
    ShaderProgramSource source = ParseShader(filepath);
    m_RenderID = CreateShader(source.VertexSource, source.FragmentSource);
}

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

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

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

//从文件中将着色器代码读取出来
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 (getline(stream, line)) {
        if (line.find("#shader") != std::string::npos) {
            if (line.find("vertex") != std::string::npos) {
                type = ShaderType::VERTEX;
            }
            else if (line.find("fragment") != std::string::npos) {
                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);

    //Syntax 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 << "Failed to compile" << (type == GL_VERTEX_SHADER ? "vertexShader"
            : "fragmentShader ") << " shader!" << std::endl;
        std::cout << message << std::endl;

        glDeleteShader(id);
        return 0;
    }

    return id;
}

//我们向OpenGL提供我们实际的着色器源代码,我的着色器文本。我们想让OpenGL编译那个程序,将这两个链接到一个
//独立的着色器程序,然后给我们一些那个着色器返回的唯一标识符,我们就可以绑定着色器并使用
unsigned int Shader::CreateShader(const std::string& vertexShader, const std::string& fragmentShader) {
    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);
    glValidateProgram(program);

    //删除一些无用的中间文件
    glDeleteShader(vs);
    glDeleteShader(fs);

    return program;
}

//Set uniforms
void Shader::SetUniform4f(const std::string& name, float v0, float v1, float v2, float v3){
    int location = GetUniformLoation(name);
    GLCall(glUniform4f(location, v0, v1, v2, v3));
}

unsigned int Shader::GetUniformLoation(const std::string& name){
    if (m_UniformLocationCache.count(name)) {
        return m_UniformLocationCache[name];
    }
    
    GLCall(int location = glGetUniformLocation(m_RenderID, name.c_str()));
    if (location == -1) {
        std::cout << "Waring: Uniform " << name <<" does not exist" << std::endl;
    }
    else {
        m_UniformLocationCache[name] = location;
    }
    return location;
}

Applicaiton 类

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

#include <iostream>
#include <fstream>
#include <string>
#include <sstream>

#include "Render.h"
#include "IndexBuffer.h"
#include "VertexBuffer.h"
#include "VertexBufferLayout.h"
#include "VertexArray.h"
#include "Shader.h"

int main(void)
{
    GLFWwindow* window;

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

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);  //指定opengl版本,3
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);  //指定opengl次要版本 0.3  即3.3 
    //glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE); //兼容版本
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 核心版本

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

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

    if (glewInit() != GLEW_OK) {  //should after MakeContextCurrent 
        std::cout << "Error" << std::endl;
    }

    std::cout << glGetString(GL_VERSION) << std::endl;

    {float positions[] = {
        -0.5f, -0.5f, //0
        0.5f, -0.5f, //1
        0.5f, 0.5f, //2
        -0.5f,0.5f, //3
    };

    unsigned int indices[] = {
        0,1,2,
        2,3,0
    };

    VertexArray va;
    // 顶点缓冲区
    VertexBuffer vb(positions, 8 * sizeof(float));
    VertexBufferLayout layout;
    layout.Push<float>((unsigned int)2);
    va.AddBuffer(vb,layout);

    //索引缓冲区
    IndexBuffer ib(indices, 6);

    Shader shader("res/Shaders/Basic.shader");
    shader.Bind();
    shader.SetUniform4f("u_Color", 1.0, 0.0, 0.7, 1.0);

    //将所有绑定解绑
    va.UnBind();
    vb.Unbind();
    ib.Unbind();
    shader.UnBind();

    float r = 0.0f;
    float increment = 0.05f;
    /* Loop until the user closes the window */
    while (!glfwWindowShouldClose(window))
    {
        /* Render here */
        glClear(GL_COLOR_BUFFER_BIT);

        GLClearError();

        //为了正确绘制图形,重新进行绑定
        shader.Bind();
        va.Bind();
        ib.Bind();

        shader.SetUniform4f("u_Color", r, 0.0f, 1.0f, 1.0f);

        //Draw call
        GLCall(glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr));

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

        r += increment;

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

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

    glfwTerminate();
    return 0;
}

写一个基础的渲染器类

今天我们的目标是剔除在主文件中遗留的 OpenGL 调用。

虽然前面几节我们用vertexBuffer ,IndexBuffer,VettexArray,Shader 等类抽象 OpenGL 代码,但是还有相当一部分遗留在 Application.cpp 中,例如 DrawCall 指令。

Applicaiton 

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

#include <iostream>
#include <fstream>
#include <string>
#include <sstream>

#include "Render.h"
#include "IndexBuffer.h"
#include "VertexBuffer.h"
#include "VertexBufferLayout.h"
#include "VertexArray.h"
#include "Shader.h"

int main(void)
{
    GLFWwindow* window;

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

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);  //指定opengl版本,3
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);  //指定opengl次要版本 0.3  即3.3 
    //glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE); //兼容版本
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 核心版本

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

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

    if (glewInit() != GLEW_OK) {  //should after MakeContextCurrent 
        std::cout << "Error" << std::endl;
    }

    std::cout << glGetString(GL_VERSION) << std::endl;

    {float positions[] = {
        -0.5f, -0.5f, //0
        0.5f, -0.5f, //1
        0.5f, 0.5f, //2
        -0.5f,0.5f, //3
    };

    unsigned int indices[] = {
        0,1,2,
        2,3,0
    };

    VertexArray va;
    // 顶点缓冲区
    VertexBuffer vb(positions, 8 * sizeof(float));
    VertexBufferLayout layout;
    layout.Push<float>((unsigned int)2);
    va.AddBuffer(vb,layout);

    //索引缓冲区
    IndexBuffer ib(indices, 6);

    Shader shader("res/Shaders/Basic.shader");
    shader.Bind();
    shader.SetUniform4f("u_Color", 1.0, 0.0, 0.7, 1.0);

    //将所有绑定解绑
    va.UnBind();
    vb.Unbind();
    ib.Unbind();
    shader.UnBind();

    Render render;

    float r = 0.0f;
    float increment = 0.05f;
    /* Loop until the user closes the window */
    while (!glfwWindowShouldClose(window))
    {
        render.Clear();
        render.Draw(va, ib, shader);

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

        r += increment;

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

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

    glfwTerminate();
    return 0;
}

这样已清理清爽多了

Render 类

.cpp

#include "Render.h"

#include <iostream>

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

    }
}

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

void Render::Clear() const {
    /* Render here */
    glClear(GL_COLOR_BUFFER_BIT);
}

void Render::Draw(const VertexArray& va, const IndexBuffer& ib, const Shader& shader) const {
    shader.Bind();
    va.Bind();
    ib.Bind();
    //Draw call
    GLCall(glDrawElements(GL_TRIANGLES, ib.GetCount(), GL_UNSIGNED_INT, nullptr));
}

.h

#pragma once

#include <GL/glew.h>

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

#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 Render {
private:
    
public:
    void Clear() const;
    void Draw(const VertexArray& va, const IndexBuffer& ib, const Shader& shader) const;
};

纹理

当大部分人想到纹理的时候,他们真的只会想到在一个游戏中的 3D 对象上呈现图像(当然不一定非得是 3D 对象)。而你们在 Ps 或画图等等中创建的图像,让它出现在你们的图形应用程序中,那就是纹理的本质,或者至少是大部分人眼中的纹理。

现在,除了这些,关于纹理的理解比我刚才描述的要多得多。纹理可以用于很多事情,当后面讲到一些非常复杂的图形技术时我们会提到,但现在我只想让你们把纹理看作当渲染时是我们可以使用的图像。那么我实际能做的就是设计或创造某种图像,不管是一张照片还是画布上一条弯弯曲曲的线亦或是纯色以及任意类似的颜色,我都可以在我的电脑中创建任何图像文件,然后把它上传到我的显存中,然后在我的着色器中使用它来绘制我现在正在做的任何东西。

这可能像在我们的 OpenGL 程序中画一个矩形一样简单,它包含了渲染纹理的元素,这样我们就可以在游戏中看到纹理。或者也可能是更复杂的东西,就像使用预先计算好的数学值将其融入到我们的纹理中,然后在着色器中对它们进行采样,这样我们可以做一些很酷的灯光效果。但现在,它只会在 OpenGL 应用中从计算机获取图像到表面。

首先我们需要以某种方式将 PNG 图像加载到 CPU 内存中,可以使用 stb_img 库加载 PNG。我们给它一个文件路径,它会给我们一个指向 RGBA 像素缓冲区的指针;然后我们会取那个像素数组把它上传到我们的显卡;最后当它绘制时就可以修改着色器来读取那个纹理,片段着色器实际计算出每个像素应该是什么,构成那个纹理每部分的颜色是什么。

Application 

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

#include <iostream>
#include <fstream>
#include <string>
#include <sstream>

#include "Render.h"
#include "IndexBuffer.h"
#include "VertexBuffer.h"
#include "VertexBufferLayout.h"
#include "VertexArray.h"
#include "Shader.h"
#include "Texture.h"

int main(void)
{
    GLFWwindow* window;

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

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);  //指定opengl版本,3
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);  //指定opengl次要版本 0.3  即3.3 
    //glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE); //兼容版本
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 核心版本

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

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

    if (glewInit() != GLEW_OK) {  //should after MakeContextCurrent 
        std::cout << "Error" << std::endl;
    }

    std::cout << glGetString(GL_VERSION) << std::endl;

    {float positions[] = {
        -0.5f, -0.5f, 0.0f,0.0f,//0
        0.5f, -0.5f,1.0f,0.0f, //1
        0.5f, 0.5f, 1.0f,1.0f,//2
        -0.5f,0.5f, 0.0f,1.0f//3
    };

    unsigned int indices[] = {
        0,1,2,
        2,3,0
    };

    VertexArray va;
    // 顶点缓冲区
    VertexBuffer vb(positions, 4 * 4 * sizeof(float));
    VertexBufferLayout layout;
    layout.Push<float>((unsigned int)2);
    layout.Push<float>((unsigned int)2);
    va.AddBuffer(vb,layout);

    //索引缓冲区
    IndexBuffer ib(indices, 6);

    Shader shader("res/Shaders/Basic.shader");
    shader.Bind();
    shader.SetUniform4f("u_Color", 1.0, 0.0, 0.7, 1.0);

    Texture texture("D:/vs2022/OpenGLProject/OpenGL/res/textures/14fnm.png");
    texture.Bind();
    shader.SetUniform1i("u_Texture", 0);

    //将所有绑定解绑
    va.UnBind();
    vb.Unbind();
    ib.Unbind();
    shader.UnBind();

    Render render;

    float r = 0.0f;
    float increment = 0.05f;
    /* Loop until the user closes the window */
    while (!glfwWindowShouldClose(window))
    {
        render.Clear();
        render.Draw(va, ib, shader);

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

        r += increment;

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

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

    glfwTerminate();
    return 0;
}
.h

#pragma once

#include "Render.h"

class Texture {
private:
	unsigned int m_RenderID;
	std::string m_FilePath;
	unsigned char* m_LocationBuffer;
	int m_Width, m_Height, m_BPP;
public:
	Texture(const std::string& path);
	~Texture();

	//slot 是想要绑定纹理的插槽
	void Bind(unsigned int slot = 0) const;  
	void UnBind() const;
	 
	inline int GetWidth() const { return m_Width; }
	inline int GetHeight() const { return m_Height; }
};

.cpp

#include "Texture.h"
#include "vendor/stb_image.h"

Texture::Texture(const std::string& path) 
	: m_RenderID(0), m_FilePath(path), m_LocationBuffer(nullptr),
	m_Width(0), m_Height(0), m_BPP(0) {

	stbi_set_flip_vertically_on_load(1); //将图片上下翻转
	m_LocationBuffer = stbi_load(path.c_str(), &m_Width,&m_Height,&m_BPP,4);

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

	//设置图片缩小放大,嵌入的采样方式
	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_RGBA8, m_Width, m_Height, 0, GL_RGBA,
		GL_UNSIGNED_BYTE, m_LocationBuffer));
	GLCall(glBindTexture(GL_TEXTURE_2D, 0));

	if (m_LocationBuffer) {
		stbi_image_free(m_LocationBuffer);
	}
}

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

//slot 是想要绑定纹理的插槽
//一般电脑支持最大32个,手机支持8个,但是具体的还得具体看
void Texture::Bind(unsigned int slot) const {
	GLCall(glActiveTexture(GL_TEXTURE0 + slot));
	GLCall(glBindTexture(GL_TEXTURE_2D, m_RenderID));
}

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

参考博文:Cherno OpenGL 教程 | Fl0w3r

视频地址:S13.抽象OpenGL成类_哔哩哔哩_bilibili

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值