shader 输入输出

opengl es的shader 有3种类型修饰:uniform,attribute,varying,区别见:http://blog.csdn.net/renai2008/article/details/7844495


向量

GLSL中的向量是一个可以包含有1、2、3或者4个分量的容器,分量的类型可以是前面默认基础类型的任意一个。它们可以是下面的形式(n代表分量的数量):

类型 含义
vecn 包含n个float分量的默认向量
bvecn 包含n个bool分量的向量
ivecn 包含n个int分量的向量
uvecn 包含n个unsigned int分量的向量
dvecn 包含n个double分量的向量

大多数时候我们使用vecn,因为float足够满足大多数要求了。

一个向量的分量可以通过vec.x这种方式获取,这里x是指这个向量的第一个分量。你可以分别使用.x.y.z.w来获取它们的第1、2、3、4个分量。GLSL也允许你对颜色使用rgba,或是对纹理坐标使用stpq访问相同的分量。

向量这一数据类型也允许一些有趣而灵活的分量选择方式,叫做重组(Swizzling)。重组允许这样的语法:

vec2 someVec;
vec4 differentVec = someVec.xyxx;
vec3 anotherVec = differentVec.zyw;
vec4 otherVec = someVec.xxxx + anotherVec.yxzy;

你可以使用上面4个字母任意组合来创建一个和原来向量一样长的(同类型)新向量,只要原来向量有那些分量即可;然而,你不允许在一

vec2向量中去获取.z元素。我们也可以把一个向量作为一个参数传给不同的向量构造函数,以减少需求参数的数量:

vec2 vect = vec2(0.5f, 0.7f);
vec4 result = vec4(vect, 0.0f, 0.0f);
vec4 otherResult = vec4(result.xyz, 1.0f);


输入和输出

如下让顶点着色器为片段着色器决定颜色:

顶点着色器:

//三角形的vertex shader
attribute vec3 a_position; // attribute是从外部传进来的,每一个顶点都会有这两个属性,所以它也叫做vertex attribute(顶点属性)
varying vec4 vertexColor;
void main()
{
    gl_Position = vec4(a_position.x, a_position.y, a_position.z, 1);
    vertexColor = vec4(1, 0, 0, 1);
}

片段着色器:

//三角形的fragment shader
varying vertexColor; //跟顶点着色器中的声明类型、命名都相同。
void main()
{
    gl_FragColor = vertexColor;
}


uniform

Uniform是一种从CPU中的应用向GPU中的着色器发送数据的方式,但uniform和顶点属性有些不同。首先,uniform是全局的(Global)。全局

意味着uniform变量必须在每个着色器程序对象中都是独一无二的,而且它可以被着色器程序的任意着色器在任意阶段访问。第二,无论你把

uniform值设置成什么,uniform会一直保存它们的数据,直到它们被重置或更新。


下例通过uniform修改三角形的颜色:

片段着色器:

//三角形的颜色
uniform vec4 myColor;

void main()
{
    gl_FragColor = myColor;
}

顶点着色器:

//三角形的vertex shader
attribute vec3 a_position; // attribute是从外部传进来的,每一个顶点都会有这两个属性,所以它也叫做vertex attribute(顶点属性)

void main()
{
    gl_Position = vec4(a_position.x, a_position.y, a_position.z, 1);
}

在 onDraw()中设置uniform 的值:

    //更新uniform颜色
    GLint vertexColorLocation = glGetUniformLocation(glprogram->getProgram(), "myColor"); //找到着色器中uniform属性的索引/位置值
    colorChange++;
    if (colorChange > 60) {
        colorChange = 1;
    }
    glUniform4f(vertexColorLocation, 0, 0, colorChange / 60.0f, 1);


glGetUniformLocation查询uniform ourColor的位置值。我们为查询函数提供着色器程序和uniform的名字(这是我们希望获得的位置值

的来源)。如果glGetUniformLocation返回-1就代表没有找到这个位置值。最后,通过glUniform4f函数设置uniform值。注意,查询

uniform地址不要求你之前使用过着色器程序,但是更新一个unform之前你必须先使用程序(调用glUseProgram),因为它是在当前激活的

着色器程序中设置unform的。

全部代码如下:

//
//  OpenGLUniform.cpp
//  shaderTest
//
//  Created by MacSBL on 2016/12/16.
//
//

#include "OpenGLUniform.h"

bool OpenGLUniform::init()
{
    if (!Layer::init()) {
        return false;
    }
    
    GLfloat vertices[] = {
        0.5, 0.5, 0, //右上
        0.5, -0.5, 0,   //右下
        -0.5, -0.5, 0,   //左下
        -0.5, 0.5, 0  //左上
    };
    GLuint indices[] = {
        0, 1, 3,
        1, 2, 3
    };
    
     //1. 绑定顶点数组对象
    //vao,
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);
    // 2. 把我们的顶点数组复制到一个顶点缓冲中,供OpenGL使用
    //vbo
    GLuint vbo;
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    // 3. 复制我们的索引数组到一个索引缓冲中,供OpenGL使用
    //ebo
    glGenBuffers(1, &ebo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
    // 4. 设定顶点属性指针
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    //5.解绑VAO(不是EBO!)
    glBindVertexArray(0);
    
    //着色器程序
    auto program = new GLProgram;
    program->initWithFilenames("uniform.vsh", "uniform.fsh");
    program->link();
    this->setGLProgram(program);
    
    return true;
}

void OpenGLUniform::visit(cocos2d::Renderer *renderer, const cocos2d::Mat4 &parentTransform, uint32_t parentFlags)
{
    Layer::visit(renderer, parentTransform, parentFlags);
    _command.init(_globalZOrder);
    _command.func = CC_CALLBACK_0(OpenGLUniform::onDraw, this);
    Director::getInstance()->getRenderer()->addCommand(&_command);
}

void OpenGLUniform::onDraw()
{
    //获取当前node 的shader
    auto glprogram = getGLProgram();
    //需要在init中指定shader才能在这use
    glprogram->use();
    
    //更新uniform颜色
    //Uniform是一种从CPU中的应用向GPU中的着色器发送数据的方式,但uniform和顶点属性有些不同。
    //首先,uniform是全局的(Global)。全局意味着uniform变量必须在每个着色器程序对象中都是独一无二的,而且它可以被着色器程序的任意着色器在任意阶段访问。
    //第二,无论你把uniform值设置成什么,uniform会一直保存它们的数据,直到它们被重置或更新。
    GLint vertexColorLocation = glGetUniformLocation(glprogram->getProgram(), "myColor"); //找到着色器中uniform属性的索引/位置值
    colorChange++;
    if (colorChange > 60) {
        colorChange = 1;
    }
    glUniform4f(vertexColorLocation, 0, 0, colorChange / 60.0f, 1);
    
    //绘制图形
    glBindVertexArray(vao);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);
}


多属性shader

这次,我们打算把颜色数据加进顶点数据中。

GLfloat vertices[] = {
    // 位置              // 颜色
     0.5f, -0.5f, 0.0f,  1.0f, 0.0f, 0.0f,   // 右下
    -0.5f, -0.5f, 0.0f,  0.0f, 1.0f, 0.0f,   // 左下
     0.0f,  0.5f, 0.0f,  0.0f, 0.0f, 1.0f    // 顶部
};

由于现在有更多的数据要发送到顶点着色器,我们有必要去调整一下顶点着色器,使它能够接收颜色值作为一个顶点属性输入。

//三角形的vertex shader
attribute vec3 a_position; // attribute是从外部传进来的,// 位置变量的属性位置值为 0
attribute vec3 color;  //从外部传入的,第2个属性; 颜色变量的属性位置值为 1

varying vec4 vertexColor; //向varying.fsh输出颜色
void main()
{
    gl_Position = vec4(a_position.x, a_position.y, a_position.z, 1);
    vertexColor = vec4(color, 1);
}

片段着色器,用varying接收颜色:

//三角形的fragment shader
varying vec4 vertexColor; //与varying.vsh中的输出变量类型、拼写都相同
void main()
{
    gl_FragColor = vertexColor;
}

因为我们添加了另一个顶点属性,并且更新了VBO的内存,我们就必须重新配置顶点属性指针。更新后的VBO内存中的数据现在看起来像这样:

知道了现在使用的布局,我们就可以使用glVertexAttribPointer函数更新顶点格式:

// 位置属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
// 颜色属性
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(3* sizeof(GLfloat)));
glEnableVertexAttribArray(1);

由于我们现在有了两个顶点属性,我们不得不重新计算 步长 值。为获得数据队列中下一个属性值(比如位置向量的下个 x 分量)我们必须向右移动6个float,其中3个是位置值,另外3个是颜色值。这使我们的步长值为6乘以float的字节数(=24字节)。
同样,这次我们必须指定一个偏移量。对于每个顶点来说,位置顶点属性在前,所以它的偏移量是0。颜色属性紧随位置数据之后,所以偏移量就是 3 * sizeof(GLfloat) ,用字节来计算就是12字节。

全部代码如下:

//
//  OpenGLAttributeVary.cpp
//  shaderTest
//
//  Created by MacSBL on 2016/12/16.
//
//

#include "OpenGLAttributeVary.h"

bool OpenGLAttributeVary::init()
{
    if (!Layer::init()) {
        return false;
    }
    
    GLfloat vertices[] = {
        //顶点         //颜色
        0.5, -0.5, 0, 1, 0, 0, //右下
        -0.5, -0.5, 0, 0, 1, 0,   //左下
        0, 0.5, 0, 0, 0, 1, //顶部
    };
    
     //1. 绑定顶点数组对象
    //vao,
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);
    // 2. 把我们的顶点数组复制到一个顶点缓冲中,供OpenGL使用
    //vbo
    GLuint vbo;
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    
    // 4. 设定顶点属性指针
    //varying.vsh中第1个attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6*sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    //varying.vsh中第2个attribute
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6*sizeof(GLfloat), (GLvoid*)(3*sizeof(GLfloat)));
    glEnableVertexAttribArray(1);
    
    //5.解绑VAO(不是EBO!)
    glBindVertexArray(0);
    
    //着色器程序
    auto program = new GLProgram;
    program->initWithFilenames("varying.vsh", "varying.fsh");
    program->link();
    this->setGLProgram(program);
    
    return true;
}

void OpenGLAttributeVary::visit(cocos2d::Renderer *renderer, const cocos2d::Mat4 &parentTransform, uint32_t parentFlags)
{
    Layer::visit(renderer, parentTransform, parentFlags);
    _command.init(_globalZOrder);
    _command.func = CC_CALLBACK_0(OpenGLAttributeVary::onDraw, this);
    Director::getInstance()->getRenderer()->addCommand(&_command);
}

void OpenGLAttributeVary::onDraw()
{
    //获取当前node 的shader
    auto glprogram = getGLProgram();
    //需要在init中指定shader才能在这use
    glprogram->use();
    
    //绘制图形
    glBindVertexArray(vao);
    glDrawArrays(GL_TRIANGLES, 0, 3);
    glBindVertexArray(0);
}









你可能感兴趣的:(shader,opengl,图形基础)