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, size.height/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, -anchorPoint.y, 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, 0,
                (float)tanf(CC_DEGREES_TO_RADIANS(_skewX)), 1, 0, 0,
                0,  0,  1, 0,
                0,  0,  0, 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, mask_layer);
glStencilOp(!_inverted ? GL_REPLACE : GL_ZERO, GL_KEEP, 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, GL_KEEP);
this->draw(renderer, _modelViewTransform, 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);
    }
}

_alphaThreshold 为0.5

_alphaThreshold 为1.0

6:绘图

(1):cocos2d drawNode绘制点:

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

 glDrawArrays(GL_POINTS, 0, _bufferCountGLPoint);//正确
  glDrawArrays(GL_POINT, 0, _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, _vboGLPoint);
glBufferData(GL_ARRAY_BUFFER, sizeof(V2F_C4B_T2F)*_bufferCapacityGLPoint, _bufferGLPoint, GL_STREAM_DRAW);

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

glBufferData(GL_ARRAY_BUFFER, sizeof(V2F_C4B_T2F)*_bufferCapacityGLPoint, _bufferGLPoint, GL_STREAM_DRAW);

换成:

glBufferData(GL_ARRAY_BUFFER, sizeof(V2F_C4B_T2F)*_bufferCapacityGLPoint, nullptr, GL_STREAM_DRAW);
######分割线
glBufferData(GL_ARRAY_BUFFER, sizeof(V2F_C4B_T2F)*_bufferCapacityGLPoint, _bufferGLPoint, 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, 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, 0, 0, 255.0)红色,显示出来的却是淡蓝色,于是查看了下shader,下面是line的着色器:

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

将v_color 硬设置成(1.0,0.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, _quadVerts, 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。

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值