最近找实习有一丢丢蛋疼,沉迷鬼泣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. Mina使用总结(一)MinaServer

    我们先看一个最简单的Mina Server服务端代码,该段代码实现了服务端Server启动并监听客户端请求 package com.bypay.mina.server; import java.io. ...

  2. UE4的csv文件导入、URL地址的读取及动态材质的设置

    1.csv文件的导入 UE4是可以直接导入csv文件的,其过程和其他文件资源(图片Texture,静态网格物体StaticMesh等)相似,但在导入过程中有一些需要注意的点. 如下图所示 这是一份编辑 ...

  3. Python之数据库模块安装 MySQLdb

    安装,下载地址 安装可能会报错, 1.需要安装VC++,到提示的地址中下载安装即可 2.在下载对应的包版本,如果是win7 64位2.7版本的python,就下载 MySQL_python-1.2.5 ...

  4. POI导出excel,本地测试没问题,linux测试无法导出

    java.lang.RuntimeException: java.io.IOException: No such file or directory        at org.apache.poi. ...

  5. 使用阿里云的maven仓库

    在maven的settings.xml文件里的mirrors节点,添加如下子节点: <mirror> <id>nexus-aliyun</id> <mirro ...

  6. Kubernetes 1.6新特性

    Kubernetes 1.6已结发布,包括9个Stable特性.12个Beta特性.8个Alpha特性,共29个新特性.Kubernetes 1.6重点关注集群规模扩展和自动化.目前最多支持5000个 ...

  7. c++问题整理

    1.C++ 多态,多态的实现,c++虚函数,虚函数和纯虚函数有什么区别,虚函数的实现原理,虚继承,析构函数能否为虚,为什么析构要虚函数,析构函数声明为虚函数的作用,构造函数为啥不能定义为虚函数,析构函 ...

  8. oracle之修改/忘记用户密码

    一.修改/忘记用户密码: ## 修改oracle用户名和密码 sqlplus /nolog ## DBA角色进入 conn /as sysdba; ## 查看用户列表 select username ...

  9. Visual Studio 2012 编译错误【error C4996: 'scanf': This function or variable may be unsafe. 】的解决方案

    在VS 2012 中编译 C 语言项目,如果使用了 scanf 函数,编译时便会提示如下错误: error C4996: 'scanf': This function or variable may ...

  10. ddt Ui 案例2

    准备用例文件:testcase1.py testcase2.py import ddt import unittest from HTMLTestRunner import HTMLTestRunne ...