OpenGL ES 学习教程(2):可编程管线 Shader 三角形

发表于2017-11-22
评论0 1.6k浏览

下面给大家介绍OpenGL可编程管线,真正开始用 OpenGL绘制图形时,使用的是下面的API:

glBegin(GL_TRIANGLES);                          // 绘制三角形  
        glVertex3f( 0.0f, 1.0f, 0.0f);                  // 上顶点  
        glVertex3f(-1.0f,-1.0f, 0.0f);                  // 左下  
        glVertex3f( 1.0f,-1.0f, 0.0f);                  // 右下  
glEnd();   

这就是已经被抛弃的固定管线编程 。


暂时忽略固定管线编程的OpenGL API,利用图形数学知识学习可编程管线。


上图中虚线是 固定管线,黑色粗实线 是 可编程管线 。

可以看到这里 用了 顶点程序片元程序字样,就是说,这两个过程是可编程的,要我们自己写代码去控制。

顶点程序就是常说的 顶点 Shader ,后缀一般是vsh  , 片元程序 就是常说的 片段Shader,后缀一般是 fsh 。


好。

我们编写的 Shader 代码是可以运行在GPU上的,既然是作为一段程序,那么和我们平时写的C++代码一样,想要最后运行起来也要经过一个流程。

1、创建一个项目 glCreateProgram

2、创建一个代码文件 glCreateShader

3、输入代码 glShaderSource

4、编译代码 glCompileShader

5、查看编译是否错误 glGetShaderiv

6、把Shader添加到项目中 glAttachShader

7、链接每个Shader glLinkProgram

8、获取链接状态 glGetProgramiv

9、运行这个项目 glUseProgram


下面是一个Shader 的完整例子,包含上面的流程

#pragma once  
#include<assert.h>  
#include<egl\egl.h>  
#include<gles2\gl2.h>  
class ShaderID  
{  
public:  
    ShaderID()  
    {  
        m_shaderId = -1;  
    }  
    int m_shaderId;  
};  
//封装OpenGL 的glProgram的一些信息;  
class GLProgram  
{  
public:  
    int m_programId;  
    ShaderID m_vertexShader;  
    ShaderID m_fragmentShader;  
public:  
    GLProgram()  
    {  
        m_programId = -1;  
    }  
public:  
    //加载shader并且创建glProgram;  
    bool createProgram(const char *vertex, const char *fragment)  
    {  
        bool error = false;  
        do  
        {  
            if (vertex)  
            {  
                //创建Shader;  
                m_vertexShader.m_shaderId = glCreateShader(GL_VERTEX_SHADER);  
                //源码;  
                glShaderSource(m_vertexShader.m_shaderId, 1, &vertex, 0);  
                //编译;  
                glCompileShader(m_vertexShader.m_shaderId);  
                GLint compileStatus;  
                glGetShaderiv(m_vertexShader.m_shaderId, GL_COMPILE_STATUS, &compileStatus);  
                error = (compileStatus == GL_FALSE);  
                if (error)  
                {  
                    GLchar message[256];  
                    glGetShaderInfoLog(m_vertexShader.m_shaderId, sizeof(message), 0, message);  
                    assert((message && 0) != 0);  
                    break;  
                }  
            }  
            if (fragment)  
            {  
                //创建Shader;  
                m_fragmentShader.m_shaderId = glCreateShader(GL_FRAGMENT_SHADER);  
                //源码;  
                glShaderSource(m_fragmentShader.m_shaderId, 1, &fragment, 0);  
                //编译;  
                glCompileShader(m_fragmentShader.m_shaderId);  
                GLint compileStatus;  
                glGetShaderiv(m_fragmentShader.m_shaderId, GL_COMPILE_STATUS, &compileStatus);  
                error = (compileStatus == GL_FALSE);  
                if (error)  
                {  
                    GLchar message[256];  
                    glGetShaderInfoLog(m_fragmentShader.m_shaderId, sizeof(message), 0, message);  
                    assert((message && 0) != 0);  
                    break;  
                }  
            }  
            //创建gl程序;  
            m_programId = glCreateProgram();  
            //代码添加到program中;  
            if (m_vertexShader.m_shaderId)  
            {  
                glAttachShader(m_programId, m_vertexShader.m_shaderId);  
            }  
            if (m_fragmentShader.m_shaderId)  
            {  
                glAttachShader(m_programId, m_fragmentShader.m_shaderId);  
            }  
            //链接;  
            glLinkProgram(m_programId);  
            GLint linkStatus;  
            glGetProgramiv(m_programId, GL_LINK_STATUS, &linkStatus);  
            if (linkStatus == GL_FALSE)  
            {  
                GLchar message[256];  
                glGetProgramInfoLog(m_programId, sizeof(message), 0, message);  
                assert((message && 0) != 0);  
                break;  
            }  
            //使用program;  
            //glUseProgram(m_programId);  
        } while (false);  
        if (error)  
        {  
            if (m_fragmentShader.m_shaderId)  
            {  
                glDeleteShader(m_fragmentShader.m_shaderId);  
                m_fragmentShader.m_shaderId = 0;  
            }  
            if (m_vertexShader.m_shaderId)  
            {  
                glDeleteShader(m_vertexShader.m_shaderId);  
                m_vertexShader.m_shaderId = 0;  
            }  
            if (m_programId)  
            {  
                glDeleteProgram(m_programId);  
                m_programId = 0;  
            }  
        }  
        return true;  
    }  
    virtual void begin()  
    {  
        glUseProgram(m_programId);  
    }  
    virtual void end()  
    {  
        glUseProgram(0);  
    }  
};</gles2\gl2.h></egl\egl.h></assert.h>  


一对简单的 Vertex Shader 和 Fragment Shader 可能长的像下面

const char* vertexShader=  
        {  
            "precision lowp float;"  
            "uniform mat4 m_mvp;"  
            "attribute vec3 m_position;"  
            "attribute vec4 m_color;"  
            "varying vec4 m_outColor;"  
            "void main()"  
            "{"  
            "   vec4 pos=vec4(m_position,1);"  
            "   gl_Position=m_mvp*pos;"  
            "   m_outColor=m_color;"  
            "}"  
        };  
        const char* fragmentShader =  
        {  
            "precision lowp float;"  
            "varying vec4 m_outColor;"  
            "void main()"  
            "{"  
            "   gl_FragColor=m_outColor;"  
            "}"  
        };  

uniform 、attribute 都是变量修饰符。这两个修饰符修饰的变量 ,我们可以在 C++ 代码中获取到然后给他们赋值。

varying 修饰符修饰的变量代表在 Vertex 和 Fragment 之间传递。不能从C++代码中操作。


uniform mat4 m_mvp   

我们从C++中传入 MVP 矩阵

attribute vec3 m_position

我们从C++中传入 顶点位置

attribute vec4 m_color

我们从C++中传入 顶点颜色


好了。下面就让我们来设置这些变量。

这里继承了上面的类

#pragma once  
#include"GLProgram.h"  
class GLProgram_Color :public GLProgram  
{  
public:  
    //传入shader中的值;  
    GLint m_position;  
    GLint m_color;  
    GLint m_mvp;  
public:  
    GLProgram_Color()  
    {  
        m_position = -1;  
        m_color = -1;  
        m_mvp = -1;  
    }  
    ~GLProgram_Color()  
    {}  
    GLint getPositionAttribute() const  
    {  
        return m_position;  
    }  
    GLint getColorAttribute() const  
    {  
        return m_color;  
    }  
    GLint getMVP() const  
    {  
        return m_mvp;  
    }  
    //attribute:只能在Vertexshader中使用;  
    //Unifrom:在Vertex和Fragment中共享使用,且不能被修改;  
    //Varying:从Vertex传递数据到Fragment中使用;  
    virtual bool Initialize()  
    {  
        const char* vertexShader=  
        {  
            "precision lowp float;"  
            "uniform mat4 m_mvp;"  
            "attribute vec3 m_position;"  
            "attribute vec4 m_color;"  
            "varying vec4 m_outColor;"  
            "void main()"  
            "{"  
            "   vec4 pos=vec4(m_position,1);"  
            "   gl_Position=m_mvp*pos;"  
            "   m_outColor=m_color;"  
            "}"  
        };  
        const char* fragmentShader =  
        {  
            "precision lowp float;"  
            "varying vec4 m_outColor;"  
            "void main()"  
            "{"  
            "   gl_FragColor=m_outColor;"  
            "}"  
        };  
        bool ret = createProgram(vertexShader, fragmentShader);  
        if (ret)  
        {  
            m_position = glGetAttribLocation(m_programId, "m_position");  
            m_color = glGetAttribLocation(m_programId, "m_color");  
            m_mvp = glGetUniformLocation(m_programId, "m_mvp");  
        }  
        return ret;  
    }  
    virtual void begin()  
    {  
        glUseProgram(m_programId);  
        glEnableVertexAttribArray(m_position);  
        glEnableVertexAttribArray(m_color);  
    }  
    virtual void end()  
    {  
        glDisableVertexAttribArray(m_position);  
        glDisableVertexAttribArray(m_color);  
        glUseProgram(0);  
    }  
};  

可以看到,在创建Shader 并且编译成功之后,就可以获取 Shader 中的变量了。

bool ret = createProgram(vertexShader, fragmentShader);  
if (ret)  
{  
    m_position = glGetAttribLocation(m_programId, "m_position");  
    m_color = glGetAttribLocation(m_programId, "m_color");  
    m_mvp = glGetUniformLocation(m_programId, "m_mvp");  
}  

然后像下面这样去传入值 到这几个变量
glUniformMatrix4fv(m_program.m_mvp, 1, false, &proj[0][0]);  
glVertexAttribPointer(m_program.m_position, 2, GL_FLOAT, false, sizeof(glm::vec3), pos);  
glVertexAttribPointer(m_program.m_color, 4, GL_FLOAT, false, sizeof(glm::vec4), color);  

这里说下MVP

MVP是一个矩阵,是由 模型矩阵 乘以 视图矩阵 乘以 投影矩阵 得来的,代表对世界坐标系中的一个点执行的一系列 操作。

想详细了解的请百度相关资料,关键字:OpenGL 矩阵变换

好了,我们在教程一的基础上添加了 一些修改,创建了Shader、给Shader设置了值,并且引入了一个三方库 GLM 来进行矩阵的计算。


示例程序下载:http://pan.baidu.com/s/1eQjW5O2  

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引