传输顶点数据:
VBO
float vertices[]...
...
//创建并绑定顶点缓冲对象VBO 即在GPU中储存一大堆顶点属性的地方,方便GPU读取
unsigned int VBO;
glGenBuffers(1, &VBO);
//GL_ARRAY_BUFFER表示顶点缓冲对象的类型
glBindBuffer(GL_ARRAY_BUFFER, VBO);
//把之前定义的顶点数据复制到缓冲的内存中
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
EBO
float indices[]...
...
//改为用索引查询顶点信息,重复利用顶点
unsigned int EBO;
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
VAO
//(阐明应当如何读取VBO的数据,并将获取的顶点数据正确链接到顶点着色器的顶点属性上)
unsigned int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
//绑定VAO后再设置该VAO里想用的VBO和EBO
...VBO code
...EBO code
//设置并启用顶点属性,以便shader获取 |比如设置了两个顶点属性 vec3 pos; vec2 uv;
//layout=0 三个值 float类型 不标准化(normalize) 步长(每隔5个float长度读取) 偏移0
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
//layout=1 2个值 float类型 不标准化(normalize) 步长(每隔5个float长度读取) 偏移3个float(跳过pos)
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3*sizeof(float));
glEnableVertexAttribArray(1);
glBindVertexArray(0);//解绑VAO以待后续使用
渲染
//可以把一个VAO看做一个等待渲染的物体
glUseProgram(shaderProgram);//使用什么着色器渲染
glBindVertexArray(VAO);//确定要渲染什么物体
//渲染物体:未开启EBO调用前者,开启调用后者|如长方形这里渲染六个顶点,(使用EBO为6个索引,实为4个顶点)
glDrawArrays(GL_TRIANGLES, 0, 6);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0)
glBindVertexArray(0);//解绑物体
编写着色器 编译使用着色程序
着色器格式
//---------------顶点着色器-------------------
#version 330 core
layout (location = 0) in vec3 aPos; // 顶点属性 输入
out vec4 vertexColor; // 输出
uniform float somedata;//全局变量,相对每个着色程序
void main()
{
gl_Position = vec4(aPos, 1.0);
vertexColor = vec4(0.5, 0.0, 0.0, 1.0);
}
//----------------片元着色器-------------------
#version 330 core
in vec4 vertexColor; // 从顶点着色器传来的输入变量(名称相同、类型相同)
out vec4 FragColor;//输出
uniform vec3 somecolor;
void main()
{
FragColor = vertexColor;
}
编译设置着色程序的shader类
#ifndef SHADER_H
#define SHADER_H
#include <glad/glad.h>
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
class Shader
{
public:
unsigned int ID;
//构造器创建shader程序
Shader(const GLchar* vertexPath, const GLchar* fragmentPath) {
//1.从文件路径中读取顶点片元着色器源码
std::string vertexCode;
std::string fragmentCode;
//创建输入i文件f流
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(fragmentPath);
//读取至字符串流
std::stringstream vShaderStream, fShaderStream;
vShaderStream << vShaderFile.rdbuf();
fShaderStream << fShaderFile.rdbuf();
//关闭文件
vShaderFile.close();
fShaderFile.close();
//转换数据流
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();
//2.编译着色器
int success;
char infoLog[512];
#pragma region 创建并编译顶点着色器
int vertexShader = glCreateShader(GL_VERTEX_SHADER);
//将着色器源码与着色器对象绑定
glShaderSource(vertexShader, 1, &vShaderCode, NULL);
//编译并检查是否编译成功
glCompileShader(vertexShader);
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}
#pragma endregion
#pragma region 创建并编译顶点着色器
int fragShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragShader, 1, &fShaderCode, NULL);
glCompileShader(fragShader);
glGetShaderiv(fragShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
#pragma endregion
#pragma region 创建着色程序并链接着色器
ID = glCreateProgram();
glAttachShader(ID, vertexShader);
glAttachShader(ID, fragShader);
glLinkProgram(ID);
//检查是否链接成功
glGetProgramiv(ID, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(ID, 512, NULL, infoLog);
}
//链接成功后删除着色器
glDeleteShader(vertexShader);
glDeleteShader(fragShader);
#pragma endregion
};
//启用这个shader程序
void Use() {
glUseProgram(ID);
}
//设置uniform值
void SetBool(const std::string &name, bool value) const {
glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value);
}
void SetInt(const std::string &name, int value) const {
glUniform1i(glGetUniformLocation(ID, name.c_str()), value);
}
void SetFloat(const std::string &name, float value) const {
glUniform1f(glGetUniformLocation(ID, name.c_str()), value);
}
void SetMat4(const std::string &name, glm::mat4 value)const {
glUniformMatrix4fv(glGetUniformLocation(ID, name.c_str()),1,GL_FALSE, glm::value_ptr(value));
}
};
#endif
纹理
读取图片数据
/*使用图像加载库 stb_image.h*/
stbi_set_flip_vertically_on_load(true);//转置图像y轴
//ps:可以用"\\"也可以用"/"
data = stbi_load("D:\\MyOpenGLLib\\Task\\chapter3\\awesomeface.png", &width, &height, &nrChannels, 0);
...
//使用后释放
stbi_image_free(data);
生成并设置纹理
unsigned int texture1, texture2;
//生成并绑定纹理对象
glGenTextures(1, &texture1);
glBindTexture(GL_TEXTURE_2D, texture1);
//配置纹理环绕模式模式 最终我们的纹理对象是绑定到GL_TEXTURE_2D的
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
//配置纹理过滤模式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
int width, height, nrChannels;
//读取图片资源
unsigned char *data = stbi_load("D:\\xax\\xaaax..jpg",&width,&height,&nrChannels,0);
if (data) {//输入图片至纹理
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "Failed to load texture" << std::endl;
}
//释放图片资源
stbi_image_free(data);
glGenTextures(1, &texture2);
glBindTexture(GL_TEXTURE_2D, texture2);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
stbi_set_flip_vertically_on_load(true);
data = stbi_load("D:\\xx\\xxxx.png", &width, &height, &nrChannels, 0);
if (data)
{
//第一个GL_RGB是GPU内部使用的格式,第二个GL_RGBA 是导入图使用的格式
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "Failed to load texture2" << std::endl;
}
传输和使用纹理
纹理-->纹理单元-->uniform sampler2D xxx;
纹理单元
//告知shader中的每个采样器选取哪个纹理单元
ourShader.SetInt("texture1", 0);
ourShader.SetInt("texture2", 1);
//每张纹理绑定至对应的纹理单元 (先激活指定纹理单元再绑定)GL_TEXTURE0~15
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture2);
着色器中使用纹理
...
//定义
uniform sampler2D texture1;
uniform sampler2D texture2;
void main()
{
//采样 uv坐标应通过顶点属性获取
FragColor = mix(texture(texture1, texCoord), texture(texture2, texCoord), 0.5);
}
...
空间坐标转换
传输矩阵至着色器
//使用数学库 glm.h
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
...
glm::mat4 transform;
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(transform));
//着色器中的定义
uniform mat4 transform;
对象空间--[Model matrix] ->世界空间
...模型矩阵前三行为对象空间三条轴在世界空间的表示向量,第四列为世界中心和对象中心的距离
0001
可以把旋转应用在这里
世界空间--[View matrix]->观察空间
即转换至摄像机的空间
这里用的lookat,构成其实和模型矩阵方法一样,三个轴向量+原点偏移
#ifndef CAMERA_H
#define CAMERA_H
#include <glad/glad.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <vector>
enum Camera_Movement {
FORWARD,
BACKWARD,
RIGHT,
LEFT
};
// 默认相机属性
const float YAW = -90.0f;
const float PITCH = 0.0f;
const float SPEED = 2.5f;
const float SENSITIVITY = 0.1f;
const float ZOOM = 45.0f;
class Camera
{
public:
glm::vec3 worldUp;
//位置属性
glm::vec3 camPos;
glm::vec3 camFront;
glm::vec3 camUp;
glm::vec3 camRight;
//欧拉角
float Yaw;
float Pitch;
//配置参数
float MovementSpeed;
float MouseSensitivity;
float Zoom;
Camera(glm::vec3 position = glm::vec3(0.f, 0.f, 0.f), glm::vec3 up = glm::vec3(0.f, 1.f, 0.f)) :camFront(glm::vec3(0.f, 0.f, -1.f)),MovementSpeed(SPEED),Yaw(YAW),Pitch(PITCH),MouseSensitivity(SENSITIVITY),Zoom(ZOOM) {
camPos = position;
worldUp = up;
RecalculateCameraVector();
}
//获取view矩阵
glm::mat4 GetViewMatrix() {
return glm::lookAt(camPos, camPos + camFront, camUp);
}
//检测输入
void ProcessKeyboard(Camera_Movement direction,float deltaTime) {
float velocity = deltaTime*MovementSpeed;
switch (direction)
{
case FORWARD:
camPos += camFront*velocity;
break;
case BACKWARD:
camPos -= camFront*velocity;
break;
case LEFT:
camPos -= camRight*velocity;
break;
case RIGHT:
camPos += camRight*velocity;
break;
}
}
void ProcessMouseMovement(float xoffset, float yoffset) {
Yaw += xoffset*MouseSensitivity;
Pitch -= yoffset*MouseSensitivity;
if (Pitch > 89.0f)
Pitch = 89.0f;
if (Pitch < -89.0f)
Pitch = -89.0f;
RecalculateCameraVector();
}
private:
//根据欧拉角计算前向,再推出右向和上向
void RecalculateCameraVector() {
glm::vec3 front;
front.x = glm::cos(glm::radians(Yaw))*glm::cos(glm::radians(Pitch));
front.y = glm::sin(glm::radians(Pitch));
front.z= glm::cos(glm::radians(Pitch))*glm::sin(glm::radians(Yaw));
camFront = glm::normalize(front);
camRight = glm::normalize(glm::cross(camFront, worldUp));
camUp = glm::normalize(glm::cross(camRight, camFront));
}
};
#endif
观察空间--[Projection matrix]->裁减空间
//投射投影
projection = glm::perspective(glm::radians(fov), width / height, near, far);
//正交投影
projection=glm::ortho(left, right, top, near , bottom , far);
裁减空间-[齐次除法]-->标准化设备坐标(NDC)
这一步会在每一个顶点着色器运行的最后被自动执行。
NDC--[根据视口viewpoirt]->屏幕空间-->片段