最近找实习有一丢丢蛋疼,沉迷鬼泣5,四周目通关,又不想写代码,写篇笔记复习一下,要好好学图形学啊

用OpenGL画一个三角形

项目的简介

记录一下跟着learnOpenGL学习的过程

笔记里的代码放在github上,依赖都用相对路径配好了,直接下载就能用,IDE是VS2017,代码

选择Triangle项目作为启动项可以测试这个代码

使用的库是glad和glfw,感觉用glad和glfw开发OpenGL的方式和DX挺像的,也有可能是我见识少, 感觉很多教材用来教学的库都是GLUT, 嘛,学图形学的话也不必纠结这些(大概

项目的架构

1.窗口初始化

2.渲染

int main()
{
BaseInit();//窗口初始化,键盘、鼠标等事件的绑定
MainLoop();//渲染相关
return 0;
}

要测试某个代码,比如说画三角形的,画正方体的,在MainLoop里实现

void MainLoop()
{
NormalTriangle();
}

渲染管线

learnOpenGL里的渲染管线的抽象描述是这样的



我们如果要画三角形的画,其实只要关注顶点着色器和片段着色器部分就好,其他操作管线会帮我们完成

从代码的层面理解画一个三角形的逻辑的话大概是

  1. 创建一个9个元素的float数组,代表三角形的坐标
  2. 编译着色器
  3. 创建顶点数组对象(Vertex Array Object)VAO并绑定
  4. 创建顶点缓存数组(Vertex Buffer Object)VBO并绑定
  5. 使用图元为三角形的绘图方法画三角形

当然不要忘记使用着色器程序,在我的理解里,VAO是在OpenGL里用来识别渲染对象的一个标识,在使用glad和glfw的情况下,OpenGL的对象都是用无符号整形数据来存储。如果想要同时画两个物体,那么只要在两个VAO之间切换绑定即可

窗口初始化

//窗口初始化
void BaseInit()
{
glfwInit();//初始化
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//配置GLFW
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//配置GLFW
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//
glfwWindowHint(GLFW_RESIZABLE, GL_TRUE);
screenWidth = 800.0f;
screenHeight = 600.0f;
//创建窗口
glWindow = glfwCreateWindow(screenWidth, screenHeight, "LearnOpenGL", nullptr, nullptr);
if (glWindow == nullptr)
{
cout << "Failed to create GLFW window" << endl;
glfwTerminate();
return;
}
glfwMakeContextCurrent(glWindow); if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return;
}
}

这段代码没什么好说的,做一些窗口和库的初始化

画三角形

void NormalTriangle()
{
float Triangle[] = {
-0.9f, -0.5f, 0.0f, // left
-0.0f, -0.5f, 0.0f, // right
-0.45f, 0.5f, 0.0f, // top
}; //编译着色器
Shader ourShader("vertex_1.vs", "fragment_1.fs");//编译着色器
ourShader.use();//使用着色器
unsigned int VBO, VAO;
glGenVertexArrays(1, &VAO); //生成顶点数组对象
glGenBuffers(1, &VBO);//生成顶点缓冲区 glBindVertexArray(VAO);// 绑定顶点数组对象
glBindBuffer(GL_ARRAY_BUFFER, VBO);//绑定顶点缓冲区
glBufferData(GL_ARRAY_BUFFER, sizeof(Triangle), Triangle, GL_STATIC_DRAW);//设置缓冲区中的数据
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); // 设置对缓冲区访问的步长为3以及相位为0,告诉着色器,这个数据输入到着色器的第一个(索引为0)输入变量,数据的长度是3个float
glEnableVertexAttribArray(0); while (!glfwWindowShouldClose(glWindow))
{
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//画三角形
glBindVertexArray(VAO);//绑定顶点数组对象
glDrawArrays(GL_TRIANGLES, 0, 3);
glfwPollEvents();
glfwSwapBuffers(glWindow);
}
}

将编译着色器的部分封装起来代码容易理解多了,顶点着色器和片段着色器的代码都比较简单

//vertex_1.vs 顶点着色器
#version 330 core
layout (location = 0) in vec3 aPos;//输入的变量的位置为0,所以glVertexAttribPointer的第一个参数为0 void main()
{
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);//输出的变量叫gl_Position
}
//fragment_1.fs 片段着色器
#version 330 core
out vec4 FragColor;//输出一个颜色 void main()
{
FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
}

最终的效果

画一个彩色的三角形

void ColourfulTriangle()
{
//颜色会在光栅化阶段被硬件进行插值计算
float vertices[] = {
// 位置 // 颜色
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // 右下
-0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // 左下
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f // 顶部
}; //编译着色器
Shader ourShader("vertex_4.vs", "fragment_4.fs");
ourShader.use();//glUseProgram(shaderProgram);
unsigned int VBO, VAO;
//顶点数组
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO); //绑定顶点数组缓存
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 位置属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);//设置数据访问的指针
glEnableVertexAttribArray(0);
// 颜色属性
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));//设置数据访问的指针
glEnableVertexAttribArray(1); while (!glfwWindowShouldClose(glWindow))
{
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//draw
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
glfwPollEvents();
glfwSwapBuffers(glWindow);
}
}

在数组里加入了颜色的属性,要把属性传入着色器,设置数据访问的指针的步长和相位就好,步长是6,相位是3

着色器的代码

//vertex_4.vs
#version 330 core
layout (location = 0) in vec3 aPos; // 位置变量的属性位置值为 0
layout (location = 1) in vec3 aColor; // 颜色变量的属性位置值为 1 out vec3 ourColor; // 向片段着色器输出一个颜色 void main()
{
gl_Position = vec4(aPos, 1.0);
ourColor = aColor; // 将ourColor设置为我们从顶点数据那里得到的输入颜色
}
//fragment_4.fs
#version 330 core
out vec4 FragColor;
in vec3 ourColor; void main()
{
FragColor = vec4(ourColor, 1.0);
}

最终的效果

编译着色器这里省了很多代码,贴上Shader.cpp和Shader.h,其实只是抄learnOpenGL给的源码

#include "Shader.h"
#include <string> void Shader::use()
{
glUseProgram(ID);
} void Shader::setBool(const std::string & name, bool value) const
{
glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value);
} void Shader::setInt(const std::string & name, int value) const
{
glUniform1i(glGetUniformLocation(ID, name.c_str()), value);
} void Shader::setFloat(const std::string & name, float value) const
{
glUniform1f(glGetUniformLocation(ID, name.c_str()), value);
}
#ifndef SHADER_H
#define SHADER_H
#include "glfw3.h"
#include "glad.h"; // 包含glad来获取所有的必须OpenGL头文件
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#define TEXTURE_PATH (std::string("Resources/Texture/")) //纹理的路径
#define SHADER_PATH (std::string("Resources/Shader/")) //Shader的路径
//检查Shader是否编译正确
inline void assertShader(unsigned int shaderObj, std::string&& shaderName)
{
int success = 0;
char infoLog[512];
glGetShaderiv(shaderObj, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(shaderObj, 512, NULL, infoLog);
std::cout << "ERROR::SHADER" << shaderName << "::COMPILATION_FAILED\n" << infoLog << std::endl;
}
}
//检查着色器程序是否编译正确
inline void assertProgram(unsigned int programObj, std::string&& programName)
{
int success = 0;
char infoLog[512];
glGetProgramiv(programObj, GL_LINK_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(programObj, 512, NULL, infoLog);
std::cout << "ERROR::PROGRAM::" << programName << "::COMPILATION_FAILED\n" << infoLog << std::endl;
}
}
class Shader
{
public:
// 程序ID
unsigned int ID;
Shader() : ID(-1)
{ };
// 构造器读取并构建着色器
template <typename S1, typename S2 = std::string>
Shader(S1&& vertexPath, S2&& 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(SHADER_PATH + std::forward<S1>(vertexPath));
fShaderFile.open(SHADER_PATH + std::forward<S2>(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(); unsigned int vertex, fragment; vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vShaderCode, NULL);
glCompileShader(vertex);
assertShader(vertex, "VertexShader"); fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fShaderCode, NULL);
glCompileShader(fragment);
assertShader(fragment, "FragmentShader"); ID = glCreateProgram();
glAttachShader(ID, vertex);
glAttachShader(ID, fragment);
glLinkProgram(ID); assertProgram(ID, "ShaderProgram"); // 删除着色器,它们已经链接到我们的程序中了,已经不再需要了
glDeleteShader(vertex);
glDeleteShader(fragment);
}
// 使用/激活程序
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

OpenGL学习笔记(1) 画一个三角形的更多相关文章

  1. OpenGL学习笔记(2) 画一个正方形

    画一个正方形 其实,画正方形就是画两个三角形,用四个顶点以及使用索引来实现 完整代码在Square项目的Application.cpp里 先贴上窗口初始化代码 void BaseInit() { gl ...

  2. OpenGL学习笔记:第一个OpenGL程序

    OpenGL环境搭建参考博客:VS2015下OpenGL库的配置. #include<GL\glew.h> #include<GLTools.h> #include<GL ...

  3. Unity3D学习笔记1——绘制一个三角形

    目录 1. 绪论 2. 概述 3. 详论 3.1. 准备 3.2. 实现 3.3. 解析 3.3.1. 场景树对象 3.3.2. 绘制方法 4. 结果 1. 绪论 最近想学习一下Unity3d,无奈发 ...

  4. Unity3D学习笔记2——绘制一个带纹理的面

    目录 1. 概述 2. 详论 2.1. 网格(Mesh) 2.1.1. 顶点 2.1.2. 顶点索引 2.2. 材质(Material) 2.2.1. 创建材质 2.2.2. 使用材质 2.3. 光照 ...

  5. OpenGL学习笔记:拾取与选择

    转自:OpenGL学习笔记:拾取与选择 在开发OpenGL程序时,一个重要的问题就是互动,假设一个场景里面有很多元素,当用鼠标点击不同元素时,期待作出不同的反应,那么在OpenGL里面,是怎么知道我当 ...

  6. OpenGL学习笔记3——缓冲区对象

    在GL中特别提出了缓冲区对象这一概念,是针对提高绘图效率的一个手段.由于GL的架构是基于客户——服务器模型建立的,因此默认所有的绘图数据均是存储在本地客户端,通过GL内核渲染处理以后再将数据发往GPU ...

  7. Effective前端3:用CSS画一个三角形

    p { text-indent: 2em } .triangle-container p { text-indent: 0 } img { margin: 15px 0 } 三角形的场景很常见,打开一 ...

  8. C#.NET学习笔记2---C#.第一个C#程序

    C#.NET学习笔记2---C#.第一个C#程序 技术qq交流群:JavaDream:251572072  教程下载,在线交流:创梦IT社区:www.credream.com 6.第一个C#程序:   ...

  9. Effective前端(3)用CSS画一个三角形

    来源:https://zhuanlan.zhihu.com/p/26160325 三角形的场景很常见,打开一个页面可以看到各种各样的三角形: 由于div一般是四边形,要画个三角形并不是那么直观.你可以 ...

随机推荐

  1. React 如何正常渲染一段HTML字符串

    dangerouslySetInnerHTMl 属性 很多时候我们做一个项目接口会返回一段 HTML 字符串文本,然后我们把它解析渲染成正常的html,这是在项目中常见不能再常见的情况了,可是在 re ...

  2. springboot整合mybatis将sql打印到日志

    在前台请求数据的时候,sql语句一直都是打印到控制台的,有一个想法就是想让它打印到日志里,该如何做呢? 见下面的mybatis配置文件: <?xml version="1.0" ...

  3. VMware 虚拟机安装

    虚拟机下载 VMware官网地址:https://www.vmware.com/ 进行官网后,点击左边的下载图标,然后 作系统选择合适的产品,在这里以Windows系统为例,点击转至下载,如下图所示. ...

  4. SQLite加密方式 [转]

    关于SQLite SQLite是一个轻量的.跨平台的.开源的数据库引擎,它的在读写效率.消耗总量.延迟时间和整体简单性上具有的优越性,使其成为移动平台数据库的最佳解决方案(如iOS.Android). ...

  5. .Net实现表达式计算(公式) 表达式字符串

    文档原址:http://blog.csdn.net/fangxing80/article/details/5992661 实现复杂公式计算,比如含IF分支判断等,可考虑通过调用EXCEL公式获取值. ...

  6. SD卡受损,千万不要再格式化了

    1.手机提示SD卡受损; 2.把内卡插入电脑后,提示格式化,点取消.然后查看一下属性; 3.点电脑上的“开始菜单”--“运行”- chkdsk H:/F (H:就是你的SD卡盘符,/F是修复参数); ...

  7. Rendering Engine 主流的浏览器内核(排版引擎、渲染引擎、解释引擎)有哪几种,分别的特点

    一.A web browser engine A rendering engine is software that draws text and images on the screen. The ...

  8. x$ksppi与x$ksppcv查询隐藏参数

    数据库版本:oracle11g 11.0.2.0.4 SQL> desc x$ksppi; Name  Null?  Type -------------------- -------- --- ...

  9. redis 数据库随笔 (一)

    redis数据库的基本类型分析: 1.string 最基本的数据类型.只存贮一个值,key-value,最大值存储512M. 创建命令:hmset  读取命令:hget 2.hash 集合,存储为一个 ...

  10. Asp调用存储过程,command.CreateParameter 参数值的类型说明

    Asp调用存储过程,command.CreateParameter 参数值的类型说明 Asp调用各种存储过程,包括带参数,无参数,输入输出参数,带返回值等. 1,调用没有参数的存储过程 <% s ...