文章目录
环境
- 系统:ubuntu20.04
- opengl版本:4.6
- glfw版本:3.3
- glad版本:4.6
- cmake版本:3.16.3
- gcc版本:10.3.0
在<opengl学习日记11-opengl的transformtions变换示例>的基础上,进行修改,实现坐标系统变换效果。
代码
CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
project(CoordinateSystems )
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(glfw3 REQUIRED)
message("PROJECT_NAME:${PROJECT_NAME}")
file(GLOB project_file glad.c main.cpp stb_image.h stbimage.h)
add_executable(${PROJECT_NAME} ${project_file})
target_link_libraries(${PROJECT_NAME}
-lglfw
-lGL
-lm
-lXrandr
-lX11
-lpthread
-ldl
)
vertexShaderSource.vs
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;
out vec2 TexCoord;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(aPos, 1.0f);
// gl_Position = vec4(aPos, 1.0);
TexCoord = vec2(aTexCoord.x,1.0 - aTexCoord.y);
}
main.cpp
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include "stbimage.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <fstream>
#include <iosfwd>
#include <iostream>
#include <sstream>
#include <string>
//定义窗口大小
int SCR_WIDTH = 800;
int SCR_HEIGHT = 600;
std::string readFile(const std::string filename)
{
std::string data;
data = readFile(filename.c_str());
return data;
}
std::string readFile(const char* filename)
{
std::string data;
std::ifstream infile;
infile.open(filename);
std::stringstream neirong;
neirong << infile.rdbuf();
infile.close();
data = neirong.str();
return data;
}
int main()
{
std::cout << "Hello World!" << std::endl;
std::cout << "初始化" << std::endl;
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "transformtions", NULL, NULL);
if (window == NULL) {
std::cout << "创建窗口失败" << std::endl;
return -1;
}
glfwMakeContextCurrent(window);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
std::cout << "初始化glad失败" << std::endl;
return -1;
}
std::cout << "顶点定义,VAO,VBO,EBO" << std::endl;
float vertices[] = {
0.5f, 0.5f, 0.0f, 1.0f, 1.0f,
0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.0f, 0.0f, 1.0f
};
//索引缓冲区
unsigned int indices[] = { 0, 1, 3,
1, 2, 3 };
unsigned int VAO, VBO, EBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
//定位点关系
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
std::cout << "着色器定义" << std::endl;
std::cout << "着色器定义:顶点着色器" << std::endl;
std::string vertexShaderSource = readFile("vertexShaderSource.vs");
const char* vertexShaderSourceCtr = vertexShaderSource.c_str();
unsigned int vertexShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSourceCtr, NULL);
glCompileShader(vertexShader);
int success;
char infolog[512] = { 0 };
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(vertexShader, 512, NULL, infolog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n"
<< infolog << std::endl;
}
std::cout << "着色器定义:片段着色器" << std::endl;
std::string fragmentShaderSource = readFile("fragmentShaderSource.fs");
const char* fragmentShaderSourceCtr = fragmentShaderSource.c_str();
unsigned int fragmentShader;
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSourceCtr, NULL);
glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(fragmentShader, 512, NULL, infolog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n"
<< infolog << std::endl;
}
std::cout << "着色器定义:启用着色器程序" << std::endl;
unsigned int shaderProgram;
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderProgram, 512, NULL, infolog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n"
<< infolog << std::endl;
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
std::cout << "纹理定义" << std::endl;
unsigned int texture1;
glGenTextures(1, &texture1);
glBindTexture(GL_TEXTURE_2D, texture1);
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);
std::cout << "读取纹理图片" << std::endl;
int width, height, nrChannels;
unsigned char* texturesData = stbi_load("container.jpg", &width, &height, &nrChannels, 0);
if (texturesData) {
//绑定和纹理图片数据结合
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGB,
width,
height,
0,
GL_RGB,
GL_UNSIGNED_BYTE,
texturesData);
glGenerateMipmap(GL_TEXTURE_2D);
} else {
std::cout << "读取图片数据错误" << std::endl;
}
stbi_image_free(texturesData);
std::cout << "纹理定义2" << std::endl;
unsigned int texture2;
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);
std::cout << "读取纹理图片" << std::endl;
// int width, height, nrChannels;
texturesData = stbi_load("awesomeface.png", &width, &height, &nrChannels, 0);
if (texturesData) {
//绑定和纹理图片数据结合
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGB,
width,
height,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
texturesData);
glGenerateMipmap(GL_TEXTURE_2D);
} else {
std::cout << "读取图片数据错误" << std::endl;
}
stbi_image_free(texturesData);
glUseProgram(shaderProgram);
glUniform1i(glGetUniformLocation((GLuint)shaderProgram, "texture1"), 0);
glUniform1i(glGetUniformLocation((GLuint)shaderProgram, "texture2"), 1);
std::cout << "绘制" << std::endl;
while (!glfwWindowShouldClose(window)) {
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture2);
glUseProgram(shaderProgram);
glm::mat4 model = glm::mat4(1.0f);
model = glm::rotate(model, glm::radians(-55.0f), glm::vec3(1.0f, 0.0f, 0.0f));
glm::mat4 view = glm::mat4(1.0f);
view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.f));
glm::mat4 projection = glm::mat4(1.0f);
projection = glm::perspective(glm::radians(45.0f), (float)SCR_WIDTH /(float) SCR_HEIGHT, 0.1f, 100.0f);
unsigned int transformLoc = glGetUniformLocation(shaderProgram, "model");
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(model));
unsigned int viewLoc = glGetUniformLocation(shaderProgram, "view");
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
unsigned int projectionLoc = glGetUniformLocation(shaderProgram, "projection");
glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projection));
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, (const GLvoid*)0);
glfwSwapBuffers(window);
glfwPollEvents();
}
std::cout << "内存释放" << std::endl;
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);
glDeleteProgram(shaderProgram);
glfwTerminate();
return 0;
}
总结
- 之前注意到需要把下面两个头文件引入放在头文件的最前端,否则报错,今天发现,不仅这两个头文件需要放在最前端,而且引入顺序也不能更改,否则同样会报错,有函数找不到
#include <glad/glad.h>
#include <GLFW/glfw3.h>
- 世界空间的变换(模型矩阵)
物体的坐标将会从局部变换到世界空间;该变换是由模型矩阵(Model Matrix)实现的。
模型矩阵是一种变换矩阵,它能通过对物体进行位移、缩放、旋转来将它置于它本应该在的位置或朝向。
所以,局部空间到世界空间的变换可以用以下glm::rotate
函数实现,只要最后得到一个mat4类型的结构就可以
glm::mat4 model;
model = glm::rotate(model, glm::radians(-55.0f), glm::vec3(1.0f, 0.0f, 0.0f));
所以,这里我们可以引申一下,就是位移、缩放、旋转
这三个功能函数都可以得到mat4结构,即将局部空间转换为世界空间
- 观察空间变换(观察矩阵)
观察空间经常被人们称之OpenGL的摄像机(Camera)(所以有时也称为摄像机空间(Camera Space)或视觉空间(Eye Space))。观察空间是将世界空间坐标转化为用户视野前方的坐标而产生的结果。
这通常是由一系列的位移和旋转的组合来完成,平移/旋转场景从而使得特定的对象被变换到摄像机的前方。这些组合在一起的变换通常存储在一个观察矩阵(View Matrix)里,它被用来将世界坐标变换到观察空间。
通过以上解释,我理解的是观察空间的变换也是由位移和旋转,平移/旋转
这些变换来的,所以这些个函数也可以用作观察空间的变换
glm::mat4 view;
// 注意,我们将矩阵向我们要进行移动场景的反方向移动。
view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f));
- 裁剪空间(投影矩阵)
在一个顶点着色器运行的最后,OpenGL期望所有的坐标都能落在一个特定的范围内,且任何在这个范围之外的点都应该被裁剪掉(Clipped)。被裁剪掉的坐标就会被忽略,所以剩下的坐标就将变为屏幕上可见的片段。这也就是裁剪空间(Clip Space)名字的由来。
投影矩阵(Projection Matrix),它指定了一个范围的坐标
透视投影glm创建函数为
glm::mat4 proj = glm::perspective(glm::radians(45.0f), (float)width/(float)height, 0.1f, 100.0f);
- 顶点坐标变换为裁剪坐标的公式
{V}_{clip} = M_{projection} ⋅ M_{view} ⋅ M_{model} ⋅ V_{local}
注意 矩阵运算的顺序是相反的(记住我们需要从右往左阅读矩阵的乘法)。
最后的顶点应该被赋值到顶点着色器中的gl_Position,OpenGL将会自动进行透视除法和裁剪。
由于笔者的水平有限, 加之编写的同时还要参与开发工作,文中难免会出现一些错误或者不准确的地方,恳请读者批评指正。如果读者有任何宝贵意见,可以加我微信 wencoo824。QQ:1419440391。
技术交流
欢迎加微信,搜索"wencoo824",进行技术交流,备注”博客音视频技术交流“
音视频领域其他技术文章的链接
opengl相关文章
- opengl日记7-ubuntu20.04开发环境opengl拓展glfw和glad环境搭建
- opengl日记8-opengl创建三角形
- opengl日记9-opengl使用纹理示例
- opengl日记10-opengl使用多个纹理示例
- opengl日记11-opengl的transformtions变换示例
- opengl日记12-opengl坐标系统
- opengl日记19-opengl文字渲染-教程示例
- opengl日记23-opengl文字渲染-渐变色-教程示例
- opengl日记25-opengl文字渲染-渲染中文渐变色动画-直线线性运动-教程示例
- opengl日记26-opengl文字渲染-渲染中文渐变色动画-贝塞尔运动-教程示例
- opengl日记27-opengl报错ERROR::SHADER::PROGRAM::LINKING_FAILED
- opengl日记28-opengl之c语言版本的glm库cglm编译使用教程
ffmpeg相关文章
ffmpeg原理相关文章
ffmpeg源码分析相关文章
- ffmpeg学习日记501-源码-parse_loglevel()函数
- ffmpeg学习日记502-源码-ffmpeg_parse_options()函数分析
- ffmpeg学习日记503-源码-transcode()函数分析
- ffmpeg学习日记504-源码-readme汉化
- ffmpeg学习日记506-源码-av_image_copy()函数分析及功能
- ffmpeg学习日记508-源码-ffmpeg --help 汉化
- ffmpeg学习日记509-源码-从ffmpeg 源码提取编码的流程分析
- ffmpeg学习日记512-源码-ubuntu20.04下源码编译
- ffmpeg学习日记513-源码-configure_filtergraph()函数分析及功能
ffmpeg指令相关文章
- ffmpeg学习日记601-指令-视频裁剪,添加bgm合成mp4
- ffmpeg学习日记602-指令-转换视频的分辨率
- ffmpeg学习日记603-指令-获取视频分辨率
- ffmpeg学习日记604-指令-将视频格式转为H264格式
- ffmpeg学习日记605-指令-获取视频的总帧数
- ffmpeg学习日记606-指令-将视频转为全I帧
- ffmpeg学习日记607-指令-将mp4视频转yuv
- ffmpeg学习日记612-指令-转换视频格式
- ffmpeg学习日记612-指令-转换视频格式
- ffmpeg学习日记614-指令-获取文件时长
- ffmpeg学习日记619-指令-透明通道视频相关指令
ffmpeg报错相关文章
libass相关文章
- libass分析1-源码分析-起源-源码编译
- libass分析2-源码分析-示例程序test.c的源码分析
- libass分析3-源码分析-libass中的宏定义分析
- libass分析5-源码分析-ASS_Track结构体分析,ass文件数据是如何存放的
- libass分析6-源码分析-ASS_Renderer结构体分析,ass文件数据是如何存放的
- libass分析8-源码分析-libass处理event中{}的逻辑
c/c++相关文章
linux相关文章
后面都是一些废话,不用看,刷分的
推广一个AI学习网站
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。
中国软件行业倡议书
精简软件开发,电脑性能越来越好,打出的程序安装包越来越大,磁盘,内存越吃越多,这不是好现象,手机同理,大家觉得呢,欢迎发表看法,各抒己见。
手机app随意读取用户通讯录,就是流氓行为,即使有时候弹窗提示是否授权,选择了否,但是他其实还是悄悄读取你的通讯录,并且随便给你的通讯录好友发推广信息,这一点是非常反感的,并且也触犯了用户的权益,这不仅是流氓行为,更是违法行为,某软件就不说了。
作者有话说
个人简介:多年工作工程经验,擅长linux下软件开发,qt,ffmpeg音视频二次开发。
欢迎各位叨扰作者,如果有什么项目合作,创业合伙需要研发,网站推广,猎头服务,内推等等,尽管来联系,对于能挣钱的事,作者可是很感兴趣的哦。
关于内卷
劝大家一句,不要内卷,内卷只能害了别人,害了自己。