openGL入门3、变换矩阵2D变换&坐标系统3D效果

学习网页:https://learnopenglcn.github.io/01%20Getting%20started/07%20Transformations/
主要是记录摘抄笔记

一、变换

1、基础

只记录一下一些特殊的,简单的可以去上面学习链接查看

1.1、向量相关

一个是点乘(Dot Product),记作v¯⋅k¯,
另一个是叉乘(Cross Product),记作v¯×k¯。
在这里插入图片描述
在这里插入图片描述

1.2、矩阵相关

矩阵相乘
在这里插入图片描述

1.3、矩阵与向量相乘实现的几个功能

1.3.1、单位矩阵

在这里插入图片描述

1.3.2、放大缩小

在这里插入图片描述

1.3.3、位移

在这里插入图片描述

1.3.4、旋转

在这里插入图片描述
在这里插入图片描述

1.3.5、矩阵相乘

要注意顺序

在这里插入图片描述

2、GLM实践

GLMgit地址:https://github.com/g-truc/glm/releases/tag/0.9.9.4

2.1、基础

2.1.1、位移矩阵定义

在这里插入图片描述

//获取位移矩阵的api
template<typename T, qualifier Q>
	GLM_FUNC_QUALIFIER mat<4, 4, T, Q> translate(mat<4, 4, T, Q> const& m, vec<3, T, Q> const& v)
	{
		mat<4, 4, T, Q> Result(m);
		Result[3] = m[0] * v[0] + m[1] * v[1] + m[2] * v[2] + m[3];
		return Result;
	}

我们把箱子逆时针旋转90度。然后缩放0.5倍,使它变成原来的一半大。

glm::mat4 trans;
GLM希望它的角度是弧度制的(Radian),所以我们使用glm::radians将角度转化为弧度。
注意有纹理的那面矩形是在XY平面上的,所以我们需要把它绕着z轴旋转。
//rotate 旋转
//trans 单位矩阵
//glm::radians(90.0f)90度
// glm::vec3(0.0, 0.0, 1.0)z轴
trans = glm::rotate(trans, glm::radians(90.0f), glm::vec3(0.0, 0.0, 1.0));沿z轴旋转90度

//scale 放缩
//每个轴的glm::vec3(0.5, 0.5, 0.5)
trans = glm::scale(trans, glm::vec3(0.5, 0.5, 0.5));每个轴都缩放到0.5倍

2.1.2、变换矩阵&着色器设置

在这里插入图片描述

2.2、实践

2.2.1、位移

在这里插入图片描述

2.2.2.、旋转放缩

在这里插入图片描述

2.2.3、位移并随时间旋转

尽管在代码中我们先位移再旋转,实际的变换却是先应用旋转再是位移的。
在这里插入图片描述

2.2.4、先旋转再位移

实践效果就是先位移再旋转;则箱子中心移动轨迹就是一个圆
在这里插入图片描述

2.2.5、代码

主代码

#include <iostream>
#include <glad/glad.h> 
#include <GLFW/glfw3.h>
#include "glm/glm/glm.hpp"
#include "glm/glm/gtc/matrix_transform.hpp"
#include "glm/glm/gtc/type_ptr.hpp"
#include "ShaderClass.h"
#include "stb_image.h"
using namespace std;
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

int main()
{
   


    /******************************1、 初始化opengl窗口*********************************************/
    //glfw 初始化和配置 
    glfwInit();//初始化GLFW
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//主版本号
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//子版本号
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//指定哪个配置文件配置上下文:GLFW我们使用的是核心模式
#ifdef __APPLE__//如果时mac os xp等系统则要进行配置一下才能生效
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

    //glfw创建窗口
    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
    if (window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);//GLFW将我们窗口的上下文设置为当前线程的主上下文
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
    
    //GLAD加载所有的opengl函数指针
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    /******************************2、 着色器编译编译链接 *********************************************/
    Shader ourShader("shader.vs", "shader.fs");

    /******************************3、 顶点属性缓存这些   *********************************************/

    //绘制两个相连的三角形
    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,   1.0f, 1.0f, 0.0f,   0.0f, 1.0f    // 左上
    };
    //索引数组
    unsigned int indices[] = {
        0, 1, 3, // first triangle
        1, 2, 3  // second triangle
    };
    unsigned int VBO, VAO, EBO;
    glGenVertexArrays(1, &VAO);//创建一个顶点数组 与顶点属性相绑定 
    glGenBuffers(1, &VBO);//创建顶点缓冲对象
    glGenBuffers(1, &EBO);

    glBindVertexArray(VAO);//绑定顶点数组 配置顶点属性

    glBindBuffer(GL_ARRAY_BUFFER, VBO);//将GL_ARRAY_BUFFER类型的缓存与VBO绑定
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//之前定义的顶点数据复制到缓冲的内存
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);//将GL_ELEMENT_ARRAY_BUFFER类型的缓冲与EBO绑定
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);//把索引复制到缓冲里

    /*
        0;指定我们要配置的顶点位置属性 就是顶点着色器里面location那个   
    */
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 *sizeof(float), (void *)0);//步长为8了
    glEnableVertexAttribArray(0);//以顶点属性位置值0作为参数,启用顶点属性
     /*
        1;指定我们要配置的顶点颜色属性 就是顶点着色器里面location那个  glVertexAttribPointer函数更新顶点格式
    */
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);
    /*
        2;指定我们要配置的顶点纹理属性 就是顶点着色器里面location那个  glVertexAttribPointer函数更新顶点格式
    */
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
    glEnableVertexAttribArray(2);
    glBindBuffer(GL_ARRAY_BUFFER, 0);//VBO 已经与顶点属性数组VAO进行绑定了 那么GL_ARRAY_BUFFER就可以解除绑定
    //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); //记住:不要在VAO激活时解除绑定EBO,因为绑定的元素缓冲区对象存储在VAO中;保持EBO绑定。
    glBindVertexArray(0);//您可以在之后解除绑定VAO,这样其他VAO调用就不会意外地修改这个VAO

    // 加载创建纹理
    unsigned int texture, texture2;
    glGenTextures(1, &texture);//生成纹理对象,同样也可以创建一个数组的纹理
    glBindTexture(GL_TEXTURE_2D, texture); //绑定它,让之后任何的纹理指令都可以配置当前绑定的纹理:
    //为当前绑定的纹理对象设置环绕、过滤方式
    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_MIPMAP_LINEAR);//缩小时采用在两个邻近的多级渐远纹理之间使用线性插值,并使用线性插值进行采样
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);//线性
    // 加载图像
    int width, height, nrChannels;
    unsigned char* data = stbi_load("container.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);//释放图像的内存

    //纹理2
    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("awesomeface.png", &width, &height, &nrChannels, 0);
    if (data)
    {
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);
    }
    else
    {
        std::cout << "Failed to load texture" << std::endl;
    }
    stbi_image_free(data);

    ourShader.use();
    glUniform1i(glGetUniformLocation(ourShader.ID, "texture1"), 0);//绑定这个着色器的第0个纹理
    ourShader.setInt("texture2", 1);//绑定这个着色器的第0个纹理

    //位移
    //glm::vec4 vec(1.0f, 0.0f, 0.0f, 1.0f);
     译注:下面就是矩阵初始化的一个例子,如果使用的是0.9.9及以上版本
     下面这行代码就需要改为:
     glm::mat4 trans = glm::mat4(1.0f)
     之后将不再进行提示
    //glm::mat4 trans;
    //trans = glm::translate(trans, glm::vec3(1.0f, 1.0f, 0.0f));
    //vec = trans * vec;
    //std::cout << vec.x << vec.y << vec.z << std::endl;

   /* //旋转
   glm::mat4 trans  = glm::mat4(1.0f);
    trans = glm::rotate(trans, glm::radians(90.0f), glm::vec3(0.0, 0.0, 1.0));
    trans = glm::scale(trans, glm::vec3(0.5, 0.5, 0.5));*/
    //渲染循环
    //程序在我们主动关闭它之前不断绘制图像并能够接受用户输入 GLFW退出前一直保持运行
    while (!glfwWindowShouldClose(window))//检查一次GLFW是否被要求退出
    {
        //可接收键盘输入esc从而退出
        processInput(window);

        glClearColor(0.2f, 0.3f, 0.3f, 1.0f); //设置状态函数
        glClear(GL_COLOR_BUFFER_BIT);//使用状态函数

        //位移+随时间旋转
        glm::mat4 trans = glm::mat4(1.0f);
        trans = glm::translate(trans, glm::vec3(0.5f, -0.5f, 0.0f));//位移
        trans = glm::rotate(trans, (float)glfwGetTime(), glm::vec3(0.0f, 0.0f, 1.0f));//旋转

        //传到顶点着色器进行设置
        unsigned int transformLoc = glGetUniformLocation(ourShader.ID, "transform");
        glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));

        //激活纹理进行绑定
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texture);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, texture2);        
        ourShader.use();
       // glUseProgram(shaderProgram);//使用着色器程序
        
        glBindVertexArray(VAO);//绑定顶点数组  就是使用顶点属性
        //glDrawArrays(GL_TRIANGLES, 0, 3);
        //配置OpenGL如何绘制图元。第一个参数表示我们打算将其应用到所有的三角形的正面和背面,第二个参数告诉我们用线来绘制
        //注意如果进行设置glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) 那么就会一直是线框模式
        //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
        //在调用glDrawElements之前绑定纹理了,它会自动把纹理赋值给片段着色器的采样器:
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);//使用当前绑定的索引缓冲对象中的索引进行绘制:

        glfwSwapBuffers(window);//交换颜色缓冲(它是一个储存着GLFW窗口每一个像素颜色值的大缓冲),它在这一迭代中被用来绘制,并且将会作为输出显示在屏幕上。
        glfwPollEvents();//检查有没有触发什么事件(比如键盘输入、鼠标移动等)、更新窗口状态,并调用对应的回调函数
    }
    
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    ourShader.dele();

    //释放/删除之前的分配的所有资源
    glfwTerminate();

	return 0;
}
void processInput(GLFWwindow* window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)//会返回这个按键是否正在被按下
        glfwSetWindowShouldClose(window, true);//把WindowShouldClose属性设置为 true的方法关闭GLFW
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    //左上角坐标xy和宽高
    glViewport(0, 0, width, height);//OpenGL的显示试图
}

顶点着色器

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord;
uniform mat4 transform;

out vec3 ourColor;
out vec2 TexCoord;

void main()
{
    gl_Position = transform * vec4(aPos, 1.0);
    ourColor = aColor;
    TexCoord = aTexCoord;
}

片段着色器

#version 330 core
out vec4 FragColor;

in vec3 ourColor;
in vec2 TexCoord;

//在调用glDrawElements之前绑定纹理了,它会自动把纹理赋值给片段着色器的采样器:
uniform sampler2D ourTexture;
//sampler2D就是表示纹理单元类型的 是uniform卡类型
uniform sampler2D texture1;
uniform sampler2D texture2;
void main()
{
//ourTexture  采样器
//TexCoord  由顶点坐标那边传来的纹理坐标
//最后输出还可以把颜色也叠加上去
    //FragColor = texture(ourTexture, TexCoord) * vec4(ourColor, 1.0);;
	FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.2) * vec4(ourColor, 1.0);
}

2.2.6、绘制第二个箱子进行放缩

在这里插入图片描述


        //第二个
        trans = glm::mat4(1.0f); // reset it to identity matrix
        trans = glm::translate(trans, glm::vec3(-0.5f, 0.5f, 0.0f));//移动
        float scaleAmount = sin(glfwGetTime());
        trans = glm::scale(trans, glm::vec3(scaleAmount, scaleAmount, scaleAmount));//放缩
       // glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));
        glUniformMatrix4fv(transformLoc, 1, GL_FALSE, &trans[0][0]);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

二、坐标系统

1、坐标的转换

OpenGL希望在每次顶点着色器运行后,我们可见的所有顶点都为标准化设备坐标(Normalized Device Coordinate, NDC)。也就是说,每个顶点的x,y,z坐标都应该在**-1.0到1.0之间**,超出这个坐标范围的顶点都将不可见。
坐标变换为标准化设备坐标,接着再转化为屏幕坐标的过程通常是分步进行的,也就是类似于流水线那样子。
在这里插入图片描述
正射投影:
就是相当于平行投影
实现

glm::ortho(0.0f, 800.0f, 0.0f, 600.0f, 0.1f, 100.0f);

透视投影:
就是相当于越远越小的视觉投影
实现

glm::mat4 proj = glm::perspective(glm::radians(45.0f), (float)width/(float)height, 0.1f, 100.0f);
它的第一个参数定义了fov的值,它表示的是视野(Field of View),并且设置了观察空间的大小。
如果想要一个真实的观察效果,它的值通常设置为45.0f,但想要一个末日风格的结果你可以将其设置一个更大的值。
第二个参数设置了宽高比,由视口的宽除以高所得。
第三和第四个参数设置了平截头体的近和远平面。
我们通常设置近距离为0.1f,而远距离设为100.0f。所有在近平面和远平面内且处于平截头体内的顶点都会被渲染。
当你把透视矩阵的 near 值设置太大时(如10.0f),OpenGL会将靠近摄像机的坐标(在0.0f和10.0f之间)都裁剪掉、
这会导致一个你在游戏中很熟悉的视觉效果:
在太过靠近一个物体的时候你的视线会直接穿过去。

详情可以查看学习链接
https://learnopengl-cn.github.io/01%20Getting%20started/08%20Coordinate%20Systems/

最后坐标实现的过程就是把这些操作整合到一起
在这里插入图片描述

2、实践流程

2.1、api步骤流程

2.1.1、创建模型矩阵:顶点坐标->世界坐标

在这里插入图片描述

2.1.2、创建观察矩阵:世界坐标->观察坐标

在这里插入图片描述

2.1.3、创建投影矩阵:观察坐标->裁剪坐标

在这里插入图片描述

2.1.4、将变换矩阵设置到顶点着色器当中

在这里插入图片描述
2.1.5、效果图
在这里插入图片描述

2.2、绘制3D立体并旋转_36个顶点

在这里插入图片描述
顶点着色器

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord;
uniform mat4 transform;

out vec3 ourColor;
out vec2 TexCoord;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
    gl_Position = projection * view * model * vec4(aPos, 1.0);
    ourColor = aColor;
    TexCoord = aTexCoord;
}

片段着色器

#version 330 core
out vec4 FragColor;

in vec3 ourColor;
in vec2 TexCoord;

//在调用glDrawElements之前绑定纹理了,它会自动把纹理赋值给片段着色器的采样器:
uniform sampler2D ourTexture;
//sampler2D就是表示纹理单元类型的 是uniform卡类型
uniform sampler2D texture1;
uniform sampler2D texture2;
void main()
{
//ourTexture  采样器
//TexCoord  由顶点坐标那边传来的纹理坐标
//最后输出还可以把颜色也叠加上去
    //FragColor = texture(ourTexture, TexCoord) * vec4(ourColor, 1.0);;
	FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.2) * vec4(ourColor, 1.0);
}

主代码

#include <iostream>
#include <glad/glad.h> 
#include <GLFW/glfw3.h>
#include "glm/glm/glm.hpp"
#include "glm/glm/gtc/matrix_transform.hpp"
#include "glm/glm/gtc/type_ptr.hpp"
#include "ShaderClass.h"
#include "stb_image.h"
using namespace std;
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

int main()
{
    /******************************1、 初始化opengl窗口*********************************************/
    //glfw 初始化和配置 
    glfwInit();//初始化GLFW
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//主版本号
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//子版本号
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//指定哪个配置文件配置上下文:GLFW我们使用的是核心模式
#ifdef __APPLE__//如果时mac os xp等系统则要进行配置一下才能生效
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

    //glfw创建窗口
    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
    if (window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);//GLFW将我们窗口的上下文设置为当前线程的主上下文
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
    
    //GLAD加载所有的opengl函数指针
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    /******************************2、 着色器编译编译链接 *********************************************/
    Shader ourShader("shader.vs", "shader.fs");

    /******************************3、 顶点属性缓存这些   *********************************************/

    //绘制两个相连的三角形
    //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,   1.0f, 1.0f, 0.0f,   0.0f, 1.0f    // 左上
    //};
    float vertices[] = {
    //-- 位置 ----       ---- 颜色 ----     - 纹理坐标 -
    -0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
     0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
     0.5f,  0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
     0.5f,  0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
    -0.5f,  0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
    -0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,

    -0.5f, -0.5f,  0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
     0.5f, -0.5f,  0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
     0.5f,  0.5f,  0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
     0.5f,  0.5f,  0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
    -0.5f,  0.5f,  0.5f,1.0f, 0.0f, 0.0f,  0.0f, 1.0f,
    -0.5f, -0.5f,  0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,

    -0.5f,  0.5f,  0.5f,1.0f, 0.0f, 0.0f,  1.0f, 0.0f,
    -0.5f,  0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
    -0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
    -0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
    -0.5f, -0.5f,  0.5f,1.0f, 0.0f, 0.0f,  0.0f, 0.0f,
    -0.5f,  0.5f,  0.5f,1.0f, 0.0f, 0.0f,  1.0f, 0.0f,

     0.5f,  0.5f,  0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
     0.5f,  0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
     0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
     0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
     0.5f, -0.5f,  0.5f,1.0f, 0.0f, 0.0f,  0.0f, 0.0f,
     0.5f,  0.5f,  0.5f,1.0f, 0.0f, 0.0f,  1.0f, 0.0f,

    -0.5f, -0.5f, -0.5f,1.0f, 0.0f, 0.0f,  0.0f, 1.0f,
     0.5f, -0.5f, -0.5f,1.0f, 0.0f, 0.0f,  1.0f, 1.0f,
     0.5f, -0.5f,  0.5f,1.0f, 0.0f, 0.0f,  1.0f, 0.0f,
     0.5f, -0.5f,  0.5f,1.0f, 0.0f, 0.0f,  1.0f, 0.0f,
    -0.5f, -0.5f,  0.5f,1.0f, 0.0f, 0.0f,  0.0f, 0.0f,
    -0.5f, -0.5f, -0.5f,1.0f, 0.0f, 0.0f,  0.0f, 1.0f,

    -0.5f,  0.5f, -0.5f,1.0f, 0.0f, 0.0f,  0.0f, 1.0f,
     0.5f,  0.5f, -0.5f,1.0f, 0.0f, 0.0f,  1.0f, 1.0f,
     0.5f,  0.5f,  0.5f,1.0f, 0.0f, 0.0f,  1.0f, 0.0f,
     0.5f,  0.5f,  0.5f,1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
    -0.5f,  0.5f,  0.5f,1.0f, 0.0f, 0.0f,  0.0f, 0.0f,
    -0.5f,  0.5f, -0.5f,1.0f, 0.0f, 0.0f,  0.0f, 1.0f
    };
    //索引数组
    unsigned int indices[] = {
        0, 1, 3, // first triangle
        1, 2, 3  // second triangle
    };
    unsigned int VBO, VAO, EBO;
    glGenVertexArrays(1, &VAO);//创建一个顶点数组 与顶点属性相绑定 
    glGenBuffers(1, &VBO);//创建顶点缓冲对象
    glGenBuffers(1, &EBO);

    glBindVertexArray(VAO);//绑定顶点数组 配置顶点属性

    glBindBuffer(GL_ARRAY_BUFFER, VBO);//将GL_ARRAY_BUFFER类型的缓存与VBO绑定
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//之前定义的顶点数据复制到缓冲的内存
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);//将GL_ELEMENT_ARRAY_BUFFER类型的缓冲与EBO绑定
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);//把索引复制到缓冲里

    /*
        0;指定我们要配置的顶点位置属性 就是顶点着色器里面location那个   
    */
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 *sizeof(float), (void *)0);//步长为8了
    glEnableVertexAttribArray(0);//以顶点属性位置值0作为参数,启用顶点属性
     /*
        1;指定我们要配置的顶点颜色属性 就是顶点着色器里面location那个  glVertexAttribPointer函数更新顶点格式
    */
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);
    /*
        2;指定我们要配置的顶点纹理属性 就是顶点着色器里面location那个  glVertexAttribPointer函数更新顶点格式
    */
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
    glEnableVertexAttribArray(2);
    glBindBuffer(GL_ARRAY_BUFFER, 0);//VBO 已经与顶点属性数组VAO进行绑定了 那么GL_ARRAY_BUFFER就可以解除绑定
    //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); //记住:不要在VAO激活时解除绑定EBO,因为绑定的元素缓冲区对象存储在VAO中;保持EBO绑定。
    glBindVertexArray(0);//您可以在之后解除绑定VAO,这样其他VAO调用就不会意外地修改这个VAO

    // 加载创建纹理
    unsigned int texture, texture2;
    glGenTextures(1, &texture);//生成纹理对象,同样也可以创建一个数组的纹理
    glBindTexture(GL_TEXTURE_2D, texture); //绑定它,让之后任何的纹理指令都可以配置当前绑定的纹理:
    //为当前绑定的纹理对象设置环绕、过滤方式
    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_MIPMAP_LINEAR);//缩小时采用在两个邻近的多级渐远纹理之间使用线性插值,并使用线性插值进行采样
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);//线性
    // 加载图像
    int width, height, nrChannels;
    unsigned char* data = stbi_load("container.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);//释放图像的内存

    //纹理2
    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("awesomeface.png", &width, &height, &nrChannels, 0);
    if (data)
    {
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);
    }
    else
    {
        std::cout << "Failed to load texture" << std::endl;
    }
    stbi_image_free(data);

    ourShader.use();
    glUniform1i(glGetUniformLocation(ourShader.ID, "texture1"), 0);//绑定这个着色器的第0个纹理
    ourShader.setInt("texture2", 1);//绑定这个着色器的第0个纹理


    //启动Z缓冲 
    glEnable(GL_DEPTH_TEST);
    //渲染循环
    //程序在我们主动关闭它之前不断绘制图像并能够接受用户输入 GLFW退出前一直保持运行
    while (!glfwWindowShouldClose(window))//检查一次GLFW是否被要求退出
    {
        //可接收键盘输入esc从而退出
        processInput(window);
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f); //设置状态函数
        //glClear(GL_COLOR_BUFFER_BIT);//使用状态函数
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//要在每次渲染迭代之前清除深度缓冲(否则前一帧的深度信息仍然保存在缓冲中
        //定义变化矩阵
        //x轴55度旋转
        glm::mat4 model = glm::mat4(1.0f);
        //model = glm::rotate(model, glm::radians(-55.0f), glm::vec3(1.0f, 0.0f, 0.0f));
         model = glm::rotate(model, (float)glfwGetTime() * glm::radians(50.0f), glm::vec3(0.5f, 1.0f, 0.0f));//随时间旋转
        //z轴负方向3个单位
        glm::mat4 view = glm::mat4(1.0f);
        view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f));
        //透视投影
        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 modelLoc = glGetUniformLocation(ourShader.ID, "model");
        unsigned int viewLoc = glGetUniformLocation(ourShader.ID, "view");
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, &view[0][0]);
        // 目前我们在每一帧都设置了投影矩阵,但由于投影矩阵很少改变,所以最好只在主循环之外设置一次。
        ourShader.setMat4("projection", projection);
        //激活纹理进行绑定
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texture);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, texture2);        
        ourShader.use();
        glBindVertexArray(VAO);//绑定顶点数组  就是使用顶点属性
       // glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);//使用当前绑定的索引缓冲对象中的索引进行绘制:
        glDrawArrays(GL_TRIANGLES, 0, 36);//绘制36个顶点

        glfwSwapBuffers(window);//交换颜色缓冲(它是一个储存着GLFW窗口每一个像素颜色值的大缓冲),它在这一迭代中被用来绘制,并且将会作为输出显示在屏幕上。
        glfwPollEvents();//检查有没有触发什么事件(比如键盘输入、鼠标移动等)、更新窗口状态,并调用对应的回调函数
    }
    
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    ourShader.dele();

    //释放/删除之前的分配的所有资源
    glfwTerminate();

	return 0;
}
void processInput(GLFWwindow* window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)//会返回这个按键是否正在被按下
        glfwSetWindowShouldClose(window, true);//把WindowShouldClose属性设置为 true的方法关闭GLFW
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    //左上角坐标xy和宽高
    glViewport(0, 0, width, height);//OpenGL的显示试图
}

2.3、绘制多个3D立体箱子

现在我们想在屏幕上显示10个立方体。每个立方体看起来都是一样的,区别在于它们在世界的位置及旋转角度不同。立方体的图形布局已经定义好了,所以当渲染更多物体的时候我们不需要改变我们的缓冲数组和属性数组,我们唯一需要做的只是改变每个对象的模型矩阵来将立方体变换到世界坐标系中。

让我们为每个立方体定义一个位移向量来指定它在世界空间的位置。

一个glm::vec3数组中定义10个立方体位置:
glm::vec3 cubePositions[] = {
  glm::vec3( 0.0f,  0.0f,  0.0f), 
  glm::vec3( 2.0f,  5.0f, -15.0f), 
  glm::vec3(-1.5f, -2.2f, -2.5f),  
  glm::vec3(-3.8f, -2.0f, -12.3f),  
  glm::vec3( 2.4f, -0.4f, -3.5f),  
  glm::vec3(-1.7f,  3.0f, -7.5f),  
  glm::vec3( 1.3f, -2.0f, -2.5f),  
  glm::vec3( 1.5f,  2.0f, -2.5f), 
  glm::vec3( 1.5f,  0.2f, -1.5f), 
  glm::vec3(-1.3f,  1.0f, -1.5f)  
};

在渲染循环中,我们调用glDrawArrays 10次,但这次在我们渲染之前每次传入一个不同的模型矩阵到顶点着色器中。我们将会在游戏循环中创建一个小的循环用不同的模型矩阵渲染我们的物体10次。注意我们也对每个箱子加了一点旋转:

//激活纹理进行绑定
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texture);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, texture2);     
        //着色器使用
        ourShader.use();
        //绑定顶点数组  就是使用顶点属性
        glBindVertexArray(VAO);
        //进行绘制
        for (unsigned int i = 0; i < 10; i++)
        {
            glm::mat4 model = glm::mat4(1.0f);
            model = glm::translate(model, cubePositions[i]);
            float angle = 20.0f * (i+1);
            model = glm::rotate(model, (float)glfwGetTime() * glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
            ourShader.setMat4("model", model);

            glDrawArrays(GL_TRIANGLES, 0, 36);
        }

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
OpenGL中,将2D点转换为3D点可以通过以下步骤实现: 1. 设置投影矩阵为透视投影矩阵。这可以通过使用gluPerspective函数来完成,该函数需要指定视野角度、屏幕宽高比、近剪切面和远剪切面。 2. 在2D屏幕坐标系中,将点的Z坐标设置为0。 3. 使用gluUnProject函数将2D点转换为3D点。该函数需要指定2D点的坐标、深度值和视图矩阵。 4. 将3D点与观察点和目标点之间的线段相交。这可以通过使用gluUnProject函数来完成,该函数需要指定观察点和目标点的坐标和视图矩阵。 以下是一个OpenGL转换2D点为3D点的示例代码: ```c++ // 2D point coordinates float x = 100; float y = 200; // Depth value of the point float depth = 0; // Viewport dimensions int viewport[4] = {0, 0, screenWidth, screenHeight}; // Modelview matrix glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // Projection matrix glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0, (float)screenWidth / (float)screenHeight, 0.1, 100.0); // Convert 2D point to 3D point GLdouble point3D_x, point3D_y, point3D_z; gluUnProject(x, screenHeight - y, depth, modelviewMatrix, projectionMatrix, viewport, &point3D_x, &point3D_y, &point3D_z); // Calculate intersection point with the line between the eye and target GLdouble eye_x = 0, eye_y = 0, eye_z = 0; GLdouble target_x = 0, target_y = 0, target_z = -1; GLdouble intersection_x, intersection_y, intersection_z; gluUnProject(x, screenHeight - y, 1.0, modelviewMatrix, projectionMatrix, viewport, &intersection_x, &intersection_y, &intersection_z); gluUnProject(x, screenHeight - y, 0.0, modelviewMatrix, projectionMatrix, viewport, &eye_x, &eye_y, &eye_z); GLdouble line_x = intersection_x - eye_x; GLdouble line_y = intersection_y - eye_y; GLdouble line_z = intersection_z - eye_z; GLdouble t = -eye_z / line_z; point3D_x = eye_x + t * line_x; point3D_y = eye_y + t * line_y; point3D_z = eye_z + t * line_z; ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值