OpenGL(一) OpenGL管线 与 可编程管线流程

由于OpenGL是一个纯渲染核心,要写OpenGL程序,得先搭建一个外壳程序。不同平台下外壳程序的实现各有不同,这个系列的文章都在win32的平台上进行。具体实现,网上能找到很多,所以这不是本文的重点。本篇主要构建探讨 OpenGL管线 本身,具体说来,就是构建可编程管线。

主体流程

大体流程是:

  1. 在main函数中拉起外壳程序,初始化一些默认参数。例如双缓冲,32位颜色等。
  2. 创建GL上下文(HGLRC)。
  3. 加载&设置OpenGL的相关参数
  4. 在帧循环中进行绘制

至于固定管线和可编程管线,主要区别在3、4部分。固定管线是将绘制的方法写死到代码中,而可编程管线可以理解为脚本化了渲染的逻辑。不过这两者渲染的数据部分仍由编译后的程序提供。

具体说来,通过一种语法定义,将一个文本文件实例化到内存中,并且在运行时动态编译,挂载,链接到GPU程序中,完成绘制算法的替换。对应的,由于所有数据都放在内存中,绘制时,需要将它传输到GPU上,这就是缓冲区(glBuffer)。缓冲区在CPU上,会将在某些时间段内缓冲区的数据一起提交给GPU。

实现

总的来看,可编程管线动态生成了一段绘制程序,在每个GPU核心通过绘制命令,绘制传给他数据。简单的看下两部分的代码片段:

首先是创建GPU绘制程序

GLuint CreateGPUProgram(const char* vsShaderPath,const char* fsShaderPath)
{
    GLuint vsShader = glCreateShader(GL_VERTEX_SHADER);
    GLuint fsShader = glCreateShader(GL_FRAGMENT_SHADER);

    //Load
    const char* vsCode = LoadFileContent(vsShaderPath);
    const char* fsCode = LoadFileContent(fsShaderPath);

    //Compile
    glShaderSource(vsShader,1,&vsCode,nullptr);
    glShaderSource(fsShader,1,&fsCode,nullptr);
    glCompileShader(vsShader);
    glCompileShader(fsShader);

    //Attach
    GLuint program = glCreateProgram();
    glAttachShader(program,vsShader);
    glAttachShader(program,fsShader);

    //Link
    glLinkProgram(program);

    //Clear
    glDetachShader(program,vsShader);
    glDetachShader(program,fsShader);
    glDeleteShader(vsShader);
    glDeleteShader(fsShader);

    return program;
}

当GPU程序创建完成之后,就可以在主线程中写一个死循环,然后调用下面的绘制命令:

struct Vertex
{
    float pos[3];
    float color[3];
};

void VFRender()
{
    glUseProgram(s_program);
    glUniformMatrix4fv(s_shaderData.MLocation,1,GL_FALSE,identify);
    glUniformMatrix4fv(s_shaderData.VLocation,1,GL_FALSE,identify);
    glUniformMatrix4fv(s_shaderData.PLocation,1,GL_FALSE,glm::value_ptr(s_shaderData.projection));

    glBindBuffer(GL_ARRAY_BUFFER,s_shaderData.vbo);
    glEnableVertexAttribArray(s_shaderData.posLocation);
    glVertexAttribPointer(s_shaderData.posLocation,3,GL_FLOAT,GL_FALSE,sizeof(Vertex),nullptr);
    glEnableVertexAttribArray(s_shaderData.colorLoacation);
    glVertexAttribPointer(s_shaderData.colorLoacation,3,GL_FLOAT,GL_FALSE,sizeof(Vertex),(void*)(sizeof(float)*3)); 

    glDrawArrays(GL_TRIANGLES,0,3);
    glBindBuffer(GL_ARRAY_BUFFER,0);
    glUseProgram(0);
}

其中,绘制的顶点数据是s_shaderData.vbo,这些VBO(VertexBufferObject)数据通常在绘制之前就已准备好了。关于VBO的组成原理,我会在之后再写一篇文章详述。

GL2.0限制

由于目前市场上大多手机还是OpenGL2.0,总结了一下2.0需要注意的事项:

  • 对于支持GL2.0最差的硬件,只支持输入8个Vector4。即attribute输入的vec4不应超过8。uniform不受此限制。
  • 矩阵会占用4个vec4,因此不应使用attribute修饰mat4,应使用uniform
  • VertexShader与FragmentShader传递的变量使用varying进行标记。
  • VS里面必须给gl_Position赋值,否则无法绘制。

GLSL 例子

最简单的shader可以写成这样:

VertexShader

attribute vec3 pos;
attribute vec4 color;

uniform mat4 M;
uniform mat4 V;
uniform mat4 P;

varying vec4 V_Color;
void main()
{
    V_Color = color;
    gl_Position = P*V*M*vec4(pos,1.0);
}

FragmentShader

varying vec4 V_Color;

void main(){
    gl_FragColor = V_Color;
}

简化看来,VertexShader处理3d的绘制信息,输出2d绘制信息。FragmentShader则是对2d信息进行色彩加工。

总结

以上就是构建 OpenGL管线 的流程。需要注意的是,一些变量需要从C++程序传入到OpenGL中。这是因为视口程序通常会提供一些动态更改这些值的方法。因此从外面传入shader会更灵活。如果在测试阶段,可以直接在shader中声明这些值,就省去了跨程序传值的烦恼。

关注我的微信公众号,获取更多优质内容

时间: 2024-10-05 13:18:09

OpenGL(一) OpenGL管线 与 可编程管线流程的相关文章

有关于OpenGL、OpenGL ES、WebGL的小结

转自原文 有关于OpenGL.OpenGL ES.WebGL的小结 一.   OpenGL简介 OpenGL(全写Open Graphics Library)是个定义了一个跨编程语言.跨平台的编程接口的规格,它用于三维图西象(二维的亦可).OpenGL是个专业的图形程序接口,是一个功能强大,调用方便的底层图形库. 关于其他的介绍我就不说了.这边我说一下有OpenGL的资源网站: OpenGL官网:http://www.opengl.org/resources/libraries/glut/ Ne

[android] OpenGL与OpenGL ES简介

简介 OpenGL OpenGL的全称是Open Graphics Library,即开放的图形库接口,它定义了一个跨编程语言,跨平台的编程接口的规范,它主要用于3D图形(2D也可以)编程.OpenGL的前身是SGI公司为其图形工作站开发的IRIS GL.IRIS RL是一个工业标准的3D图形软件接口,功能虽然强大,但是移植性不好,于是SGI公司在IRIS GL的基础上开发了OpenGL. OpenGL体系简单,而且具有跨平台特性,它不像Direct3D,是Microsoft开发的windows

基于Qt的OpenGL可编程管线学习(1)- 绘制一个三角形

0.写在前面的话 这里只是学习的时候做的笔记记录方便日后的查看,如果有大神看到觉得有问题的地方希望能给予指出,方便日后的学习,谢谢! 我是用的Qt版本为Qt5.6,开发环境为Qt Creator 1.QOpenGLWidget 在Qt开发环境下,使用OpenGL的可编程管线绘制一个三角形 效果如下图所示: 这里使用QOpenGLWidget进行绘制的,在QOpenGLWidget中需要重写 void initializeGL(void); void resizeGL(int w, int h);

OpenGL管线(用经典管线代说着色器内部)

图形管线(graphics pipeline)向来以复杂为特点,这归结为图形任务的复杂性和挑战性.OpenGL作为图形硬件标准,是最通用的图形管线版本.本文用自顶向下的思路来简单总结OpenGL图形管线,即从最高层开始,然后逐步细化到管线图中的每个框,再进一步细化到OpenGL具体函数.注意,这里用经典管线代说着色器内部,也就是OpenGL固定管线功能(Fixed-Function,相对于programmable也即可编程着色器),也会涉及着色器,但差不多仅限于“这些固定管线功能对应xx着色器”

【OpenGL】OpenGL初学

转载自:http://www.cppblog.com/doing5552/archive/2009/01/08/71532.html 说起编程作图,大概还有很多人想起TC的#include <graphics.h>吧? 但是各位是否想过,那些画面绚丽的PC游戏是如何编写出来的?就靠TC那可怜的640*480分辨率.16色来做吗?显然是不行的. 本帖的目的是让大家放弃TC的老旧图形接口,让大家接触一些新事物. OpenGL作为当前主流的图形API之一,它在一些场合具有比DirectX更优越的特性

OpenGL(十一) 可编程管线 基础光照 的实现

在OpenGL中创建 基础光照 ,主要的工作将模型中的法线信息和法线空间运算矩阵传入到shader中.另一方面,LightDir,ViewDir通常是在shader中从引擎参数获取的,为了简化光照模型的实现,这里我们可以在shader中写死.至于经典的 ambient+diffuse+specular 光照原理,不是本文的重点,就在shader中一笔带过了. 原理 通过函数 glm::mat4 normalMatrix = glm::inverseTranspose(s_shaderData.m

OpenGL(八)使用 subroutine 切换可编程管线

Subroutine 功能是在OpenGL 4.0 版本号里才添加的.因此对于各种Android手机.这个功能基本跪了.假设你发现你的程序报错:ARB_shader_subroutine.那就说明当前显卡不支持.只是大体思路能够了解一下.由于思路相似的功能有其它的实现方式. 原理 在shader中声明一个函数变量,然后定义它的指针,并将其作为一个uniform变量公开出去.最后定义非常多复写函数就可以. 实现 由于版本号限制.使用 subroutine 要注意在shader中添加版本号的编译宏:

基于Qt的OpenGL可编程管线学习(3)- 使用Instanced方式绘制

绘制多个重复的模型时,使用Instanced方式绘制可以大大加快显然速度. 绘制效果如下图所示: 1.Vertex Shader中定义如下: attribute vec3 pos; attribute vec2 coord; attribute vec3 normal; attribute vec3 offset; uniform mat4 M; uniform mat4 V; uniform mat4 P; uniform mat4 NM; varying vec2 M_coord; vary

基于Qt的OpenGL可编程管线学习(10)- 膨胀与腐蚀

膨胀:取一个像素周围的点,取最亮的点为当前的点颜色,为膨胀效果 腐蚀:取一个像素周围的点,取最暗的点为当前的点颜色,为腐蚀效果 膨胀Fragment Shader varying vec2 M_coord; varying vec3 M_normal; varying vec3 M_WordPos; uniform sampler2D U_MainTexture; uniform sampler2D U_SubTexture; void main() {     vec4 maxValue=ve