1. 向量
向量有一个方向和一个大小。如果一个向量有2个维度,它表示一个平面的方向(想象一下2D的图像),当它有3个维度的时候它可以表达一个3D世界的方向。
可以把这些2D向量当做z坐标为0的3D向量。
2. 向量内积和外积
向量的点乘(内积):简单来说就是可以判断向量方向的相似性
向量外积:简单来说就是可以得到其法向量
3. 向量的加减和长度
向量的加法可以被定义为是分量的(Component-wise)相加,即将一个向量中的每一个分量加上另一个向量的对应分量。
v = (4, 2)
和k = (1, 2)相加得到(4+1,2+2)
向量的减法等于加上第二个向量的相反向量。两个向量的相减会得到这两个向量指向位置的差。
我们使用勾股定理(Pythagoras Theorem)来获取向量的长度(Length)/大小(Magnitude)。如果你把向量的x与y分量画出来,该向量会和x与y分量为边形成一个三角形。
4. 旋转
5. 代码实现旋转和缩放
texture.vs
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCorrd;
out vec2 TexCoord;
uniform mat4 transform;
void main()
{
gl_Position = transform * vec4(aPos, 1.0);
TexCoord=vec2(aTexCorrd.x,1.0-aTexCorrd.y);
}
texture.fs
#version 330 core
out vec4 FragColor;
in vec3 ourColor;
in vec2 TexCoord;
uniform float mixValue;
//texture sampler
uniform sampler2D textureone;
uniform sampler2D texturetwo;
void main()
{
FragColor = mix(texture(textureone,TexCoord),texture(texturetwo,TexCoord),mixValue);
}
shader.h
#pragma once
#include <glad/glad.h>
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
class Shader {
public:
//程序ID
unsigned int ID;
//构造器读取并构建着色器
Shader(const char* vertexPath, const char* fagmentPath) {
std::string vertexCode;
std::string fragmentCode;
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(fagmentPath);
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_SUCCESSFULLY_READ: " << e.what() << std::endl;
}
const char* vShaderCode = vertexCode.c_str();
const char* fShaderCode = fragmentCode.c_str();
//2.complie shaders
unsigned int vertex, fragment;
//vertex shader
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vShaderCode, NULL);
glCompileShader(vertex);
checkCompileErros(vertex, "VERTEX");
//fragment Shader
fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fShaderCode, NULL);
glCompileShader(fragment);
checkCompileErros(fragment, "FRAGMENT");
//shader Program
ID = glCreateProgram();
glAttachShader(ID, vertex);
glAttachShader(ID, fragment);
glLinkProgram(ID);
checkCompileErros(ID, "PROGAM");
glDeleteShader(vertex);
glDeleteShader(fragment);
}
//使用/激活程序
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);
}
private:
void checkCompileErros(unsigned int shader, std::string type) {
int success;
char infoLog[1024];
if (type != "PROGRAM") {
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(shader, 1024, NULL, infoLog);
std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
}
}
else
{
glGetProgramiv(shader, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(shader, 1024, NULL, infoLog);
std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
}
}
}
};
main.cpp
下载glm
Tags · g-truc/glm (github.com)
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include "stb_image.h"
#include <cmath>
#include "shader.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);
// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
float mixValue = 0.2f;
int main() {
//1.初始化配置
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, GLFW_TRUE);
#endif // __APPLE__
//2.gltf 窗口创建
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LeranOpenGL", NULL, NULL);
if (window == NULL) {
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
//3. 加载所有GL函数指针
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
Shader ourShader("./texture.vs", "./texture.fs");
//4. 设置顶点数据
float vertices[] = {
// positions // colors // texture coords
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, // first triangle
1, 2, 3 // second triangle
};
unsigned int VBO, VAO, EBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1,&EBO);//元素缓冲对象:Element Buffer Object,EBO
glBindVertexArray(VAO);
//复制顶点数组到缓冲区中供opengl使用
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);
//加载和创建纹理
unsigned int textureone,texturetwo;
glGenTextures(1, &textureone);
glBindTexture(GL_TEXTURE_2D, textureone);
//设置纹理环绕参数
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);
//加载图像和生成mipmaps
int width, height, nrChannels;
stbi_set_flip_vertically_on_load(true);
std::string filePath = R"(D:\CPlusProject\LearnOpenGL\DataSet\container.jpg)";
unsigned char* data = stbi_load(filePath.c_str(), &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, &texturetwo);
glBindTexture(GL_TEXTURE_2D, texturetwo);
//设置纹理环绕参数
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);
//加载图像和生成mipmaps
filePath = R"(D:\CPlusProject\LearnOpenGL\DataSet\awesomeface.png)";
unsigned char* data2 = stbi_load(filePath.c_str(), &width, &height, &nrChannels, 0);
if (data2) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data2);
glGenerateMipmap(GL_TEXTURE_2D);
}
else {
std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data2);
ourShader.use();
glUniform1i(glGetUniformLocation(ourShader.ID, "textureone"), 0);//二选一
ourShader.setInt("texturetwo", 1);//二选一
//glm::vec4 vec(1.0f, 0.0f, 0.0f, 1.0f);
译注:下面就是矩阵初始化的一个例子,如果使用的是0.9.9及以上版本
//glm::mat4 trans = glm::mat4(1.0f);
//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.f), glm::vec3(0.0, 0.0, 1.0));
trans = glm::scale(trans, glm::vec3(0.5, 0.5, 0.5));
unsigned int transformLoc = glGetUniformLocation(ourShader.ID, "transform");
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));
//5. 循环渲染
while (!glfwWindowShouldClose(window)) {
processInput(window);
// render
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//绑定纹理
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureone);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texturetwo);
ourShader.setFloat("mixValue", mixValue);
ourShader.use();
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,0);
glfwSwapBuffers(window);
glfwPollEvents();
}
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);
glfwTerminate();
return 0;
}
// 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);
if (glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS) {
std::cout << "up" << std::endl;
mixValue += 0.001f;
if (mixValue >= 1.0f) {
mixValue = 1.0f;
}
}
if (glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS)
{
std::cout << "down" << std::endl;
mixValue -= 0.001f; // change this value accordingly (might be too slow or too fast based on system hardware)
if (mixValue <= 0.0f)
mixValue = 0.0f;
}
std::cout << "mixValue:" << mixValue <<std::endl;
}
// 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);
}
6. 持续旋转
要让箱子随着时间推移旋转,我们必须在游戏循环中更新变换矩阵。也就是在while结构体中。
在这里我们先把箱子围绕原点(0, 0, 0)旋转,之后,我们把旋转过后的箱子位移到屏幕的右下角。
//5. 循环渲染
while (!glfwWindowShouldClose(window)) {
processInput(window);
// render
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//绑定纹理
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureone);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texturetwo);
ourShader.setFloat("mixValue", mixValue);
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));
ourShader.use();
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,0);
glfwSwapBuffers(window);
glfwPollEvents();
}
7. 先平移后旋转And先旋转后平移
上面的例子是先平移后旋转,下面的代码是先旋转后平移
//5. 循环渲染
while (!glfwWindowShouldClose(window)) {
processInput(window);
// render
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//绑定纹理
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureone);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texturetwo);
ourShader.setFloat("mixValue", mixValue);
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));
//先旋转后平移
trans = glm::rotate(trans, (float)glfwGetTime(), glm::vec3(0.0f, 0.0f, 1.0f));
trans = glm::translate(trans, glm::vec3(0.5f, -0.5f, 0.0f));
unsigned int transformLoc = glGetUniformLocation(ourShader.ID, "transform");
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));
ourShader.use();
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,0);
glfwSwapBuffers(window);
glfwPollEvents();
}
8. 绘制两个三角形,不同的显示方式
尝试再次调用glDrawElements画出第二个箱子,只使用变换将其摆放在不同的位置。让这个箱子被摆放在窗口的左上角,并且会不断的缩放(而不是旋转)。(
sin
函数在这里会很有用,不过注意使用sin
函数时应用负值会导致物体被翻转)。
//5. 循环渲染
while (!glfwWindowShouldClose(window)) {
processInput(window);
// render
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//绑定纹理
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureone);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texturetwo);
ourShader.setFloat("mixValue", mixValue);
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));
//先旋转后平移
trans = glm::rotate(trans, (float)glfwGetTime(), glm::vec3(0.0f, 0.0f, 1.0f));
trans = glm::translate(trans, glm::vec3(0.5f, -0.5f, 0.0f));
unsigned int transformLoc = glGetUniformLocation(ourShader.ID, "transform");
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));
ourShader.use();
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,0);
//second transformation
trans = glm::mat4(1.0f);
trans = glm::translate(trans, glm::vec3(-0.5f, 0.5f, 0.0f));
float scaleAmount = static_cast<float>(sin(glfwGetTime()));
trans = glm::scale(trans, glm::vec3(scaleAmount, scaleAmount, scaleAmount));
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, &trans[0][0]);// this time take the matrix value array's first element as its memory pointer value
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glfwSwapBuffers(window);
glfwPollEvents();
}
向量点乘与叉乘的概念及几何意义 - 知乎 (zhihu.com)
变换 - LearnOpenGL CN (learnopengl-cn.github.io)