文章目录

  • 1.摄像机坐标系
  • 2.Look At 矩阵:
  • 3.控制摄像机移动
  • 4.控制移动速度
  • 5.控制视角
  • 6.鼠标控制角度
  • 7.缩放
  • 8.源码

参考:https://learnopengl-cn.github.io/01%20Getting%20started/09%20Camera/

1.摄像机坐标系

OpenGL本身没有摄像机(Camera)的概念,但我们可以通过把场景中的所有物体往相反方向移动的方式来模拟出摄像机的移动,产生一种 我们在移动,而不是场景在移动 的感觉

要定义一个摄像机,我们需要它在世界空间中的位置观察的方向一个指向它右测的向量以及一个指向它上方的向量,我们实际上创建了一个三个单位轴相互垂直的、以摄像机的位置为原点的坐标系

  • 摄像机的方向,这里指的是摄像机指向哪个方向,指的是摄像机指向哪个方向: 用场景原点向量减去摄像机位置向量的结果就是摄像机的指向向量。由于我们知道摄像机指向z轴负方向,但我们希望方向向量(Direction Vector)指向摄像机的z轴正方向。如果我们交换相减的顺序,我们就会获得一个指向摄像机正z轴方向的向量

  • 另一个向量是一个右向量(Right Vector),它代表摄像机空间的x轴的正方向.先定义一个上向量(Up Vector)。接下来把上向量和第二步得到的方向向量进行叉乘。两个向量叉乘的结果会同时垂直于两向量,因此我们会得到指向x轴正方向的那个向量(如果我们交换两个向量叉乘的顺序就会得到相反的指向x轴负方向的向量)

  • 右向量和方向向量进行叉乘就可以得到一个向上的向量

  • glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);glm::vec3 cameraTarget = glm::vec3(0.0f, 0.0f, 0.0f);
    // 摄像机的Z轴正方向,Z轴的正方向是从屏幕指向我们的
    glm::vec3 cameraDirection = glm::normalize(cameraPos - cameraTarget);glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f);
    // 指向x轴正方向
    glm::vec3 cameraRight = glm::normalize(glm::cross(up, cameraDirection));// 指向y轴正方向
    glm::vec3 cameraUp = glm::cross(cameraDirection, cameraRight);
    

2.Look At 矩阵:

使用3个相互垂直(或非线性)的轴定义了一个坐标空间,你可以用这3个轴外加一个平移向量来创建一个矩阵,并且你可以用这个矩阵乘以任何向量来将其变换到那个坐标空间。这正是LookAt矩阵所做的,LookAt可以看做是摄像机。

GLM已经提供了这些支持。我们要做的只是定义一个摄像机位置,一个目标位置和一个表示世界空间中的上向量的向量(我们计算右向量使用的那个上向量)。接着GLM就会创建一个LookAt矩阵,我们可以把它当作我们的观察矩阵

glm::mat4 view;
view = glm::lookAt(glm::vec3(0.0f, 0.0f, 3.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));

当我们设置好摄像机之后,就要响应键盘鼠标,以达到在空间中移动视角


3.控制摄像机移动

同过定义三个变量,来控制LookAt矩阵,达到控制摄像机

// 相机位置
glm::vec3 cameraPos   = glm::vec3(0.0f, 0.0f,  3.0f);
// 相机指向物体的向量
glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
// 相机向上的向量
glm::vec3 cameraUp    = glm::vec3(0.0f, 1.0f,  0.0f);
// 世界空间:        相机位置          目标位置          向上向量
view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);

通过键盘就可以控制方向:(按下WASD键的任意一个,摄像机的位置都会相应更新)

void processInput(GLFWwindow *window)
{float cameraSpeed = 0.05f; // adjust accordinglyif (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)cameraPos += cameraSpeed * cameraFront;if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)cameraPos -= cameraSpeed * cameraFront;if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
}

4.控制移动速度

根据处理器的能力不同,每秒绘制的帧数也就不一样,所以要控制。

图形程序和游戏通常会跟踪一个时间差(Deltatime)变量,它储存了渲染上一帧所用的时间。我们把所有速度都去乘以deltaTime值。结果就是,如果我们的deltaTime很大,就意味着上一帧的渲染花费了更多时间,所以这一帧的速度需要变得更高来平衡渲染所花去的时间。

// 两个全局变量来计算出deltaTime值:while循环外
float deltaTime = 0.0f; // 当前帧与上一帧的时间差
float lastFrame = 0.0f; // 上一帧的时间// 在每一帧中我们计算出新的deltaTime:while循环内
float currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;// 计算速度:processInput函数内
float cameraSpeed = 2.5f * deltaTime;

5.控制视角

对于我们的摄像机系统来说,我们只关心俯仰角和偏航角:

  • 俯仰角(Pitch):上下看
  • 偏航角(Yaw):左右看
  • 给定一个俯仰角和偏航角,可以把它们转换为一个代表新的方向向量的3D向量
  • // 根据角,转化成空间方向向量
    direction.x = cos(glm::radians(pitch)) * cos(glm::radians(yaw)); // 译注:direction代表摄像机的前轴(Front),这个前轴是和本文第一幅图片的第二个摄像机的方向向量是相反的
    direction.y = sin(glm::radians(pitch));
    direction.z = cos(glm::radians(pitch)) * sin(glm::radians(yaw));
    

6.鼠标控制角度

鼠标水平的移动影响偏航角,竖直的移动影响俯仰角

原理就是,储存上一帧鼠标的位置,在当前帧中我们当前计算鼠标位置与上一帧的位置相差多少。如果水平/竖直差别越大那么俯仰角或偏航角就改变越大,也就是摄像机需要移动更多的距离。

首先我们要告诉GLFW,它应该隐藏光标,并捕捉(Capture)它:glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
接着设置鼠标回调函数:glfwSetCursorPosCallback(window, mouse_callback);

// 设置鼠标位置初始值,初始值设置为屏幕的中心
float lastX = 400, lastY = 300;// 计算鼠标距上一帧的偏移量。
// 在void mouse_callback(GLFWwindow* window, double xpos, double ypos)函数中
float xoffset = xpos - lastX;
float yoffset = lastY - ypos; // 注意这里是相反的,因为y坐标是从底部往顶部依次增大的
lastX = xpos;
lastY = ypos;// 设置sensitivity(灵敏度)值,防止偏移过大
float sensitivity = 0.05f;
xoffset *= sensitivity;
yoffset *= sensitivity;// 把偏移量添加到摄像机的俯仰角和偏航角中。
yaw   += xoffset;
pitch += yoffset;// 对偏航角和俯仰角进行最大和最小值的限制。
if(pitch > 89.0f)pitch =  89.0f;
if(pitch < -89.0f)pitch = -89.0f;// 计算方向向量。
glm::vec3 front;
front.x = cos(glm::radians(pitch)) * cos(glm::radians(yaw));
front.y = sin(glm::radians(pitch));
front.z = cos(glm::radians(pitch)) * sin(glm::radians(yaw));
cameraFront = glm::normalize(front);

此外设置一个bool值,判别是否鼠标第一次进入屏幕

if(firstMouse) // 这个bool变量初始时是设定为true的
{lastX = xpos;lastY = ypos;firstMouse = false;
}

7.缩放

通过鼠标滚轮来设置缩放

鼠标滚轮回调函数:glfwSetScrollCallback(window, scroll_callback);

// yoffset值代表我们竖直滚动的大小
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
// fov是全局变量 if(fov >= 1.0f && fov <= 45.0f)fov -= yoffset;if(fov <= 1.0f)fov = 1.0f;if(fov >= 45.0f)fov = 45.0f;
}

最后将改变传到GPU:projection = glm::perspective(glm::radians(fov), 800.0f / 600.0f, 0.1f, 100.0f);


8.源码

// shader.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.0);TexCoord = aTexCoord;
}// shader.fs
#version 330 core
out vec4 FragColor;in vec2 TexCoord;
uniform sampler2D ourTexture0;
uniform sampler2D ourTexture1;void main()
{FragColor = mix(texture(ourTexture0, TexCoord), texture(ourTexture1, TexCoord), 0.4f);
}// main.cpp
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include "shader.h"
#include "stb_image.h"// 回调函数
void processInput(GLFWwindow* window);
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);// 宽高
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;// 设置鼠标位置初始值,初始值设置为屏幕的中心
float lastX = 400, lastY = 300;// 控制相机位置
glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);    // 相机位置
glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f); // 相机指向物体的向量
glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);     // 相机向上的向量// 相关控制变量
bool firstMouse = true;
float yaw = -90.0f;
float pitch = 0.0f;
float fov = 45.0f;// 两个全局变量来计算出deltaTime值:控制相机移动速度
float deltaTime = 0.0f;  // 当前帧与上一帧的时间差
float lastFrame = 0.0f;  // 上一帧的时间int main()
{// 初始化和配置 glfwglfwInit();glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);// 创建glfw窗口GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "Hello world", NULL, NULL);if (window == NULL){std::cout << "Failed to create GLFW window" << std::endl;glfwTerminate();return -1;}glfwMakeContextCurrent(window);// 使用glad加载OpenGL函数指针if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout << "Failed to inittalize GLAD" << std::endl;return -1;}// 确定视口(Viewport)的大小glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT);glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);glfwSetCursorPosCallback(window, mouse_callback);glfwSetScrollCallback(window, scroll_callback);// 设置两个必须的着色器:顶点和片段着色器Shader ourShader("shader.vs", "shader.fs");// 定义正方形坐标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};// 设置顶点缓冲对象(Vertex Buffer Objects, VBO)管理内存(坐标点数据)与 VAOunsigned int VAO, VBO;glGenVertexArrays(1, &VAO);  // 使用glGenVertexArrays函数和一个缓冲ID生成一个VAO对象glGenBuffers(1, &VBO);       // 使用glGenBuffers函数和一个缓冲ID生成一个VBO对象// 绑定: 先VAO 再VBOglBindVertexArray(VAO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);// 位置属性glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);  // 使用glEnableVertexAttribArray,以顶点属性位置值作为参数,启用顶点属性,顶点属性默认是禁用的// 纹理属性glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));glEnableVertexAttribArray(1);// 生成纹理unsigned int texture0, texture1;stbi_set_flip_vertically_on_load(true); // 防止图片倒置// glGenTextures先输入要生成纹理的数量,然后把它们储存在第二个参数的`unsigned int`数组中glGenTextures(1, &texture0);// 绑定glBindTexture(GL_TEXTURE_2D, texture0);// 为当前绑定的纹理对象设置环绕、过滤方式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);// 加载并生成纹理int width, height, nrChannels;unsigned char* data = stbi_load("wall.jpg", &width, &height, &nrChannels, 0);if (data){// 生成纹理glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);// 注:不需要手动更改我们在片段着色器定义的uniform sampler2D ourTexture,它会自动把纹理赋值给片段着色器的采样器ourTexture// 为当前绑定的纹理自动生成所有需要的多级渐远纹理glGenerateMipmap(GL_TEXTURE_2D);}else{std::cout << "Failed to load texture" << std::endl;}// 释放图像的内存stbi_image_free(data);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);data = stbi_load("hh.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设置每个采样器的方式告诉OpenGL每个着色器采样器属于哪个纹理单元。我们只需要设置一次即可,所以这个会放在渲染循环的前面:glUniform1i(glGetUniformLocation(ourShader.ID, "ourTexture0"), 0); // 手动设置ourShader.setInt("ourTexture1", 1); // 或者使用着色器类设置// 渲染循环之前,开启深度测试glEnable(GL_DEPTH_TEST);// 开启渲染循环(Render Loop)while (!glfwWindowShouldClose(window)){// 在每一帧中我们计算出新的deltaTimefloat currentFrame = glfwGetTime();deltaTime = currentFrame - lastFrame;lastFrame = currentFrame;// 输入控制:processInput(window);// 渲染指令// 修改背景颜色glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);// 绘图ourShader.use();glActiveTexture(GL_TEXTURE0);            // 激活纹理单元glBindTexture(GL_TEXTURE_2D, texture0);  // 绑定纹理glActiveTexture(GL_TEXTURE1);            // 激活纹理单元glBindTexture(GL_TEXTURE_2D, texture1);  // 绑定纹理glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  // 先清缓存// 创建矩阵glm::mat4 model = glm::rotate(glm::mat4(1.0f), (float)glfwGetTime() * glm::radians(50.0f), glm::vec3(0.5f, 1.0f, 0.0f));glm::mat4 view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);glm::mat4 projection = glm::perspective(glm::radians(fov), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);// 将矩阵传入着色器ourShader.setMat4("model", model);ourShader.setMat4("view", view);ourShader.setMat4("projection", projection);glBindVertexArray(VAO);                 // 启动VAOglDrawArrays(GL_TRIANGLES, 0, 36);// 检查调用事件,并交换缓冲glfwPollEvents();glfwSwapBuffers(window);}// 回收资源glDeleteVertexArrays(1, &VAO);glDeleteBuffers(1, &VBO);glfwTerminate();return 0;
}// 窗口改变回调函数
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{glViewport(0, 0, width, height);
}// 窗口按键输入
void processInput(GLFWwindow* window)
{// 按下esc按键,退出程序if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)glfwSetWindowShouldClose(window, true);float cameraSpeed = 2.5f * deltaTime;if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)cameraPos += cameraSpeed * cameraFront;if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)cameraPos -= cameraSpeed * cameraFront;if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
}void mouse_callback(GLFWwindow * window, double xpos, double ypos)
{if (firstMouse) // 这个bool变量初始时是设定为true的{lastX = xpos;lastY = ypos;firstMouse = false;}float xoffset = xpos - lastX;float yoffset = lastY - ypos; // 注意这里是相反的,因为y坐标是从底部往顶部依次增大的lastX = xpos;lastY = ypos;// 设置sensitivity(灵敏度)值,防止偏移过大float sensitivity = 0.05f;xoffset *= sensitivity;yoffset *= sensitivity;// 把偏移量添加到摄像机的俯仰角和偏航角中。yaw += xoffset;pitch += yoffset;// 对偏航角和俯仰角进行最大和最小值的限制。if (pitch > 89.0f)pitch = 89.0f;if (pitch < -89.0f)pitch = -89.0f;// 计算方向向量。glm::vec3 front;front.x = cos(glm::radians(pitch)) * cos(glm::radians(yaw));front.y = sin(glm::radians(pitch));front.z = cos(glm::radians(pitch)) * sin(glm::radians(yaw));cameraFront = glm::normalize(front);
}void scroll_callback(GLFWwindow * window, double xoffset, double yoffset)
{fov -= (float)yoffset;if (fov < 1.0f)fov = 1.0f;if (fov > 45.0f)fov = 45.0f;
}

OpenGL 摄像机相关推荐

  1. OpenGL摄像机(Look At矩阵)

    OpenGL摄像机 1. 摄像机/观察空间 2. Look At 3. lookAt 矩阵案例 1. 摄像机/观察空间 观察矩阵将所有的世界坐标变换为相对于摄像机位置与方向的观察坐标.当定义一个摄像机 ...

  2. OpenGL 摄像机Camera

    OpenGL 摄像机Camera 摄像机Camera简介 摄像机/观察空间 摄像机位置 摄像机方向 右轴 上轴 Look At 自由移动 移动速度 视角移动 欧拉角 鼠标输入 缩放 摄像机类 摄像机C ...

  3. LearnOpenGL学习笔记——OpenGL摄像机

    摄像机/观察空间 当我们讨论摄像机的观察空间(Camera/View Space)的时候,是在讨论以摄像机的视角作为场景原点时场景中所有的顶点坐标,通俗来讲,就是眼睛的位置,角度. 要在三维的世界中确 ...

  4. OpenGL摄像机键盘交互

    摄像机交互 对于摄像机基础实现内容较为生疏的可以参考该文章

  5. android opengl旋转,OpenGL纹理旋转及翻转问题详解

    大家好,我是程序员kenney,今天给大家讲解一下Android上OpenGL开发可能会遇到的一些纹理旋转及翻转的问题,其中有些原理在其它平台上如ios,osx上也是类似的.纹理旋转的问题一定要搞清楚 ...

  6. OpenGL完整教程专栏完整目录

    OpenGL完整教程专栏完整目录 专栏说明如下 专栏目录 专栏说明如下 内容:OpenGL完整教程 数量:314篇博文(2023年2月15日截止) 更新时间至:2023年2月15日(后续加上去的博文, ...

  7. android 图片浏览器旋转,OpenGL纹理旋转及翻转问题详解

    大家好,我是程序员kenney,今天给大家讲解一下Android上OpenGL开发可能会遇到的一些纹理旋转及翻转的问题,其中有些原理在其它平台上如ios,osx上也是类似的.纹理旋转的问题一定要搞清楚 ...

  8. ✠OpenGL-4-管理3D图形数据

    目录 数据类型和函数名前后缀含义 缓冲区和顶点属性 统一变量 顶点属性插值(光栅着色器) 模型-视图和透视矩阵 第一个3D程序--一个3D立方体 打印[模型-视图矩阵]对象 分析:物体经模型-视图矩阵 ...

  9. Portal Rendering与镜子特效

    Portal Rendering与镜子特效 Portal Rendering,之前在网上看到过有人翻译成视口渲染.Portal Rendering有点类似于漫威电影里面奇异博士的传送门, 透过传送门可 ...

最新文章

  1. mysql年月分表_MySQL之按月拆分主表并按月分表写入数据提高数据查询速度
  2. 全球知识图谱专家分布、研究流派(附学者名单)
  3. CentOS7 安装和配置 mysql5.7
  4. linux 找不到动态链接库 .so文件的解决方法
  5. 《Shell脚本学习指南》第一章 背景知识
  6. 价格的格式化 php,价格格式化问题。
  7. 《Javascript高级程序设计》读书笔记之bind函数详解
  8. rap技术原理_「水深坑多」做分子海绵,你还需要了解这些技术
  9. 文献学习(part65)--稳健主成分聚类方法的构建及其比较研究
  10. java内容置剪贴板
  11. UML:图的分类及作用(共5类图,有9种图形)
  12. Word vba 替换
  13. Tomcat的下载安装及静态部署
  14. 公司部门英文缩写简称大全(1)
  15. 世界上第一台二进制电子计算机,世界上公认第一台电子计算机.doc
  16. 个人征信报告有哪些版本?
  17. 小程序发布新版本后,部分用户手机白屏
  18. 水面反光如何拍摄_拍摄水景的技巧方法
  19. 自建网站开通SSL协议
  20. 如何在宝贝详情页中制作一张图片多个链接

热门文章

  1. 发布Java应用实践结合CCE
  2. XLA IR:HLO、LHLO、MHLO和LMHLO
  3. c语言1064加密字符,ZZULIOJ 1064加密字符
  4. 2012年度IT博客大赛50强报道:宋守炯
  5. 报考条件及择校建议:法硕(非法学)与法硕(法学)
  6. java枚举类Enum入门理解
  7. PDF如何转换成PPT?教你们几个简单方法
  8. 评估托管SDWAN服务
  9. ElementUI的Table组件在无数据情况下让“暂无数据”文本居中显示
  10. 多元矩阵乘积的导数问题