openGL 提升渲染性能 之 顶点数组 VBO IBO VAO

使用openGL图形库绘制,都需要通过openGL接口向图像显卡提交顶点数据,显卡根据提交的数据绘制出相应的图形。

 

openGL绘制方式有:直接模式,显示列表,顶点数组,顶点索引。

 

直接模式:最简单,最直接的模式,但是性能是最差的,因为每绘制一个基本图元都需要提交一次数据;

glBegin(GL_TRIANGLE_STRIP);  

glColor3ub(255, 0, 0);  

glVertex3f(-0.5f, 0.5f, 0.0f);  

glColor3ub(0, 255, 0);  

glVertex3f(-0.5f, -0.5f, 0.0f);  

glColor3ub(0, 0, 255);  

glVertex3f(0.5f, 0.5f, 0.0f);  

glColor3ub(255, 0, 255);  

glVertex3f(0.5f, -0.5f, 0.0f);  

glEnd();  

上面就是用直接模式绘制一个三角形带的所有openGL命令,如果要绘制无数个三角形,那么函数调用的开销是巨大的,而且这样的写法让顶点数据非常不容易扩展和修改,所以基本上这种模式不可能用在实际的用途中。

 

显示列表:直接模式在每次绘制的时候,都需要将顶点数组从cpu端重新发送到gpu端,如果每次数据都没有任何变化,这种重复的发送就显得没有意义而且低效。显示列表就是为了解决这个重复发送的性能问题,显示列表相当于把一组绘制命令存储在服务器端(gpu端),每次只需要发送一个调用命令,而不需要重复发送所有顶点数据,就可以执行已经预定好的绘制命令了。虽然显示列表解决了不用重发发送顶点数据的问题,但是缺点也是显而易见的,就是显示列表一旦定义好,就无法被修改,因此显示列表只适用于那些不会被修改的绘制命令。而且显示列表和直接模式依然具有相同的问题,就是函数调用开销和难以扩展和修改。只是相当于命令直接在gpu端执行,减少了从cpu发送gpu的过程而已。

定义一个显示列表:

glNewList (listName, GL_COMPILE);

glColor3f (1.0, 0.0, 0.0); 

glBegin (GL_TRIANGLES);

glVertex2f (0.0, 0.0);

glVertex2f (1.0, 0.0); 

glVertex2f (0.0, 1.0);

glEnd ();

glTranslatef (1.5, 0.0, 0.0);

glEndList (); 

执行一个显示列表:

glCallList (listName);

删除一个显示列表:

glDeleteLists(listName, 1);

 

顶点数组:由于直接模式的局限性,openGL提供了另一种更加高效的绘制模式,顶点数组。

顶点数组顾名思义就是允许我们将我们的顶点数据放置到一个数组中,一次性提交给显卡进行绘制。这样只需要极少量的函数调用,而且数组全部聚合在一起,也更加容易修改和扩展。

 // 先定义顶点位置,颜色数组,纹理数组的顶点相关数据:

GLfloat vertexes[] = {
0.0f, 0.0f, 0.0f, 1.0f,
0.0f, 512, 0.0f, 1.0f,
1024, 512, 0.0f, 1.0f,
1024, 0.0f, 0.0f, 1.0f
};
GLfloat colores[] = {
1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f
};
GLfloat texCoordes[] = {
0.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f,
1.0f, 1.0f
};

// 0,1,2代表着色器的input的location,分别代表顶点位置,顶点颜色,顶点纹理坐标(这里使用的是可编程管线,如果使用固定管线,指定方式也是类似的)

// 为0,1,2指定相应的数据

glEnableVertexAttribArray(0); // 固定管线使用glEnableClientState(GL_VERTEX_ARRAY)
glEnableVertexAttribArray(1); // 固定管线使用glEnableClientState(GL_COLOR_ARRAY)
glEnableVertexAttribArray(2); // 固定管线使用glEnableClientState(GL_TEXTURE_COORD_ARRAY)
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, vertexes); // 固定管线使用glVertexPointer(4, GL_FLOAT, 0, vertexes);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, colores); // 固定管线使用glColorPointer(4, GL_FLOAT, 0, colores);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, texCoordes); // 固定管线使用glTexCoordPointer(2, GL_FLOAT, 0, texCoordes);

// 绘制一个四边形

glDrawArrays(GL_QUADS, 0, 4);

 

这里将位置,颜色,纹理分为三个数组分别存放,我们也可把所有数据放在一个数组中,称之为交错数组:

GLfloat data[] = {
0.0f, 0.0f, 0.0f, 1.0f,    1.0f, 1.0f, 1.0f, 1.0f,   0.0f, 1.0f,
0.0f, 512, 0.0f, 1.0f,    1.0f, 1.0f, 1.0f, 1.0f,   0.0f, 0.0f,
1024, 512, 0.0f, 1.0f,  1.0f, 1.0f, 1.0f, 1.0f,   1.0f, 0.0f,
1024, 0.0f, 0.0f, 1.0f,  1.0f, 1.0f, 1.0f, 1.0f,   1.0f, 1.0f
};

glEnableVertexAttribArray(0); 
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 10 * sizeof(GLfloat), vertexes); // 第四个参数指的是两个位置数据在数组中间距,第五个参数指的是第一个位置数据在数组中的起始位置
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 10 * sizeof(GLfloat), colores + 4); // 第四个参数指的是两个颜色数据在数组中间距,第五个参数指的是第一个颜色数据在数组中的起始位置
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 10 * sizeof(GLfloat), texCoordes + 8); // 第四个参数指的是两个纹理坐标数据在数组中间距,第五个参数指的是第一个纹理坐标数据在数组中的起始位置

// 绘制一个四边形

glDrawArrays(GL_QUADS, 0, 4);

 

除此之外,交错无数也可以直接使用:

  glInterleavedArrays (GLenum format, GLsizei stride, const GLvoid *pointer);

进行一次性指定,但是数组中数据的构成规则必须和format指定的规则一致。例如:glInterleavedArrays(GL_V2F,0,data); 表示数据组合规则是仅仅2个浮点数的位置数据

 

顶点索引:顶点数组已经可以为我们提供了方便指定绘制数据的方式,大幅提升了渲染的性能。但是依然还存在一个问题:如果我们需要绘制一个模型,这个模型由无数三角形片构成,大部分三角形都是连续拼接的没有空隙,所以每相邻的两个三角形可能会有拥有一个或者两个完全相同顶点(位置,颜色,纹理坐标都相同)。如果我们使用顶点数组的数据来构成这个模型,我们将为相邻的两个三角形片分别制定6个顶点数据,其中有可能会出现最多两对完全相同的顶点,而这些顶点其实是可以共享的,但却出现了冗余数据。在模型复杂的情况下,冗余数据也是巨大的。于是openGL为我们指定了一种更加灵活的方式,顶点索引数组,即我们只需要创建好必要的顶点数据,顶点数据在数组中的排列也不受图元绘制方式的限制,理论上可以随意排列。然后用索引去对应每一个顶点数据,绘制图元需要提交顶点数据的时候,直接指定顶点索引即可,因为顶点索引会一一映射到顶点数据,这样就消除了冗余的顶点数据,而且以更加灵活的方式进行渲染。

// 指定顶点数据,注意渲染顺序的迎风面是逆时针还是顺时针,这里绘制4个顶点,分别对应顶点数组中的前4个顶点

GLuint indexes = {0, 1, 2, 3};

// 用顶点索引进行绘制的绘制调用

glDrawElements(GL_QUADS, 4, GL_UNSIGNED_INT, indexes);

 

VBO: 顶点数组加上顶点索引,似乎已经可以完美解决大部分问题,但是图形渲染对性能的追求是永无止境的。虽然使用顶点数组和顶点索引,我们可以一次性提交所有绘制数据,并只需要调用一次绘制命令。但是在数据很大的情况下,我们依然都要从cpu端向gpu端提交大量数据,如果这些数据又几乎不会发生改变,那么这种操作将是极大的性能浪费。

为了减少这种耗时但又无意义的工作,openGL为我们提供了VBO(顶点缓冲对象)来改善这种问题。

使用VBO,可以将我们的顶点数据存放在图像显卡的内存中,而不需要存放在cpu端的内存中,就不需要在每次绘制时,发送大量顶点数据到gpu端了。

// 生成VBO,并为VBO绑定顶点数据

size_t dataSize = sizeof(GLfloat) * vertexCount * 4; // 在图像显卡中需要分配的内存大小
GLuint vbos[1] = { 0 }; // VBO名字

glGenBuffers(1, vbos); // 生成一个可用的VBO名字
if (vbos[0] > 0) // 如果名字可用
{
vertexVBO = vbos[0];

glBindBuffer(GL_ARRAY_BUFFER, vertexVBO); // 绑定当前的VBO,GL_ARRAY_BUFFER是VBO使用的固定参数

glBufferData(GL_ARRAY_BUFFER, dataSize, vertexes, GL_STATIC_DRAW); // 将位置数据绑定到当前的VBO上,dataSize是需要的内存大小,vertexes是顶点的位置数据

// GL_STATIC_DRAW 是一个性能提示参数,这个参数指示了当前VBO的用途,该参数必须是GL_STREAM_DRAWGL_STATIC_DRAW, or GL_DYNAMIC_DRAW之一。openGL会根据该指示,尽可能将数据放置在性能最优的内存中,可能是显存,AGP内存,或者cpu内存中。

// GL_STATIC_DRAW:数据指定一次,并多次被用于绘制。

// GL_STREAM_DRAW:数据指定一次,最多几次用于绘制。

// GL_DYNAMIC_DRAW:数组多次指定,多次用于绘制。

delete[] vertexes;
m_vertexes = nullptr;

CHECK_GL_ERROR();
}

// 使用VBO进行绘制,和使用顶点数组类似

#define BUFFER_OFFSET(offset) ((GLvoid*)(NULL + offset)) // 数据在缓冲区中的偏移位置,和顶点数组指针位置效果类似

glBindBuffer(GL_ARRAY_BUFFER, vertexVBO); // 绑定位置VBO
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0)); // 指定位置数据
glBindBuffer(GL_ARRAY_BUFFER, colorVBO); // 绑定颜色VBO
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0)); // 指定颜色数据
glBindBuffer(GL_ARRAY_BUFFER, textureVBO); // 绑定纹理VBO
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0)); // 指定纹理数据
glBindBuffer(GL_ARRAY_BUFFER, 0);

glDrawArrays(GL_QUADS, 0, m_vertexCount); // 绘制

 

IBO: 索引缓冲对象,和VBO一样,只是存储的是索引数组。

glGenBuffers(1, &IBO);
if (sphereIBO > 0)
{
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO); // 参数必须使用GL_ELEMENT_ARRAY_BUFFER
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * indexCount, indexes, GL_STATIC_DRAW);

CHECK_GL_ERROR();

delete[] indexes;
indexes = nullptr;
}

// 绘制

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
glDrawElements(GL_QUADS, indexCount, GL_UNSIGNED_INT, BUFFER_OFFSET(0));
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

 

VAO: 有了VBO和IBO,已经可以很好的解决问题了,但是使用VAO可以使我们的开发更加灵活。VAO其实就是可以绑定VBO和IBO的一个包装对象,我们把有关联的VBO和IBO一起绑定到一个VAO上,我们每次只需要使用VAO就可以进行绘制了。

// 生成VAO

glGenVertexArrays(1, &VAO); // 生成一个VAO
if (VAO > 0) // 如果VAO可用
{
glBindVertexArray(VAO); // 绑定到当前的VAO

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO); // 绑定一个IBO到当前的VAO上

glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);

glBindBuffer(GL_ARRAY_BUFFER, vertexVBO); // 绑定位置VBO到当前的VAO上
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0)); // 指定位置数据
glBindBuffer(GL_ARRAY_BUFFER, colorVBO); // 绑定颜色VBO到当前的VAO上
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0)); // 指定颜色数据
glBindBuffer(GL_ARRAY_BUFFER, textureVBO); // 绑定纹理VBO到当前的VAO上
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0)); // 指定纹理数据

CHECK_GL_ERROR_DEBUG();
}

// 绘制

glBindVertexArray(VAO);
glDrawElements(GL_QUADS, indexCount, GL_UNSIGNED_INT, BUFFER_OFFSET(0));
glBindVertexArray(0);

 

下面是基于cocos2d的完整的代码:

Sphere.h:

#ifndef __SPHERE__
#define __SPHERE__

#include "cocos2d.h"

USING_NS_CC;

class Sphere : public cocos2d::Node
{
public:
    static Sphere* create();

    virtual ~Sphere();

    bool init();

    virtual void draw(Renderer *renderer, const Mat4& transform, uint32_t flags);

    virtual void visit(Renderer *renderer, const Mat4& parentTransform, uint32_t parentFlags);

private:
    GLProgram* m_program;
    GLProgram* m_program_planview;
    GLuint m_textureName;

    GLfloat* m_vertexes;
    GLfloat* m_colors;
    GLfloat* m_texcoordes;
    GLuint* m_indexes;
    unsigned int m_vertexCount;
    unsigned int m_indexCount;

    float m_sphereLatitude; // 纬度
    float m_sphereLongitude; // 经度
    unsigned int m_sphereLatitudeCount;
    unsigned int m_sphereLongitudeCount;
    unsigned int m_sphereQuadCount;

    float m_sphereRadius; // 半径
    float m_spherePerimeter; // 周长
    float m_planViewWidth;
    float m_planViewHeight;

    Mat4 m_sphereTransform;
    float m_rotateXAngle;
    float m_rotateYAngle;
    float m_rotateZAngle;
    float m_sphereTranslateX;
    float m_sphereTranslateY;

    float m_planViewTranslateX;
    float m_planViewTranslateY;

    bool m_isContinue;
    float m_continueRotateY;
    float m_continueRotateX;
    float m_decreateDetal;

    std::thread* m_thread_1;
    std::thread* m_thread_2;
    bool m_isThread1Done;
    bool m_isThread2Done;

    std::vector<float> m_offsetDataList;
    std::vector<float> m_offsetDataListCopy;

    GLuint m_sphereVAO;
    GLuint m_sphereIBO;
    GLuint m_sphereVertexVBO;
    GLuint m_sphereColorVBO;
    GLuint m_sphereTextureVBO;

    bool initProgram();
    bool initVertexData();

    void createQuaternion(float rotateX, float rotateY, float rotateZ, Quaternion& quat);

    void generateVertexesSphere(float radius);
    void generateVertexesSphereNew(float radius);
    void generateTexture();
    void generateVAO();
    void generateIBO();
    void generateVBO();

    void transformSphere(float touchOffsetX, float touchOffsetY);

    void drawBg(const Mat4& transform);
    void drawSphere(const Mat4& transform, GLfloat* vertexes);
    void drawPlanView(const Mat4& transform, GLfloat* vertexes);

    void update();

    bool onTouchBegan(Touch* touch, Event* event);
    void onTouchMoved(Touch* touch, Event* event);
    void onTouchEnded(Touch* touch, Event* event);

    void onThread1Proc();
    void onThread2Proc();

    void noticeToTransform(float touchOffsetX, float touchOffsetY);
};

#endif

Sphere.cpp:

#include "SphereNew.h"
#include "math.h"
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <thread>
#include <functional>

#define MPI 3.1415926f
#define HMPI (3.1415926f / 2.0f)
#define DMPI (3.1415926f * 2.0f)
#define A_TO_R(angle) (3.1415926f / 180.0f * (float)(angle))

#define TEXTURE_WIDTH 1024
#define TEXTURE_HEIGHT 512

#define BUFFER_OFFSET(offset) ((GLvoid*)(NULL + offset))

const GLchar* ccPositionTextureColor_v = " \
attribute vec4 a_position; \n\
attribute vec2 a_texCoord; \n\
attribute vec4 a_color; \n\
\n\
#ifdef GL_ES \n\
varying lowp vec4 v_fragmentColor; \n\
varying mediump vec2 v_texCoord; \n\
#else \n\
varying vec4 v_fragmentColor; \n\
varying vec2 v_texCoord; \n\
#endif \n\
\n\
void main() \n\
{ \n\
    gl_Position = CC_MVPMatrix * a_position; \n\
    v_fragmentColor = a_color; \n\
    v_texCoord = a_texCoord; \n\
} \n\
";

const GLchar* ccPositionTextureColorForSphere_v = " \
uniform vec2 translate; \n\
uniform float radius; \n\
attribute vec4 a_position; \n\                                                                                                                                                                                attribute vec2 a_texCoord; \n\
attribute vec4 a_color; \n\
\n\
#ifdef GL_ES \n\
varying lowp vec4 v_fragmentColor; \n\
varying mediump vec2 v_texCoord; \n\
#else \n\
varying vec4 v_fragmentColor; \n\
varying vec2 v_texCoord; \n\
#endif \n\
const float pi = 3.1415926; \n\
const float hpi = 1.5707963; \n\
\n\
void main() \n\
{ \n\
vec4 _position = CC_MVMatrix * a_position; \n\
float _angle1 = atan(_position.x, _position.z); \n\
float _angle2 = atan(_position.z, _position.y); \n\
float _xOffset = _angle1 * radius; \n\
float _yOffset = _position.y * hpi; \n\
_position.x = _xOffset + translate.x; \n\
_position.y = _yOffset + translate.y; \n\
_position.z = 0.0f; \n\
gl_Position = CC_PMatrix * _position; \n\
\n\
v_fragmentColor = a_color; \n\
v_texCoord = a_texCoord; \n\
} \n\
";

const GLchar* ccPositionTextureColor_f = " \
#ifdef GL_ES \n\
precision lowp float; \n\
#endif \n\
\n\
varying vec4 v_fragmentColor; \n\
varying vec2 v_texCoord; \n\
\n\
void main() \n\
{ \n\
vec4 color = v_fragmentColor * texture2D(CC_Texture0, v_texCoord); \n\
color.a = 1.0; \n\
gl_FragColor = color; \n\
} \n\
";

static std::condition_variable cv_transform;
static std::condition_variable cv_notice;
static std::condition_variable cv_transform_sphere;
static std::condition_variable cv_transform_planview;
static std::mutex mx_transform;
static std::mutex mx_transform_planview;
static std::mutex mx_transform_sphere;
static bool b_transform_sphere = false;
static bool b_transform_planview = false;

static float _touchOffsetX = 0.0f;
static float _touchOffsetY = 0.0f;
static float _touchOffsetXCopy = 0.0f;
static float _touchOffsetYCopy = 0.0f;
static Mat4 _sphereTransformationCopy;

Sphere* Sphere::create()
{
    Sphere *pRet = new(std::nothrow) Sphere();

    if (pRet && pRet->init())
    {
        pRet->autorelease();
        return pRet;
    }
    else
    {
        delete pRet;
        pRet = nullptr;
        return nullptr;
    }
}

bool Sphere::init()
{
    if (!this->initProgram())
        return false;

    if (!this->initVertexData())
        return false;

    this->generateVertexesSphereNew(m_sphereRadius);
    this->generateTexture();
    this->generateVBO();
    this->generateIBO();
    this->generateVAO();

    this->transformSphere(0, 0);

    auto eventListener = EventListenerTouchOneByOne::create();
    eventListener->setSwallowTouches(true);
    eventListener->onTouchBegan = CC_CALLBACK_2(Sphere::onTouchBegan, this);
    eventListener->onTouchMoved = CC_CALLBACK_2(Sphere::onTouchMoved, this);
    eventListener->onTouchEnded = CC_CALLBACK_2(Sphere::onTouchEnded, this);
    CCDirector::sharedDirector()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(eventListener, this);

    /*
    m_isThread1Done = false;
    m_isThread2Done = false;
    m_thread_1 = new std::thread(std::bind(&Sphere::onThread1Proc, this));
    m_thread_2 = new std::thread(std::bind(&Sphere::onThread2Proc, this));
    */
    m_thread_1 = nullptr;
    m_thread_2 = nullptr;

    return true;
}

bool Sphere::initProgram()
{
    bool suc = false;

    m_program = new CCGLProgram();
    if (m_program)
    {
        if (m_program->initWithVertexShaderByteArray(ccPositionTextureColor_v, ccPositionTextureColor_f))
        {
            if (m_program->link())
            {
                m_program->updateUniforms();
                suc = true;
            }
        }
    }
    if (!suc)
    {
        delete m_program;
        m_program = NULL;
    }

    suc = false;

    m_program_planview = new CCGLProgram();
    if (m_program_planview)
    {
        if (m_program_planview->initWithVertexShaderByteArray(ccPositionTextureColorForSphere_v, ccPositionTextureColor_f))
        {
            if (m_program_planview->link())
            {
                m_program_planview->updateUniforms();
                suc = true;
            }
        }
    }
    if (!suc)
    {
        delete m_program_planview;
        m_program_planview = NULL;
    }

    return suc;
}

bool Sphere::initVertexData()
{
    m_vertexes = nullptr;
    m_colors = nullptr;
    m_texcoordes = nullptr;
    m_indexes = nullptr;
    m_vertexCount = 0;
    m_indexCount = 0;

    m_sphereTranslateX = 512.0f;
    m_sphereTranslateY = 412.0f;
    m_rotateYAngle = 0.0f;
    m_rotateXAngle = 0.0f;
    m_rotateZAngle = 0.0f;

    m_planViewTranslateX = 512.0f;
    m_planViewTranslateY = 150.0f;
    m_planViewWidth = 400.0f;
    m_planViewHeight = 200.0f;

    m_sphereRadius = 100.0f;
    m_spherePerimeter = 2.0f * 3.1415926f * m_sphereRadius;

    m_isContinue = false;
    m_continueRotateX = 0.0f;
    m_continueRotateY = 0.0f;
    m_decreateDetal = 0.01f;

    m_sphereLatitude = 1.0f;
    m_sphereLongitude = 1.0f;
    m_sphereLatitudeCount = ceil(180.0f / m_sphereLatitude);
    m_sphereLongitudeCount = ceil(360.0f / m_sphereLongitude);
    m_sphereQuadCount = m_sphereLatitudeCount * m_sphereLongitudeCount;

    m_sphereTransform.setIdentity();
    //m_sphereTransform.rotateY(A_TO_R(90.0f));

    m_sphereVAO = 0;
    m_sphereIBO = 0;
    m_sphereVertexVBO = 0;
    m_sphereColorVBO = 0;
    m_sphereTextureVBO = 0;

    return true;
}

Sphere::~Sphere()
{
    if (m_thread_1)
    {
        m_isThread1Done = true;
        m_thread_1->join();
        delete m_thread_1;
    }

    if (m_thread_2)
    {
        m_isThread2Done = true;
        b_transform_planview = true;
        cv_transform_planview.notify_all();
        m_thread_2->join();
        delete m_thread_2;
    }

    if (m_vertexes) 
        delete[] m_vertexes;
    if (m_colors) 
        delete[] m_colors;
    if (m_texcoordes) 
        delete[] m_texcoordes;
    if (m_indexes)
        delete[] m_indexes;

    if (m_sphereVertexVBO > 0)
        glDeleteBuffers(1, &m_sphereVertexVBO);
    if (m_sphereColorVBO > 0)
        glDeleteBuffers(1, &m_sphereColorVBO);
    if (m_sphereTextureVBO > 0)
        glDeleteBuffers(1, &m_sphereTextureVBO);
    if (m_sphereIBO > 0)
        glDeleteBuffers(1, &m_sphereIBO);
    if (m_sphereVAO > 0)
        glDeleteVertexArrays(1, &m_sphereVAO);
}

void Sphere::generateVertexesSphere(float radius)
{
    float rx = 0.0f, ry = 0.0f, rz = 0.0f;

    m_vertexes = new GLfloat[m_sphereQuadCount * 16];
    m_colors = new GLfloat[m_sphereQuadCount * 16];
    m_texcoordes = new GLfloat[m_sphereQuadCount * 8];
    m_indexes = new GLuint[m_sphereQuadCount * 4];

    unsigned int offset = 4 * 4;
    unsigned int textureOffset = 4 * 2;
    unsigned int stride = m_sphereLatitudeCount / 2 * m_sphereLongitudeCount * offset;
    unsigned int textureStride = m_sphereLatitudeCount / 2 * m_sphereLongitudeCount * textureOffset;

    float unitU = m_sphereLongitude / 360.0f;
    float unitV = m_sphereLatitude / 360.0f * 2;

    GLuint vertexIndex = 0;

    for (unsigned int latiIndex = 0; latiIndex < m_sphereLatitudeCount / 2; ++latiIndex)
    {
        // 纬度
        float latiAngle = m_sphereLatitude * (latiIndex + 1);
        float latiRadian1 = A_TO_R(latiAngle - m_sphereLatitude);
        float latiRadian2 = A_TO_R(latiAngle);

        for (unsigned int longiIndex = 0; longiIndex < m_sphereLongitudeCount; ++longiIndex)
        {
            // 经度
            float longiAngle = m_sphereLongitude * (longiIndex + 1);
            float longiRadian1 = A_TO_R(longiAngle - m_sphereLongitude);
            float longiRadian2 = A_TO_R(longiAngle);

            unsigned int index = latiIndex * m_sphereLongitudeCount + longiIndex;

            // vertex
            // 上半球
            m_vertexes[index * offset + 0] = rx + radius * cos(latiRadian1) * cos(longiRadian1);
            m_vertexes[index * offset + 1] = ry + radius * sin(latiRadian1);
            m_vertexes[index * offset + 2] = rz + radius * cos(latiRadian1) *sin(longiRadian1);
            m_vertexes[index * offset + 3] = 1.0f;
            // 上半球
            m_vertexes[index * offset + 4] = rx + radius * cos(latiRadian1) * cos(longiRadian2);
            m_vertexes[index * offset + 5] = ry + radius * sin(latiRadian1);
            m_vertexes[index * offset + 6] = rz + radius * cos(latiRadian1) * sin(longiRadian2);
            m_vertexes[index * offset + 7] = 1.0f;
            // 上半球
            m_vertexes[index * offset + 8] = rx + radius * cos(latiRadian2) * cos(longiRadian2);
            m_vertexes[index * offset + 9] = ry + radius * sin(latiRadian2);
            m_vertexes[index * offset + 10] = rz + radius * cos(latiRadian2)  * sin(longiRadian2);
            m_vertexes[index * offset + 11] = 1.0f;
            // 上半球
            m_vertexes[index * offset + 12] = rx + radius * cos(latiRadian2) * cos(longiRadian1);
            m_vertexes[index * offset + 13] = ry + radius * sin(latiRadian2);
            m_vertexes[index * offset + 14] = rz + radius * cos(latiRadian2) * sin(longiRadian1);
            m_vertexes[index * offset + 15] = 1.0f;
            // 下半球
            m_vertexes[stride + index * offset + 0] = m_vertexes[index * offset + 12];
            m_vertexes[stride + index * offset + 1] = -1 * m_vertexes[index * offset + 13];
            m_vertexes[stride + index * offset + 2] = m_vertexes[index * offset + 14];
            m_vertexes[stride + index * offset + 3] = 1.0f;
            // 下半球
            m_vertexes[stride + index * offset + 4] = m_vertexes[index * offset + 8];
            m_vertexes[stride + index * offset + 5] = -1 * m_vertexes[index * offset + 9];
            m_vertexes[stride + index * offset + 6] = m_vertexes[index * offset + 10];
            m_vertexes[stride + index * offset + 7] = 1.0f;
            // 下半球
            m_vertexes[stride + index * offset + 8] = m_vertexes[index * offset + 4];
            m_vertexes[stride + index * offset + 9] = -1 * m_vertexes[index * offset + 5];
            m_vertexes[stride + index * offset + 10] = m_vertexes[index * offset + 6];
            m_vertexes[stride + index * offset + 11] = 1.0f;
            // 下半球
            m_vertexes[stride + index * offset + 12] = m_vertexes[index * offset + 0];
            m_vertexes[stride + index * offset + 13] = -1 * m_vertexes[index * offset + 1];
            m_vertexes[stride + index * offset + 14] = m_vertexes[index * offset + 2];
            m_vertexes[stride + index * offset + 15] = 1.0f;

            // color
            //上半球
            m_colors[index * offset + 0] = 1.0f;
            m_colors[index * offset + 1] = 1.0f;
            m_colors[index * offset + 2] = 1.0f;
            m_colors[index * offset + 3] = 0.6f;
            m_colors[index * offset + 4] = 1.0f;
            m_colors[index * offset + 5] = 1.0f;
            m_colors[index * offset + 6] = 1.0f;
            m_colors[index * offset + 7] = 0.6f;
            m_colors[index * offset + 8] = 1.0f;
            m_colors[index * offset + 9] = 1.0f;
            m_colors[index * offset + 10] = 1.0f;
            m_colors[index * offset + 11] = 0.6f;
            m_colors[index * offset + 12] = 1.0f;
            m_colors[index * offset + 13] = 1.0f;
            m_colors[index * offset + 14] = 1.0f;
            m_colors[index * offset + 15] = 0.6f;
            // 下半球
            m_colors[stride + index * offset + 0] = 1.0f;
            m_colors[stride + index * offset + 1] = 1.0f;
            m_colors[stride + index * offset + 2] = 1.0f;
            m_colors[stride + index * offset + 3] = 0.6f;
            m_colors[stride + index * offset + 4] = 1.0f;
            m_colors[stride + index * offset + 5] = 1.0f;
            m_colors[stride + index * offset + 6] = 1.0f;
            m_colors[stride + index * offset + 7] = 0.6f;
            m_colors[stride + index * offset + 8] = 1.0f;
            m_colors[stride + index * offset + 9] = 1.0f;
            m_colors[stride + index * offset + 10] = 1.0f;
            m_colors[stride + index * offset + 11] = 0.6f;
            m_colors[stride + index * offset + 12] = 1.0f;
            m_colors[stride + index * offset + 13] = 1.0f;
            m_colors[stride + index * offset + 14] = 1.0f;
            m_colors[stride + index * offset + 15] = 0.6f;

            // texture
            float startU = (m_sphereLongitudeCount - longiIndex) * unitU;
            float startV = (m_sphereLatitudeCount / 2 - latiIndex) * unitV;
            // 上半球
            m_texcoordes[index * textureOffset + 0] = startU - unitU;
            m_texcoordes[index * textureOffset + 1] = startV;
            m_texcoordes[index * textureOffset + 2] = startU;
            m_texcoordes[index * textureOffset + 3] = startV;
            m_texcoordes[index * textureOffset + 4] = startU;
            m_texcoordes[index * textureOffset + 5] = (startV + unitV);
            m_texcoordes[index * textureOffset + 6] = startU - unitU;
            m_texcoordes[index * textureOffset + 7] = (startV + unitV);
            // 下半球
            m_texcoordes[textureStride + index * textureOffset + 0] = startU;
            m_texcoordes[textureStride + index * textureOffset + 1] = 1.0f - (startV + unitV);
            m_texcoordes[textureStride + index * textureOffset + 2] = startU - unitU;
            m_texcoordes[textureStride + index * textureOffset + 3] = 1.0f - (startV + unitV);
            m_texcoordes[textureStride + index * textureOffset + 4] = startU - unitU;;
            m_texcoordes[textureStride + index * textureOffset + 5] = 1.0f - startV;
            m_texcoordes[textureStride + index * textureOffset + 6] = startU;
            m_texcoordes[textureStride + index * textureOffset + 7] = 1.0f - startV;

            m_indexes[vertexIndex++] = index * offset / 4;
            m_indexes[vertexIndex++] = index * offset / 4 + 1;
            m_indexes[vertexIndex++] = index * offset / 4 + 2;
            m_indexes[vertexIndex++] = index * offset / 4 + 3;
            m_indexes[vertexIndex++] = (stride + index * offset) / 4;
            m_indexes[vertexIndex++] = (stride + index * offset) / 4 + 1;
            m_indexes[vertexIndex++] = (stride + index * offset) / 4 + 2;
            m_indexes[vertexIndex++] = (stride + index * offset) / 4 + 3;

            m_vertexCount += 8;
            m_indexCount += 8;
        }
    }
}

void Sphere::generateVertexesSphereNew(float radius)
{
    unsigned int quadCount = m_sphereLatitudeCount * m_sphereLongitudeCount;
    unsigned int vertexCount = (m_sphereLatitudeCount + 1) * (m_sphereLongitudeCount + 1);

    m_vertexes = new GLfloat[vertexCount * 4];
    m_colors = new GLfloat[vertexCount * 4];
    m_texcoordes = new GLfloat[vertexCount * 2];
    m_indexes = new GLuint[quadCount * 4];

    float unitU = m_sphereLongitude / 360.0f;
    float unitV = m_sphereLatitude / 180.0f;

    for (unsigned int latiIndex = 0; latiIndex <= m_sphereLatitudeCount; ++latiIndex)
    {
        // 纬度
        float latiAngle = m_sphereLatitude * latiIndex;
        float latiRadian = A_TO_R(latiAngle);

        latiRadian = HMPI - latiRadian;

        for (unsigned int longiIndex = 0; longiIndex <= m_sphereLongitudeCount; ++longiIndex)
        {
            // 经度
            float longiAngle = m_sphereLongitude * longiIndex;
            float longiRadian = A_TO_R(longiAngle);

            unsigned int index = latiIndex * (m_sphereLongitudeCount + 1) + longiIndex;

            m_vertexes[index * 4] = m_sphereRadius * cos(latiRadian) * sin(longiRadian);
            m_vertexes[index * 4 + 1] = m_sphereRadius * sin(latiRadian);
            m_vertexes[index * 4 + 2] = m_sphereRadius * cos(latiRadian) * cos(longiRadian);
            m_vertexes[index * 4 + 3] = 1.0f;

            m_colors[index * 4] = 1.0f;
            m_colors[index * 4 + 1] = 1.0f;
            m_colors[index * 4 + 2] = 1.0f;
            m_colors[index * 4 + 3] = 1.0f;

            m_texcoordes[index * 2] = longiIndex * unitU;
            m_texcoordes[index * 2 + 1] = latiIndex * unitV;
        }
    }
    m_vertexCount = vertexCount;

    for (unsigned int i = 0; i < m_sphereLatitudeCount; ++i)
    {
        for (unsigned int j = 0; j < m_sphereLongitudeCount; ++j)
        {
            unsigned index = i * m_sphereLongitudeCount + j;

            m_indexes[index * 4] = i * (m_sphereLongitudeCount + 1) + j;
            m_indexes[index * 4 + 1] = i * (m_sphereLongitudeCount + 1) + j + 1;
            m_indexes[index * 4 + 2] = (i + 1) * (m_sphereLongitudeCount + 1) + j + 1;
            m_indexes[index * 4 + 3] = (i + 1) * (m_sphereLongitudeCount + 1) + j;
        }
    }
    m_indexCount = quadCount * 4;
}

void Sphere::generateTexture()
{
    CCTexture2D* texture = CCTextureCache::sharedTextureCache()->addImage("images/diqiu.jpg");
    if (texture)
        m_textureName = texture->getName();
    else
        assert(0, "create texture failed");
}

void Sphere::generateVAO()
{
    glGenVertexArrays(1, &m_sphereVAO);
    if (m_sphereVAO > 0)
    {
        glBindVertexArray(m_sphereVAO);

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_sphereIBO);

        glEnableVertexAttribArray(0);
        glEnableVertexAttribArray(1);
        glEnableVertexAttribArray(2);

        glBindBuffer(GL_ARRAY_BUFFER, m_sphereVertexVBO);
        glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
        glBindBuffer(GL_ARRAY_BUFFER, m_sphereColorVBO);
        glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
        glBindBuffer(GL_ARRAY_BUFFER, m_sphereTextureVBO);
        glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));

        CHECK_GL_ERROR_DEBUG();
    }
    else
    {
        assert(0);
    }

    glBindVertexArray(0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}

void Sphere::generateIBO()
{
    glGenBuffers(1, &m_sphereIBO);
    if (m_sphereIBO > 0)
    {
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_sphereIBO);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * m_indexCount, m_indexes, GL_STATIC_DRAW);

        CHECK_GL_ERROR_DEBUG();

        delete[] m_indexes;
        m_indexes = nullptr;
    }

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}

void Sphere::generateVBO()
{
    size_t dataSize = sizeof(GLfloat) * m_vertexCount * 4;
    GLuint vbos[3] = { 0, 0, 0 };

    glGenBuffers(3, vbos);
    if (vbos[0] > 0)
    {
        m_sphereVertexVBO = vbos[0];

        glBindBuffer(GL_ARRAY_BUFFER, m_sphereVertexVBO);
        glBufferData(GL_ARRAY_BUFFER, dataSize, m_vertexes, GL_STATIC_DRAW);

        delete[] m_vertexes;
        m_vertexes = nullptr;

        CHECK_GL_ERROR_DEBUG();
    }
    else
    {
        assert(0);
    }
    if (vbos[1] > 0)
    {
        m_sphereColorVBO = vbos[1];

        glBindBuffer(GL_ARRAY_BUFFER, m_sphereColorVBO);
        glBufferData(GL_ARRAY_BUFFER, dataSize, m_colors, GL_STATIC_DRAW);

        delete[] m_colors;
        m_colors = nullptr;

        CHECK_GL_ERROR_DEBUG();
    }
    if (vbos[2] > 0)
    {
        m_sphereTextureVBO = vbos[2];

        glBindBuffer(GL_ARRAY_BUFFER, m_sphereTextureVBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * m_vertexCount * 2, m_texcoordes, GL_STATIC_DRAW);

        delete[] m_texcoordes;
        m_texcoordes = nullptr;

        CHECK_GL_ERROR_DEBUG();
    }

    glBindBuffer(GL_ARRAY_BUFFER, 0);
}

void Sphere::drawBg(const Mat4& transform)
{
    CCTexture2D* texture = CCTextureCache::sharedTextureCache()->addImage("images/bg.jpg");
    GL::bindTexture2D(texture->getName());
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    // GLProgram* program = CCShaderCache::sharedShaderCache()->getGLProgram(GLProgram::SHADER_NAME_POSITION_TEXTURE);
    m_program->use();
    m_program->setUniformsForBuiltins(transform);

    CCSize& size = CCDirector::sharedDirector()->getVisibleSize();
    GLfloat vertexes[] = {
        0.0f, 0.0f, 0.0f, 1.0f,
        0.0f, 512, 0.0f, 1.0f,
        1024, 512, 0.0f, 1.0f,
        1024, 0.0f, 0.0f, 1.0f
    };
    GLfloat colores[] = {
        1.0f, 1.0f, 1.0f, 1.0f,
        1.0f, 1.0f, 1.0f, 1.0f,
        1.0f, 1.0f, 1.0f, 1.0f,
        1.0f, 1.0f, 1.0f, 1.0f
    };
    GLfloat texCoordes[] = {
        0.0f, 1.0f,
        0.0f, 0.0f,
        1.0f, 0.0f,
        1.0f, 1.0f
    };

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glEnableVertexAttribArray(2);
    glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, vertexes);
    glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, colores);
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, texCoordes);

    glDrawArrays(GL_QUADS, 0, 4);

    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);
    glDisableVertexAttribArray(2);

    CHECK_GL_ERROR_DEBUG();
}

void Sphere::drawSphere(const Mat4& transform, GLfloat* vertexes)
{
    GL::bindTexture2D(m_textureName);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    Mat4 _transform;
    _transform.setIdentity();
    _transform.translate(m_sphereTranslateX, m_sphereTranslateY, 0.0f);
    _transform.multiply(m_sphereTransform);

    m_program->use();
    m_program->setUniformsForBuiltins(_transform);

    CHECK_GL_ERROR_DEBUG();

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glEnableVertexAttribArray(2);

    if (m_sphereVAO == 0)
    {
        glBindBuffer(GL_ARRAY_BUFFER, m_sphereVertexVBO);
        glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
        glBindBuffer(GL_ARRAY_BUFFER, m_sphereColorVBO);
        glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
        glBindBuffer(GL_ARRAY_BUFFER, m_sphereTextureVBO);
        glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
        glBindBuffer(GL_ARRAY_BUFFER, 0);

        CHECK_GL_ERROR_DEBUG();

        if (m_sphereIBO == 0)
        {
            if (m_indexes)
                glDrawElements(GL_QUADS, m_indexCount, GL_UNSIGNED_INT, m_indexes);
            else
                glDrawArrays(GL_QUADS, 0, m_vertexCount);
        }
        else
        {
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_sphereIBO);
            glDrawElements(GL_QUADS, m_indexCount, GL_UNSIGNED_INT, BUFFER_OFFSET(0));
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
        }
    }
    else
    {
        glBindVertexArray(m_sphereVAO);
        glDrawElements(GL_QUADS, m_indexCount, GL_UNSIGNED_INT, BUFFER_OFFSET(0));
        glBindVertexArray(0);
    }

    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);
    glDisableVertexAttribArray(2);

    CHECK_GL_ERROR_DEBUG();
}

void Sphere::drawPlanView(const Mat4& transform, GLfloat* vertexes)
{
    GL::bindTexture2D(m_textureName);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

    Mat4 _transform;
    _transform.setIdentity();
    //_transform.translate(m_planViewTranslateX, m_planViewTranslateY, 0.0f);
    _transform.multiply(m_sphereTransform);

    m_program_planview->use();
    GLfloat _translate[] = { m_planViewTranslateX, m_planViewTranslateY };
    GLuint location_radius = m_program_planview->getUniformLocation("radius");
    m_program_planview->setUniformLocationWith1f(location_radius, m_sphereRadius);
    GLuint location_translate = m_program_planview->getUniformLocation("translate");
    m_program_planview->setUniformLocationWith2f(location_translate, _translate[0], _translate[1]);
    m_program_planview->setUniformsForBuiltins(_transform);

    /*
    m_program->use();
    m_program->setUniformsForBuiltins(_transform);
    */

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glEnableVertexAttribArray(2);

    if (m_sphereVAO == 0)
    {
        glBindBuffer(GL_ARRAY_BUFFER, m_sphereVertexVBO);
        glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
        glBindBuffer(GL_ARRAY_BUFFER, m_sphereColorVBO);
        glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, 0);
        glBindBuffer(GL_ARRAY_BUFFER, m_sphereTextureVBO);
        glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, 0);
        glBindBuffer(GL_ARRAY_BUFFER, 0);

        if (m_sphereIBO == 0)
        {
            if (m_indexes)
                glDrawElements(GL_QUADS, m_indexCount, GL_UNSIGNED_INT, m_indexes);
            else
                glDrawArrays(GL_QUADS, 0, m_vertexCount);
        }
        else
        {
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_sphereIBO);
            glDrawElements(GL_QUADS, m_indexCount, GL_UNSIGNED_INT, BUFFER_OFFSET(0));
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
        }
    }
    else
    {
        glBindVertexArray(m_sphereVAO);
        glDrawElements(GL_QUADS, m_indexCount, GL_UNSIGNED_INT, BUFFER_OFFSET(0));
        glBindVertexArray(0);
    }

    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);
    glDisableVertexAttribArray(2);

    CHECK_GL_ERROR_DEBUG();
}

void Sphere::draw(Renderer *renderer, const Mat4& transform, uint32_t flags)
{
    glEnable(GL_CULL_FACE);
    glFrontFace(GL_CW);

    GL::blendFunc(BlendFunc::ALPHA_NON_PREMULTIPLIED.src, BlendFunc::ALPHA_NON_PREMULTIPLIED.dst);

    this->drawBg(transform);

    {
        if (mx_transform_sphere.try_lock())
        {
            this->drawSphere(transform, m_vertexes);
        
            mx_transform_sphere.unlock();
        }
    }

    {
        if (mx_transform_planview.try_lock())
        {
            this->drawPlanView(transform, m_vertexes);

            mx_transform_planview.unlock();
        }
    }

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glEnableVertexAttribArray(2);
}

void Sphere::visit(Renderer *renderer, const Mat4& parentTransform, uint32_t parentFlags)
{
    this->update();

    Node::visit(renderer, parentTransform, parentFlags);
}

void Sphere::transformSphere(float touchOffsetX, float touchOffsetY)
{
    m_rotateYAngle += touchOffsetX / m_spherePerimeter * 360.0f;
    // m_rotateXAngle -= touchOffsetY / m_spherePerimeter * 360.0f;
    // m_rotateZAngle -= touchOffsetX / m_spherePerimeter * 360.0f;

    if (m_rotateYAngle >= 90.0f && m_rotateYAngle <= 270.0f)
        m_rotateXAngle += touchOffsetY / m_spherePerimeter * 360.0f;
    else
        m_rotateXAngle -= touchOffsetY / m_spherePerimeter * 360.0f;

    if (m_rotateYAngle < 0.0f)
        m_rotateYAngle += 360.0f;
    if (m_rotateYAngle > 360.0f)
        m_rotateYAngle -= 360.0f;

    if (m_rotateXAngle < 0.0f)
        m_rotateXAngle += 360.0f;
    if (m_rotateXAngle >= 360.0f)
        m_rotateXAngle -= 360.0f;

    /*
    cocos2d::log("m_rotateXAngle:%f", m_rotateXAngle);
    cocos2d::log("m_rotateYAngle:%f", m_rotateYAngle);
    cocos2d::log("m_rotateZAngle:%f", m_rotateZAngle);
    */

    Quaternion quat1;
    this->createQuaternion(m_rotateXAngle, 0, 0, quat1);
    Quaternion quat2;
    this->createQuaternion(0, m_rotateYAngle, 0, quat2);
    Quaternion quat3;
    this->createQuaternion(0, 0, m_rotateZAngle, quat3);
    Quaternion quat4;
    this->createQuaternion(m_rotateXAngle, m_rotateYAngle, m_rotateZAngle, quat4);

    m_sphereTransform.setIdentity();
    //m_sphereTransform.rotate(quat4);
    //m_sphereTransform.rotate(quat3);
    //m_sphereTransform.rotate(quat2);
    //m_sphereTransform.rotate(quat1);
    m_sphereTransform.rotate(quat3);
    m_sphereTransform.rotate(quat2);
    m_sphereTransform.rotate(quat1);

    // m_sphereTransform.rotateX(A_TO_R(m_rotateXAngle));
    // m_sphereTransform.rotateY(A_TO_R(m_rotateYAngle));
}

void Sphere::createQuaternion(float rotateX, float rotateY, float rotateZ, Quaternion& quat)
{
    float halfRadx = CC_DEGREES_TO_RADIANS(rotateX / 2.f), halfRady = CC_DEGREES_TO_RADIANS(rotateY / 2.f), halfRadz = CC_DEGREES_TO_RADIANS(rotateZ / 2.f);
    float coshalfRadx = cosf(halfRadx), sinhalfRadx = sinf(halfRadx), coshalfRady = cosf(halfRady), sinhalfRady = sinf(halfRady), coshalfRadz = cosf(halfRadz), sinhalfRadz = sinf(halfRadz);
    quat.x = sinhalfRadx * coshalfRady * coshalfRadz - coshalfRadx * sinhalfRady * sinhalfRadz;
    quat.y = coshalfRadx * sinhalfRady * coshalfRadz + sinhalfRadx * coshalfRady * sinhalfRadz;
    quat.z = coshalfRadx * coshalfRady * sinhalfRadz - sinhalfRadx * sinhalfRady * coshalfRadz;
    quat.w = coshalfRadx * coshalfRady * coshalfRadz + sinhalfRadx * sinhalfRady * sinhalfRadz;
}

void Sphere::update()
{
    if (m_isContinue)
    {
        float _rotateX = fabsf(m_continueRotateX);
        float _rotateY = fabsf(m_continueRotateY);
        
        _rotateX -= m_decreateDetal;
        if (_rotateX < 0.0f)
            m_continueRotateX = 0.0f;
        else
            m_continueRotateX += (m_continueRotateX >= 0.0f) ? (-m_decreateDetal) : m_decreateDetal;

        _rotateY -= m_decreateDetal;
        if (_rotateY < 0.0f)
            m_continueRotateY = 0.0f;
        else
            m_continueRotateY += (m_continueRotateY >= 0.0f) ? (-m_decreateDetal) : m_decreateDetal;

        if (m_continueRotateX == 0.0f && m_continueRotateY == 0.0f)
        {
            m_isContinue = false;
        }
        else
        {
            this->transformSphere(m_continueRotateY, m_continueRotateX);

            // this->noticeToTransform(m_continueRotateY, m_continueRotateX);
        }
    }
}

void Sphere::onThread1Proc()
{
    do {
        if (m_isThread1Done)
            break;

        float x = 0.0f, y = 0.0f;
        {
            std::lock_guard<std::mutex> lock(mx_transform_sphere);

            if (b_transform_sphere)
            {
                x = _touchOffsetXCopy = _touchOffsetX;
                y = _touchOffsetYCopy = _touchOffsetY;

                this->transformSphere(x, y);

                _sphereTransformationCopy = m_sphereTransform;

                b_transform_sphere = false;
                b_transform_planview = true;
                cv_transform_planview.notify_all();
            }
            else
            {
                std::this_thread::sleep_for(std::chrono::microseconds(1));
            }
        }
    }
    while (1);
}

void Sphere::onThread2Proc()
{
    do {
        if (m_isThread2Done)
            break;

        {
            std::unique_lock<std::mutex> lock(mx_transform_planview);
            cv_transform_planview.wait(lock, [this](){
                return b_transform_planview;
            });
            b_transform_planview = false;
        }

        {
            std::lock_guard<std::mutex> lock(mx_transform_planview);
        }
    } 
    while (1);
}

void Sphere::noticeToTransform(float touchOffsetX, float touchOffsetY)
{
    {
        std::lock_guard<std::mutex> lock(mx_transform_sphere);

        _touchOffsetX = touchOffsetX;
        _touchOffsetY = touchOffsetY;

        b_transform_sphere = true;
    }
}

bool Sphere::onTouchBegan(Touch* touch, Event* event)
{
    const cocos2d::Vec2& touchPoint = touch->getLocation();

    float _x = touchPoint.x - m_sphereTranslateX, _y = touchPoint.y - m_sphereTranslateY;
    float _dis = _x * _x + _y * _y;

    if (_dis <= m_sphereRadius * m_sphereRadius)
    {
        m_isContinue = false;
        return true;
    }
    else
    {
        return false;
    }
}

void Sphere::onTouchMoved(Touch* touch, Event* event)
{
    const cocos2d::Vec2& touchPoint = touch->getLocation();
    const cocos2d::Vec2& preTouchPoint = touch->getPreviousLocation();
    float touchOffsetX = touchPoint.x - preTouchPoint.x;
    float touchOffsetY = touchPoint.y - preTouchPoint.y;

    this->transformSphere(touchOffsetX, touchOffsetY);

    // this->noticeToTransform(touchOffsetX, touchOffsetY);
    
    m_continueRotateY = touchOffsetX / m_spherePerimeter * 360.0f;
    m_continueRotateX = touchOffsetY / m_spherePerimeter * 360.0f;
}

void Sphere::onTouchEnded(Touch* touch, Event* event)
{
    m_isContinue = true;
}

 

转载于:https://www.cnblogs.com/iRidescent-ZONE/p/5475337.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我来为你解答。 1. OpenGL图形渲染管线 OpenGL图形渲染管线是OpenGL对图形数据进行处理的流程。它包括两个主要部分:顶点处理阶段和片元处理阶段。其中,顶点处理阶段将顶点数据转换成片元数据,片元处理阶段将片元数据转换成最终的图像颜色值。 2. VBO VBO(Vertex Buffer Object)是OpenGL中的一种缓冲区对象,它用于存储顶点数据。使用VBO可以将顶点数据存储到显存中,从而提高渲染效率。 3. VAO VAO(Vertex Array Object)是OpenGL中的另一种缓冲区对象,它用于存储顶点数组状态。使用VAO可以将多个VBO绑定到一个VAO中,从而方便在不同的渲染环境中切换。 4. EBO EBO(Element Buffer Object)也是OpenGL中的一种缓冲区对象,它用于存储顶点索引数据。使用EBO可以减少顶点数据的冗余,从而节省显存空间。 下面是一个简单的用例,演示如何使用VBOVAO和EBO进行三角形的渲染: ```c++ // 顶点数据 float 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 }; // 索引数据 unsigned int indices[] = { 0, 1, 2 }; // 创建VBOVAO、EBO对象 unsigned int VBO, VAO, EBO; glGenBuffers(1, &VBO); glGenVertexArrays(1, &VAO); glGenBuffers(1, &EBO); // 绑定VBOVAO、EBO对象 glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); // 设置顶点属性指针 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float))); glEnableVertexAttribArray(1); // 渲染三角形 glBindVertexArray(VAO); glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0); glBindVertexArray(0); // 删除VBOVAO、EBO对象 glDeleteBuffers(1, &VBO); glDeleteBuffers(1, &EBO); glDeleteVertexArrays(1, &VAO); ``` 上面的代码首先创建了一个顶点数组和一个索引数组,然后创建了VBOVAO、EBO对象,并将顶点数据和索引数据存储到对应的缓冲区对象中。接着,设置了顶点属性指针,并在渲染时使用glDrawElements函数渲染了三角形。最后,删除了VBOVAO、EBO对象。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值