六 cocos2dx(3.X)中使用shader
阿新 • • 發佈:2019-01-23
一 shader的基本概念
1 什麼是shader
shader即著色器,就是專門用來渲染3D圖形的一種技術。
通過shader,可以自己編寫顯示卡渲染畫面的演算法,使畫面更漂亮、更逼真。
2 shader分類
shader又分兩種,一種是頂點shader(3D圖形是由三角形組成的,頂點shader就是計算頂點位置,併為後期畫素渲染做準備的),
另一種是畫素shader,就是以畫素為單位,計算光照、顏色的一系列演算法。
3 shader語言
幾個不同的圖形API有各自的shader語言:
在DirectX中,頂點shader叫做vertex shader,畫素shader叫做pixel shader;
在OpenGL中,頂點shader也叫做vertex shader,但畫素shader叫做fragment shader。
此外顯示卡晶片廠商NVIDIA還推出CG顯示卡程式語言,也支援shader。
二 shader開發流程
- 編寫vertex Shader和fragment shader原始碼。
- 建立兩個shader 例項:GLuint glCreateShader(GLenum type); [gl.createShader]
- 給Shader例項指定原始碼。 glShaderSource [gl.shaderSource]
- 編譯shaer原始碼 void glCompileShader(GLuint shader) [gl.compileShader]
- 建立shader program – GLuint glCreateProgram(void) [gl.createProgram]
- 繫結shader到program 。 void glAttachShader(GLuint program, GLuint shader)。每個program必須繫結一個vertex shader 和一個fragment shader。 [gl.attachShader]
- 連結program 。 void glLinkProgram(GLuint program) [gl.linkProgram]
- 使用porgram 。 void glUseProgram(GLuint program) [gl.useProgram]
對於使用獨立shader編譯器編譯的二進位制shader程式碼,可使用glShaderBinary來載入到一個shader例項中。
三 shader程式
1 語言glsl
glsl即OpenGL Shading Language(OpenGL著色語言),是用來在OpenGL中著色程式設計的語言。
2 頂點著色器
// vert.vsh
// 頂點著色器,VBO/VAO提供的每個頂點都執行一遍頂點著色器,輸出一個varying和gl_Position等
// 變數修飾:
// attribute: 只讀,隨不同頂點變化的全域性變數,應用程式傳入,只能用在頂點著色器中
// uniform: 只讀,隨不同圖元變化的全域性變數,應用程式傳入,
// varying: 在頂點shader中可寫,在片斷shader中只讀,用於在頂點著色器和片段著色器之間傳遞資料
// 輸入: attribute, 輸出:varying+gl_positon + gl_Position + gl_PointSize
attribute vec4 a_position;
attribute vec4 a_color;
varying vec4 v_fragmentColor;
// 每一個Shader程式都有一個main函式
void main()
{
// gl開頭的變數名是系統內建的變數
gl_Position = CC_MVPMatrix * a_position;// 每個點固有的Varying,表示點的空間位置。
v_fragmentColor = a_color;
}
3 片段著色器
// frag.fsh
// 片元著色器,光柵化輸出的每個片元都執行一遍片段著色器,生成一個或多個(多重渲染)顏色值作為輸出
// 輸入: varying, 輸出: gl_FragColor + gl_FragDepth
//用於在頂點著色器和片段著色器之間傳遞資料,因此型別必須完全一直
varying vec4 v_fragmentColor;
// 每一個Shader程式都有一個main函式
void main()
{
// gl開頭的變數名是系統內建的變數
gl_FragColor = v_fragmentColor;// gl_FragColor 定義最終畫在螢幕上面的畫素點的顏色
}
三 程式呼叫
新建cocos工程,將上面兩個檔案放到Resource/shaders資料夾下,修改程式碼如下:
// .h
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__
#include "cocos2d.h"
USING_NS_CC;
class HelloWorld : public cocos2d::Layer
{
public:
static cocos2d::Scene* createScene();
virtual bool init();
CREATE_FUNC(HelloWorld);
virtual void visit(Renderer *renderer, const Mat4& parentTransform, uint32_t parentFlags);
void onDraw();
private:
CustomCommand _customCommand;
GLuint _vao;
GLuint _vertVBO;
GLuint _colorVBO;
};
#endif // __HELLOWORLD_SCENE_H__
// .cpp
#include "HelloWorldScene.h"
USING_NS_CC;
Scene* HelloWorld::createScene()
{
// 'scene' is an autorelease object
auto scene = Scene::create();
// 'layer' is an autorelease object
auto layer = HelloWorld::create();
// add layer as a child to scene
scene->addChild(layer);
// return the scene
return scene;
}
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
//////////////////////////////
// 1. super init first
if ( !Layer::init() )
{
return false;
}
/*
在OpenGL中,GLSL的shader使用的流程與C語言相似,每個shader類似一個C模組,首先需要單獨編譯(compile),
然後一組編譯好的shader連線(link)成一個完整程式。
*/
auto program = CCGLProgram::createWithFilenames("shader/vert.vsh", "shader/frag.fsh");
program->link();
program->updateUniforms();
this->setGLProgram(program);
/*
使用VBO和VAO的步驟都差不多,步驟如下:
1 glGenXXX
2 glBindXXX
*/
// 建立和繫結vao
glGenVertexArrays(1, &_vao);
glBindVertexArray(_vao);
// 建立和繫結vbo
glGenBuffers(1, &_vertVBO);// 生成VBO
glBindBuffer(GL_ARRAY_BUFFER, _vertVBO); // 關聯到當前的VAO
auto size = Director::getInstance()->getVisibleSize();
float vertercies[] = {// 三角形頂點位置
0, 0, // 第1個點座標
size.width, 0, // 第2個點座標
size.width / 2, size.height // 第3個點座標
};
// 給VBO設定資料
glBufferData(GL_ARRAY_BUFFER, sizeof(vertercies), vertercies, GL_STATIC_DRAW);
// 獲得變數a_position在記憶體中的位置
GLuint positionLocation = glGetAttribLocation(program->getProgram(), "a_position");
glEnableVertexAttribArray(positionLocation);
// 提交包含資料的陣列指標
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0);
// 設定顏色
float color[] = {// 三角形頂點顏色RGBA
0, 1, 0, 1,
1, 0, 0, 1,
0, 0, 1, 1
};
glGenBuffers(1, &_colorVBO);
glBindBuffer(GL_ARRAY_BUFFER, _colorVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(color), color, GL_STATIC_DRAW);
// 獲得變數a_color在記憶體中的位置
GLuint colorLocation = glGetAttribLocation(program->getProgram(), "a_color");
glEnableVertexAttribArray(colorLocation);
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
return true;
}
void HelloWorld::visit(cocos2d::Renderer *renderer, const Mat4 &transform, uint32_t parentFlags)
{
Layer::draw(renderer, transform, parentFlags);
_customCommand.init(_globalZOrder);
_customCommand.func = CC_CALLBACK_0(HelloWorld::onDraw, this);
renderer->addCommand(&_customCommand);
}
void HelloWorld::onDraw()
{
auto glProgram = getGLProgram();
glProgram->use();
glProgram->setUniformsForBuiltins();
/*
VAO裡的VBOs都設定好了以後,在繪製的地方只需要設定當前繫結的VAO是哪個,
就能按照初始化的VAO來繪製,即呼叫glDrawArrays
*/
// 設定當前繫結的VAO
glBindVertexArray(_vao);
// 繪製三角形
glDrawArrays(GL_TRIANGLES, 0, 3);
// 解綁當前VAO,但並不釋放
glBindVertexArray(0);
CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1, 3);
CHECK_GL_ERROR_DEBUG();
}
效果如下:
win7預設關閉VAO,需要手動開啟:libcocos2d工程-》屬性-》C/C++-》前處理器,在前處理器定義中新增CC_TEXTURE_ATLAS_USE_VAO=1