接下来的变换是旋转变换,就是说给定一个角度和点,我们将点绕着一个坐标轴旋转。在旋转过程中发生变化的总是x,y,z三个坐标里面的其中两个,而不让第三个坐标值变化。这意味着,旋转路径总在三个坐标轴平面中的一个之中:绕 Z 轴的是 xy 面、绕 X 轴的是 yz 面、绕 Y 轴的是 xy 面。还有许多复杂的旋转变换可以让你绕任意一个向量旋转,但是眼下我们并不需要讨论这些。 让我们概括地定义这个问题这个问题。看看下面的这个图: [
源代码详解:
在上个教程的基础上这里代码的变换非常少。我们只是改变代码中变换矩阵的内容,将平移变换矩阵换成了旋转变换矩阵而已:
World.m[0][0]=cosf(Scale);World.m[0][1]=-sinf(Scale);World.m[0][2]=0.0f; World.m[0][3]=0.0f;
World.m[1][0]=sinf(Scale);World.m[1][1]=cosf(Scale);World.m[1][2]=0.0f; World.m[1][3]=0.0f;
World.m[2][0]=0.0f;World.m[2][1]=0.0f;World.m[2][2]=1.0f;World.m[2][3]=0.0f;
World.m[3][0]=0.0f;World.m[3][1]=0.0f;World.m[3][2]=0.0f;World.m[3][3]=1.0f;
这样会看到图形绕着Z轴旋转了。我们也可以尝试绕其他轴的旋转,但是没有从3d到2d的投影另外两种旋转看上起去会很奇怪,我们会在后面教程中在一个完整的图形变换管线类中来完善它。 源代码:
#include <stdio.h>
#include<string.h>
#include <GL/glew.h>
#include <GL/freeglut.h>
#include “ogldev_math_3d.h” //用于OpenGL的3d数学库
#include “ogldev_util.h” //用于读取文本文件
GLuint VBO; //全局GLuint引用变量,来操作顶点缓冲器对象
GLuint gWorldLocation; // 平移变换一致变量的句柄引用
// 定义要读取的顶点着色器脚本和片断着色器脚本的文件名,作为文件读取路径
const char* pVSFileName = “shader.vs”;
const char* pFSFileName = “shader.fs”;
static void RenderCallBack()
{
glClear(GL_COLOR_BUFFER_BIT); //清空颜色缓存
static float Scale = 0.0f; //维护一个不断慢慢增大的静态浮点数
Scale += 0.0001f; //如果图像变化太快或者太慢,可调节此数值
// 4x4的旋转变换矩阵
Matrix4f World;
World.m\[0\]\[0\] = cosf(Scale); World.m\[0\]\[1\] = -sinf(Scale); World.m\[0\]\[2\] = 0.0f; World.m\[0\]\[3\] = 0.0f;
World.m\[1\]\[0\] = sinf(Scale); World.m\[1\]\[1\] = cosf(Scale); World.m\[1\]\[2\] = 0.0f; World.m\[1\]\[3\] = 0.0f;
World.m\[2\]\[0\] = 0.0f; World.m\[2\]\[1\] = 0.0f; World.m\[2\]\[2\] = 1.0f; World.m\[2\]\[3\] = 0.0f;
World.m\[3\]\[0\] = 0.0f; World.m\[3\]\[1\] = 0.0f; World.m\[3\]\[2\] = 0.0f; World.m\[3\]\[3\] = 1.0f;
// 将矩阵数据加载到shader中
glUniformMatrix4fv(gWorldLocation, 1, GL\_TRUE, &World.m\[0\]\[0\]);
glEnableVertexAttribArray(0); //开启顶点属性
glBindBuffer(GL\_ARRAY\_BUFFER, VBO); //绑定GL\_ARRAY\_BUFFER缓冲器
glVertexAttribPointer(0, 3, GL\_FLOAT, GL\_FALSE, 0, 0); //管线解析bufer中的数据
glDrawArrays(GL\_TRIANGLES, 0, 3); //画三角形,3个顶点
glDisableVertexAttribArray(0); //禁用顶点数据
glutSwapBuffers(); //交换前后缓存
}
static void InitializeGlutCallbacks()
{
glutDisplayFunc(RenderCallBack);
glutIdleFunc(RenderCallBack); //将渲染回调注册为全局闲置回调
}
static void CreateVertexBuffer()
{
Vector3f Vertices[3]; //创建含有3个顶点的顶点数组
Vertices[0] = Vector3f(-1.0f, -1.0f, 0.0f);
Vertices[1] = Vector3f(1.0f, -1.0f, 0.0f);
Vertices[2] = Vector3f(0.0f, 1.0f, 0.0f);
glGenBuffers(1, &VBO); //创建缓冲器
glBindBuffer(GL\_ARRAY\_BUFFER, VBO); //绑定GL\_ARRAY\_BUFFER缓冲器
glBufferData(GL\_ARRAY\_BUFFER, sizeof(Vertices), Vertices, GL\_STATIC\_DRAW); //绑定顶点数据
}
//使用shader文本编译shader对象,并绑定shader到着色器程序中
static void AddShader(GLuint ShaderProgram, const char* pShaderText, GLenum ShaderType)
{
// 根据shader类型参数定义两个shader对象
GLuint ShaderObj = glCreateShader(ShaderType);
// 检查是否定义成功
if (ShaderObj == 0)
{
fprintf(stderr, “Error creating shader type %d\n”, ShaderType);
exit(0);
}
// 定义shader的代码源
const GLchar\* p\[1\];
p\[0\] = pShaderText;
GLint Lengths\[1\];
Lengths\[0\] = strlen(pShaderText);
glShaderSource(ShaderObj, 1, p, Lengths);
glCompileShader(ShaderObj); // 编译shader对象
// 检查和shader相关的错误
GLint success;
glGetShaderiv(ShaderObj, GL\_COMPILE\_STATUS, &success);
if (!success)
{
GLchar InfoLog\[1024\];
glGetShaderInfoLog(ShaderObj, 1024, NULL, InfoLog);
fprintf(stderr, "Error compiling shader type %d: '%s'\\n", ShaderType, InfoLog);
exit(1);
}
// 将编译好的shader对象绑定到program object程序对象上
glAttachShader(ShaderProgram, ShaderObj);
}
// 编译着色器函数
static void CompileShaders()
{
// 创建着色器程序
GLuint ShaderProgram = glCreateProgram();
// 检查是否创建成功
if (ShaderProgram == 0)
{
fprintf(stderr, “Error creating shader program\n”);
exit(1);
}
// 存储着色器文本的字符串缓冲
string vs, fs;
// 分别读取着色器文件中的文本到字符串缓冲区
if (!ReadFile(pVSFileName, vs))
{
exit(1);
};
if (!ReadFile(pFSFileName, fs))
{
exit(1);
};
// 添加顶点着色器和片段着色器
AddShader(ShaderProgram, vs.c\_str(), GL\_VERTEX\_SHADER);
AddShader(ShaderProgram, fs.c\_str(), GL\_FRAGMENT\_SHADER);
// 链接shader着色器程序,并检查程序相关错误
GLint Success = 0;
GLchar ErrorLog\[1024\] = { 0 };
glLinkProgram(ShaderProgram);
glGetProgramiv(ShaderProgram, GL\_LINK\_STATUS, &Success);
if (Success == 0)
{
glGetProgramInfoLog(ShaderProgram, sizeof(ErrorLog), NULL, ErrorLog);
fprintf(stderr, "Error linking shader program: '%s'\\n", ErrorLog);
exit(1);
}
// 检查验证在当前的管线状态程序是否可以被执行
glValidateProgram(ShaderProgram);
glGetProgramiv(ShaderProgram, GL\_VALIDATE\_STATUS, &Success);
if (!Success)
{
glGetProgramInfoLog(ShaderProgram, sizeof(ErrorLog), NULL, ErrorLog);
fprintf(stderr, "Invalid shader program: '%s'\\n", ErrorLog);
exit(1);
}
// 设置到管线声明中来使用上面成功建立的shader程序
glUseProgram(ShaderProgram);
// 查询获取一致变量的位置
gWorldLocation = glGetUniformLocation(ShaderProgram, "gWorld");
assert(gWorldLocation != 0xFFFFFFFF); // 检查错误
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowSize(400, 400);
glutInitWindowPosition(100, 100);
glutCreateWindow("旋转变换");
glutDisplayFunc(RenderCallBack); //开始渲染
InitializeGlutCallbacks();
// 检查GLEW是否就绪,必须要在GLUT初始化之后!
GLenum res = glewInit();
if (res != GLEW\_OK)
{
fprintf(stderr, "Error: '%s'\\n", glewGetErrorString(res));
return 1;
}
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); //缓存清空后的颜色值
CreateVertexBuffer(); //创建顶点缓冲器
CompileShaders(); // 编译着色器
glutMainLoop(); //通知开始GLUT的内部循环
return 0;
}
片断着色器shader.fs脚本代码:
#version 330 //告诉编译器我们的目标GLSL编译器版本是3.3
out vec4 FragColor; // 片段着色器的输出颜色变量
// 着色器的唯一入口函数
void main()
{
// 定义输出颜色值
FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
顶点着色器shader.vs脚本代码:
#version 330 //告诉编译器我们的目标GLSL编译器版本是3.3
// 绑定定点属性名和属性,方式二缓冲属性和shader属性对应映射
layout (location = 0) in vec3 Position;
// 平移变换聚矩阵一致变量
uniform mat4 gWorld;
void main()
{
// 用平移变换矩阵乘以图形顶点位置对应的4X4矩阵相乘,完成平移变换
gl_Position = gWorld * vec4(Position, 1.0);
}
最终效果(gif图): 参考链接: http://ogldev.atspace.co.uk/www/tutorial07/tutorial07.html https://learnopengl.com/Getting-started/Transformations http://blog.csdn.net/cordova/article/details/52558133