cocos2d opengl的一下知识总结一

前端之家收集整理的这篇文章主要介绍了cocos2d opengl的一下知识总结一前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

1:矩阵变换

case Projection::_3D:
        {
            float zeye = this->getZEye();

            Mat4 matrixPerspective,matrixLookup;

            loadIdentityMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);

#if CC_TARGET_PLATFORM == CC_PLATFORM_WP8
            //if needed,we need to add a rotation for Landscape orientations on Windows Phone 8 since it is always in Portrait Mode
            GLView* view = getOpenGLView();
            if(getOpenGLView() != nullptr)
            {
                multiplyMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION,getOpenGLView()->getOrientationMatrix()); } #endif // issue #1334 Mat4::createPerspective(60,(GLfloat)size.width/size.height,10,zeye+size.height/2,&matrixPerspective); multiplyMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION,matrixPerspective); Vec3 eye(size.width/2,size.height/2,zeye),center(size.width/2,0.0f),up(0.0f,1.0f,0.0f); Mat4::createLookAt(eye,center,up,&matrixLookup); multiplyMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION,matrixLookup); loadIdentityMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); break; }

好奇的是为什么视图矩阵放在投影矩阵栈中的呢?网上我也看到过别人的吐槽,但是cocos为什么这么做肯定是有它的原因的。仔细一想其实这样是很正常的,一个顶点坐标要经过MVP变换,但是cocos只会传入模型变换(相对根节点的各种变换,但是投影和视图变换对所有节点都是一样的), 虽然模型和视图矩阵总是放在一起的,但其实也没必要这么死板,M*(V*p) = (M*V)*P,反正都一样。既然视图变换不会改变,为何不把视图和投影放在一起呢。如果真的要纠结这个问题,那我们就试着改一下吧,将视图变换移到loadIdentityMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
MATRIX_STACK_PROJECTION换成MATRIX_STACK_MODELVIEW,其实也是一样的。

10和zeye+size.height/2分别代表了近截面和远截面。如果一个顶点的z 坐标是9或者 zeye+size.height/2 +1 ,将都显示不出来。

2:节点的变换

const Mat4& Node::getNodeToParentTransform() const
{
    if (_transformDirty)
    {
        // Translate values
        float x = _position.x;
        float y = _position.y;
        float z = _positionZ;

        if (_ignoreAnchorPointForPosition)
        {
            x += _anchorPointInPoints.x;
            y += _anchorPointInPoints.y;
        }

        bool needsSkewMatrix = ( _skewX || _skewY );


        Vec2 anchorPoint(_anchorPointInPoints.x * _scaleX,_anchorPointInPoints.y * _scaleY);

        // caculate real position
        if (! needsSkewMatrix && !_anchorPointInPoints.equals(Vec2::ZERO))
        {
            x += -anchorPoint.x;
            y += -anchorPoint.y;
        }

        // Build Transform Matrix = translation * rotation * scale
        Mat4 translation;
        //move to anchor point first,then rotate
        Mat4::createTranslation(x + anchorPoint.x,y + anchorPoint.y,z,&translation);  //需要绕锚点旋转,先移动到锚点

        Mat4::createRotation(_rotationQuat,&_transform);  

        if (_rotationZ_X != _rotationZ_Y)  //表示这部分还没懂
        {
            // Rotation values
            // Change rotation code to handle X and Y
            // If we skew with the exact same value for both x and y then we're simply just rotating
            float radiansX = -CC_DEGREES_TO_RADIANS(_rotationZ_X);
            float radiansY = -CC_DEGREES_TO_RADIANS(_rotationZ_Y);
            float cx = cosf(radiansX);
            float sx = sinf(radiansX);
            float cy = cosf(radiansY);
            float sy = sinf(radiansY);

            float m0 = _transform.m[0],m1 = _transform.m[1],m4 = _transform.m[4],m5 = _transform.m[5],m8 = _transform.m[8],m9 = _transform.m[9];
            _transform.m[0] = cy * m0 - sx * m1,_transform.m[4] = cy * m4 - sx * m5,_transform.m[8] = cy * m8 - sx * m9;
            _transform.m[1] = sy * m0 + cx * m1,_transform.m[5] = sy * m4 + cx * m5,_transform.m[9] = sy * m8 + cx * m9;
        }
        _transform = translation * _transform;  //旋转矩阵
        //move by (-anchorPoint.x,-anchorPoint.y,0) after rotation
        _transform.translate(-anchorPoint.x,0);  //之前移到了锚点,现在移回。


        if (_scaleX != 1.f)
        {
            _transform.m[0] *= _scaleX,_transform.m[1] *= _scaleX,_transform.m[2] *= _scaleX;
        }
        if (_scaleY != 1.f)
        {
            _transform.m[4] *= _scaleY,_transform.m[5] *= _scaleY,_transform.m[6] *= _scaleY;
        }
        if (_scaleZ != 1.f)
        {
            _transform.m[8] *= _scaleZ,_transform.m[9] *= _scaleZ,_transform.m[10] *= _scaleZ;
        }

        //以上为translation * rotation * scale
        //其中rotation采用四元数来做的,具体请看其他blog。_rotationQuat如何生成,旋转矩阵如何从四元数来的。

        // FIXME:: Try to inline skew
        // If skew is needed,apply skew and then anchor point
        if (needsSkewMatrix)
        {
            float skewMatArray[16] =
            {
                1,(float)tanf(CC_DEGREES_TO_RADIANS(_skewY)),0,(float)tanf(CC_DEGREES_TO_RADIANS(_skewX)),1,1
            };
            Mat4 skewMatrix(skewMatArray);

            _transform = _transform * skewMatrix;  //也还没懂

            // adjust anchor point
            if (!_anchorPointInPoints.equals(Vec2::ZERO))  //这部分也还没懂
            {
                // FIXME:: Argh,Mat4 needs a "translate" method.
                // FIXME:: Although this is faster than multiplying a vec4 * mat4
                _transform.m[12] += _transform.m[0] * -_anchorPointInPoints.x + _transform.m[4] * -_anchorPointInPoints.y;
                _transform.m[13] += _transform.m[1] * -_anchorPointInPoints.x + _transform.m[5] * -_anchorPointInPoints.y;
            }
        }

        if (_useAdditionalTransform)
        {
            _transform = _transform * _additionalTransform;
        }

        _transformDirty = false;
    }

    return _transform;
}

一些链接
万向节死锁问题
四元数和旋转矩阵
四元数和旋转矩阵

3: 使用的shader

sprite竟然使用的是SHADER_NAME_POSITION_TEXTURE_COLOR_NO_MVP,没有mv矩阵的,只有投影矩阵CC_PMatrix。
在fillQuads已经对QuadCommand对应的节点进行了变换。
感觉很乱啊。(我的版本是3.4。)

4:离屏渲染和帧缓冲对象,渲染缓冲区对象

请参考:

相关内容

可以用来实现场景截图。参考cocos2d的RenderTexture。

5: 模板测试,alpha测试和遮罩

cocos2d的遮罩ClippingNode采用模板测试和alpha测试。
设置翻转属性可以决定是显示模板内的背景还是模板外的背景。
先将不翻转的情况吧,此时显示模板内的背景区域。
如果做到这点呢?
1:先覆盖模板缓冲,设置为0;此时如果要在一个点绘制一个像素,肯定不能成功。

glStencilMask(mask_layer);
glStencilFunc(GL_NEVER,mask_layer,mask_layer);
glStencilOp(!_inverted ? GL_ZERO : GL_REPLACE,GL_KEEP,GL_KEEP);

2:然后绘制模板

glStencilFunc(GL_NEVER,mask_layer);
glStencilOp(!_inverted ? GL_REPLACE : GL_ZERO,GL_KEEP);
_stencil->visit(renderer,_modelViewTransform,flags);

此时在模板区域内的值都为mask_layer(glStencilMask),其他部分的值还是0。模板并没有绘制出来,它只是为了填充模板缓冲中的一部分模板值。
3:绘制背景

glStencilFunc(GL_EQUAL,_mask_layer_le,_mask_layer_le);
glStencilOp(GL_KEEP,GL_KEEP);
this->draw(renderer,flags);

GL_EQUAL表示只有模板值和_mask_layer_le相等,才通过验证,但是目前模板缓冲中的所有值要么是0,要么是mask_layer。(这里我也是又点疑问的,为什么是_mask_layer_le,知道它是为了多层嵌套,但是还是理解不了它要做的效果。如果没有多层嵌套_mask_layer_le = mask_layer。估计先不管那么多吧。我们就考虑单层的情况吧!),只有模板区域内的模板值和参考值相等,所以只有模板区域内的像素能画出来。

现在拿一张背景图和一张部分透明的模板图做成遮罩,那部分透明的区域,我也不像显示背景的部分,该怎么做呢?此时加入alpha测试,alpha测试在模板测试之前,所以设置模板样图中透明部分不参加模板测试就好了,alpha测试过滤掉模板样图的透明部分。
做法如下:

// enable alpha testing
glEnable(GL_ALPHA_TEST);
// pixel will be drawn only if greater than an alpha threshold
glAlphaFunc(GL_GREATER,_alphaThreshold);

此时只有alpha值大于_alphaThreshold的模板区域才会去进行模板测试。
渲染完模板样图之后,要恢复alpha测试之前的状态。

if (_alphaThreshold < 1.0)  //恢复以前的alpha测试的状态
{
    glAlphaFunc(_currentAlphaTestFunc,_currentAlphaTestRef);
    if (!_currentAlphaTestEnabled)
    {
        glDisable(GL_ALPHA_TEST);
    }
}

6:绘图

(1):cocos2d drawNode绘制点:

这个比较简单,代码很好懂,我实现了一遍,碰到的几个问题:

glDrawArrays(GL_POINTS,_bufferCountGLPoint);//正确
  glDrawArrays(GL_POINT,_bufferCountGLPoint);//错误

我不小心写成了下面这种,导致不能正确显示出来。那如何断点到这里的呢,我们可以获取opengl错误glGetError,cocos2d里面也封装了获取opengl错误函数:CHECK_GL_ERROR_DEBUG,直接拿来调试就好了。
它们有什么区别呢:GL_POINTS是绘制模式是图元,GL_POINT多用来表示多边形的绘制填充模式(轮廓点式多边形、轮廓线式多边形或全填充式多边形)。
(2):还有个疑问,那就是glBufferData中的GL_STREAM_DRAW,为什么绘制点的时候是GL_STREAM_DRAW模式呢?
网上这样描述几种的区别:GL_STATIC_DRAW,GL_STREAM_DRAW,GL_DYNAMIC_DRAW用于给OpenGL系统提醒:预期数据是一直不变、数据每帧变一次或几帧变一次、数据每帧变两三次以上,方便硬件内部优化吧。

(3):吐槽一下:

GL::bindVAO(_vaoGLPoint);
glGenBuffers(1,&_vboGLPoint);
glBindBuffer(GL_ARRAY_BUFFER,_vboGLPoint);
glBufferData(GL_ARRAY_BUFFER,sizeof(V2F_C4B_T2F)*_bufferCapacityGLPoint,_bufferGLPoint,GL_STREAM_DRAW);

DrawNode初始化vao,vbo就向显卡传递了所有初始化的数据(这些根据就没用)

glBindBuffer(GL_ARRAY_BUFFER,GL_STREAM_DRAW);

这里又传递了所有数据。真的有这个必要嘛!!!
我的修改

glBufferData(GL_ARRAY_BUFFER,GL_STREAM_DRAW);

换成:

glBufferData(GL_ARRAY_BUFFER,nullptr,GL_STREAM_DRAW);
######分割线
glBufferData(GL_ARRAY_BUFFER,GL_STREAM_DRAW);

换成

glBufferData(GL_ARRAY_BUFFER,_bufferGLpointCount*sizeof(V2F_C4F_T2F),_bufferPoints,GL_STREAM_DRAW);

也是同样的效果,如果不对,望大神指正。

(4):如何制定点的大小:(cocos2d的点是方形的)

V2F_C4F_T2F point = { position,color,Tex2F(pointSize,0) };

一时不解,改了这个Tex2F(pointSize,0),换成Tex2F(pointSize,pointSize),Tex2F(0,0)如何呢,发现后两个都没反应,默认大小还是1。于是查看了shader,才发现问题所在:

gl_Position = CC_MVPMatrix * a_position;
gl_PointSize = a_texCoord.x;
v_fragmentColor = a_color;

(7):颜色有问题
通过drawNode画点或者画线。颜色都感觉有问题,我传进去Color4F(255.0,255.0)红色,显示出来的却是淡蓝色,于是查看了下shader,下面是line的着色器:

v_color = vec4(a_color.rgb * a_color.a,a_color.a);

将v_color 硬设置成(1.0,0.0,1.0)是ok的,所以问题就出在我们的程序到顶点着色器。

glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR,4,GL_UNSIGNED_BYTE,GL_TRUE,sizeof(V2F_C4B_T2F),(GLvoid *)offsetof(V2F_C4B_T2F,colors));

这是设置line的vao和vbo时候对颜色的设置。感觉不对啊,为什么是GL_UNSIGNED_BYTE,改成GL_FLOAT就对了。

7:数据传递方式:

方式一:

glBufferData(GL_ARRAY_BUFFER,sizeof(_quadVerts[0]) * _numberQuads * 4,nullptr,GL_DYNAMIC_DRAW);
void *buf = glMapBuffer(GL_ARRAY_BUFFER,GL_WRITE_ONLY);
memcpy(buf,_quadVerts,sizeof(_quadVerts[0])* _numberQuads * 4);
glUnmapBuffer(GL_ARRAY_BUFFER);

这是一段往vbo中填充数据的代码

方式二:

glBufferData(GL_ARRAY_BUFFER,sizeof(_quadVerts[0]) * _numberQuads * 4,GL_DYNAMIC_DRAW);

一样的效果
函数解释:

GLvoid *glMapBuffer(GLenum target,GLuenum access):返回一个指向缓冲区对象的指针,可以在这个缓冲区对象中写入新值及更新之后,再调用GLboolean glUnMapBuffer(GLenum target)表示已经完成了对数据的更新,取消对这个缓冲区的映射。如果只需要更新所需范围内的数据值,也可调用GLvoid *glMapBuffwerRange(GLenum target,GLintptr offset,GLsizeiptr length,GLbitfield access)

void glBufferData(GLenum target,GLsizeiptr size,const GLvoid * data,GLenum usage );
功能是分配size个存储单位的OpenGL服务器内存,用于存储顶点数据或索引。以前有与当前缓冲区对象相关联的数据将删除
glBufferData()首先在OpenGL拂去其中分配内存以存储数据。如果成功分配,data!=NULL,size个单位就会从客户机内存复制到这个对象中。如果data=NULL,为数据保留适当的空间,但不会初始化。

详细请看:opengl缓冲对象
具体采用哪种现在也还没个具体方案。cocos2d两种都有,render中采用glMapBuffer,很多其他地方用glBufferData。

第一部分就暂时这样吧!!!

原文链接:https://www.f2er.com/cocos2dx/339469.html

猜你在找的Cocos2d-x相关文章