zoukankan      html  css  js  c++  java
  • 【OpenGL】OpenGL绘图的一点理解

    主要是在这篇文章的基础上理解的,唉,这东西真麻烦,肖姐姐基本没讲OpenGL的具体工作,其实现在关于OpenGL有了很多新技术,但是按肖姐姐给的库,很多还都是很早的,已经是弃用或者不推荐使用的库,虽然可以说原理都一样,但是……为了实现作业,乱七八糟的搜资料,很多时间都是浪费在这上面,而且各种实现,比如在这个网上它应该是使用了最新的OpenGL3.3+版本,就和之前的不太一样,真是缭乱在风雨中啊,好吧,吐槽完毕……

    我就按它源代码的顺序记下自己现在的理解,因为不记下来我自己又会凌乱的。首先声明,这篇文章并没有从头讲怎么使用OpenGL,只是讲了一些对于不懂的地方的理解,如果要具体学习,还请移步该网址

    1.初始化工作。这里都是一些固定的初始化,就不讲了,具体的我还不是很清楚。比较重要的是这里启用了深度测试:

    // Enable depth test
    glEnable(GL_DEPTH_TEST);
    // Accept fragment if it closer to the camera than the former one
    glDepthFunc(GL_LESS); 

    2.创建一个顶点数组对象,并将它设为当前对象(细节暂不深入)。这里其实只是做了一个初始化的工作,后面在绘图工作中并没有用到这个数据:

    GLuint VertexArrayID;		// 该顶点数组对象的ID
    glGenVertexArrays(1, &VertexArrayID);		// 真正创建一个数组,返回值传给VertexArrayID
    glBindVertexArray(VertexArrayID);		// 设置为当前对象

    当窗口创建成功后(即OpenGL上下文创建后),马上做这一步工作;必须在任何其他OpenGL调用前完成。

    我的理解是,这里是把顶点数据进行分组。比如这个数组里的顶点用来绘一个正方体,那个数组用来绘一个三角形。对于每一个这样的顶点数组对象,想要把它下面的数据绘在屏幕上都需要经过下面的几个步骤。

    最后的这句话将告诉下面创建的缓冲区啊什么的,都是这个数组对象的。

    3. 载入外部shader。

            // 从外部shader中,创建并编译我们的GLSL(OpenGL Shader Language)
    	GLuint programID = LoadShaders( "TransformVertexShader.vertexshader", "ColorFragmentShader.fragmentshader" );
    
    	// 得到vertaxshader中的“MVP” uniform的句柄
    	GLuint MatrixID = glGetUniformLocation(programID, "MVP");
    
    	// 透视矩阵 : 45?Field of View, 4:3 ratio, display range : 0.1 unit <-> 100 units
    	glm::mat4 Projection = glm::perspective(45.0f, 4.0f / 3.0f, 0.1f, 100.0f);
    	// 视图矩阵
    	glm::mat4 View       = glm::lookAt(
    								glm::vec3(4,3,-3), // Camera is at (4,3,-3), in World Space
    								glm::vec3(0,0,0), // and looks at the origin
    								glm::vec3(0,1,0)  // Head is up (set to 0,-1,0 to look upside-down)
    						   );
    	// 模型矩阵 : an identity matrix (model will be at the origin)
    	glm::mat4 Model      = glm::mat4(1.0f);
    	// 我们的 ModelViewProjection : multiplication of our 3 matrices,在主循环中将会传递给上述的MVP句柄,从而改变视角
    	glm::mat4 MVP        = Projection * View * Model; // Remember, matrix multiplication is the other way around


    下面就是两个shader的内容。

    TransformVertexShader.vertexshader:

    #version 330 core
    
    // 输入的顶点数据,这里是和下面将要讲的顶点属性一一对应,下面这些值实际上是靠我们将要定义在内存中的数据(如位置,颜色)来初始化的.
    layout(location = 0) in vec3 vertexPosition_modelspace;
    layout(location = 1) in vec3 vertexColor;
    
    // 输出数据: will be interpolated for each fragment.
    out vec3 fragmentColor;
    // Values that stay constant for the whole mesh.
    uniform mat4 MVP;
    
    void main(){	
    
    	// Output position of the vertex, in clip space : MVP * position
    	gl_Position =  MVP * vec4(vertexPosition_modelspace,1);
    
    	// The color of each vertex will be interpolated
    	// to produce the color of each fragment
    	fragmentColor = vertexColor;
    }
    
    ColorFragmentShader.fragmentshader:

    #version 330 core
    
    // 从上面vertaxshader中传入的数据,经过了插值后的值
    in vec3 fragmentColor;
    
    // 输出的数据,这里我没有懂,好像OpenGL直接就使用这个值用于绘制颜色了
    out vec3 color;
    
    void main(){
    
    	// Output color = 我们在vertex shader中定义的颜色
    	// interpolated between all 3 surrounding vertices
    	color = fragmentColor;
    
    }

    4.现在需要告诉OpenGL有哪些内容,换句话就是说,应该往屏幕上画些什么。首先我们要在内存中定义这些顶点:

    // 包含了3个向量的数组,表示3个顶点
    static const GLfloat g_vertex_buffer_data[] = { 
    		-1.0f,-1.0f,-1.0f,
    		-1.0f,-1.0f, 1.0f,
    		-1.0f, 1.0f, 1.0f,
    		 1.0f, 1.0f,-1.0f,
    		-1.0f,-1.0f,-1.0f,
    		-1.0f, 1.0f,-1.0f,
    		 1.0f,-1.0f, 1.0f,
    		-1.0f,-1.0f,-1.0f,
    		 1.0f,-1.0f,-1.0f,
    		 1.0f, 1.0f,-1.0f,
    		 1.0f,-1.0f,-1.0f,
    		-1.0f,-1.0f,-1.0f,
    		-1.0f,-1.0f,-1.0f,
    		-1.0f, 1.0f, 1.0f,
    		-1.0f, 1.0f,-1.0f,
    		 1.0f,-1.0f, 1.0f,
    		-1.0f,-1.0f, 1.0f,
    		-1.0f,-1.0f,-1.0f,
    		-1.0f, 1.0f, 1.0f,
    		-1.0f,-1.0f, 1.0f,
    		 1.0f,-1.0f, 1.0f,
    		 1.0f, 1.0f, 1.0f,
    		 1.0f,-1.0f,-1.0f,
    		 1.0f, 1.0f,-1.0f,
    		 1.0f,-1.0f,-1.0f,
    		 1.0f, 1.0f, 1.0f,
    		 1.0f,-1.0f, 1.0f,
    		 1.0f, 1.0f, 1.0f,
    		 1.0f, 1.0f,-1.0f,
    		-1.0f, 1.0f,-1.0f,
    		 1.0f, 1.0f, 1.0f,
    		-1.0f, 1.0f,-1.0f,
    		-1.0f, 1.0f, 1.0f,
    		 1.0f, 1.0f, 1.0f,
    		-1.0f, 1.0f, 1.0f,
    		 1.0f,-1.0f, 1.0f
    	};

    这里只是在内存里定义了所有点的位置信息,但是还没有跟OpenGL联系起来。所以接下来就要把这个三角形传给OpenGL。我们通过创建一个缓冲区完成:

    // 用于标识缓冲区
    GLuint vertexbuffer;
    
    // 生成一个缓冲区,并将返回的标识符传给vertexbuffer
    glGenBuffers(1, &vertexbuffer);
    
    // 讲vertexbuffer设置为当前的数组缓冲对象,也就是告诉OpenGL,好了,我下面都是对这个缓冲区进行操作的
    glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
    
    // 将之前定义的数据传给OpenGL
    glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);

    这只要做一次。它所做的实际上是告诉OpenGL,标识符为vertexbuffer的缓冲区里放了哪些数据,以后你画的时候就要根据这些数据进行绘图。我们可以定义多个缓冲区

    因此颜色缓冲区的操作类似。



            // One color for each vertex. They were generated randomly.
    	static const GLfloat g_color_buffer_data[] = { 
    		0.583f,  0.771f,  0.014f,
    		0.609f,  0.115f,  0.436f,
    		0.327f,  0.483f,  0.844f,
    		0.822f,  0.569f,  0.201f,
    		0.435f,  0.602f,  0.223f,
    		0.310f,  0.747f,  0.185f,
    		0.597f,  0.770f,  0.761f,
    		0.559f,  0.436f,  0.730f,
    		0.359f,  0.583f,  0.152f,
    		0.483f,  0.596f,  0.789f,
    		0.559f,  0.861f,  0.639f,
    		0.195f,  0.548f,  0.859f,
    		0.014f,  0.184f,  0.576f,
    		0.771f,  0.328f,  0.970f,
    		0.406f,  0.615f,  0.116f,
    		0.676f,  0.977f,  0.133f,
    		0.971f,  0.572f,  0.833f,
    		0.140f,  0.616f,  0.489f,
    		0.997f,  0.513f,  0.064f,
    		0.945f,  0.719f,  0.592f,
    		0.543f,  0.021f,  0.978f,
    		0.279f,  0.317f,  0.505f,
    		0.167f,  0.620f,  0.077f,
    		0.347f,  0.857f,  0.137f,
    		0.055f,  0.953f,  0.042f,
    		0.714f,  0.505f,  0.345f,
    		0.783f,  0.290f,  0.734f,
    		0.722f,  0.645f,  0.174f,
    		0.302f,  0.455f,  0.848f,
    		0.225f,  0.587f,  0.040f,
    		0.517f,  0.713f,  0.338f,
    		0.053f,  0.959f,  0.120f,
    		0.393f,  0.621f,  0.362f,
    		0.673f,  0.211f,  0.457f,
    		0.820f,  0.883f,  0.371f,
    		0.982f,  0.099f,  0.879f
    	};
    
    	GLuint colorbuffer;
    	glGenBuffers(1, &colorbuffer);
    	glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
    	glBufferData(GL_ARRAY_BUFFER, sizeof(g_color_buffer_data), g_color_buffer_data, GL_STATIC_DRAW);


    总结一下,对于顶点位置信息和顶点颜色信息,实际上都是 在内存中定义数据——为其生成一个缓冲区——用glBindBuffer将刚刚生成的缓冲区设置为当前值——使用定义好的数据填充缓冲区。这样就完成了所有需要的缓冲区的创建工作。


    5.准备工作都已经做好了,现在进入我们的主循环中。
    在经过了一些屏幕清除工作后,我们要告诉OpenGL,我们要使用自己的shader。

                    // 使用我们的shader
    		glUseProgram(programID);
    
    
    
    
    		// 把我们的变换矩阵传送给当前绑定的shader
    		// 你应该还记得MatrixID是我们得到shader中名字为"MVP" uniform的句柄
    		glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);


    接下来就是使用缓冲区来绘图了。我们需要把缓冲区中的数据和shader中的各个值进行对应。
                   
                    // 1rst attribute buffer : vertices 开启了某一个顶点属性后,在渲染时就会使用这个属性。这句话的位置是无所谓的,只要在渲染前开启就可以了。
    		glEnableVertexAttribArray(0);
    		// 这里再次调用了glBindBuffer函数,上一次调用(见上)是为了给这个缓冲区填充数据,现在是为了使用vertexbuffer缓冲区
    		glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); 
    		// 这里按我现在的理解是,是将这个缓冲区和属性0绑定了。也就是说,这个缓冲区里的数据用来指定每个点属性0的值
    		glVertexAttribPointer(
    		   0,                  // 属性 0。没有特别的原因必须使用0,但是一定要与shader中的布局匹配
    		   3,                  // 大小
    		   GL_FLOAT,           // 类型
    		   GL_FALSE,           // 是否正则化
    		   0,                  // 步幅
    		   (void*)0            // 数组缓冲区偏移
    		);


    如果还是不懂,往上翻看到vertaxshader,有这样一行代码:
    layout(location = 0) in vec3 vertexPosition_modelspace;


    实际上就是把vertexbuffer里的数据作为属性0传给了shader,shader中这个值被赋给了名字为vertexPosition_modelspace的变量,并用于gl_Position的赋值,gl_Position是内定的变量,就是点的位置。
    颜色数据的对应也是类似的。

                    // 2nd attribute buffer : colors
    		glEnableVertexAttribArray(1);
    		glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
    		glVertexAttribPointer(
    			1,                                // attribute. No particular reason for 1, but must match the layout in the shader.
    			3,                                // size
    			GL_FLOAT,                         // type
    			GL_FALSE,                         // normalized?
    			0,                                // stride
    			(void*)0                          // array buffer offset
    		);
    


    6. 呼呼,终于做完了。下面就使用shader把图形画在屏幕上!还要记得把之前用到的属性再次disable掉。
                    // Draw the triangle !
    		glDrawArrays(GL_TRIANGLES, 0, 12*3); // 12*3 indices starting at 0 -> 12 triangles
    
    
    
    
    		glDisableVertexAttribArray(0);
    		glDisableVertexAttribArray(1);
    
    
    
    
    		// Swap buffers
    		glfwSwapBuffers();



    思考:
    如果要绘制两个图形呢?比如一个正方体和一个三角形。我试了下,简单的话就是直接在定义顶点数据时再加上3个点就行了。但这样是不太科学的,扩展性也不好,又试了下定义两个顶点数据对象(见第2步),然后分别绑定各自的缓冲区,最后在主循环中通过glBindVertexArray(VertexArrayID);来进行切换,也是可以的。

    事实上,我们应当使用后面一种方法,也就是通过切换不同的VBO来绘制不同的图形。


  • 相关阅读:
    CI工具Jenkins的安装配置【linux】——jenkins集成sonarqube-异常解决
    高可用架构,期刊下载
    struct
    Fragment与Activity相互传递值
    Android ble (蓝牙低功耗)使用注意事项(转)
    Android ble蓝牙问题(转)
    Android-BlutoothBle,蓝牙中心设备(peripheral)向外围设备(GattServer)连续写入多个Characteristic的注意事项
    Android滑动导航菜单TabLayout+ViewPager+Fragment
    Material Design:TabLayout的使用
    Android-BLE蓝牙原理
  • 原文地址:https://www.cnblogs.com/xiaowangba/p/6314732.html
Copyright © 2011-2022 走看看