OpenGL(六)——坐标系统

目录

一、前言

二、空间系

2.1 局部空间

2.2 世界空间

2.3 观察空间

2.4裁剪空间

2.5 正射投影

2.6 透视投影

2.7 屏幕空间

 三、透视箱子

3.1 创建模型矩阵

3.2 创建观察矩阵

3.3 创建透视投影矩阵

3.4 修改顶点着色器

3.5 传递变换矩阵

四、旋转的箱子 

 五、好多箱子


一、前言

OpenGL显示的顶点在(-1,1)之间,这个坐标叫做标准化设别坐标(Normalized Device Coordinate, NDC)。将物体坐标变化到最终的NDC坐标需要几个过度坐标,在这些坐标系下计算非常容易和方便。过渡坐标分别在局部空间(物体空间)、世界空间、观察空间、裁剪空间、屏幕空间。

 物体坐标从一个坐标系变换到另一个坐标系需要用到几个变换矩阵,最重要的几个分别是模型(Model)观察(View)投影(Projection)三个矩阵。我们的顶点坐标起始于局部坐标(Local Coordinate),之后会变为世界坐标(World Coordinate),观察坐标(View Coordinate),裁剪坐标(Clip Coordinate),并以屏幕坐标(Screen Coordinate)的形式结束。

局部坐标:相对局部原点坐标,物体起始坐标,便于对物体本身进行修改

世界坐标:相对于世界全局原点,和其他物体一起相对于世界原点摆放,便于对一个物体的相对其它物体位置进行操作

观察空间坐标:相对于摄像机或者观察者的角度进行的

裁剪坐标:投影到裁剪空间,被处理至-1到1的范围,并判断哪些顶点出现在屏幕上

屏幕坐标:使用视口变换,将-1到1的坐标转换到glViewPort函数定义的坐标范围内,送给光栅器,转化为片段

二、空间系

2.1 局部空间

局部空间是指物体所在的坐标空间,模型都以(0,0,0)为初始位置,但是它们出现在世界的不同位置。模型的所有顶点都是在局部坐标系中,它们相对于你的物体来说都是局部的。

2.2 世界空间

将所有模型导入到程序中,每个模型物体都有自己的位置,需要定义世界空间原点,模型矩阵用来完成物体坐标从局部变换到世界空间。

模型矩阵:可以对物体进行位移、缩放、旋转它本应该在位置和朝向。

2.3 观察空间

观察空间也叫OpenGL摄像机,也称为摄像机空间、视觉空间。观察空间将世界空间坐标转化为用户视野前方坐标。

观察矩阵:世界空间变换到观察空间使用一系列位移、旋转等组合来完成。

2.4裁剪空间

在顶点着色器运行的最后,OpenGL期望所有坐标落在一个特定范围内(-1,1),范围外的点应该被剪掉(Clip)。屏幕上可见的片段叫做裁剪空间。

投影矩阵(projection Matrix):将指定的坐标集(coordinate set)并将它变化为标准化设备坐标系。投影矩阵可以将3D坐标投影到2D标准化设备坐标中。

由投影矩阵创建的观察箱viewing box被称为平截头体,每个出现在平截头体范围内的坐标最终出现在屏幕上。

一旦所有顶点被变换到裁剪空间,将会执行透视除法,将位置向量的x,y,z分量除以向量齐次w分量。透视除法是将4D裁剪空间坐标变换为3D标准化设备坐标的过程。

观察坐标转换为裁剪坐标投影矩阵有两种形式:正射投影矩阵、透视投影矩阵

2.5 正射投影

正射投影定义一个裁剪空间,空间之外的顶点都会被裁剪掉。创建正射投影需要指定空间的长宽高。下图定义了可见坐标,由宽、高、远近平面指定。该空间内部所有坐标映射为标准设备坐标NDC。

创建正射投影矩阵:

glm::ortho(0.0f, 800.0f, 0.0f, 600.0f, 0.1f, 100.0f);
参数1:裁剪空间左坐标
参数2:裁剪空间右坐标
参数3:裁剪空间底部坐标
参数4:裁剪空间顶部坐标
参数5:近平面距离
参数6:远平面距离

 正射投影矩阵直接将坐标映射到2D平面(屏幕上),实际上一个直接投影矩阵会产生不真实结果,因为没有将透视(perspective)考虑进去。因此正射投影主要用于二维渲染以及一些建筑或者工程的程序,这些场景顶点不会被透视干扰。

2.6 透视投影

透视:立体空间中,近大远小的效果。透视使得两条平行线在远距离会相交,这是透视投影矩阵要完成的效果。

 透视投影矩阵将给定的平截头体映射到裁剪空间,还会修改每个顶点坐标w值,从而使离观察者越远的顶点坐标w分量越大。被变换到裁剪空间的坐标都会在(-w,w)范围内(超出范围坐标被剪掉)。

OpenGL要求所有可见坐标落在(-1,1)范围内,作为顶点着色器输出,当坐标在裁剪空间后,进行透视除法操作,近大远小:

 

 

glm::mat4 proj = glm::perspective(glm::radians(45.0f), (float)width/(float)height, 0.1f, 100.0f);
参数1:fov值,视野,设置观察空间大小
参数2:设置高宽比,视口宽除以高所得
参数3:平截头体近平面距离,0.1
参数4:平截头体远平面距离,100

2.7 屏幕空间

裁剪空间后的坐标映射到屏幕空间(glViewPort设定),并变换为片段。

一个顶点坐标将会经过下面变化,到裁剪坐标,注意矩阵运算顺序是相反的,最后顶点被赋值到gl_Position,OpenGL会自动对其进行透视除法变换到标准化设备坐标。OpenGL使用glViewPort内部的参数将NDC映射到屏幕坐标,每个坐标关联一个屏幕点,该过程称为视口变换。

 三、透视箱子

3.1 创建模型矩阵

位移、缩放、旋转操作。该模型矩阵将应用到所有物体顶点上,来映射到全局世界坐标。变换平面,让其绕x轴旋转。(当前位于原点(0,0,0)

 顶点乘以该模型矩阵,将顶点坐标变换到世界坐标,看起来在地板上,表示全局世界的平面。

glm::mat4 model;
model = glm::rotate(model,glm::radians(-55.0f),glm::vec3(1.0,0.0,0.0));

3.2 创建观察矩阵

想要在场景里移动,可以将摄像机向后移动,或者将整个场景向前移动。以相反与摄像机移动的方向移动整个场景。OpenGL是右手坐标系,当我们要沿z轴正方向移动时,我们将场景沿z轴负方向平移来实现。

右手坐标系 大拇指指向正x轴方向,食指指向正y轴方向,中指指向正z轴方向(DirectX使用左手坐标系)注意在标准化设备坐标系中OpenGL实际上使用的是左手坐标系(投影矩阵交换了左右手)。

glm:mat4 view;
// 注意,我们将矩阵向我们要进行移动场景的反方向移动
view = glm::translate(view,glm::vec3(0.0,0.0,-3.0));

3.3 创建透视投影矩阵

glm::mat4 projection;
projection = glm::perspective(glm::radians(45.0f), screenWidth/screenHight,0.1,100.0);

3.4 修改顶点着色器

使用uniform声明变换矩阵,然后乘以顶点坐标:

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

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 = vec2(aTexCoord.x, aTexCoord.y);
}

3.5 传递变换矩阵

        //model matrix
        glm::mat4 modelMat = glm::mat4(1.0f);
        modelMat = glm::rotate(modelMat, glm::radians(-55.0f), glm::vec3(1.0, 0.0, 0.0));
        //view matrix
        glm::mat4 viewMat = glm::mat4(1.0f);
        viewMat = glm::translate(viewMat, glm::vec3(0.0f, 0.0f, -3.0f));
        //projection matrix
        glm::mat4 projectionMat = glm::mat4(1.0f);
        projectionMat = glm::perspective(glm::radians(45.0f), (float)(SCR_WIDTH / SCR_HEIGHT), 0.1f, 100.0f);
        //着色器引用
        ourShader.use();
        unsigned int modelLoc = glGetUniformLocation(ourShader.ID, "model");
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(modelMat));
        unsigned int viewlLoc = glGetUniformLocation(ourShader.ID, "view");
        glUniformMatrix4fv(viewlLoc, 1, GL_FALSE, glm::value_ptr(viewMat));
        unsigned int projectionlLoc = glGetUniformLocation(ourShader.ID, "projection");
        glUniformMatrix4fv(projectionlLoc, 1, GL_FALSE, glm::value_ptr(projectionMat));

四、旋转的箱子 

3D箱子顶点:

    float vertices[] = {
    -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
     0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
     0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
     0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
    -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
    -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,

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

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

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

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

    -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
     0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
     0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
     0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
    -0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
    -0.5f,  0.5f, -0.5f,  0.0f, 1.0f
    };

修改模型矩阵:

        glm::mat4 modelMat = glm::mat4(1.0f);
        //modelMat = glm::rotate(modelMat, glm::radians(-55.0f), glm::vec3(1.0, 0.0, 0.0));//model matrix
        modelMat = glm::rotate(modelMat, (float)glfwGetTime(), glm::vec3(0.5f, 1.0f, 0.0f));

解释VAO数据:

    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);

 立方体某些本应遮挡住的面被绘制在立方体其他面上。OpenGL是一个三角形一个三角形绘制立方体,即便有东西也会覆盖之前的像素。OpenGL存储深度信息在Z缓冲中,允许OpenGL是否去执行覆盖像素。通过Z缓冲来配置OpenGL深度测试

GLFW会自动生成一个深度缓冲,深度值存储在每个片段里面,当片段要输出它的颜色时,OpenGL将它的深度值与Z缓冲进行比较,如果当前片段在其他片段之后会被丢失,否则会被覆盖。

glEnable(GL_DEPTH_TEST);//glDisable关闭

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//清除深度缓冲

 五、好多箱子

要在屏幕上显示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)  
};

渲染主循环中循环绘制cubePositions

glBindVertexArray(VAO);
for(unsigned int i = 0; i < 10; i++)
{
  glm::mat4 model;
  model = glm::translate(model, cubePositions[i]);
  float angle = 20.0f * i; 
  model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
  ourShader.setMat4("model", model);

  glDrawArrays(GL_TRIANGLES, 0, 36);
}

 

#include <iostream>
#include <string>

#include "glad.h"
#include "GL/glfw3.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "Shader.h"
// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow* window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
}

// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    // make sure the viewport matches the new window dimensions; note that width and 
    // height will be significantly larger than specified on retina displays.
    glViewport(0, 0, width, height);
}

int main()
{
    // glfw: 初始化、配置
    // ------------------------------
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

#ifdef __APPLE__
    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);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
    // glad: 加载OpenGL相关函数指针
    // ---------------------------------------
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }
    // 构建、编译着色器
    // ------------------------------------
    Shader ourShader("vertexTexture.vs", "fragmentTexture.fms");
    // 顶点数据
    // ------------------------------------------------------------------
    /*float vertices[] = {
        // 位置                 // 纹理坐标
         0.5f,  0.5f, 0.0f,     1.0f, 1.0f, // top right
         0.5f, -0.5f, 0.0f,     1.0f, 0.0f, // bottom right
        -0.5f, -0.5f, 0.0f,     0.0f, 0.0f, // bottom left
        -0.5f,  0.5f, 0.0f,     0.0f, 1.0f  // top left 
    };
    unsigned int indices[] = {
        0, 1, 3, // 第一个三角形
        1, 2, 3  // 第二个三角形
    };*/
    float vertices[] = {
    -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
     0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
     0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
     0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
    -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
    -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,

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

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

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

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

    -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
     0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
     0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
     0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
    -0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
    -0.5f,  0.5f, -0.5f,  0.0f, 1.0f
    };
    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)
    };
    //创建顶点对象
    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);
    //创建纹理对象1
    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_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    //加载图片1到纹理数据
    int width, height, nrChannels;
    unsigned char* data = stbi_load(std::string("container.jpg").c_str(), &width, &height, &nrChannels, 0);
    if (data)
    {
        //加载数据,生成OpenGL纹理图
        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
    unsigned int texture2;
    // texture 2
// ---------
    glGenTextures(1, &texture2);
    glBindTexture(GL_TEXTURE_2D, texture2);
    // set the texture wrapping parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);	// set texture wrapping to GL_REPEAT (default wrapping method)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // set texture filtering parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // load image, create texture and generate mipmaps
    stbi_set_flip_vertically_on_load(true);
    data = stbi_load(std::string("awesomeface.png").c_str(), &width, &height, &nrChannels, 0);
    if (data)
    {
        // note that the awesomeface.png has transparency and thus an alpha channel, so make sure to tell OpenGL the data type is of GL_RGBA
        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);

    //OpenGL解释纹理单元的编号
    ourShader.use(); 
    ourShader.setInt("textur1", 0);
    ourShader.setInt("textur2", 1);
    glEnable(GL_DEPTH_TEST);
    //旋转90度,缩放0.5倍
    //glm::mat4 trans = glm::mat4(1.0f);
   // trans = glm::rotate(trans, glm::radians(90.0f), glm::vec3(0, 0, 1.0));
    //trans = glm::scale(trans, glm::vec3(0.5, 0.5, 0.5));

    //渲染
    while (!glfwWindowShouldClose(window))
    {
        processInput(window);

        //背景颜色
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
        //绑定对应的纹理
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texture1);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, texture2);
        //glm::mat4 modelMat = glm::mat4(1.0f);
        glm::mat4 viewMat = glm::mat4(1.0f);
        glm::mat4 projectionMat = glm::mat4(1.0f);
        //modelMat = glm::rotate(modelMat, glm::radians(-55.0f), glm::vec3(1.0, 0.0, 0.0));//model matrix
        //modelMat = glm::rotate(modelMat, (float)glfwGetTime(), glm::vec3(0.5f, 1.0f, 0.0f));
        viewMat = glm::translate(viewMat, glm::vec3(0.0f, 0.0f, -3.0f));//view matrix 
        projectionMat = glm::perspective(glm::radians(45.0f), (float)(SCR_WIDTH / SCR_HEIGHT), 0.1f, 100.0f);//projection matrix
        //着色器引用
        ourShader.use();
        //unsigned int modelLoc = glGetUniformLocation(ourShader.ID, "model");
        //glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(modelMat));
        unsigned int viewlLoc = glGetUniformLocation(ourShader.ID, "view");
        glUniformMatrix4fv(viewlLoc, 1, GL_FALSE, glm::value_ptr(viewMat));
        unsigned int projectionlLoc = glGetUniformLocation(ourShader.ID, "projection");
        glUniformMatrix4fv(projectionlLoc, 1, GL_FALSE, glm::value_ptr(projectionMat));
        //绘图
        glBindVertexArray(VAO);
        //glDrawArrays(GL_TRIANGLES, 0, 36);
        for (unsigned int i = 0; i < 10; i++)
        {
            glm::mat4 modelMat = glm::mat4(1.0f);
            modelMat = glm::translate(modelMat, cubePositions[i]);
            modelMat = glm::rotate(modelMat, (float)glfwGetTime(), glm::vec3(1.0f, 0.3f, 0.5f));
            ourShader.setMat4("model", modelMat);
            glDrawArrays(GL_TRIANGLES, 0, 36);
        }

        //glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
        /*
        * 
        void glDrawElements(	GLenum mode,
 	    GLsizei count,
 	    GLenum type,
 	    const GLvoid * indices);
        */
        //刷新
        glfwSwapBuffers(window);
        glfwPollEvents();
    }
    //释放缓存资源
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteBuffers(1, &EBO);
    //释放glfw资源
    glfwTerminate();

    return 0;
}

参考:

坐标系统 - LearnOpenGL CN (learnopengl-cn.github.io)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/17897.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[Gitops--10]微服务项目部署流水线编写

微服务项目部署流水线编写 1. 部署环境说明 序号管理地址作用1192.168.31.199GitLab2192.168.31.104Harbor3192.168.31.131kubesphere 1.1 GitLab 1.2 流水线 1.2.1 创建流水线 1.2.2 创建凭证 1.2.3 创建kubeconfig凭证 这里需要注意的是,config中如果使用的是域名,那么需…

BetaFlight统一硬件配置文件研读之serial命令

BetaFlight统一硬件配置文件研读之serial命令 1. 源由2. 代码分析3. 实例分析4. 配置情况5. 参考资料 统一硬件配置文件的设计是一种非常好的设计模式&#xff0c;可以将硬件和软件的工作进行解耦。 1. 源由 cli命令中serial是对UART串口的配置&#xff0c;通常情况下BetaFli…

Redo log详解

WAL&#xff08;Write-Ahead Logging&#xff09;机制 WAL 的全称是 Write-Ahead Logging&#xff0c;中文称预写式日志(日志先行)&#xff0c;是一种数据安全写入机制。就是先写日志&#xff0c;然后再写入磁盘&#xff0c;这样既能提高性能又可以保证数据的安全性。Mysql中的…

基于Python的PROSAIL模型介绍以及使用

1、介绍 PROSAIL是两种模型耦合得到的。 SAIL是冠层尺度的辐射传输模型&#xff0c;把冠层假设成是连续的且具有给定几何形状和密度的水平均匀分布的介质层&#xff0c;从而模拟入射辐射与均匀介质之间的相互作用&#xff0c;具体还是挺复杂的。而PROSPECT就是叶片尺度的辐射传…

Kafka知识概况

Kafka知识概况 Kafka简介Kafka 生产者Kafka BrokerKafka 消费者Kafka-Eagle 监控Kafka-Kraft 模式集成 SpringBoot Kafka简介 消息队列简介&#xff1a; 目 前企业中比较常见的消息队列产 品主 要有 Kafka、ActiveMQ 、RabbitMQ 、RocketMQ 等。在大数据场景主要采用 Kafka 作…

vmware虚拟机安装k8s(之前已经安装过docker)

1、安装开始 先执行&#xff1a;curl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | sudo apt-key add 再执行更改源&#xff1a;echo "deb https://mirrors.aliyun.com/kubernetes/apt kubernetes-xenial main" >> /etc/apt/sources.list …

redis分布式锁

文章目录 锁的种类一个靠谱分布式锁需要具备的条件和刚需独占性高可用防死锁不乱抢重入性 如何一步一步实现一个完备的分布式锁单机版加锁Redis分布式锁setnx宕机与过期 防死锁防止误删key的问题lua脚本保证原子性 hsetnx 可重入锁简单工厂模式RedisDistributeLockDistributed…

阿里云服务器购买教程(新手入门指南)

阿里云服务器ECS选购指南&#xff0c;阿里云百科分享2023阿里云服务器新手选择流程&#xff0c;选购云服务器有两个入口&#xff0c;一个是选择活动机&#xff0c;只需要选择云服务器地域、系统、带宽即可&#xff1b;另一个是在云服务器页面&#xff0c;自定义选择云服务器配置…

mac下用git客户端生成ssh秘钥并配置到souretree进行使用

一、使用git 生成 ssh 密钥 1、Mac 安装 git 客户端 打开终端&#xff0c;执行命令&#xff1a; $ brew install git2、执行命令 $ git config --global user.name "xxx" 你自己的名字 $ git config --global user.email "xxxxxx.com&q…

深度学习的环境搭建(window+pytorch)

1.检查是否安装CUDA CUDA&#xff08;Compute Unified Device Architecture&#xff09;是由 NVIDIA 推出的一种并行计算平台和编程模型&#xff0c;用于利用 NVIDIA GPU&#xff08;Graphics Processing Unit&#xff09;的强大计算能力进行高性能计算任务。CUDA 的主要特点是…

App违反Google数据安全政策,解决方案

前言 google隐私政策阶段性会进行更新&#xff0c;时长关注隐私政策变化&#xff0c;避免app被强制下架&#xff0c;影响后续运营工作。 邮件内容 摘录邮件详情 我们在以下区域发现了问题&#xff1a; SPLIT_BUNDLE 2:政策声明&#xff0c;数据安全部分&#xff1a;“https:…

大数据企业应用合作解决方案案例

打造产教融合的就业育人的综合服务平台&#xff0c;给予十余年的数据智能产业实践经验&#xff0c;专注于大数据和人工智能方向。 目前合作的企业案例包括&#xff1a;信访大数据平台解决方案、工业废水处理解决方案、找齐远程监控解决方案、道路运输安全、广电用户服务大数据解…

清洁赛道新势力,米博凭“减法”突围?

在五四青年节这个特殊的日子&#xff0c;方太旗下的高端智能清洁品牌“米博”发布了新一代无滚布洗地机7系列。 5月4日晚&#xff0c;米博以“减法生活&#xff0c;净请7代”为主题&#xff0c;举办了新品发布会。在发布会上&#xff0c;从小红书翻红的董洁作为方太集团米博产…

Windows命令提示行使用指南一

命令提示行使用指南 前言一、起源和发展二、和DOS的关系三、常用命令 前言 cmd 是 Windows 操作系统中的命令行界面&#xff08;CLI&#xff09;&#xff0c;也称为命令提示符&#xff08;CMD&#xff09;或批处理文件。它是 Windows 命令行界面的主要组成部分&#xff0c;用于…

ASK,FSK和PSK

一、ASK&#xff0c;FSK和PSK 数字信号只有有限个离散值&#xff0c;使用数字信号对载波进行调制的方式称为键控(Keying),分为幅度键控&#xff08;ASK)、频移键控&#xff08;FSK)和相移键控&#xff08;PSK)。 幅度键控可以通过乘法器和开关电路来实现&#xff0c;在数字信…

八、(了解即可)MyBatis懒加载(或者叫延迟加载)

文章目录 八、懒加载(了解即可)8.1 为啥需要懒加载?8.2 懒加载是什么&#xff1f;8.3 开启方式8.4 既然fetchType可以控制懒加载那么我仅仅配置fetchType不配置全局的可以吗&#xff1f;8.5 aggressiveLazyLoading是做什么么的&#xff1f;8.6 注意点8.7 案例验证懒加载准备工…

自学Python必须知道的优秀社区

国内学习Python网站&#xff1a; 知乎学习平台&#xff1a;Python - 基础入门 - 知学堂黑马程序员视频库&#xff1a;大数据学习路线2023版-黑马程序员大数据学习路线图菜鸟教程&#xff1a;菜鸟教程 - 学的不仅是技术&#xff0c;更是梦想&#xff01;极客学院&#xff1a;极…

进阶自动化测试,这3点你一定要知道的...

自动化测试指软件测试的自动化&#xff0c;在预设状态下运行应用程序或系统&#xff0c;预设条件包括正常和异常&#xff0c;最后评估运行结果。将人为驱动的测试行为转化为机器执行的过程。 自动化测试框架一般可以分为两个层次&#xff0c;上层是管理整个自动化测试的开发&a…

玩转服务器之Java Web篇:手把手教你搭建Java Web环境

前言 Java Web项目&#xff0c;简单来说就是我们在浏览器上可以看到的内容。就简单的Web来说&#xff0c;服务器上也同样需要有计算机上所需要的运行环境&#xff0c;如&#xff1a;java,tomcat,mysql 。Java Web环境可以用来开发和部署各种Web应用程序&#xff0c;例如网站、…

Redis基础

Redis基础 课程内容 Redis入门Redis数据类型Redis常用命令在Java中操作Redis 1. 前言 1.1 什么是Redis Redis是一个基于内存的key-value结构数据库。Redis 是互联网技术领域使用最为广泛的存储中间件&#xff0c;它是「Remote Dictionary Service」的首字母缩写&#xff0…
最新文章