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;
个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是一种从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);
}
//更新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);
}
这次,我们打算把颜色数据加进顶点数据中。
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);
}
//三角形的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字节)。
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);
}