没想到原文出了那么多错别字,实在对不起观众了。介绍opengl es 2.0的不多。相信介绍基于Cocos2d-x学习OpenGL ES 2.0之多纹理的,我是独此一家吧。~~
子龙山人出了一个系列:基于Cocos2d-x学习OpenGL ES 2.0。弄c++来搞cocos2dx的可以看看。
教程是参考iphone的教程来写的,坑点也有不少,最主要的坑点还是在版本。所以还是弄个cocos2dx 3.2比较好。前两天辉辉说cocos2dx 3.2也很操蛋,.h里声明的返回值在源码实现的时候返回类型竟然变了。不得不吐槽一下~
子龙山人的教程只能教你照葫芦画瓢,什么原理的东西是压根就没涉及,有的还因为cocos2dx的封装过度会产生一些误导。
cocos2dx对opengl es封装的有点让人恶心,如果想学习opengl es是不建议在cocos2dx下进行学习的。
废话少说吧,开始正文。
根据子龙山人的教程,弄出了立方体纹理贴图,但是假如想在同一个面上贴多个纹理呢?该怎么实现?
本文提到两个方法,第一就是获取两个纹理,分别画图贴纹理,意思就是装顶点,装索引,绑定纹理,画图一。装顶点,装纹理,画图二。此时用到的都是GL_TEXTURE0,在frag文件中,只需要一个采样器就ok。
第二中方法就是用两个sampler,装顶点,装索引,绑定纹理一,绑定纹理二,画图。就OK了。
说起来比较简单,真要自己动手做,对于一个新手来说,过程还是有点小郁闷的。
下面就上源码了,对于步骤和方法的含义,此处不作介绍。相信强大的google和百度可以发挥巨大的作用。
第一种是立方体六个面贴上纹理,其中一个面再次贴上第二个纹理。
所用到的shader文件:
attribute vec4 a_position; //
attribute vec4 a_color; //
attribute vec2 TextureCoord; varying vec4 DestinationColor; //
varying vec2 v_texCoord; void main(void) { //
DestinationColor = a_color; //
v_texCoord = TextureCoord;
gl_Position = CC_MVPMatrix * a_position; //
}
myshader.vert
varying vec4 DestinationColor; //
varying vec2 v_texCoord; void main(void) { // gl_FragColor = DestinationColor * texture2D(CC_Texture0, v_texCoord) ; //
}
myshader.frag
所用到的两张图
头文件:
#ifndef _CubeTexture_H__
#define _CubeTexture_H__ #include "cocos2d.h" using namespace cocos2d; class CubeTexture : public cocos2d::Layer
{
public:
// there's no 'id' in cpp, so we recommend returning the class instance pointer
static cocos2d::Scene* createScene(); // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
virtual bool init(); virtual void draw(Renderer *renderer, const Mat4 &transform, uint32_t transformUpdated) override;
//we call our actual opengl commands here
void onDraw(); // implement the "static create()" method manually
CREATE_FUNC(CubeTexture); private:
Mat4 _modelViewMV;
CustomCommand _customCommand; GLProgram *mShaderProgram;
GLint _colorLocation;
GLint _positionLocation;
GLint _textureLocation; GLuint _textureUniform; GLuint _textureID;
GLuint _textureID2; GLuint vertexBuffer;
GLuint indexBuffer; GLuint _vertexBuffer2;
GLuint _indexBuffer2; }; #endif // __HELLOWORLD_SCENE_H__
CubeTexture.h
#include "CubeTexture.h"
using namespace GL; cocos2d::Scene* CubeTexture::createScene()
{
auto scene = Scene::create();
auto layer = CubeTexture::create();
scene->addChild(layer);
return scene;
} bool CubeTexture::init()
{
if ( Layer::init() )
{
mShaderProgram = new GLProgram;
mShaderProgram->initWithFilenames("myshader.vert","myshader.frag");
mShaderProgram->link();
mShaderProgram->updateUniforms(); _textureID = Director::getInstance()->getTextureCache()->addImage( "HelloWorld.png" )->getName();
_textureID2 = Director::getInstance()->getTextureCache()->addImage("item_powerup_fish.png")->getName();
glGenBuffers( , &vertexBuffer );
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer ); glGenBuffers( , &indexBuffer );
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer ); return true;
}
return false;
} void CubeTexture::draw( Renderer *renderer, const Mat4 &transform, uint32_t transformUpdated )
{
Layer::draw(renderer, transform, transformUpdated); _customCommand.init(_globalZOrder);
_customCommand.func = CC_CALLBACK_0(CubeTexture::onDraw,this);
renderer->addCommand(&_customCommand);
} void CubeTexture::onDraw()
{
Director::getInstance()->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
Director::getInstance()->loadIdentityMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
Director::getInstance()->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
Director::getInstance()->loadIdentityMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION); Mat4 modelViewMatrix;
Mat4::createLookAt(Vec3(,,), Vec3(,,), Vec3(,-,), &modelViewMatrix);
modelViewMatrix.translate(, , ); static float rotation = ;
modelViewMatrix.rotate(Vec3(,,),CC_DEGREES_TO_RADIANS(rotation)); Mat4 projectionMatrix;
Mat4::createPerspective(, /, 1.0, , &projectionMatrix);
Director::getInstance()->multiplyMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, projectionMatrix);
Director::getInstance()->multiplyMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, modelViewMatrix); typedef struct {
float Position[];
float Color[];
float TexCoord[];
} Vertex;
#define TEX_COORD_MAX 1 Vertex Vertices[] = {
// Front
{{, -, }, {, , , }, {TEX_COORD_MAX, }},
{{, , }, {, , , }, {TEX_COORD_MAX, TEX_COORD_MAX}},
{{-, , }, {, , , }, {, TEX_COORD_MAX}},
{{-, -, }, {, , , }, {, }},
// Back
{{, , -}, {, , , }, {TEX_COORD_MAX, }},
{{-, -, -}, {, , , }, {TEX_COORD_MAX, TEX_COORD_MAX}},
{{, -, -}, {, , , }, {, TEX_COORD_MAX}},
{{-, , -}, {, , , }, {, }},
// Left
{{-, -, }, {, , , }, {TEX_COORD_MAX, }},
{{-, , }, {, , , }, {TEX_COORD_MAX, TEX_COORD_MAX}},
{{-, , -}, {, , , }, {, TEX_COORD_MAX}},
{{-, -, -}, {, , , }, {, }},
// Right
{{, -, -}, {, , , }, {TEX_COORD_MAX, }},
{{, , -}, {, , , }, {TEX_COORD_MAX, TEX_COORD_MAX}},
{{, , }, {, , , }, {, TEX_COORD_MAX}},
{{, -, }, {, , , }, {, }},
// Top
{{, , }, {, , , }, {TEX_COORD_MAX, }},
{{, , -}, {, , , }, {TEX_COORD_MAX, TEX_COORD_MAX}},
{{-, , -}, {, , , }, {, TEX_COORD_MAX}},
{{-, , }, {, , , }, {, }},
// Bottom
{{, -, -}, {, , , }, {TEX_COORD_MAX, }},
{{, -, }, {, , , }, {TEX_COORD_MAX, TEX_COORD_MAX}},
{{-, -, }, {, , , }, {, TEX_COORD_MAX}},
{{-, -, -}, {, , , }, {, }}
};
int vertexCount = sizeof(Vertices) / sizeof(Vertices[]); GLubyte Indices[] = {
// Front
, , ,
, , ,
// Back
, , ,
, , ,
// Left
, , ,
, , ,
// Right
, , ,
, , ,
// Top
, , ,
, , ,
// Bottom
, , ,
, ,
}; // 1) Add to top of file
const Vertex Vertices2[] = {
{{0.5, -0.5, 0.01}, {, , , }, {, }},
{{0.5, 0.5, 0.01}, {, , , }, {, }},
{{-0.5, 0.5, 0.01}, {, , , }, {, }},
{{-0.5, -0.5, 0.01}, {, , , }, {, }},
}; const GLubyte Indices2[] = {
, , ,
}; glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER,sizeof(Vertices),Vertices, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices),Indices,GL_STATIC_DRAW); _positionLocation = glGetAttribLocation(mShaderProgram->getProgram(), "a_position");
_colorLocation = glGetAttribLocation(mShaderProgram->getProgram(), "a_color"); _textureLocation = glGetAttribLocation(mShaderProgram->getProgram(), "TextureCoord");
_textureUniform = glGetUniformLocation(mShaderProgram->getProgram(), "CC_Texture0"); mShaderProgram->use();
mShaderProgram->setUniformsForBuiltins(); glEnableVertexAttribArray(_positionLocation);
glEnableVertexAttribArray(_colorLocation);
glEnableVertexAttribArray(_textureLocation); glVertexAttribPointer(_positionLocation, , GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, Position)); glVertexAttribPointer(_colorLocation, , GL_FLOAT, GL_FALSE, sizeof(Vertex),(GLvoid*)offsetof(Vertex, Color)); glVertexAttribPointer(_textureLocation, , GL_FLOAT, GL_FALSE, sizeof(Vertex),
(GLvoid*)offsetof(Vertex, TexCoord));
//
////set sampler
GL::bindTexture2DN(, _textureID);
//glActiveTexture( GL_TEXTURE0 );
//glBindTexture(GL_TEXTURE_2D, _textureID);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
glDrawElements(GL_TRIANGLES, , GL_UNSIGNED_BYTE, );
glUniform1i(_textureUniform, ); // unnecc in practice glGenBuffers(, &_vertexBuffer2);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer2);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices2), Vertices2, GL_STATIC_DRAW); glGenBuffers(, &_indexBuffer2);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer2);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices2), Indices2, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer2);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer2); GL::bindTexture2DN(, _textureID2);
glUniform1i(_textureUniform, ); // unnecc in practice glVertexAttribPointer(_positionLocation, , GL_FLOAT, GL_FALSE, sizeof(Vertex), );
glVertexAttribPointer(_colorLocation, , GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) (sizeof(float) * ));
glVertexAttribPointer(_textureLocation, , GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) (sizeof(float) * )); glDrawElements(GL_TRIANGLE_STRIP, sizeof(Indices2)/sizeof(Indices2[]), GL_UNSIGNED_BYTE, ); CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(,vertexCount); CHECK_GL_ERROR_DEBUG();
glDisable(GL_DEPTH_TEST); Director::getInstance()->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
Director::getInstance()->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
}
CubeTexture
运行结果:
方法二,所需要的shader文件:
attribute vec4 a_position;
attribute vec2 a_texCoord; varying vec2 v_texCoord; void main(void) {
gl_Position = CC_MVPMatrix * a_position;
v_texCoord = a_texCoord;
}
multiTexture.vert
precision mediump float;
varying vec2 v_texCoord;
//uniform sampler2D s_lightMap;
//uniform sampler2D s_baseMap; void main(void) {
vec4 baseColor;
vec4 lightColor;
//baseColor = texture2D( s_baseMap, v_texCoord);
//lightColor = texture2D( s_lightMap, v_texCoord );
baseColor = texture2D( CC_Texture0, v_texCoord);
lightColor = texture2D( CC_Texture1, v_texCoord );
gl_FragColor = baseColor * ( lightColor + 0.25 );
}
multiTexture
所需要的图
头文件
#ifndef _MultiTexture_H__
#define _MultiTexture_H__ #include "cocos2d.h" using namespace cocos2d; class MultiTexture : public cocos2d::Layer
{
public:
// there's no 'id' in cpp, so we recommend returning the class instance pointer
static cocos2d::Scene* createScene(); // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
virtual bool init(); virtual void draw(Renderer *renderer, const Mat4 &transform, uint32_t transformUpdated) override;
//we call our actual opengl commands here
void onDraw(); // implement the "static create()" method manually
CREATE_FUNC(MultiTexture); private:
Mat4 _modelViewMV;
CustomCommand _customCommand; GLProgram *mShaderProgram;
// attribute locations
GLint _positionLoc;
GLint _texCoordLoc; // sampler locations
GLuint _baseMapLoc;
GLuint _lightMapLoc;
// Texture handle
GLuint _baseMapTexId;
GLuint _lightMapTexId; GLuint VAO;
GLuint vertexBuffer;
GLuint indexBuffer; }; #endif // __HELLOWORLD_SCENE_H__
MultiTexture.h
源文件
#include "MultiTexture.h" cocos2d::Scene* MultiTexture::createScene()
{
auto scene = Scene::create();
auto layer = MultiTexture::create();
scene->addChild(layer);
return scene;
} bool MultiTexture::init()
{
if ( Layer::init() )
{
mShaderProgram = new GLProgram;
mShaderProgram->initWithFilenames("multiTexture.vert","multiTexture.frag");
mShaderProgram->link();
mShaderProgram->updateUniforms(); _baseMapTexId = Director::getInstance()->getTextureCache()->addImage( "HelloWorld.png" )->getName();
_lightMapTexId = Director::getInstance()->getTextureCache()->addImage("crate.jpg")->getName();
glGenVertexArrays(, &VAO);
glBindVertexArray(VAO); glGenBuffers( , &vertexBuffer );
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer ); glGenBuffers( , &indexBuffer );
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer ); return true;
}
return false;
} void MultiTexture::draw( Renderer *renderer, const Mat4 &transform, uint32_t transformUpdated )
{
Layer::draw(renderer, transform, transformUpdated); _customCommand.init(_globalZOrder);
_customCommand.func = CC_CALLBACK_0(MultiTexture::onDraw,this);
renderer->addCommand(&_customCommand);
} void MultiTexture::onDraw()
{
Director::getInstance()->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
Director::getInstance()->loadIdentityMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
Director::getInstance()->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
Director::getInstance()->loadIdentityMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION); typedef struct {
float Position[];
float TexCoord[];
} Vertex;
#define TEX_COORD_MAX 1 Vertex Vertices[] = { {{-0.5, 0.5, }, {, }},
{{-0.5, -0.5, }, {, TEX_COORD_MAX}},
{{0.5, -0.5, }, { TEX_COORD_MAX,TEX_COORD_MAX}},
{{0.5, 0.5, }, {TEX_COORD_MAX, }}, };
int vertexCount = sizeof(Vertices) / sizeof(Vertices[]); GLubyte Indices[] = {
, , ,
, , ,
}; glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER,sizeof(Vertices),Vertices, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices),Indices,GL_STATIC_DRAW); _positionLoc = glGetAttribLocation(mShaderProgram->getProgram(), "a_position");
_texCoordLoc = glGetAttribLocation(mShaderProgram->getProgram(), "a_texCoord");
//_baseMapLoc = glGetUniformLocation(mShaderProgram->getProgram(), "s_baseMap");
//_lightMapLoc = glGetUniformLocation(mShaderProgram->getProgram(), "s_lightMap");
_baseMapLoc = glGetUniformLocation(mShaderProgram->getProgram(), "CC_Texture0");
_lightMapLoc = glGetUniformLocation(mShaderProgram->getProgram(), "CC_Texture1"); glEnableVertexAttribArray(_positionLoc);
glEnableVertexAttribArray(_texCoordLoc); glVertexAttribPointer(_positionLoc, , GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, Position));
glVertexAttribPointer(_texCoordLoc, , GL_FLOAT, GL_FALSE, sizeof(Vertex),
(GLvoid*)offsetof(Vertex, TexCoord)); mShaderProgram->use();
mShaderProgram->setUniformsForBuiltins(); glEnable(GL_DEPTH_TEST);
glBindVertexArray(VAO); GL::bindTexture2DN(, _lightMapTexId);
glUniform1i(_lightMapLoc, ); // unnecc in practice GL::bindTexture2DN(, _baseMapTexId);
glUniform1i(_baseMapLoc, ); // unnecc in practice glDrawElements(GL_TRIANGLES,, GL_UNSIGNED_BYTE, ); glBindVertexArray();
CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(,); CHECK_GL_ERROR_DEBUG();
glDisable(GL_DEPTH_TEST); Director::getInstance()->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
Director::getInstance()->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
}
MultiTexture
运行效果:
第二种方法中,
cpp内的:
//_baseMapLoc = glGetUniformLocation(mShaderProgram->getProgram(), "s_baseMap");
//_lightMapLoc = glGetUniformLocation(mShaderProgram->getProgram(), "s_lightMap");
_baseMapLoc = glGetUniformLocation(mShaderProgram->getProgram(), "CC_Texture0");
_lightMapLoc = glGetUniformLocation(mShaderProgram->getProgram(), "CC_Texture1");
和shader内的
//baseColor = texture2D( s_baseMap, v_texCoord);
//lightColor = texture2D( s_lightMap, v_texCoord );
baseColor = texture2D( CC_Texture0, v_texCoord);
lightColor = texture2D( CC_Texture1, v_texCoord );
将注释解开,把下面两行注掉。同样可以。这说明了,cocos2dx在进行编译shader的时候内置了多个uniform值。大家可以看一下,其中就包括CC_Texture0系列。
其实用起来不算方便了,而不注意的人,可能会声明和内置变量相同的名字,此时,咳咳咳咳咳~ shader编译的时候就会出错了。不知道为啥cocos2dx要多次一举。
还有一点:
////set sampler
GL::bindTexture2DN(0, _textureID);
//glActiveTexture( GL_TEXTURE0 );
//glBindTexture(GL_TEXTURE_2D, _textureID);
我使用了GL::bindTexture2DN 方法,而并没有使用opengl es原装的 glActiveTexture 和glBindTexture这两个方法。其实GL::bindTexture2DN 内部就调用了后面的两个方法,那为何不直接用后面的两个方法呢?
原因是,直接用opengl es的方法,会出错!!!尼玛 这才是坑点。跟踪后发现,cocos2dx给GL相关的东西添加了一个StateCache的东西,当activeTexture时,需要更改cache内的东西。直接调用glActiveTexture的方法,就略去了往cache内塞东西的步骤,这样就出错了。
猜测是cocos2dx进行渲染的时候,调用了cache内的什么东西。尼玛,你还能封装的再恶心一点么?
所以说,不喜欢cocos2dx这种处理问题的方法的,可以完全抛开cocos2dx了。