C++ OpenGL学习笔记(4、绘制贴图纹理)
相关链接:
C++ OpenGL学习笔记(1、Hello World空窗口程序)
C++ OpenGL学习笔记(2、绘制橙色三角形绘制、绿色随时间变化的三角形绘制)
C++ OpenGL学习笔记(3、绘制彩色三角形、绘制彩色矩形)
通过前面几章,彩色三角形也可以画出来进行显示了。现在我有一张图片,我想把图像显示绘制出来,怎么操作。这里就需要openGL纹理绘制相关知识了,最终效果如下图
目录
- 课前准备
- 1、stb_image库
- 2、测试图像下载
- 3、其他事项
- 封装的文件及代码
- 1、Base.h头文件
- 2、ffimage类
- 3、Shader类
- 纹理贴图代码相关修改及说明
- 1、initTexture纹理初始化函数
- 2、initModel函数
- vertexShader.glsl文件修改
- fragmentShader.glsl文件修改
- rend函数
- 主函数完整代码
- 变形
课前准备
本节代码是基于上一节绘制彩色矩形代码基础上进行升级的。
1、stb_image库
因为要绘制图片,至少需要打开图片等一些操作,所以这里引入一个图片库stb_image库,之所以引入这个库是因为它很简单,这个库所有文件就一个点.h文件,不会涉及很麻烦的编译过程,网盘下载链接:
通过网盘分享的文件:stb_image.h
链接: https://pan.baidu.com/s/1S019c2jQqFnWxAvsZNvd1w?pwd=cx6y 提取码: cx6y
--来自百度网盘超级会员v5的分享
该库使用方法很简单,在这个头文件中已经说了,先要定义一个宏,然后包含头文件即可,如下图。
2、测试图像下载
wall.jpg网盘下载
通过网盘分享的文件:wall.jpg
链接: https://pan.baidu.com/s/1L6m9_jOxVPCY_Xg5mWrBJg?pwd=n2vr 提取码: n2vr
--来自百度网盘超级会员v5的分享
直接另存为也可以
3、其他事项
因为代码慢慢多了起来,这次代码中进行一些封装:
1、将stb_image图像库封装在自定义的ffimage类中,保留一个读入接口,和图片的一些基础信息(图片宽、高等信息)
2、将Shader程序shaderProgram变量封装到自定义的Shader类中,同时把初始化Shder(initShader函数)放进去
封装的文件及代码
1、Base.h头文件
该头文件放入openGL基础的头文件,另外放入了几个自定义结构体:描述颜色信息、3维点信息、2维点信息
Base.h代码如下
#pragma once
//存放各种头文件#include <glad/glad.h>
#include "GLFW/glfw3.h"
#include <iostream>#include <string>
#include <fstream>
#include <sstream>typedef unsigned int uint;
typedef unsigned char byte;
struct ffRGBA
{//描述一个点的颜色信息byte m_r;byte m_g;byte m_b;byte m_a;ffRGBA(byte _r = 255, byte _g = 255, byte _b = 255, byte _a = 255){m_r = _r;m_g = _g;m_b = _b;m_a = _a;}};template <typename T>
struct tVec3 {//三维向量结构体T m_x;T m_y;T m_z;tVec3(T _x = 0, T _y = 0, T _z = 0){m_x = _x;m_y = _y;m_z = _z;}
};template <typename T>
struct tVec2 {//二维向量结构体T m_x;T m_y;tVec2(T _x = 0, T _y = 0){m_x = _x;m_y = _y;}
};
2、ffimage类
下载stb_image.h文件放入项目同级目录
ffimage.h代码如下
#pragma once
#include "Base.h"class ffImage
{
private:int m_width;//图片宽int m_height;//图片高int m_picType;//图片数据类型ffRGBA * m_data;//图片Buffer信息
public:int getWidth() const { return m_width; }int getHeight() const { return m_height; }int getPicType() const { return m_picType; }ffRGBA* getData()const { return m_data; }ffRGBA getColor(int x, int y)const{if (x<0 || x>m_width - 1 || y<0 || y>m_height - 1){return ffRGBA(0,0,0,0);}return m_data[y*m_width + x];}ffImage(int _width = 0, int _height = 0, int _picType = 0, ffRGBA* _data = NULL){m_width = _width;m_height = _height;m_picType = _picType;int _sumSize = m_width * m_height;if (_data&& _sumSize){m_data = new ffRGBA[_sumSize];memcpy(m_data,_data,sizeof(ffRGBA)*_sumSize);}else{m_data = NULL;}};~ffImage(){}public:static ffImage* readFromFile(const char* _fileName);//读入影像函数};
ffimage.cpp代码如下
#include "ffimage.h"#define STB_IMAGE_IMPLEMENTATION//调用stb_image.h前需要进行这个宏定义
#include "stb_image.h"ffImage * ffImage::readFromFile(const char * _fileName)
{int _picType = 0;int _width = 0;int _height = 0;//stbimage读入的图像是反过来的,上下颠倒stbi_set_flip_vertically_on_load(true);//读的时候给我颠倒回来unsigned char* bits = stbi_load(_fileName, &_width, &_height, &_picType, STBI_rgb_alpha);//按照8位RGBA读取ffImage* _image = new ffImage(_width,_height,_picType,(ffRGBA*)bits);stbi_image_free(bits);return _image;
}
3、Shader类
主要封装Shader程序为m_shaderProgram变量,和初始化Shader的函数
Shader.h文件代码如下
#pragma once
#include "Base.h"
class Shader
{
public:Shader(){m_shaderProgram = 0;}~Shader() {}void initShader(const char* _vertexPath, const char* _fragPath);void start() {glUseProgram(m_shaderProgram);//启用m_shaderProgram}void end() {glUseProgram(0);//关闭m_shaderProgram}private:unsigned int m_shaderProgram;};
Shader.cpp文件
#include "Shader.h"void Shader::initShader(const char * _vertexPath, const char * _fragPath)
{std::string _vertexCode("");std::string _fragCode("");std::ifstream _vShaderFile;std::ifstream _fShaderFile;_vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);_fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);try {_vShaderFile.open(_vertexPath);_fShaderFile.open(_fragPath);std::stringstream _vShaderStream, _fShaderStream;_vShaderStream << _vShaderFile.rdbuf();_fShaderStream << _fShaderFile.rdbuf();_vertexCode = _vShaderStream.str();_fragCode = _fShaderStream.str();}catch (std::ifstream::failure e) {std::string errStr = "rerad shader fail";std::cout << errStr << std::endl;}const char* _vShaderStr = _vertexCode.c_str();const char* _fShaderStr = _fragCode.c_str();//shader的编译链接unsigned int _vertexID = 0, _fragID = 0;char _infoLog[512];//存储错误信息int _successFlag = 0;//是否成功//编译_vertexID = glCreateShader(GL_VERTEX_SHADER);//编译的是VERTEX类型glShaderSource(_vertexID, 1, &_vShaderStr, NULL);//把代码传过去glCompileShader(_vertexID);//编译glGetShaderiv(_vertexID, GL_COMPILE_STATUS, &_successFlag);//获取编译情况如何if (!_successFlag) {//如果编译不成功glGetShaderInfoLog(_vertexID, 512, NULL, _infoLog);std::string errStr(_infoLog);std::cout << errStr << std::endl;}//frag shader _fragID = glCreateShader(GL_FRAGMENT_SHADER);//编译的是FRAGMENT类型glShaderSource(_fragID, 1, &_fShaderStr, NULL);//把代码传过去glCompileShader(_fragID);//编译glGetShaderiv(_fragID, GL_COMPILE_STATUS, &_successFlag);//获取编译情况如何if (!_successFlag) {//如果编译不成功glGetShaderInfoLog(_fragID, 512, NULL, _infoLog);std::string errStr(_infoLog);std::cout << errStr << std::endl;}//链接m_shaderProgram = glCreateProgram();glAttachShader(m_shaderProgram, _vertexID);//向program好的加入编译好的glAttachShader(m_shaderProgram, _fragID);//向program好的加入glLinkProgram(m_shaderProgram);//开始链接//检查链接是否成功glGetProgramiv(m_shaderProgram, GL_LINK_STATUS, &_successFlag);if (!_successFlag) {//如果链接不成功glGetProgramInfoLog(m_shaderProgram, 512, NULL, _infoLog);std::string errStr(_infoLog);std::cout << errStr << std::endl;}//释放glDeleteShader(_vertexID);glDeleteShader(_fragID);}
纹理贴图代码相关修改及说明
再次说明:本节代码是基于上一节绘制彩色矩形代码基础上进行升级的。
主要变化:
1、增加纹理全局变量纹理初始化函数initTexture
2、initModel()函数,该函数里面需要增加纹理位置信息
3、修改Shader相关代码
1、initTexture纹理初始化函数
1、该函数在主程序初始化模型下面增加该函数入口
2、定义全局变量
3、initTexture函数代码
void initTexture()
{//初始化纹理贴图,构建完成全局变量_texture对象_pImage = ffImage::readFromFile("wall.jpg");//同级目录下准备了一张图片glGenTextures(1, &_texture);//新建一个textureglBindTexture(GL_TEXTURE_2D,_texture);//绑定texture纹理类型为GL_TEXTURE_2D//设置纹理属性glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);//设置S方向填充方式为重复REPEAT方式glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);//设置T方向填充方式为重复REPEAT方式glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);//设置图像缩小时候采样方式为NEAREST最近邻方式glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);//设置图像放大时候采样方式为LINEAR双线性方式//读入图像数据glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _pImage->getWidth(), _pImage->getHeight(),0,GL_RGBA,GL_UNSIGNED_BYTE, _pImage->getData());}
2、initModel函数
1、修改变量vertices,要引入纹理贴的顶点位置。引入后每行顶点坐标含义:前三个点位置坐标、中间三个颜色信息,每行最后两位为纹理坐标位置(u、v)
2、修改锚点0、锚点1的步长信息,新增锚点2为纹理坐标信息,最后启动锚点2
void initModel()
{//构建模型,在模型数据发送GPU,VAO,VBO 在这里完成的//带颜色信息的顶点,0.0是黑色,1.0是白色,下面三个点分别设置红、绿、蓝//float vertices[] = {//-0.5f,-0.5f,0.0f, 1.0f,0.0f,0.0f,//0.5,-0.5,0.0f, 0.0f,1.0f,0.0f,//0.0f,0.5f,0.0f,0.0f,0.0f,1.0f,//};//带纹理坐标、带颜色信息的顶点,每行最后两位为纹理坐标位置(u、v)float vertices[] = {0.5f, 0.5f,0.0f, 1.0f,0.0f,0.0f, 1.0f,1.0f,0.5f,-0.5f,0.0f, 0.0f,1.0f,0.0f, 1.0f,0.0f,-0.5f,-0.5f,0.0f, 0.0f,0.0f,1.0f, 0.0f,0.0f, -0.5f,0.5f,0.0f, 0.0f,1.0f,0.0f, 0.0f,1.0f,};unsigned int indices[] = {0,1,3,1,2,3};glGenVertexArrays(1, &VAO);//创建1个VAOglBindVertexArray(VAO);//绑定VAOunsigned int EBO = 0;//这个容器绑定角点和indexglGenBuffers(1, &EBO);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);//下面初始化VBO,下面的VBO就属于VAO的管理范围,以后绘图直接使用VAO即可glGenBuffers(1, &VBO);//可以同时获取多个VBO的indexglBindBuffer(GL_ARRAY_BUFFER, VBO);//绑定VBOglBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//给GL_ARRAY_BUFFER分配空间,第二个参数:分配多大的空间,第三个参数:从哪里开始读取数据,第四个参数:告诉openGL怎么使用这个数据//下面做锚定点//glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);//每个顶点包含3个坐标,每个坐标都是float类型,不进行正则化,步长3 * sizeof(float)增加颜色信息,修改锚定//glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);//0代表顶点信息,每个顶点包含3个坐标,每个坐标都是float类型,不进行正则化,步长6 * sizeof(float),顶点起始点是从0开始,所以(void*)0//glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));//1代表颜色信息,每个顶点包含3个坐标,每个坐标都是float类型,不进行正则化,步长6 * sizeof(float),颜色起始点是从3开始,所以3*sizeof(float)//增加纹理信息和颜色信息,修改锚定glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);//顶点步长改成了8glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));//颜色步长改成了8glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));//新建2号锚点,该锚点是连续读取2个数据,最后的起始位置是6,所以最后一个是(void*)(6 * sizeof(float))glEnableVertexAttribArray(0);//启动0这个锚定点,弄顶点glEnableVertexAttribArray(1);//启动1这个锚定点,弄颜色glEnableVertexAttribArray(2);//启动2这个锚定点,弄纹理glBindBuffer(GL_ARRAY_BUFFER, 0);//给VBO解绑glBindVertexArray(0);//给VAO解绑}
vertexShader.glsl文件修改
initModel函数新增了锚点2,所以顶点Shader文件需要进行相关变化
#version 330 core
layout(location = 0) in vec3 aPos;//layout为0是位置顶点信息
layout(location = 1) in vec3 aColor;//layout为1是颜色信息
layout(location = 2) in vec2 aUV;//layout为2是纹理位置,UV坐标
out vec4 outColor;//下一步输出变量
out vec2 outUV;//纹理位置void main()
{/*gl_Position 是opengl内置全局变量,该变量会在后面进行调用*/gl_Position = vec4(aPos.x,aPos.y,aPos.z,1.0);//outColor = vec4(aColor.x, aColor.y, aColor.z, 1.0);outColor = vec4(aColor, 1.0);outUV = aUV;//把纹理位置信息增加到管线中};
fragmentShader.glsl文件修改
在上一步顶点管线中新增了纹理位置输出,所以在渲染管线中要新增该变量
#version 330 core
out vec4 FragColor;in vec4 outColor;//从vertexShader.glsl文件过来的变量,要求变量名也一致
in vec2 outUV;//上个管线输入的变量uniform sampler2D ourTexture;//内置的数据类型sampler2D,专门描述纹理的。如果C++外部不传入ourTexture这个变量,则它默认为0void main()
{//FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);//FragColor = outColor;FragColor = texture(ourTexture, outUV);//内置函数texture函数,从outUV位置上取纹理ourTexture的颜色值给FragColor 方案1:直接取出纹理颜色值};
rend函数
该函数还是用绘制矩形的那个函数,没有变化
void rend()
{//渲染函数//每次循环都会调用该函数,直接进行渲染glBindTexture(GL_TEXTURE_2D, _texture);//绘制前绑定纹理_shader.start();glBindVertexArray(VAO);//使用VAO方式进行绘制//glDrawArrays(GL_TRIANGLES, 0, 3);//绘制,从第0个开始画,起作用的是3个glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);//绘制EBO元素,6个索引_shader.end();
}
主函数完整代码
其他几个函数完整代码都贴出来了的,包含下图文件:
这里只贴出主程序文件所有代码,main.cpp
/*
三角形绘制基础代码
在这节课介绍三角形绘制的基础方法,也会涉及到基础的shader调用,其中最关键的概念是
VAO、VBO在OpenGL核心模式下的使用及内涵
也会做一个小小的程序结构,让大家方便今后的架构慢慢改进先对vertexShader.glsl 进行顶点变换,再传入fragmentShader.glsl里面插值1、获取VBO的index
2、绑定VBO的index
3、给VBO分配显存空间,传输数据
4、告诉shader数据解析方式
5、激活锚点//本节内容:
1、纹理贴图做准备
2、进一步封装类
3、引入stb_image库,该库只有一个stb_image.h头文件*/
#include "Base.h"
#include "Shader.h"
#include "ffimage.h"Shader _shader;unsigned int VAO = 0;
unsigned int VBO = 0;unsigned int _texture = 0;//纹理
ffImage* _pImage = NULL;//纹理贴图影像void rend()
{//渲染函数//每次循环都会调用该函数,直接进行渲染glBindTexture(GL_TEXTURE_2D, _texture);//绘制前绑定纹理_shader.start();glBindVertexArray(VAO);//使用VAO方式进行绘制//glDrawArrays(GL_TRIANGLES, 0, 3);//绘制,从第0个开始画,起作用的是3个glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);//绘制EBO元素,6个索引_shader.end();
}void initModel()
{//构建模型,在模型数据发送GPU,VAO,VBO 在这里完成的//带颜色信息的顶点,0.0是黑色,1.0是白色,下面三个点分别设置红、绿、蓝//float vertices[] = {//-0.5f,-0.5f,0.0f, 1.0f,0.0f,0.0f,//0.5,-0.5,0.0f, 0.0f,1.0f,0.0f,//0.0f,0.5f,0.0f,0.0f,0.0f,1.0f,//};//带纹理坐标、带颜色信息的顶点,每行最后两位为纹理坐标位置(u、v)float vertices[] = {0.5f, 0.5f,0.0f, 1.0f,0.0f,0.0f, 1.0f,1.0f,0.5f,-0.5f,0.0f, 0.0f,1.0f,0.0f, 1.0f,0.0f,-0.5f,-0.5f,0.0f, 0.0f,0.0f,1.0f, 0.0f,0.0f, -0.5f,0.5f,0.0f, 0.0f,1.0f,0.0f, 0.0f,1.0f,};unsigned int indices[] = {0,1,3,1,2,3};glGenVertexArrays(1, &VAO);//创建1个VAOglBindVertexArray(VAO);//绑定VAOunsigned int EBO = 0;//这个容器绑定角点和indexglGenBuffers(1, &EBO);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);//下面初始化VBO,下面的VBO就属于VAO的管理范围,以后绘图直接使用VAO即可glGenBuffers(1, &VBO);//可以同时获取多个VBO的indexglBindBuffer(GL_ARRAY_BUFFER, VBO);//绑定VBOglBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//给GL_ARRAY_BUFFER分配空间,第二个参数:分配多大的空间,第三个参数:从哪里开始读取数据,第四个参数:告诉openGL怎么使用这个数据//下面做锚定点//glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);//每个顶点包含3个坐标,每个坐标都是float类型,不进行正则化,步长3 * sizeof(float)增加颜色信息,修改锚定//glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);//0代表顶点信息,每个顶点包含3个坐标,每个坐标都是float类型,不进行正则化,步长6 * sizeof(float),顶点起始点是从0开始,所以(void*)0//glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));//1代表颜色信息,每个顶点包含3个坐标,每个坐标都是float类型,不进行正则化,步长6 * sizeof(float),颜色起始点是从3开始,所以3*sizeof(float)//增加纹理信息和颜色信息,修改锚定glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);//顶点步长改成了8glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));//颜色步长改成了8glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));//新建2号锚点,该锚点是连续读取2个数据,最后的起始位置是6,所以最后一个是(void*)(6 * sizeof(float))glEnableVertexAttribArray(0);//启动0这个锚定点,弄顶点glEnableVertexAttribArray(1);//启动1这个锚定点,弄颜色glEnableVertexAttribArray(2);//启动2这个锚定点,弄纹理glBindBuffer(GL_ARRAY_BUFFER, 0);//给VBO解绑glBindVertexArray(0);//给VAO解绑}void initTexture()
{//初始化纹理贴图,构建完成全局变量_texture对象_pImage = ffImage::readFromFile("wall.jpg");//同级目录下准备了一张图片glGenTextures(1, &_texture);//新建一个textureglBindTexture(GL_TEXTURE_2D,_texture);//绑定texture纹理类型为GL_TEXTURE_2D//设置纹理属性glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);//设置S方向填充方式为重复REPEAT方式glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);//设置T方向填充方式为重复REPEAT方式glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);//设置图像缩小时候采样方式为NEAREST最近邻方式glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);//设置图像放大时候采样方式为LINEAR双线性方式//读入图像数据glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _pImage->getWidth(), _pImage->getHeight(),0,GL_RGBA,GL_UNSIGNED_BYTE, _pImage->getData());}void initShader(const char* _vertexPath, const char* _fragPath)
{//shader写出来,编译出来_shader.initShader(_vertexPath, _fragPath);}void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{glViewport(0, 0, width, height);
}void processInput(GLFWwindow *window)
{//检测是否有外部输入if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS){glfwSetWindowShouldClose(window, true);//把关闭状态设置为true}
}int main()
{glfwInit();//初始化上下文环境glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//要求opengl 3版本以上glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//设置CORE模式,只能用VAO绘制GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL Core", NULL, NULL);//创建窗体if (window == NULL){std::cout << "Failed to create GLFW window" << std::endl;glfwTerminate();return -1;}glfwMakeContextCurrent(window);//上下文绑定窗体if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))//初始化函数指针,为下面函数做准备{std::cout << "Failed to initialize GLAD" << std::endl;return -1;}glViewport(0, 0, 800, 600);//设置需要渲染的视口glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);//设置回调函数initModel();//初始化模型initTexture();//调取读纹理函数initShader("vertexShader.glsl", "fragmentShader.glsl");while (!glfwWindowShouldClose(window))//创建的window关掉后就退出while循环{processInput(window);//glClearColor(0.2f, 0.3f, 0.3f, 1.0f);//设置颜色glClear(GL_COLOR_BUFFER_BIT);//用设置的颜色把画布进行清零掉rend();glfwSwapBuffers(window);glfwPollEvents();}glfwTerminate();std::cout << "Hello World!\n";return 0;
}
变形
因为每个顶点的颜色信息还进行了保留,如果在fragmentShader.glsl文件中的核心代码进行相关变形就会得到一个彩色的纹理图像
#version 330 core
out vec4 FragColor;in vec4 outColor;//从vertexShader.glsl文件过来的变量,要求变量名也一致
in vec2 outUV;uniform sampler2D ourTexture;//内置的数据类型sampler2D,专门描述纹理的。如果C++外部不传入ourTexture这个变量,则它默认为0void main()
{FragColor = texture(ourTexture, outUV)*outColor;//方案2:纹理像素和原来彩色进行相乘,进行颜色混合};
输出效果
相关文章:
C++ OpenGL学习笔记(4、绘制贴图纹理)
相关链接: C OpenGL学习笔记(1、Hello World空窗口程序) C OpenGL学习笔记(2、绘制橙色三角形绘制、绿色随时间变化的三角形绘制) C OpenGL学习笔记(3、绘制彩色三角形、绘制彩色矩形) 通过前面…...
以二进制形式创建gitea仓库
1、官方文档: 数据库准备 | Gitea Documentation 使用二进制文件安装 | Gitea Documentation 2、具体操作 1)创建gitea数据库 2)检查是否安装 Git。要求 Git 版本 > 2.0。 如需升级git请参考以下链接:linux升级git版本-C…...
数据库系统原理:数据库安全性与权限控制
2.1vue技术 Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式JavaScript框架。 [5] 与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项…...
bugkctf 渗透测试1超详细版
bugkctf 渗透测试1 下发环境进行访问 场景1 通过查看网页源代码成功找到1个flag 得到flag和提示 <!--flag{950d83a16ad47127859d414009432171} PS:下个flag网站管理员才能看到哦-->场景2 根据提示需要管理员权限,进行登录,直接登录需要邮箱和…...
window安装TradingView
目录 下载安装包 修改文件后缀,解压 将K线换成国内涨红跌绿样式 下载安装包 https://www.tradingview.com/desktop/ 下载完成后是.msix格式文件 (我在win10和win11的系统中尝试运行msix都没有成功,所以放弃直接双击运行msixÿ…...
网络下载ts流媒体
网络下载ts流媒体 查看下载排序合并 很多视频网站,尤其是微信小程序中的长视频无法获取到准确视频地址,只能抓取到.ts片段地址,下载后发现基本都是5~8秒时长。 例如: 我们需要将以上地址片段全部下载后排序后再合成新的长视频。 …...
log4j2漏洞复现(CVE-2021-44228)
靶场环境 步骤一:设置出战规则 步骤二:开启靶场 cd vulhub cd log4j cd CVE-2021-44228 docker-compose up -d docker ps 访问端口 靶机开启 步骤三:外带注入 获得dnslog 靶机访问dnslog 得到dnslog的二级域名信息 步骤四:构造…...
应用(APP)部署容器化演进之路
应用(Application)部署容器化演进之路 一、应用程序部署痛点 1.1 应用程序部署流程 举例:部署一个JAVA编程语言开发的Web应用,以War包放入Tomcat方式部署。 部署过程如下: 服务器配置运行环境:JAVA代码运行环境&am…...
Pytorch | 从零构建EfficientNet对CIFAR10进行分类
Pytorch | 从零构建EfficientNet对CIFAR10进行分类 CIFAR10数据集EfficientNet设计理念网络结构性能特点应用领域发展和改进 EfficientNet结构代码详解结构代码代码详解MBConv 类初始化方法前向传播 forward 方法 EfficientNet 类初始化方法前向传播 forward 方法 训练过程和测…...
Saprk和Flink的区别
1 、设计理念方面 Spark 的技术理念是使用微批来模拟流的计算,基于 Micro-batch ,数据流以时间为单位被切分为一个个 批次,通过分布式数据集RDD 进行批量处理,是一种伪实时。 Flink 是基于事件驱动的,是面向流的处理…...
Spring Boot 动态定时任务管理系统(轻量级实现)
Spring Boot项目中,实现动态增删和启停定时任务的功能对于许多应用场景来说至关重要。虽然Quartz框架是一个广泛使用的解决方案,但其复杂性和重量级特性可能使得项目变得臃肿和难以维护。为了解决这个问题,本项目旨在实现一个轻量级的定时任务…...
开发微信小程序的过程与心得
起因 作为家长,我近期参与了学校的护学岗工作。在这个过程中,我发现需要使用水印相机来记录护学活动,但市面上大多数水印相机应用都要求开通会员才能使用完整功能。作为一名程序员,我决定利用自己的技术背景,开发一个…...
项目代码第6讲:UpdownController.cs;理解 工艺/工序 流程、机台信息;前端的“历史 警报/工艺 记录”
一、UpdownController.cs 1、前端传入 当用户在下图的“记录查询”中的 两个界面选项 中,点击“导出”功能时,向后端发起请求,请求服务器下载文件的权限 【权限是在Program.cs中检测的,这个控制器里只需要进行“谁在哪个接口下载了文件”的日志记录】 【导出:是用户把…...
Apache RocketMQ 5.1.3安装部署文档
官方文档不好使,可以说是一坨… 关键词:Apache RocketMQ 5.0 JDK 17 废话少说,开整。 1.版本 官网地址,版本如下。 https://rocketmq.apache.org/download2.配置文件 2.1namesrv端口 在ROCKETMQ_HOME/conf下 新增namesrv.pro…...
uni-app开发收货地址管理
目录 一:功能描述 二:功能实现 一:功能描述 收货地址在个人中心的我的地址里面,点击我的地址可以查看我的收货地址列表,可以新增收货地址,点击特定收货地址可以编辑和删除该地址信息。 二:功能实现 1:收货地址列表 <view v-for="(item, index) in data_lis…...
BOE(京东方)“向新2025”年终媒体智享会落地成都 持续创新引领产业步入高价值增长新纪元
12月20日,BOE(京东方)“向新 2025”年终媒体智享会的脚步从上海延伸至成都。川渝之地,作为 BOE(京东方)产业生态战略布局中的关键一子,此刻再度成为行业瞩目的焦点。本次活动全面回溯了BOE(京东方)在2024年多个关键领域斩获的斐然佳绩,深入剖析了六大维度构建的“向新”发展格局…...
JavaWeb - ⭐ AOP 面相切面编程原理及用户校验功能实战
一、概述 定义: AOP (Aspect Oriented Programming 面向切面编程) ,一种面向方法编程的思想 功能:管理 bean 对象的过程中,通过底层的动态代理机制对特定方法进行功能的增强或改变 实现方式:动态代理技术,…...
鸿道Intewell工业操作系统,三大关键技术,领跑行业前沿
鸿道工业操作系统,是软件定义控制的工业实时操作系统,具备三大关键技术: 1、确定性计算与高实时响应(微秒级、关键指标) 提供确定性的计算与高实时的系统环境,保障工业生产运作的灵活高效。 2、微内核、并发处理(微内核的强大…...
java 对ElasticSearch数据库操作封装工具类(对你是否适用嘞)
在 Java 中封装 ElasticSearch(ES)数据库操作(ES版本之间变动挺大的,别轻易换版本),可以使得与 ES 的交互更加简洁和易于维护。通过封装常见的操作,如插入文档、查询、更新和删除等,…...
HarmonyOS NEXT 技术实践-基于基础视觉服务实现骨骼点识别
本示例展示了如何在HarmonyOS Next中实现基于基础视觉服务的骨骼点识别功能。骨骼点识别是计算机视觉中的一项重要技术,广泛应用于运动分析、健身监控和增强现实等领域。通过使用HarmonyOS Next提供的视觉API,开发者能够轻松地对人物图像进行骨骼点检测&…...
广州大学计算机组成原理课程设计
一.课设性质,目的,任务 《计算机组成与系统结构课程设计》是计算机学院各专业集中实践性环节之一,是学习完《计算机组成与系统结构》课程后进行的一次全面的综合练习。其目的是综合运用所学计算机原理知识,设计并实现一台模型计算…...
【漏洞复现】CVE-2021-45788 SQL Injection
漏洞信息 NVD - cve-2021-45788 Time-based SQL Injection vulnerabilities were found in Metersphere v1.15.4 via the “orders” parameter. Authenticated users can control the parameters in the “order by” statement, which causing SQL injection. API: /test…...
【Java基础面试题035】什么是Java泛型的上下界限定符?
回答重点 Java泛型的上下界限定符用于对泛型类型参数进行范围限制,主要有上界限定符和下届限定符。 1)上界限定符 (? extends T): 定义:通配符?的类型必须是T或者T的子类,保证集合元素一定是T或者T的子类作用&…...
无需GPU也能跑的修图神器(祛痣、去水印、去路人)
背景 在这个视觉内容创作盛行的时代,大家对图片质量要求愈发严苛。无论是日常分享的自拍照想祛痣变得完美无瑕,还是摄影作品需去水印彰显原创,亦或是旅游打卡照要去路人还原美景。如今,一款能在 CPU 上流畅运行的修改神器应运而生…...
R语言数据分析案例47-上海译文出版社旗舰店图书分析和可视化
一、研究背景 随着数字化时代的发展,图书出版行业面临着日益激烈的市场竞争。上海译文出版社作为一家知名的出版机构,其旗舰店的图书销售数据蕴含着丰富的信息。对最新入库图书进行深入分析和可视化呈现,有助于出版社更好地了解市场动态、读…...
牵手红娘:牵手App红娘助力“牵手”,脱单精准更便捷
随着互联网的普及,现代青年的社交圈层加速扩大,他们的恋爱观也正经历着前所未有的转变。在繁忙的工作之余,人们希望能够找到一种既高效又真诚的交友方式。于是,线上交友平台成为了他们寻找爱情的新选择。让不同文化背景、不同工作…...
什么是根服务器?有什么作用?
你知道什么是根服务器吗?在互联网的庞大架构中,根服务器很多人对它的了解并不深入。那么,根服务器到底是什么,它有什么作用呢? 什么是根服务器? 根服务器是互联网域名系统(DNS)的一部分,负责管理和维护最顶层的域名信息。简单…...
Tavily人工智能代理和检索增强生成应用设计的搜索引擎
Tavily人工智能代理和检索增强生成应用设计的搜索引擎 Tavily是一款专为人工智能代理和检索增强生成应用设计的搜索引擎.以下是其使用示例: 安装与配置 需先安装相应的包,并获取API密钥来设置环境变量. %pip install -qU "langchain-community>=0.2.11" tavi…...
web的五个Observer API
IntersectionObserver: 一个元素从不可见到可见,从可见到不可见 ??IntersectionObserver是一种浏览器提供的 JavaScript API,用于监测元素与视窗的交叉状态。它可以告诉开发者一个元素是否进入或离开视窗,以及两者的交叉区域的…...
CSS学习记录19
CSS文本效果 CSS text-overflow 属性规定应如何向用户呈现未显示的溢出的内容。 //裁剪 text-overflow: clip; //隐藏 text-overflow: ellipsis; CSS字换行(Word Wrapping) CSS word-wrap 属性使长文字能够被折断并换到下一行。 如果一个单词太长而…...
使用Amazon Bedrock的无服务器的智能工作流
使用Amazon Bedrock的无服务器的智能工作流 智能工作流基于用户输入处理不可预见的任务,比如发起API调用。无服务器架构可以高效地管理这些任务和不同的工作负载,而无需维护服务器,从而加快部署速度。 你将学习如何通过使用带有保护措施的智能…...
SDMTSP:粒子群优化算法PSO求解单仓库多旅行商问题,可以更改数据集和起点(MATLAB代码)
一、单仓库多旅行商问题 单仓库多旅行商问题(Single-Depot Multiple Travelling Salesman Problem, SD-MTSP):𝑚个推销员从同一座中心城市出发,访问其中一定数量的城市并且每座城市只能被某一个推销员访问一次&#x…...
Java中的访问修饰符:分类、作用及应用场景
在Java编程中,访问修饰符(Access Modifiers)是控制类、方法、变量和构造函数访问权限的关键工具。通过合理使用访问修饰符,可以有效地封装代码,保护数据,并确保代码的安全性和可维护性。本文将详细介绍Java…...
【day16】Java开发常用API
模块15回顾 在深入探讨模块16之前,让我们回顾一下【day15】中的重点内容: String类: 构造方法:String(), String(String s), String(char[] chars), String(byte[] bytes), String(char[] chars, int offset, int count), String…...
LLaMA-Factory(二)界面解析
这里写目录标题 1. 基础选项语言模型选择模型路径微调方法检查点路径量化等级量化方法提示模板RoPE加速方式 2.模型训练界面训练方式数据集超参数设置其他参数部分参数设置LoRA参数设置RLHF参数设置GaLore参数设置BAdam参数设置训练 3.评估预测界面4.Chat界面5.导出Export界面 …...
(14)CT137A- 动态数码管设计
(1)了解板卡原理图中数码管的特性:共阳极数码管,公共端连接了电源,FPGA IO口低电平有效,另外,可以看到位选端FPGA位选低电平时选通。 (2)刷新时间的设定:众所…...
RT-DETR学习笔记(2)
七、IOU-aware query selection 下图是原始DETR。content query 是初始化为0的label embedding, position query 是通过nn.Embedding初始化的一个嵌入矩阵,这两部分没有任何的先验信息,导致DETR的收敛慢。 RT-DETR则提出要给这两部分(conten…...
JS、JSX 和 TSX 的对比指南
JS、JSX 和 TSX 的对比指南 1. JavaScript (.js) 简介 JavaScript 是一种轻量级的、解释型的编程语言,是 Web 开发中最基础的语言之一。 优点 学习曲线较低,容易入门使用范围广泛,生态系统成熟灵活性高,支持多种编程范式不需…...
【STM32】RTT-Studio中HAL库开发教程十:EC800M-4G模块使用
文章目录 一、简介二、模块测试三、OneNet物联网配置四、完整代码五、测试验证 一、简介 EC800M4G是一款4G模块,本次实验主要是进行互联网的测试,模块测试,以及如何配置ONENET设备的相关参数,以及使用STM32F4来测试模块的数据上报…...
增加nginx配置文件(conf.d), 管理多个项目
1.切换到nginx目录下, 新建conf.d文件夹 mkdir conf.d 2.赋予conf.d权限 chmod 777 conf.d 3.进入conf.d, 编辑conf文件 vim zc_travel.conf server { listen 13101; server_name localhost;location / {root /home/baoxin/app/web/insight-radar-rcfx-pre/html_dev;index …...
服务器中了挖矿病毒-应急响应
事件:客户反映服务器响应很慢,出行卡顿,服务器操作系统为windows server 2008。 现场解决步骤: 1、打开任务管理器,看到一个javs.exe的程序,占用CPU使用率比较高,怀疑可能是木马。 2、右键打开…...
pid学习感悟
飞行控制PID算法——无人机飞控_飞控算法-CSDN博客 首先pid是对现测量值和目标值的差值,即偏差值的操作 p是线性操作,偏差越大,则对于控制单元操作的值越大 d是微分操作,我们知道微分其实就是数值变化的斜率,所以当偏…...
Centos7.9安装openldap+phpldapadmin+grafana配置LDAP登录最详细步骤 亲测100%能行
一、部署LDAP 1、安装LDAP yum install -y openldap-servers openldap-clients openldap openldap-devel compat-openldap openldap-servers-sql systemctl start slapd systemctl enable slapd2、创建第一个管理账号密码(设置为ldapadmin) slappass…...
唯品会C++面试题及参考答案
内存池怎么可以保证不会产生碎片? 内存池通过预先分配一块较大的内存区域,然后对这块内存进行管理,来减少碎片产生。 首先,内存池采用固定大小的内存块分配策略。比如,将内存池划分成大小相同的小块,当有内存分配请求时,就从这些固定大小的块中选取合适的进行分配。这种…...
Flask中@app.route()的methods参数详解
诸神缄默不语-个人CSDN博文目录 在 Flask 中,app.route 是用于定义路由的核心装饰器,开发者可以通过它为应用指定 URL 映射及相应的处理函数。在处理 HTTP 请求时,不同的业务场景需要支持不同的 HTTP 方法,而 app.route 的 metho…...
使用Grafana中按钮插件实现收发HTTP请求
最近项目中需要监控分布式集群的各项指标信息,需要用到PrometheusGrafana的技术栈实现对分布式集群的各个节点状态进行可视化显示,但是要求前端需要提供一个易用的接口让用户可以触发一些操作,例如负载高时进行负载均衡或弹性伸缩。网上找到的…...
workman服务端开发模式-应用开发-vue-element-admin挂载websocket
一、项目根目录main.js添加全局引入 import /utils/websocket 二、在根目录app.vue 中初始化WebSocket连接 <template><div id"app"><router-view /></div> </template><script>import store from ./store export default {n…...
堆排序【东北大学oj数据结构9-4】C++
堆排序是一种基于堆的数据结构的排序,是一种快速排序算法,可以在输入数组中实现排序处理(内存高效)。 堆排序可以实现如下: maxHeapify(A, i) l left(i) r right(i) // select the node which has the m…...
Linux RTC 驱动框架
目录 一、实时时钟(RTC)介绍1.1 概述1.2 功能1.3 应用场景1.4 工作原理1.5 对外接口1.6 常见 RTC 芯片1.7 在 Linux 系统中的应用1.8 注意事项 二、Linux 内核 RTC 驱动框架2.1 相关源码文件介绍2.2 核心数据结构2.2.1 struct rtc_device2.2.2 rtc_class…...
智能订餐:基于JSP的管理系统设计与优化
相关技术介绍 2.1 B/S结构 目前使用较多的开发结构模式大致可以包括C/S模式和B/S模式[5]。其中,C/S模式全称为客户端/服务器模式(Client/Server模式),B/S模式全称为浏览器/服务器模式(Browser/Server模式)。…...