为您推荐:
精华内容
最热下载
问答
  • 5星
    256KB weixin_42691065 2021-10-01 08:46:25
  • 5星
    140KB faner200801 2011-11-25 13:20:22
  • 9.39MB tuanz7 2017-04-15 18:00:28
  • //平移图像 //trans = glm::rotate(trans, glm::radians(45.0f), glm::vec3(0.0, 0.0, 1.0)); //旋转图像 trans = glm::scale(trans, glm::vec3(1.5f, 1.5f, 1.5f)); //缩放图像 //渲染循环 while (!...

     

    #define GLEW_STATIC
    #include  <GL\glew.h>
    #include <GLFW/glfw3.h>
    
    #include <iostream>
    
    #include "Shader.h"
    
    #define STB_IMAGE_IMPLEMENTATION
    #include "stb_image.h"
    
    
    #include <glm.hpp>
    #include <gtc/matrix_transform.hpp>
    #include <gtc/type_ptr.hpp>
    
    using namespace std;
    //QQ技术交流群:386476712
    
    //terminate	[ˈtɜ:mɪneɪt]  结束 终结
    //hint [hɪnt] 提示 注意事项
    
    
    void framebuffer_size_callback(GLFWwindow* window, int width, int height)
    {
    	cout << "width :  " << width << endl;
    	glViewport(0, 0, width, height);
    }
    
    int main(){
    	//glfw初始化
    	//告诉glfw当前所用的OpenGL的版本号是3.3
    	//告诉glfw当前使用核心模式,意味着我们只能使用OpenGL功能的一个子集(没有我们已不再需要的向后兼容特性)
    	glfwInit();
    	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    
    	//使用glfw创建一个窗口
    	GLFWwindow* window = glfwCreateWindow(800, 600, "Hunk Xu  OpenGL", NULL, NULL);
    	if (window == NULL)
    	{
    		std::cout << "Failed to create GLFW window" << std::endl;
    		glfwTerminate();
    		return -1;
    	}
    	//通知GLFW将window的上下文设置为当前线程的主上下文,设置主活动窗口
    	glfwMakeContextCurrent(window);
    
        
    	//glew初始化
    	if (glewInit() != GLEW_OK){
    		printf("glew init failed");
    		glfwTerminate();
    		return -1;
    	}
    
    
    	unsigned int texture;
    	glGenTextures(1, &texture);
    	glBindTexture(GL_TEXTURE_2D, texture);
    	// 为当前绑定的纹理对象设置环绕、过滤方式
    	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);
    		glGenerateMipmap(GL_TEXTURE_2D);
    	}
    	else
    	{
    		std::cout << "Failed to load texture" << std::endl;
    	}
    	stbi_image_free(data);
    
    
    
    	//告诉OpenGL视口(Viewport)大小
    	//前两个参数为窗口左下角位置
    	//后两个参数渲染窗口的宽和高(像素)
    	glViewport(0, 0, 800, 600);
    	//每当窗口调整大小时候,就调用这个函数
    	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
    
    	float vertices[] = {
    		//     ---- 位置 ----       ---- 颜色 ----     - 纹理坐标 -
    		0.5f, 0.5f, 0.0f,         1.0f, 0.0f, 0.0f,     1.0f, 1.0f,   // 右上
    		0.5f, -0.5f, 0.0f,        0.0f, 1.0f, 0.0f,     1.0f, 0.0f,   // 右下
    		-0.5f, -0.5f, 0.0f,       0.0f, 0.0f, 1.0f,     0.0f, 0.0f,   // 左下
    	};
    	unsigned int indices[] = {  // note that we start from 0!
    		0, 1, 2,  // first Triangle
    	};
    	unsigned int VBO, VAO, 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, 8 * sizeof(float), (void*)0);
    	glEnableVertexAttribArray(0);
    	// 颜色属性
    	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
    	glEnableVertexAttribArray(1);
    
    	glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
    	glEnableVertexAttribArray(2);
    
    
    	//将顶点数据buffer和之前的绑定关系进行解绑 用于打破之前的顶点数据buffer的绑定关系
    	//使OpenGL的顶点数据buffer绑定状态恢复到默认状态。
    	glBindBuffer(GL_ARRAY_BUFFER, 0); 
    
    
    
    	//将VAO绑定到默认的VAO处,一般用于打破之前的VAO绑定关系
    	//使OpenGL的VAO绑定状态恢复到默认状态
    	glBindVertexArray(0);
    
    
    	Shader* myShader = new Shader("vertexSource.txt", "fragementSource.txt");
    
    	glm::mat4 trans;
    	//trans = glm::translate(trans, glm::vec3(-0.5f, 0.0f, 0.0f));   //平移图像
    	//trans = glm::rotate(trans, glm::radians(45.0f), glm::vec3(0.0, 0.0, 1.0));  //旋转图像
    	trans = glm::scale(trans, glm::vec3(1.5f, 1.5f, 1.5f));  //缩放图像
    	//渲染循环
    	while (!glfwWindowShouldClose(window)){
    
    		myShader->use();
    
    
    		glBindTexture(GL_TEXTURE_2D, texture);
    		glBindVertexArray(VAO); 
    
    		unsigned int transformLoc = glGetUniformLocation(myShader->ID, "transform");
    		glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));
    
    		glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0); //3表示3个顶点索引
    
    
    
    
    		//双缓冲(Double Buffer)
    		//前缓冲保存着最终输出的图像,它会在屏幕上显示;
    		//而所有的的渲染指令都会在后缓冲上绘制。
    		//当所有的渲染指令执行完毕后,我们交换(Swap)前缓冲和后缓冲,这样图像就立即呈显出来
    		//不会出现图像闪烁的问题
    		glfwSwapBuffers(window);  
    		glfwPollEvents(); //轮询用户的输入(键盘移动,鼠标输入)
    	}
    	//终止
    	glfwTerminate();
    	return 0;
    }
    
    

    #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 transform;
    
    void main()
    {
        gl_Position = transform*vec4(aPos, 1.0);
        ourColor = aColor;
        TexCoord = aTexCoord;
    }

     下面是改动地方



     


    下面就是放大后的图形 

     


    FR:海涛高软(Hunk Xu)
    QQ技术群:386476712

    展开全文
    qq_15267341 2018-10-28 22:58:37
  • 16.9MB weixin_43919950 2019-10-29 08:47:58
  • 2KB u014495817 2016-02-26 18:53:48
  • 4星
    12.95MB qq_37690918 2018-01-02 09:15:39
  • 5星
    7.87MB oyiyeguzhou123 2012-12-26 16:14:41
  • 4.78MB lanhaixuan1 2019-03-15 13:03:29
  • OpenGL里面的旋转缩放平移其实就是坐标系的相关变换,不过由于初学者(像我)很容易被OpenGL里面各种坐标系搞晕而难以理解,现在将我的理解记录如下。PS: 由于学校课程要求,使用的是过时的固定管线。为了清楚地理解...

    OpenGL里面的旋转缩放与平移其实就是坐标系的相关变换,不过由于初学者(像我)很容易被OpenGL里面各种坐标系搞晕而难以理解,现在将我的理解记录如下。PS: 由于学校课程要求,使用的是过时的固定管线。

    为了清楚地理解这三种变换,我们只关心两个坐标系:建模坐标系世界坐标系。眼坐标之类的使用默认值。文末有一段程序代码,绘制了一个正方体,并实现了这三种变换。

    世界坐标系(WC):你现在正对绘图窗口,右方是世界坐标系的x轴,上方是y轴,从屏幕指向你的是z轴。

    模型坐标系(MC):使用glVertex() 之类的函数定义图元时用的坐标系,初始时MC和WC是重合的。

    下文中的图,用蓝色表示MC,黑色表示WC

    coordinate

    旋转

    旋转时世界坐标系不变,但是建模坐标系会旋转,所以旋转之后MC和WC是不重合的。

    旋转需要选定一个向量以及一个角度,相应的函数为glRotatef()注意:这个向量指的是建模坐标系下的一个向量,建模坐标系绕着这个向量旋转。

    例如:绕y轴旋转30度,向量为(0, 1, 0),角度为30。角度可正可负,opengl里面的是右手坐标系,所以旋转的正方向就是右手大拇指朝向旋转向量所指方向时,四个手指所指方向。下图为绕(0, 1, 0)旋转30度。

    绕y旋转30度之后

    这样其实也不难理解,但是当时被自己写的代码坑到了。

    文末的代码中,旋转是在一对glPushMatrix()glPopMatrix() 中的,所以你在屏幕上看到的图形是由最开始的MC旋转得到的,而不是由上次旋转后的MC再次旋转得到的。

    在我写的程序中显示的图形是由绕x, y, z轴依次旋转而得到的(当然也可以不绕着坐标轴),按下xyz键会改变绕xyz轴旋转的角度r_x, r_y, r_z,然后依次地绕xyz轴旋转而得到最终显示的图形。

    现在有一种情形,绕xyz轴旋转的角度为r_x, r_y, r_z且均不为0,图形已经绘制好了,我现在按着y键,使r_y一直增加,我看到的是图形绕着y轴转动了一定角度吗?

    不是。由于是依次绕着xyz轴旋转,r_y增加,所以受影响的只是基于最开始的MC绘制时,绕y轴旋转的角度,但是绕y轴之后还有绕z轴转动啊,最终的到的图形与之前旋转了r_x,r_y,r_z时相比,并不是绕y轴旋转。如果想直观观察到,可以编译运行下面的代码,先胡乱按xyz,然后按着y键不放。

    平移

    平移时MC相对与WC的位置不变,但是MC里面所有的坐标都将在原来的基础上加上偏移量(dx, dy, dz)。使用的函数为glTranslatef() ,平移相对来说比较容易理解,不详细叙述了。

    缩放

    先说说现实中的缩放,如果你想要一个物体变小,有两个方法:

    • 增加你和物体之间的距离
    • 对物体施加魔法,改变它的实际尺寸

    同理,OpenGL里面你如果想要对一个图形进行缩放,可以用glOrtho() 之类的函数调整投影区域,也可以使用glScalef() 等将图形的各个坐标缩小或放大。

    #include <iostream>
    #include <GL/gl.h>
    #include <GL/glu.h>
    #include <GL/glut.h>
    #include <math.h>
    #include <unistd.h>
    
    /*---------------全局变量---------------------*/
    //--------------------------------------------
    const double PI = 3.14159;
    //旋转角度
    GLfloat r_xyz[3] = {0.0f};
    
    //平移偏移量
    GLfloat dx=0, dy=0, dz=0;
    
    //缩放比例
    double radio = 1.0d;
    
    int cx = 200, cy = 200; //中心坐标窗口
    //窗口大小
    int w = 400;
    int h = 400;
    
    //标记是否着色,默认着色
    int mode = 1;
    
    /*------------------函数声明--------------------*/
    //---------------------------------------------
    void onDisplay();
    void onReshape(int , int);
    void Drawing();
    void myRotate();
    void rgb(int r, int g, int b);
    void specialKeys(int key, int x, int y);
    
    
    void rgb(int r, int g, int b)
    {
        glColor3f(r/255.0f, g/255.0f, b/255.0f);
    }
    
    
    void onDisplay()
    {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glEnable(GL_DEPTH_TEST);
        Drawing();
        glutSwapBuffers();
    }
    
    void onReshape(int width, int height)
    {
        GLfloat aspect = (GLfloat) w/(GLfloat)h;
        GLfloat nRange = 400;
        w = width;
        h = height;
        int min = w > h ? h : w;
        glViewport(0, 0, min, min);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glOrtho(-nRange, nRange, -nRange , nRange, -nRange, nRange);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        glutSwapBuffers();
        glutPostRedisplay();
    }
    
    void myRotate()
    {
        int vec[3][3] = {
            {1, 0, 0},
            {0, 1, 0},
            {0, 0, 1}
        };
        for (int i = 0 ; i <= 2 ; i ++)
        {
            glRotatef(r_xyz[i], vec[i][0], vec[i][1], vec[i][2]);
        }
    }
    void Drawing()
    {
        glPushMatrix();
        glLoadIdentity();
        //旋转
        myRotate();
        glScalef(radio, radio, radio);
        glTranslatef(dx, dy, dz);
    
        if (mode == 2)
        {
            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
            glLineWidth(3);
        }
        else
        {
            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
        }
    
        //顶点向量
        //下面表示一个正方体
        int A[] = {-100, -100, 100};
        int a[] = {-100, -100, -100};
        int B[] = {100, -100, 100};
        int b[] = {100, -100, -100};
        int C[] = {100, 100, 100};
        int c[] = {100, 100, -100};
        int D[] = {-100, 100, 100};
        int d[] = {-100, 100, -100};
    
    
        glBegin(GL_QUADS);
            //front  tomato
            rgb(234, 67, 53);
            glVertex3iv(A);
            glVertex3iv(B);
            glVertex3iv(C);
            glVertex3iv(D);
    
            //fruit salad
            rgb(52, 168, 83);
            glVertex3iv(D);
            glVertex3iv(C);
            glVertex3iv(c);
            glVertex3iv(d);
    
            //gray
            rgb(120, 120, 120);
            glVertex3iv(d);
            glVertex3iv(c);
            glVertex3iv(b);
            glVertex3iv(a);
    
            //white
            rgb(255, 255, 255);
            glVertex3iv(a);
            glVertex3iv(b);
            glVertex3iv(B);
            glVertex3iv(A);
    
            //yellow
            rgb(251, 188, 5);
            glVertex3iv(a);
            glVertex3iv(A);
            glVertex3iv(D);
            glVertex3iv(d);
    
            //blue
            rgb(66, 132, 243);
            glVertex3iv(B);
            glVertex3iv(C);
            glVertex3iv(c);
            glVertex3iv(b);
    
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
    
        glEnd();
        glPopMatrix();
        glutSwapBuffers();
    }
    
    void keyFunc(unsigned char key, int x, int y)
    {
        switch(key)
        {
            case 'z': r_xyz[2] += 2; break;
            case 'Z': r_xyz[2] -= 2; break;
            case 'y': r_xyz[1] += 2; break;
            case 'Y': r_xyz[1] -= 2; break;
            case 'x': r_xyz[0] += 2; break;
            case 'X': r_xyz[0] -= 2; break;
    
            case '-': if (radio > 0)radio -= 0.01; break;
            case '+': radio += 0.01; break;
            case 'q': exit(0);
    
            case '1': mode = 1;break;
            case '2': mode = 2; break;
        }
        glutPostRedisplay();
    }
    
    void specialKeys(int key, int x, int y)
    {
        switch(key)
        {
            case GLUT_KEY_UP: dy += 4;break;
            case GLUT_KEY_DOWN: dy -= 4; break;
            case GLUT_KEY_LEFT: dx -= 4; break;
            case GLUT_KEY_RIGHT: dx += 4; break;
        }
        glutPostRedisplay();
    }
    
    int main(int argc, char** argv)
    {
        glutInit(&argc, argv);
        glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
        glutInitWindowPosition(10, 10);
        glutInitWindowSize(400, 400);
        glutCreateWindow("this is not what I wanted");
    
        glutDisplayFunc(onDisplay);
        glutReshapeFunc(onReshape);
        glutKeyboardFunc(keyFunc);
        glutSpecialFunc(specialKeys);
    
        glutMainLoop();
    }
    展开全文
    charles_neil 2017-11-29 13:22:40
  • 14.59MB yiding6351 2018-05-27 15:53:05
  • OpenGL通常是在3D空间进行操作的,对于2D的情况我们可以把z轴缩放1倍,这样z轴的值就不变了。我们刚刚的缩放操作是不均匀(Non-uniform)缩放,因为每个轴的缩放因子(Scaling Factor)都不一样。如果每个轴的缩放因子都...

    1、矩阵缩放

    对一个向量进行缩放(Scaling)就是对向量的长度进行缩放,而保持它的方向不变。由于我们进行的是2维或3维操作,我们可以分别定义一个有2或3个缩放变量的向量,每个变量缩放一个轴(x、y或z)。
    OpenGL通常是在3D空间进行操作的,对于2D的情况我们可以把z轴缩放1倍,这样z轴的值就不变了。我们刚刚的缩放操作是不均匀(Non-uniform)缩放,因为每个轴的缩放因子(Scaling Factor)都不一样。如果每个轴的缩放因子都一样那么就叫均匀缩放(Uniform Scale)
    我们下面会构造一个变换矩阵来为我们提供缩放功能。我们从单位矩阵了解到,每个对角线元素会分别与向量的对应元素相乘。如果我们把1变为3会怎样?这样子的话,我们就把向量的每个元素乘以3了,这事实上就把向量缩放3倍。如果我们把缩放变量表示为(S1,S2,S3)我们可以为任意向量(x,y,z)定义一个缩放矩阵:
    在这里插入图片描述
    注意,第四个缩放向量仍然是1,因为在3D空间中缩放w分量是无意义的。w分量另有其他用途,在后面我们会看到。

    2、矩阵平移

    位移(Translation)是在原始向量的基础上加上另一个向量从而获得一个在不同位置的新向量的过程,从而在位移向量基础上移动了原始向量。我们已经讨论了向量加法,所以这应该不会太陌生。

    和缩放矩阵一样,在4×4矩阵上有几个特别的位置用来执行特定的操作,对于位移来说它们是第四列最上面的3个值。如果我们把位移向量表示为(Tx,Ty,Tz),我们就能把位移矩阵定义为:
    在这里插入图片描述
    这样是能工作的,因为所有的位移值都要乘以向量的w行,所以位移值会加到向量的原始值上(想想矩阵乘法法则)。而如果你用3x3矩阵我们的位移值就没地方放也没地方乘了,所以是不行的。
    在这里插入图片描述
    有了位移矩阵我们就可以在3个方向(x、y、z)上移动物体,它是我们的变换工具箱中非常有用的一个变换矩阵。

    3、矩阵旋转

    上面几个的变换内容相对容易理解,在2D或3D空间中也容易表示出来,但旋转(Rotation)稍复杂些。如果你想知道旋转矩阵是如何构造出来的,我推荐你去看可汗学院线性代数的视频。
    首先我们来定义一个向量的旋转到底是什么。2D或3D空间中的旋转用角(Angle)来表示。角可以是角度制或弧度制的,周角是360角度或2 PI弧度。我个人更喜欢用角度,因为它们看起来更直观。

    大多数旋转函数需要用弧度制的角,但幸运的是角度制的角也可以很容易地转化为弧度制的:
    弧度转角度:角度 = 弧度 * (180.0f / PI)
    角度转弧度:弧度 = 角度 * (PI / 180.0f)
    PI约等于3.14159265359。

    在3D空间中旋转需要定义一个角和一个旋转轴(Rotation Axis)。物体会沿着给定的旋转轴旋转特定角度。如果你想要更形象化的感受,可以试试向下看着一个特定的旋转轴,同时将你的头部旋转一定角度。当2D向量在3D空间中旋转时,我们把旋转轴设为z轴(尝试想象这种情况)。

    使用三角学,给定一个角度,可以把一个向量变换为一个经过旋转的新向量。这通常是使用一系列正弦和余弦函数(一般简称sin和cos)各种巧妙的组合得到的。当然,讨论如何生成变换矩阵超出了这个教程的范围。

    旋转矩阵在3D空间中每个单位轴都有不同定义,旋转角度用θ表示:
    在这里插入图片描述
    利用旋转矩阵我们可以把任意位置向量沿一个单位旋转轴进行旋转。也可以将多个矩阵复合,比如先沿着x轴旋转再沿着y轴旋转。但是这会很快导致一个问题——万向节死锁(Gimbal Lock,可以看看这个视频(优酷)来了解)。在这里我们不会讨论它的细节,但是对于3D空间中的旋转,一个更好的模型是沿着任意的一个轴,比如单位向量 ( 0.662 , 0.2 , 0.7222 ) (0.662, 0.2, 0.7222) (0.662,0.2,0.7222)旋转,而不是对一系列旋转矩阵进行复合。这样的一个(超级麻烦的)矩阵是存在的,见下面这个公式,其中(Rx,Ry,Rz)代表任意旋转轴:

    在数学上讨论如何生成这样的矩阵仍然超出了本节内容。但是记住,即使这样一个矩阵也不能完全解决万向节死锁问题(尽管会极大地避免)。避免万向节死锁的真正解决方案是使用四元数(Quaternion),它不仅更安全,而且计算会更有效率。
    在这里插入图片描述

    4、矩阵的组合

    使用矩阵进行变换的真正力量在于,根据矩阵之间的乘法,我们可以把多个变换组合到一个矩阵中。让我们看看我们是否能生成一个变换矩阵,让它组合多个变换。假设我们有一个顶点(x, y, z),我们希望将其缩放2倍,然后位移(1, 2, 3)个单位。我们需要一个位移和缩放矩阵来完成这些变换。结果的变换矩阵看起来像这样:

    注意,当矩阵相乘时我们先写位移再写缩放变换的。矩阵乘法是不遵守交换律的,这意味着它们的顺序很重要。当矩阵相乘时,在最右边的矩阵是第一个与向量相乘的,所以你应该从右向左读这个乘法。建议您在组合矩阵时,先进行缩放操作,然后是旋转,最后才是位移,否则它们会(消极地)互相影响。比如,如果你先位移再缩放,位移的向量也会同样被缩放(译注:比如向某方向移动2米,2米也许会被缩放成1米)!

    用最终的变换矩阵左乘我们的向量会得到以下结果:
    在这里插入图片描述

    不错!向量先缩放2倍,然后位移了(1, 2, 3)个单位。
    在这里插入图片描述

    展开全文
    m0_37251750 2021-03-24 11:02:54
  • 5.22MB qq_32800087 2015-11-14 10:31:13
  • 有了刚才平移变换的基础,我们知道了旋转变换其实也就是找到一个旋转矩阵,来与我们位置坐标相乘能得到旋转后的位置坐标 3.1 推导 旋转变换示意图如下: 示意图为从z轴的正方向朝负方向看,从位置1...

    代码放在github上

    本文根据教程:ogldev进行扩充学习,一步步从零开始,记录学习历程

    一、准备工作

    1.1 表示一个矩阵

    我们需要在程序中使用矩阵,我们需要用计算机程序语言来表示出来一个矩阵。

    我们用数组来表示一个矩阵,并且规定列填充优先,即一个4*4矩阵表示为:

    //      4 * 4 矩阵:
    //      0       4       8       12
    //      1       5       9       13
    //      2       6       10      14
    //      3       7       11      15
    typedef float Matrix44f[16];    

    此时如果要将一个4*4的矩阵设置为单位矩阵可以这样操作:

    inline void LoadIdentity44(Matrix44f m)
    {
        m[0] = 1.0f; m[4] = 0.0f; m[8] = 0.0f;  m[12] = 0.0f;
        m[1] = 0.0f; m[5] = 1.0f; m[9] = 0.0f;  m[13] = 0.0f;
        m[2] = 0.0f; m[6] = 0.0f; m[10] = 1.0f; m[14] = 0.0f;
        m[3] = 0.0f; m[7] = 0.0f; m[11] = 0.0f; m[15] = 1.0f;
    }

    1.2 矩阵相乘

    学过线性代数的同学应该都应该记得矩阵的乘法,这里为了更加扎实的进一步学习,先复习一下矩阵的乘法。

    百度百科中的讲解:矩阵乘法

    这里写图片描述
    - 首先要明确只有当矩阵A的列数等于矩阵B的行数时,A和B才可以相乘
    - 矩阵C的行数等于矩阵A的行数,C的列数等于B的列数
    - 乘积C的第m行第n列的元素等于矩阵A的第m行的元素与矩阵B的第n列对应元素乘积之和

    代码种得到两个矩阵相乘的结果:

    inline void MatrixMultiply44(Matrix44f product, const Matrix44f a, const Matrix44f b)
    {
        unsigned int j, k;
        for (unsigned int i = 0; i < 16; i++) {
            j = i % 4;
            k = i / 4 * 4;
            product[i] = a[j] * b[k] + a[j + 4] * b[k + 1] + a[j + 8] * b[k + 2] + a[j + 12] * b[k + 3];
        }
    }

    1.3 角度和弧度

    弧度的定义:弧长等于半径的弧,其所对的圆心角为1弧度

    一周的弧度是2π,一周的角度是360°

    弧度= 角度 * ( π / 180 )

    角度 = 弧度 * ( 180 / π )

    我们在程序中使用的sinf(),cosf(),tanf()参数都是弧度,如果我们使用角度,则需要把角度转化为弧度

    代码中角度弧度互相转化

    #define PI (3.14159265358979323846)
    #define PI_DIV_180 (0.017453292519943296)
    #define INV_PI_DIV_180 (57.2957795130823229)
    
    #define DegToRad(x)  ((x)*PI_DIV_180)
    #define RadToDeg(x)  ((x)*INV_PI_DIV_180)

    二、平移变换

    平移变换示意图如下:

    这里写图片描述
    我们的任务便是找到一个矩阵M(称为平移矩阵),对于给定的点P(x,y,z)和平移向量V(v1,v2,v3),使得M*P=P1(x+v1,y+v2,z+v3)

    最后通过种种努力,发现了一个4*4的矩阵可以达到这种效果:

    这里写图片描述
    - 像这样使用4维向量表示一个三维向量叫做齐次坐标
    - 4维向量的四个分量分别是X、Y、Z、W,之前看到的着色器中的内部变量gl_Position就是一个四维向量
    - 通常表示点的矩阵让W=1,表示向量的矩阵让W=0,因为点可以被做变换而向量不可以
    - 总之先不要管不懂的地方,只要知道平移矩阵我们已经找到了,我们只需要在程序中把平移矩阵与我们的图形的位置坐标相乘就能得到平移后的图形了

    在代码中设置一个平移矩阵:

    inline void TranslationMatrix44(Matrix44f m, float x, float y, float z)
    {
        LoadIdentity44(m); m[12] = x; m[13] = y; m[14] = z;
    }

    三、旋转变换

    有了刚才平移变换的基础,我们知道了旋转变换其实也就是找到一个旋转矩阵,来与我们位置坐标相乘能得到旋转后的位置坐标

    3.1 推导

    旋转变换示意图如下:

    这里写图片描述
    示意图为从z轴的正方向朝负方向看,从位置1旋转到位置2(即从(x1,y1)到(x2,y2)),如果圆的半径为1(即是单位圆),则可以得到:
    这里写图片描述

    再由高中数学三角函数公式可以推导:
    这里写图片描述

    此时找到了旋转α度角之后的x,y坐标,此时只需要找到旋转矩阵与原坐标向量相乘等于新的坐标向量即可

    3.2 旋转矩阵

    绕z轴旋转(z轴不变):
    这里写图片描述

    绕y轴旋转(y轴不变):
    image

    绕x轴旋转(x轴不变):
    这里写图片描述

    代码设置一个旋转矩阵:

    inline void RotationMatrix44(Matrix44f m, float angle, float x, float y, float z)
    {
        LoadIdentity44(m);
        if (z == 1)//绕z轴
        {
            m[0] = cosf(angle); m[4] = -sinf(angle);
            m[1] = sinf(angle); m[5] = cosf(angle);
        }
        else if (y == 1)//绕y轴
        {
            m[0] = cosf(angle); m[8] = -sinf(angle);
            m[2] = sinf(angle); m[10] = cosf(angle);
        }
        else if (x == 1)//绕x轴
        {
            m[5] = cosf(angle); m[9] = -sinf(angle);
            m[6] = sinf(angle); m[10] = cosf(angle);
        }
    }
    inline void RotationMatrix44(Matrix44f m, float RotateX, float RotateY, float RotateZ)
    {
        Matrix44f rx, ry, rz, temp;
    
        const float x = DegToRad(RotateX);
        const float y = DegToRad(RotateY);
        const float z = DegToRad(RotateZ);
    
        RotationMatrix44(rx, x, 1, 0, 0);
        RotationMatrix44(ry, y, 0, 1, 0);
        RotationMatrix44(rz, z, 0, 0, 1);
    
        MatrixMultiply44(temp, rz, ry);
        MatrixMultiply44(m,temp, rx);
    
    }

    四、缩放变换

    缩放变换可以由平移矩阵推导出来
    这里写图片描述
    这里写图片描述

    代码设置一个缩放矩阵:

    inline void ScaleMatrix44(Matrix44f m, float xScale, float yScale, float zScale)
    {
        LoadIdentity44(m); m[0] = xScale; m[5] = yScale; m[10] = zScale;
    }

    五、代码解释

    5.1 opengl_math.h:

    #ifndef __OPENGL_MATH_H
    #define __OPENGL_MATH_H
    
    #include <math.h>
    #include <string.h>
    
    #define PI (3.14159265358979323846)
    #define PI_DIV_180 (0.017453292519943296)
    #define INV_PI_DIV_180 (57.2957795130823229)
    
    #define DegToRad(x)  ((x)*PI_DIV_180)
    #define RadToDeg(x)  ((x)*INV_PI_DIV_180)
    //向量       
    typedef float   Vector3f[3];                
    
    //向量赋值
    
    inline void LoadVector3(Vector3f v, const float x, const float y, const float z)
    {
        v[0] = x; v[1] = y; v[2] = z;
    }
    //缩放向量
    inline void ScaleVector3(Vector3f v, const float scale)
    {
        v[0] *= scale; v[1] *= scale; v[2] *= scale;
    }
    //  4 * 4 矩阵:
    //      0       4       8       12
    //      1       5       9       13
    //      2       6       10      14
    //      3       7       11      15
    typedef float Matrix44f[16];         
    //4*4单位矩阵
    inline void LoadIdentity44(Matrix44f m)
    {
        m[0] = 1.0f; m[4] = 0.0f; m[8] = 0.0f;  m[12] = 0.0f;
        m[1] = 0.0f; m[5] = 1.0f; m[9] = 0.0f;  m[13] = 0.0f;
        m[2] = 0.0f; m[6] = 0.0f; m[10] = 1.0f; m[14] = 0.0f;
        m[3] = 0.0f; m[7] = 0.0f; m[11] = 0.0f; m[15] = 1.0f;
    }
    //4*4矩阵相乘
    inline void MatrixMultiply44(Matrix44f product, const Matrix44f a, const Matrix44f b)
    {
        unsigned int j, k;
        for (unsigned int i = 0; i < 16; i++) {
            j = i % 4;
            k = i / 4 * 4;
            product[i] = a[j] * b[k] + a[j + 4] * b[k + 1] + a[j + 8] * b[k + 2] + a[j + 12] * b[k + 3];
        }
    }
    //缩放变换
    inline void ScaleMatrix44(Matrix44f m, float xScale, float yScale, float zScale)
    {
        LoadIdentity44(m); m[0] = xScale; m[5] = yScale; m[10] = zScale;
    }
    //旋转变换
    inline void RotationMatrix44(Matrix44f m, float angle, float x, float y, float z)
    {
        LoadIdentity44(m);
        if (z == 1)//绕z轴
        {
            m[0] = cosf(angle); m[4] = -sinf(angle);
            m[1] = sinf(angle); m[5] = cosf(angle);
        }
        else if (y == 1)//绕y轴
        {
            m[0] = cosf(angle); m[8] = -sinf(angle);
            m[2] = sinf(angle); m[10] = cosf(angle);
        }
        else if (x == 1)//绕x轴
        {
            m[5] = cosf(angle); m[9] = -sinf(angle);
            m[6] = sinf(angle); m[10] = cosf(angle);
        }
    }
    inline void RotationMatrix44(Matrix44f m, float RotateX, float RotateY, float RotateZ)
    {
        Matrix44f rx, ry, rz, temp;
    
        const float x = DegToRad(RotateX);
        const float y = DegToRad(RotateY);
        const float z = DegToRad(RotateZ);
    
        RotationMatrix44(rx, x, 1, 0, 0);
        RotationMatrix44(ry, y, 0, 1, 0);
        RotationMatrix44(rz, z, 0, 0, 1);
    
        MatrixMultiply44(temp, rz, ry);
        MatrixMultiply44(m,temp, rx);
    
    }
    //平移变换
    inline void TranslationMatrix44(Matrix44f m, float x, float y, float z)
    {
        LoadIdentity44(m); m[12] = x; m[13] = y; m[14] = z;
    }
    #endif

    这次我们自创的3d函数头文件中,增加了4*4矩阵和对其的相关操作(所有操作在上面已经解释过),都定义为内联函数,如果需要引用则只需给好参数在主程序中引用即可。

    5.2 main.cpp:

    #include <stdio.h>
    #include <string>
    #include <math.h>
    #include <gl/glew.h>
    #include <gl/freeglut.h>
    #include <fstream>
    #include <assert.h>
    #include "opengl_math.h"
    
    using namespace std;
    
    GLuint VBO;
    GLint gWorldLocation1;
    GLint gWorldLocation2;
    GLint gWorldLocation3;
    
    const char* pVSFileName = "shader.vs";
    const char* pFSFileName = "shader.fs";
    
    bool ReadFile(const char* pFileName, string &outFile)
    {
        ifstream f(pFileName);
    
        bool ret = false;
    
        if (f.is_open()) {
            string line;
            while (getline(f, line)) {
                outFile.append(line);
                outFile.append("\n");
            }
            f.close();
            ret = true;
        }
        else {
            fprintf(stderr, "%s:%d: unable to open file '%s'\n", __FILE__, __LINE__, pFileName);
            system("pause");
        }
        return ret;
    }
    
    static void Render()
    {
        glClear(GL_COLOR_BUFFER_BIT);
    
        static float Scale = 0.0f;
    
        Scale += 0.001f;
    
        Matrix44f World1;
        Matrix44f World2;
        Matrix44f World3;
    
        TranslationMatrix44(World1, sinf(Scale), 0.0f, 0.0f);
    
        RotationMatrix44(World2, Scale, 0, 0, 1);
    
        Vector3f vScale;
        LoadVector3(vScale, sinf(Scale), sinf(Scale), sinf(Scale));
        ScaleMatrix44(World3,vScale[0], vScale[1], vScale[2]);
    
        glUniformMatrix4fv(gWorldLocation1, 1, GL_FALSE, &World1[0]);//GL_FLASE: 数组列优先
        glUniformMatrix4fv(gWorldLocation2, 1, GL_FALSE, &World2[0]);
        glUniformMatrix4fv(gWorldLocation3, 1, GL_FALSE, &World3[0]);
    
        glEnableVertexAttribArray(0);
        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
    
        glDrawArrays(GL_TRIANGLES, 0, 3);
        glDisableVertexAttribArray(0);
        glutSwapBuffers();
    }
    
    
    static void CreateVertexBuffer()
    {
        Vector3f Vertices[3];
    
        LoadVector3(Vertices[0], -1.0f, -1.0f, 0.0f);
        LoadVector3(Vertices[1], 1.0f, -1.0f, 0.0f);
        LoadVector3(Vertices[2], 0.0f, 1.0f, 0.0f);
    
        glGenBuffers(1, &VBO);
        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);
    
    }
    
    static void AddShader(GLuint ShaderProgram, const char* pShaderText, GLenum ShaderType)
    {
        GLuint ShaderObj = glCreateShader(ShaderType);
        if (ShaderObj == 0) {
            fprintf(stderr, "Error creating shader type %d\n", ShaderType);
            system("pause");
            exit(1);
        }
    
        const GLchar* p[1];
        p[0] = pShaderText;
        GLint Lengths[1];
        Lengths[0] = strlen(pShaderText);
        glShaderSource(ShaderObj, 1, p, Lengths);
        glCompileShader(ShaderObj);
        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);
            system("pause");
            exit(1);
        }
        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);
    
        GLint Success = 0;
        GLchar ErrorLog[1024] = { 0 };
    
        glLinkProgram(ShaderProgram);
        glGetProgramiv(ShaderProgram, GL_LINK_STATUS, &Success);
        if (!Success) {
            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);
        }
        glUseProgram(ShaderProgram);
    
        gWorldLocation1 = glGetUniformLocation(ShaderProgram, "gWorld1");
        assert(gWorldLocation1 != 0xFFFFFFFF);
    
        gWorldLocation2 = glGetUniformLocation(ShaderProgram, "gWorld2");
        assert(gWorldLocation2 != 0xFFFFFFFF);
    
        gWorldLocation3 = glGetUniformLocation(ShaderProgram, "gWorld3");
        assert(gWorldLocation3 != 0xFFFFFFFF);
    }
    
    
    static void InitializeGlutCallbacks()
    {
        glutDisplayFunc(Render);
    
        glutIdleFunc(Render);
    }
    
    int main(int argc, char **argv)
    {
        glutInit(&argc, argv);
        glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
        glutInitWindowSize(1024, 768);
        glutInitWindowPosition(10, 10);
        glutCreateWindow("Transformation");
    
        InitializeGlutCallbacks();
    
        GLenum res = glewInit();
        if (res != GLEW_OK) {
            fprintf(stderr, "Error: '%s'\n", glewGetErrorString(res));
            system("pause");
            return 1;
        }
    
        printf("GL version: %s\n", glGetString(GL_VERSION));
    
        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    
        CreateVertexBuffer();
    
        CompileShaders();
    
        glutMainLoop();
    
        return 0;
    
    
    }

    这里只说明与上一节不一样的地方

    5.2.1 获取Uniform变量的位置

    gWorldLocation1 = glGetUniformLocation(ShaderProgram, "gWorld1");
    assert(gWorldLocation1 != 0xFFFFFFFF);
    gWorldLocation2 = glGetUniformLocation(ShaderProgram, "gWorld2");
    assert(gWorldLocation2 != 0xFFFFFFFF);
    gWorldLocation3 = glGetUniformLocation(ShaderProgram, "gWorld3");
    assert(gWorldLocation3 != 0xFFFFFFFF);

    这次我们声明了三个uniform变量,分别代表平移矩阵、旋转矩阵和缩放矩阵,所以这里需要使用glGetUniformLocation()函数三次,分别找到三个uniform变量在列表中的索引

    5.2.2 设置Uniform变量的值

        Matrix44f World1, World2,World3;
        TranslationMatrix44(World1, sinf(Scale), 0.0f, 0.0f);
    
        RotationMatrix44(World2, Scale, 0, 0, 1);
    
        Vector3f vScale;
        LoadVector3(vScale, sinf(Scale), sinf(Scale), sinf(Scale));
        ScaleMatrix44(World3,vScale[0], vScale[1], vScale[2]);
    
        glUniformMatrix4fv(gWorldLocation1, 1, GL_FALSE, &World1[0]);//GL_FLASE: 数组列优先
        glUniformMatrix4fv(gWorldLocation2, 1, GL_FALSE, &World2[0]);
        glUniformMatrix4fv(gWorldLocation3, 1, GL_FALSE, &World3[0]);
    • 这里引用了内联函数TranslationMatrix44()来得到平移矩阵,并且是在x轴上做平移运动,从x轴的-1到1
    • 引用RotationMatrix44()得到旋转矩阵,并且是绕z轴旋转
    • ScaleMatrix44()得到缩放矩阵
    • 这里因为要传送一个4*4的矩阵Uniform变量值到着色器中,所以使用glUniformMatrix4fv()函数
    void glUniformMatrix {234}{fd}v(GLint location , GLsizei count ,
    GLboolean transpose,
    const GLfloat * values );
    • location是索引位置,载入count个数据的集合,transpose设置数组行优先还是数组列优先,values是要传入的值的地址
    • 因为我们的数组定义的是列填充优先,所以第三个参数要设置为GL_FALSE
    • 这里我们用一个长度为16的数组传递16个float值作Uniform变量的值,所以第二个参数为1,第四个参数是&Worldx[0]

    5.3 着色器

    5.3.1 shader.vs:

    #version 330
    
    layout (location=0) in vec3 Position;
    
    uniform mat4 gWorld1;
    uniform mat4 gWorld2;
    uniform mat4 gWorld3;
    
    
    void main()
    {
        gl_Position =  gWorld1*gWorld2*gWorld3 * vec4(Position,1.0);
    }

    可以看到这次声明了三个4*4的矩阵变量,分别从程序中接收平移矩阵,旋转矩阵和缩放矩阵的值。
    并且内置变量gl_Position最后的值是三个矩阵相乘后再乘顶点坐标,即先缩放再旋转再平移

    5.3.1 shader.fs:

    #version 330
    
    out vec4 FragColor;
    
    void main()
    {
        FragColor = vec4(1.0,0.0,0.0,1.0);
    }

    片元着色器没有变化,还是让渲染的图形颜色为红

    六、运行结果

    这里写图片描述
    可以看到红色的三角形在x轴的-1到1间来回移动,并且在xy平面上旋转,并且由大变小再由小变大做缩放运动

    展开全文
    Zach_z 2018-04-25 00:17:22
  • weixin_34377065 2017-11-10 16:22:00
  • 12.22MB u014552102 2019-03-04 20:52:49
  • 4星
    3KB u011526600 2015-05-07 19:12:27
  • z896435317 2017-08-30 14:47:15
  • 94.94MB cao_jie_xin 2019-05-05 15:25:25
  • 5星
    110KB pathfinder1987 2013-05-10 15:50:32
  • 1.41MB gmajvfhp 2018-06-04 11:17:11
  • 4星
    340KB mz_liu 2010-03-04 16:28:25
  • wang_huizhang 2018-06-06 15:05:10
  • 5星
    2.1MB qshbbh 2013-03-14 10:13:07
  • 148KB limeng918 2020-10-31 16:00:28
  • weixin_44326002 2019-08-06 19:27:01
  • 44KB weixin_38631329 2020-08-29 03:49:32

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 6,819
精华内容 2,727
关键字:

opengl平移缩放旋转