本文主要講3D的變換绍弟,3D的旋轉(zhuǎn)在OpenGL擴展輔助庫中有GLM實現(xiàn)澈蚌,本文主要從理解角度毁腿,手工實現(xiàn)3D變換嵌莉,其中使用的是4元矩陣。
??1. 縮放
??2. 平移
??3. 旋轉(zhuǎn)
縮放
縮放公式
實現(xiàn):
- 只要直接構(gòu)造一個$4 \times 4$矩陣即可實現(xiàn)縮放筛谚。
- 構(gòu)造方式:
1. C/C++風格
2. 使用glm
void transform(GLuint pid, GLfloat sx, GLfloat sy, GLfloat sz){
/**
* 這里使用的是專門遵循GLSL標準的數(shù)學運算庫:GLM磁玉,該庫的使用可以從上面頭文件的說明得到;
* 官方網(wǎng)頁說明速度慢驾讲,更加詳細的說明可以參考官方github文檔:https://github.com/g-truc/glm/blob/master/manual.md
*/
// glm::mat4 trans = glm::mat4(1.0f);
// trans = glm::scale(trans, glm::vec3(0.5, 0.5, 0.5));
GLfloat scale[] ={
sx, 0.0f, 0.0f, 0.0f,
0.0f, sy, 0.0f, 0.0f,
0.0f, 0.0f, sz, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
unsigned int transformLoc = glGetUniformLocation(pid, "transform");
// glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, scale);
}
- C/C++風格
GLfloat scale[] ={
sx, 0.0f, 0.0f, 0.0f,
0.0f, sy, 0.0f, 0.0f,
0.0f, 0.0f, sz, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
unsigned int transformLoc = glGetUniformLocation(pid, "transform");
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, scale);
- glm風格
glm::mat4 trans = glm::mat4(1.0f);
trans = glm::scale(trans, glm::vec3(0.5, 0.5, 0.5));
unsigned int transformLoc = glGetUniformLocation(pid, "transform");
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));
平移
平移公式
實現(xiàn)
void transform(GLuint pid, GLfloat sx, GLfloat sy, GLfloat sz){
/**
* 這里使用的是專門遵循GLSL標準的數(shù)學運算庫:GLM蚊伞,該庫的使用可以從上面頭文件的說明得到;
* 官方網(wǎng)頁說明速度慢吮铭,更加詳細的說明可以參考官方github文檔:https://github.com/g-truc/glm/blob/master/manual.md
*/
// glm::mat4 trans = glm::mat4(1.0f);
// trans = glm::translate(trans, glm::vec3(sx, sy, sz));
GLfloat translate[] ={
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
sx, sy, sz, 1.0f
};
unsigned int transformLoc = glGetUniformLocation(pid, "transform");
// glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, translate);
}
-
注意:
- 因為內(nèi)存表達的緣故时迫,所以最后一列,在表示為數(shù)組的時候是最后一行谓晌。
旋轉(zhuǎn)
旋轉(zhuǎn)公式
-
常見的2D平面中的旋轉(zhuǎn)公式
-
3D中按照z軸旋轉(zhuǎn)的公式
-
3D中按照x軸旋轉(zhuǎn)的公式
-
3D中按照y旋轉(zhuǎn)的公式
-
3D中按照指定向量旋轉(zhuǎn)的公式
實現(xiàn)
void transform(GLuint pid, GLfloat angle, GLfloat sx, GLfloat sy, GLfloat sz){
/**
* 這里使用的是專門遵循GLSL標準的數(shù)學運算庫:GLM掠拳,該庫的使用可以從上面頭文件的說明得到;
* 官方網(wǎng)頁說明速度慢纸肉,更加詳細的說明可以參考官方github文檔:https://github.com/g-truc/glm/blob/master/manual.md
*/
glm::mat4 trans = glm::mat4(1.0f);
trans = glm::rotate(trans, glm::radians(angle), glm::vec3(sx, sy, sz));
GLfloat r_sin = sinf(angle * (3.14159f / 180.0f));
GLfloat r_cos = cosf(angle * (3.14159f / 180.0f));
GLfloat dis = sqrt(sx * sx + sy * sy + sz * sz); // 注意旋轉(zhuǎn)向量(軸)必須單位化碳想,否會引起縮放效果
sx /= dis;
sy /= dis;
sz /= dis;
GLfloat rotate[] ={
sx*sx*(1-r_cos)+r_cos, sy*sx*(1-r_cos)+sz*r_sin, sz*sx*(1-r_cos)-sy*r_sin, 0.0f,
sx*sy*(1-r_cos)-sz*r_sin, sy*sy*(1-r_cos)+r_cos, sz*sy*(1-r_cos)+sx*r_sin, 0.0f,
sx*sz*(1-r_cos)+sy*r_sin, sy*sz*(1-r_cos)-sx*r_sin, sz*sz*(1-r_cos)+r_cos, 0.0f,
0.0f, 0.0f , 0.0f, 1.0f
};
unsigned int transformLoc = glGetUniformLocation(pid, "transform");
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));
// glUniformMatrix4fv(transformLoc, 1, GL_FALSE, rotate);
}
GLM使用
-
GLM的向量與矩陣都是結(jié)構(gòu)體,具備結(jié)構(gòu)體的知識毁靶,結(jié)合.h文件胧奔,不使用API幫助都可以調(diào)用。
- 需要注意的是使用下標訪問预吆,訪問的是列(這也是內(nèi)存的結(jié)構(gòu)表示的問題)
下面是一個簡答的入門例子
// https://github.com/g-truc/glm
/*
下載后直接拷貝到系統(tǒng)的include目錄使用即可
*/
#include <stdio.h>
#include <stdlib.h>
#include <glm/vec3.hpp> // glm::vec3
#include <glm/vec4.hpp> // glm::vec4
#include <glm/mat4x4.hpp> // glm::mat4
#include <glm/ext/matrix_transform.hpp> // glm::translate, glm::rotate, glm::scale
#include <glm/ext/matrix_clip_space.hpp> // glm::perspective
#include <glm/ext/scalar_constants.hpp> // glm::pi
#include <glm/gtc/type_ptr.hpp>
int main(int argc, char const *argv[])
{
/* code */
glm::vec4 vec(1.0f, 0.0f, 0.0f, 1.0f); // 向量
glm::mat4 trans = glm::mat4(1.0f); // 矩陣
trans = glm::scale(trans, glm::vec3(0.5, 0.5, 0.5));
// vec = trans * vec;
float *m = glm::value_ptr(trans); // 返回glm對象的內(nèi)存地址
for(int i = 0; i < 16; i++){ // 使用指針訪問數(shù)據(jù)
printf("%f\t", m[I]);
if((i+ 1)%4==0){
printf("\n");
}
}
printf("\n=================\n");
glm::vec4 c= trans[3]; // 使用glm對象訪問數(shù)據(jù)
printf("%f,%f,%f,%f\n", c.x, c.y, c.z,c.w);
return 0;
}
// g++ glm01_mat.cpp -omain
完整代碼結(jié)構(gòu)
- 公用glfw封裝頭文件
#ifndef COMMON_H
#define COMMON_H
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
GLFWwindow* initContext(); // 上下文初始化
void destroyConext();
GLboolean initOpenGL(); // OpenGL初始化與加載
////////////////////////////////
#endif
- 公用glfw封裝實現(xiàn)
#include "common.h"
// 上下文初始化
GLFWwindow* initContext(){
if(!glfwInit()){
printf("GLFW初始化失斄睢!\n");
return NULL;
}
// 設(shè)置提示
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
GLFWwindow* win = glfwCreateWindow(600,400, "OpenGL著色器", NULL, NULL);
if(! win){
printf("創(chuàng)建窗體失敼詹妗岩遗!\n");
return NULL;
}
// 設(shè)置當前調(diào)用線程的上下文為win;
glfwMakeContextCurrent(win);
return win;
}
void destroyConext(){
glfwTerminate();
}
// OpenGL初始化與加載
GLboolean initOpenGL(){
if(glewInit() != GLEW_OK){ // GLEW_OK:#define GLEW_OK 0
printf("OpenGL加載失敺锸荨宿礁!\n");
return GL_FALSE;
}
return GL_TRUE;
}
- 主體結(jié)構(gòu)實現(xiàn)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <math.h>
#include "common.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#include <glm/vec3.hpp> // glm::vec3
#include <glm/vec4.hpp> // glm::vec4
#include <glm/mat4x4.hpp> // glm::mat4
#include <glm/ext/matrix_transform.hpp> // glm::translate, glm::rotate, glm::scale
#include <glm/ext/matrix_clip_space.hpp> // glm::perspective
#include <glm/ext/scalar_constants.hpp> // glm::pi
#include <glm/gtc/type_ptr.hpp>
GLuint yqData(); // 數(shù)據(jù)準備
GLuint yqIndecies(); // 索引
void yqTexture(GLuint textureID[2]); // 紋理
GLuint yqShader(); // GLSL
void transform(GLuint pid, GLfloat angle, GLfloat sx, GLfloat sy, GLfloat sz); // 變換
int main(int argc, char const *argv[]){
GLFWwindow *win = initContext();
if(!win){
return -1;
}
if(!initOpenGL()){
destroyConext();
return -1;
}
GLuint arrayID = yqData();
glBindVertexArray(arrayID); // 必須開啟頂點分組
GLuint indeciesID = yqIndecies();
glBindVertexArray(0); // 關(guān)閉頂點分組
GLuint programmID = yqShader();
GLuint textureID[2];
yqTexture(textureID);
/**
* 需要把紋理對象傳遞給Shader處理
*/
// 獲取Shader中的sTexture對象
// 激活Shader程序
glUseProgram(programmID); // 已經(jīng)激活,就無需再激活
// 獲取Shader程序中
GLint location = glGetUniformLocation(programmID, "sTexture");
glUniform1i(location, 0); // 設(shè)置紋理對象為索引為0蔬芥,
// 紋理也是與頂點類似梆靖,使用所用管理控汉,紋理與頂點的管理采用16個單元完成,使用都需要激活
// 頂點激活:glVertexAttribPointer
// 紋理激活:glActiveTexture
GLint location_2 = glGetUniformLocation(programmID, "sTexture_2");
glUniform1i(location_2, 1); // 設(shè)置紋理對象為索引為0返吻,
glUseProgram(0); // 關(guān)閉Shader程序
GLdouble oldTime = glfwGetTime();
GLfloat angle = 0.0f;
while(!glfwWindowShouldClose(win)){
if(glfwGetTime() - oldTime > 0.1){
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glBindVertexArray(arrayID); // 綁定頂點分組
glUseProgram(programmID); // 使用Shader
transform(programmID, angle, 1.0f, 1.0f, 1.0f);
angle -= 1.0f;
glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, 0);
glUseProgram(0); // 解除使用Shader
glBindVertexArray(0); // 接觸頂點分組
glfwSwapBuffers(win);
oldTime = glfwGetTime();
}
// glfwWaitEvents();
glfwPollEvents();
}
destroyConext();
return 0;
}
GLuint yqData(){
// 頂點屬性數(shù)組
GLuint arrayID;
glGenVertexArrays(1, &arrayID);
glBindVertexArray(arrayID);
// 數(shù)據(jù)(頂點坐標 + 紋理坐標)
GLfloat vertices[] = { // 紋理坐標按照(0姑子,0) - (1,1)之間的4個點確定
0.0f, 0.8f, 0.0f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
-0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f,
0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f,
-0.3f, -0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f
};
// 數(shù)據(jù)緩沖
GLuint bufferID;
glGenBuffers(1, &bufferID);
glBindBuffer(GL_ARRAY_BUFFER, bufferID);
glBufferData(GL_ARRAY_BUFFER,sizeof(vertices), vertices, GL_STATIC_DRAW);
// 頂點屬性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(GLfloat), NULL);
glEnableVertexAttribArray(0);
// 文理屬性
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 9 * sizeof(GLfloat), (const void *)(3 * sizeof(GLfloat))); // 頂點屬性(輸入):注意location=3测僵,對應的頂點索引也是3
glEnableVertexAttribArray(1);
// 顏色索引
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, 9 * sizeof(GLfloat), (const void *)(5 * sizeof(GLfloat))); // 頂點屬性(輸入):注意location=3街佑,對應的頂點索引也是3
glEnableVertexAttribArray(2);
// 關(guān)閉頂點分組的操作
glBindVertexArray(0); // 要使用再切換回來
return arrayID;
}
GLuint yqIndecies(){
unsigned int indices[] = { // 注意繪制的順序
0, 1, 2,
0, 1, 3,
1, 2, 3,
0, 2, 3
};
GLuint indexID;
glGenBuffers(1, &indexID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexID); // 指定索引緩沖的類型
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); // 拷貝索引數(shù)據(jù)
return indexID;
}
GLuint yqShader(){
// 紋理坐標的傳遞,紋理坐標的使用
const char *vertexShaderSource = ""
"#version 410 core\n"
"layout (location = 0) in vec3 aPos;\n" // 頂點坐標
"layout (location = 1) in vec2 aTexture;\n" // 紋理坐標
"layout (location = 2) in vec4 aColor;\n" // 顏色坐標
"out vec2 vTexture;\n" // 傳遞紋理坐標到片著色器
"out vec4 vColor;\n" // 傳遞顏色坐標到片著色器
"uniform mat4 transform;\n"
"void main(){\n"
" gl_Position = transform * vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
" vTexture = aTexture;\n" // 輸出到下一個著色器
" vColor = aColor;\n" // 輸出到下一個著色器
"}\0"; // 空字符
const char *fragmentShaderSource = ""
"#version 410 core\n"
"out vec4 FragColor;\n"
"in vec2 vTexture;\n" // 上面頂點著色器傳遞過來的紋理坐標捍靠,用來生成采樣
"in vec4 vColor;\n" // 上面頂點著色器傳遞過來的紋理坐標沐旨,用來生成采樣
"uniform sampler2D sTexture;\n"
"uniform sampler2D sTexture_2;\n"
"void main(){\n"
" FragColor = mix(texture(sTexture, vTexture), texture(sTexture_2, vTexture), 0.2) * vColor;\n" // 采樣
"}\n\0";
unsigned int vertexShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
unsigned int fragmentShader;
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
unsigned int shaderProgram;
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
// glUseProgram(shaderProgram);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
return shaderProgram;
}
void yqTexture(GLuint textureID[2]){
// 創(chuàng)建一個紋理ID
glGenTextures(1, textureID);
// 綁定紋理ID到紋理的管理數(shù)據(jù)
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureID[0]); //指定內(nèi)存的紋理類型
// 使用第三方庫加載圖像
int width, height, depth;
unsigned char *data = stbi_load("texture.png", &width, &height, &depth, 0);
printf("圖像大小:(%d,%d,%d)\n", width, height, depth);
// 設(shè)置紋理使用的圖像數(shù)據(jù)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D); // 生成類型為2D的紋理映射
// 釋放圖像
stbi_image_free(data);
//////////////////////////////////////////////////
// 綁定紋理ID到紋理的管理數(shù)據(jù)
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, textureID[1]); //指定內(nèi)存的紋理類型
// 使用第三方庫加載圖像
data = stbi_load("arrow.jpeg", &width, &height, &depth, 0);
printf("圖像大小:(%d,%d,%d)\n", width, height, depth);
// 設(shè)置紋理使用的圖像數(shù)據(jù)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D); // 生成類型為2D的紋理映射
// 釋放圖像
stbi_image_free(data);
}
void transform(GLuint pid, GLfloat angle, GLfloat sx, GLfloat sy, GLfloat sz){
/**
* 這里使用的是專門遵循GLSL標準的數(shù)學運算庫:GLM,該庫的使用可以從上面頭文件的說明得到榨婆;
* 官方網(wǎng)頁說明速度慢磁携,更加詳細的說明可以參考官方github文檔:https://github.com/g-truc/glm/blob/master/manual.md
*/
glm::mat4 trans = glm::mat4(1.0f);
trans = glm::rotate(trans, glm::radians(angle), glm::vec3(sx, sy, sz));
GLfloat r_sin = sinf(angle * (3.14159f / 180.0f));
GLfloat r_cos = cosf(angle * (3.14159f / 180.0f));
GLfloat dis = sqrt(sx * sx + sy * sy + sz * sz);
sx /= dis;
sy /= dis;
sz /= dis;
GLfloat rotate[] ={
sx*sx*(1 - r_cos) + r_cos, sy*sx*(1 - r_cos) + sz * r_sin, sz*sx*(1 - r_cos) - sy * r_sin, 0.0f,
sx*sy*(1 - r_cos) - sz * r_sin, sy*sy*(1 - r_cos) + r_cos, sz*sy*(1 - r_cos) + sx * r_sin, 0.0f,
sx*sz*(1 - r_cos) + sy * r_sin, sy*sz*(1 - r_cos) - sx * r_sin, sz*sz*(1 - r_cos) + r_cos, 0.0f,
0.0f, 0.0f , 0.0f, 1.0f
};
unsigned int transformLoc = glGetUniformLocation(pid, "transform");
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));
// glUniformMatrix4fv(transformLoc, 1, GL_FALSE, rotate);
}
// g++ -o main gl03_rotate.cpp common.cpp -l glfw -l glew -framework opengl
-
效果