编写自己的 shader

渲染管线工作原理

  • 在学习shader之前首先了解一下OpenGL 渲染管线的工作原理, 对于学习 OpenGL 极其重要.(参考资料 wiki):
    管线工作原理示意图(wiki)
管线工作原理示意图( 引自子龙山人的博客 )
  1. 顶点准备阶段 ( VBO, VAO 或者 FBO 传递数据给 OpenGL )
  2. 顶点处理阶段:
    2.1. 每一个顶点都是通过 Vertex Shader 来处理过的, 并从流水线中同步到输出的顶点数据中
    2.2. Optional primitive tessellation stages (初始顶点数据组装阶段).
    2.3. Optional Geometry Shader primitive processing. The output is a sequence of primitives.( 初始几何处理阶段 )
  3. 顶点后处理阶段 ( 顶点转换反馈, Primitive Clipping, 采样分割, viewport 转换到窗口上 )
  4. Primitive组装
  5. 光栅化成一个个像素
  6. 使用Fragment shader来处理这些像素
  7. 采样处理(主要包括Scissor Test, Depth Test, Blending, Stencil Test等)

Vertex Shader

     attribute vec4 a_position;
     attribute vec4 a_color;
     varying vec4 v_fragmentColor;
     void main()
     {
         gl_Position = CC_MVPMatrix * a_position;
         v_fragmentColor = a_color;
     }

Fragment Shader

   varying vec4 v_fragmentColor;
   void main()
   {
       gl_FragColor = v_fragmentColor;
   }

总体代码

// on "init" you need to initialize your instance
bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !Layer::init() )
    {
        return false;
    }
    
    auto program = new GLProgram();
    program->initWithByteArrays(vertex_shader.c_str(), fragment_shader.c_str());

    if ( !program->link() )
    {
        printf("link shader error\n");
    }
    
    //set uniform locations
    program->updateUniforms();
    
    this->setGLProgram(program);
    
    program->release();
    
    return true;
}

void HelloWorld::onDraw()
{
    auto _program = getGLProgram();
    
    _program->use();
    
    //设置该shader的一些内置uniform,主要是MVP,即model-view-project矩阵
    _program->setUniformsForBuiltins();
    
    // 创建和绑定 vao
    GLuint vao = 0;
    glGenVertexArraysOES(1, &vao);
    glBindVertexArrayOES(vao);
    
    // 创建和绑定 vbo
    GLuint vertexVBO = 0;
    glGenBuffers(1, &vertexVBO);
    glBindBuffer(GL_ARRAY_BUFFER, vertexVBO);
    
    auto size = Director::getInstance()->getWinSize();
    
    //指定将要绘制的三角形的三个顶点,分别位到屏幕左下角,右下角和正中间的顶端
    float vertercises[] = {
        0,0,
        size.width, 0,
        size.width / 2, size.height
    };
    
    //指定每一个顶点的颜色,颜色值是RGBA格式的,取值范围是0-1
    float color[] = {
        0, 1,0, 1,    //第一个点的颜色,绿色
        1,0,0, 1,  //第二个点的颜色, 红色
        0, 0, 1, 1};  //第三个点的颜色, 蓝色
    
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertercises), vertercises, GL_STATIC_DRAW);
    
    // 获取 vertex attribute "a_position" 的入口点
    GLuint a_position = glGetAttribLocation(_program->getProgram(), "a_position");
    assert(_program->getProgram());
    // 打开 "a_position" 入口点
    glEnableVertexAttribArray(a_position);
    // 传递数据给 "a_position", 注意最有一个参数是数组的偏移
    glVertexAttribPointer(a_position, 2, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0);
    
    
    // set for color
    GLuint colorVBO = 0;
    glGenBuffers(1, &colorVBO);
    glBindBuffer(GL_ARRAY_BUFFER, colorVBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(color), color, GL_STATIC_DRAW);
    
    GLuint a_color = glGetAttribLocation(_program->getProgram(), "a_color");
    glEnableVertexAttribArray(a_color);
    glVertexAttribPointer(a_color, 4, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0);
    
    // for safty
//    glBindVertexArrayOES(0);
//    glBindBuffer(GL_ARRAY_BUFFER, 0);

    // 以下主要为  vao 的数据源分配内存以及初始化内存
    GLuint ibuffer;
    glGenBuffers(1, &ibuffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuffer);
    
//    static GLubyte indexData[3] = {0, 1, 2};
        static GLubyte indexData[3] = {2, 0, 1};
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, 3 * sizeof(GLubyte), indexData, GL_STATIC_DRAW);
    
    // 分配 vao 并 绘制
    glBindVertexArrayOES(0);
    glBindVertexArrayOES(vao);
    glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, (GLvoid*)0);
    
    //绘制三角形,所谓的draw call就是指这个函数调用
//    glDrawArrays(GL_TRIANGLES, 0, 3);
    
    //通知cocos2d-x 的renderer,让它在合适的时候调用这些OpenGL命令
    CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1, 3);
    
    //如果出错了,可以使用这个函数来获取出错信息
    CHECK_GL_ERROR_DEBUG();
}

vertex 渲染流程

graphics_pipeline

关于 VAO 和 VBO

  • VBO ( Vertex Buffer Object )
    它是 GPU 里面的一块缓冲区, 生成步骤向 GPU 申请i 一块内存,然后往里面填充数据
  • VAO
    它主要用来记录 VBO 的绘制顺序, 生成和初始化和 VBO 类似
  • 关于他们更详尽的介绍请参考 wiki

顶点数据是如何传递给 shader

要弄明白程序里面定义的数组是怎么传递到vertex shader的,我们需要先弄清楚vertex attribute。

     attribute vec4 a_position;
     attribute vec4 a_color;
     varying vec4 v_fragmentColor;
     void main()
     {
         gl_Position = CC_MVPMatrix * a_position;
         v_fragmentColor = a_color;
     }

每一个attribute在vertex shader里面有一个location,它是用来传递数据的入口。我们可以通过下列代码获取这个入口值:

GLuint positionLocation = glGetAttribLocation(program->getProgram(), "a_position");
glEnableVertexAttribArray(positionLocation);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容

  • 你好,三角形 图形渲染管线(Pipeline) 3D坐标转为2D坐标的处理过程是由OpenGL的图形渲染管线(Pi...
    IceMJ阅读 7,394评论 2 13
  • 1 前言 一直想沿着图像处理这条线建立一套完整的理论知识体系,同时积累实际应用经验。因此有了从使用AVFounda...
    RichardJieChen阅读 5,631评论 5 12
  • 第三章 管线一览 本章我们会学到什么 OpenGL管线的每个阶段做什么的 如果连接着色器和固定功能管线阶段 如果创...
    葭五阅读 6,175评论 2 18
  • 1, OpenGL 编程模型 2, 管线流程(重点,每一个细节都要讲清楚) 渲染流水线,就是一系列有序的处理阶段的...
    rogerwu1228阅读 717评论 0 0
  • 01 宋代美学为现在的很多小伙伴喜欢。 事实上,宋代的画家是很热衷表达季节的,早春图、溪山图、万壑松风图... 尤...
    赵小文儿阅读 1,035评论 21 24