【OpenGL_01】入门——配置、创建窗口、绘制三角形、着色器类

LearnOpenGL网站学习,记录一下学习笔记

GLFW与GLAD

GLFW是一个专门针对OpenGL的C语言库,它提供了一些渲染物体所需的最低限度的接口。它允许用户创建OpenGL上下文,定义窗口参数以及处理用户输入。
GLAD:OpenGL的驱动版本有很多,它的大多数函数的位置都无法在编译时确认下来,需要找到指向函数位置的指针,取得地址的方法因为平台不同而不同。但是GLAD可以简化这个过程。

创建窗口

创建窗口主要有以下几个步骤:
1 初始化GLFW
2 创建窗口并检测是否窗口创建是否成功
3 初始化GLAD,管理GLFW函数的指针.
4 创建视口
5 渲染引擎

初始化GLFW

	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE,GLFW_OPENGL_CORE_PROFILE);

创建窗口并检测是否窗口创建是否成功

glfwCreateWindow(800,600,"Hello World"NULL,NULL):创建窗口的命令,参数以此是窗口宽高、窗口名字、

glfwMakeContextCurrent(window):通知GLFW将我们窗口的上下文设置为当前线程的主上下文了
GLFWwindow* window = glfwCreateWindow(800,600,"Hello Triangle",NULL,NULL);
	if (window == NULL) {
		cout << "Failed to Create Window" << endl;
		return -1;
	}
glfwMakeContextCurrent(window);

初始化GLAD,管理GLFW函数的指针.

	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
		cout << "Failed to Init GLAD" << endl;
		return -1;
	}

创建视口

void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
	glViewport(0,0,width,height);
}
glViewport(0,0,800,600);
//每当窗口大小发生变化时,调用framebuffer_size_callback函数
glfwSetFramebufferSizeCallback(window,framebuffer_size_callback);

渲染引擎

glClearColor(0.2f,0.3f,0.3f,1.0f):用括号内的RGBA值来清空屏幕

glClear(GL_COLOR_BUFFER_BIT):清空屏幕的颜色缓冲,
它接受一个缓冲位(Buffer Bit)来指定要清空的缓冲。
缓冲位有GL_COLOR_BUFFER_BIT,GL_DEPTH_BUFFER_BIT
和GL_STENCIL_BUFFER_BIT,分别为:颜色缓冲、深度缓冲、模板缓冲

glfwPollEvents():检查并调用事件
glfwSwapBuffers(window):交换缓冲,原理:双缓冲渲染
while (!glfwWindowShouldClose(window)) {
		glClearColor(0.2f,0.3f,0.3f,1.0f);
		glClear(GL_COLOR_BUFFER_BIT);
		glfwSwapBuffers(window);
		glfwPollEvents();

绘制三角形

在了解绘制三角形之前,需要了解渲染管线
绘制三角形的过程,我大致将其分为以下几个步骤:
1 顶点数据的输入
2 顶点数据、VBO、VAO、EBO之间的作用原理
3 顶点着色器
4 片段着色器
5 着色程序

顶点数据的输入

在OpenGL中,顶点数据是一系列顶点的集合。一个顶点(Vertex)是一个3D坐标的数据的集合。而顶点数据是用顶点属性(Vertex Attribute)表示的,它可以包含任何我们想用的数据。其中常见的数据有位置、纹理、法线。

下面是一个三角形平面的输出的顶点数据:v:位置、vt:纹理坐标、vn:法线、f:位置/纹理坐标/法线

顶点数据、VBO、VAO、EBO之间的作用原理

根据CPU和GPU之间的数据传输来理解。
在CPU中,对应着顶点数据,有各种值
在GPU中,首先将顶点数据复制一份得到VBO(顶点缓冲对象)
之后,对VBO中的数据进行分类索引得到VAO(顶点数组对象),一般情况下一个模型一个VAO。一个VAO也可以同时对应另外一个缓冲EBO(索引缓冲对象)

着色器

在OpenGL中,我们能够编写代码控制的着色器有vertexShader和FragmentShader。

VertexShader的主要作用是处理顶点相关的信息,比如:position、normal、texture等等,在顶点着色器中处理的主要变换是顶点的位置信息,比如说MVP 变换,也可以进行光照信息的处理,比如:Gouraud着色。

FragmentShader主要是颜色、光照、纹理、材质方面的处理

简单对比: 顶点着色器的处理效率比较高,因为只处理顶点,但是效果较差。比说说Gourand着色效果比在片段着色器的处理效果差。片元着色器的处理效率可能相对较低,因为,片元着色器会对顶点之间的颜色进行插值处理,但是处理的效果较好。对比Gouraud着色与Phong模型。

Uniform

Uniform是一种从CPU中的应用向GPU中的着色器发送数据的方式,但uniform和顶点属性有些不同。首先,uniform是全局的(Global)。全局意味着uniform变量必须在每个着色器程序对象中都是独一无二的,而且它可以被着色器程序的任意着色器在任意阶段访问。第二,无论你把uniform值设置成什么,uniform会一直保存它们的数据,直到它们被重置或更新。

自定义着色器类

了解上述一定概念之后,编写着色器类,用来实现读取vertexShader、FragmentShader着色器、Linked Shader、Uniform的使用

功能设计

主要功能有:

1.构造函数读取shader文件、构造着色器、连接着色器
2.use()函数:使用着色器
3.setInt()函数:设置Uniform值

#ifndef SHADER_H
#define SHADER_H

#include <glad/glad.h>; // 包含glad来获取所有的必须OpenGL头文件

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


class Shader
{
public:
    // 程序ID
    unsigned int ID;

    // 构造器读取并构建着色器
    Shader(const GLchar* vertexPath, const GLchar* fragmentPath);
    // 使用/激活程序
    void use();
    // uniform工具函数
    void setBool(const std::string &name, bool value) const;  
    void setInt(const std::string &name, int value) const;   
    void setFloat(const std::string &name, float value) const;
};

#endif

C++读取文件

简单记录一下C++读取文件步骤:
1.std::ifstream 打开文件,读取文件的缓冲内容,打开文件之前要先进行异常抛出检测
2.std::stringstream:将文件的缓冲内容读取到数据流中
3.std::ifstream:关闭文件处理器
4.std::string:转换数据流到string
5.char* :转换为char类型

Shader(const char* vertexPath, const char* fragmentPath)
{
    // 1. 从文件路径中获取顶点/片段着色器
    std::string vertexCode;
    std::string fragmentCode;
    std::ifstream vShaderFile;
    std::ifstream fShaderFile;
    // 保证ifstream对象可以抛出异常:
    vShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit);
    fShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit);
    try 
    {
        // 打开文件
        vShaderFile.open(vertexPath);
        fShaderFile.open(fragmentPath);
        std::stringstream vShaderStream, fShaderStream;
        // 读取文件的缓冲内容到数据流中
        vShaderStream << vShaderFile.rdbuf();
        fShaderStream << fShaderFile.rdbuf();       
        // 关闭文件处理器
        vShaderFile.close();
        fShaderFile.close();
        // 转换数据流到string
        vertexCode   = vShaderStream.str();
        fragmentCode = fShaderStream.str();     
    }
    catch(std::ifstream::failure e)
    {
        std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
    }
    const char* vShaderCode = vertexCode.c_str();
    const char* fShaderCode = fragmentCode.c_str();
    [...]

编译、链接着色器

主要流程:
1.glCraeteShader():创建着色器
2.glShaderSource():写入shader文本
3.glCompileShader():编译着色器
… …
n-2:glCreateProgram():创建着色器程序
n-1:glAttachShader():绑定顶点、片段着色器
n:glLinkProgram(): 链接着色器

// 2. 编译着色器
unsigned int vertex, fragment;
int success;
char infoLog[512];

// 顶点着色器
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vShaderCode, NULL);
glCompileShader(vertex);
// 打印编译错误(如果有的话)
glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
if(!success)
{
    glGetShaderInfoLog(vertex, 512, NULL, infoLog);
    std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
};

// 片段着色器也类似
[...]

// 着色器程序
ID = glCreateProgram();
glAttachShader(ID, vertex);
glAttachShader(ID, fragment);
glLinkProgram(ID);
// 打印连接错误(如果有的话)
glGetProgramiv(ID, GL_LINK_STATUS, &success);
if(!success)
{
    glGetProgramInfoLog(ID, 512, NULL, infoLog);
    std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
}

// 删除着色器,它们已经链接到我们的程序中了,已经不再需要了
glDeleteShader(vertex);
glDeleteShader(fragment);

use( )函数

glUseProgram():使用着色器程序

void use() 
{ 
    glUseProgram(ID);
}

Uniform 传值

glGetUniformLocation( ):获取着色器程序中Uniform的位置
glUniform1i( ):传值

void setBool(const std::string &name, bool value) const
{
    glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value); 
}

着色器类的使用

Shader ourShader("path/to/shaders/shader.vs", "path/to/shaders/shader.fs");
...
while(...)   //渲染器
{
    ourShader.use();
    ourShader.setFloat("someUniform", 1.0f);
    DrawStuff();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值