求助:关于父子结点不同camera的绘制异常


表现: 层L,其下有结点N1,N2由于某种原因N1需要使用自定义camera C1绘制(我这里是因为3d)。 C1 的depth属性为-2(低于defaultCamera)。 这种情况下从一开始,N1的位置就不正确,并且移动层L的时候,N1并不会跟着移动,而N2正常。但若更改一次N1的位置,无论变化多小,N1会跳动到相对于L的正确的位置,但仍然不会跟随移动。


原因:目前有层L,其子结点N1,N2,默认摄像机C和自定义摄像机C1, C1的depth小于C。 N1属于C1摄像机, L,N2属于C。 状态:L的_modelViewTransform处于dirty true状态。
渲染时camera按depth升序排列。
第一帧:
C1优先渲染,在L的Node::visit函数中通过processParentFlags更新L的_modelViewTransform矩阵时,由于L不属于C1,isVisitableByVisitingCamera判定失败,导致L的_modelViewTransform矩阵没有被更新,dirty状态也不发生改变,然后将未更新的_modelViewTransform传入子结点N1,N2。N1使用未更新的L的_modelViewTransform重新计算N1的_modelViewTransform。导致位置异常。N2同L,_modelViewTransform不被重算,dirty状态也不发生改变。
其次渲染C,此次L属于C,_modelViewTransform被重算正确,dirty状态被清除。然后将正确的L的_modelViewTransform传入N1,但由于N1不属于C而且N1的dirty状态已经被清,导致N1的_modelViewTransform又不会被重算。。。。。。。,虽然本次N1不会被draw,但其_modelViewTransform依然是错误的。
第二帧:
第二帧之后,由于L,N1,的dirty均被清理,所以_modelViewTransform再不会被重算,就这样错下去。 如果L的位置被改变,那么会重走第一帧的流程。但如果N1的位置变化,由于L的_modelViewTransform已经正确,那N1的位置会被重新计算的正确,但L再发生变化时,N1又不会有变化。


解决方法:未解决,求帮忙。 这个问题很矛盾,正反都会有影响。 本来想要从设计上避免,但发现这样做会特别麻烦。

                             尝试过禁止processParentFlags的isVisitableByVisitingCamera判断,但可能会导致dirty状态被其他camera的遍历清除,导致所属camera渲染时,不重算也不重绘。
                             有想过在camera遍历时,让不是本camera所属的结点强制重算_modelViewTransform但不改变dirty状态,直到结点所属的camera将其重算并清除。但这样做会使无用计算次数增多,如果子结点特别多结构比较复杂,将会严重影响效率。

camera 渲染 层次 _modelViewTransform

在线等结果啊,有遇到过,解决过,有看法,有想法,有疑问,有思路,有xxxxxxxxxxxxxxx的都帮下忙 多谢:3:

…没人关注,没人帮忙解决:6:

研究了下自己找了个简易方法算是解决了,贴出来有需要的一起研究。

思路:
主要思路是保证每帧渲染时,dirty的结点首次被遍历无论是否属于当前camera都会被更新flag和_modelViewTransform,清理dirty但额外保存flag, 之后本帧内遍历到该结点时不会再判断dirty而是直接使用保存的flag,直到下一帧才会在首次遍历该结点时重新检测dirty状态和重算flag以及_modelViewTransform。
如此既保证了非camera结点的位置正确性,又不会每次都dirty耗费性能,总之上面提到的问题没有了。

方法:
文件CCScene.h,类cocos2d::Scene,增加变量 uint32_t _uRenderCount, 用于区分不同帧,初始化为0,在Scene::render的末尾++
文件CCNode.h,类cocos2d:Node,增加变量
uint32_t _uRenderCountRecord; 用于记录上次更新flag和_modelViewTransform时的帧数,若相同无需再算,若不同,根据dirty重算。 初始值-1
uint32_t _uRenderFlagsRecord; 用于记录上次重算得到的flag值。初始值0
Node::processParentFlags(const Mat4& parentTransform, uint32_t parentFlags)中改动部分如下

// if (!isVisitableByVisitingCamera())
// return parentFlags;

uint32_t flags = parentFlags;
auto scene = getScene();
uint32_t uNewRenderCount = 0;
if (scene) {
    uNewRenderCount = scene->getRenderCounts();
}
if (_uRenderCountRecord == uNewRenderCount) {
    flags = _uRenderFlagsRecord;
}
else {
    flags |= (_transformUpdated ? FLAGS_TRANSFORM_DIRTY : 0);
    flags |= (_contentSizeDirty ? FLAGS_CONTENT_SIZE_DIRTY : 0);


    if (flags & FLAGS_DIRTY_MASK)
        _modelViewTransform = this->transform(parentTransform);

#if CC_USE_PHYSICS
if (_updateTransformFromPhysics) {
_transformUpdated = false;
_contentSizeDirty = false;
}
#else
_transformUpdated = false;
_contentSizeDirty = false;
#endif
_uRenderCountRecord = uNewRenderCount;
_uRenderFlagsRecord = flags;
}

return flags;

简易的方法,对本人来说解决了问题,目前未发现新的弊端,如若有同需发现新问题,望能提出,欢迎指教。