WebGL——osg框架学习一

  从今天开始,我们开始正式的学习osg框架,今天我们学习的是osg的渲染模块,我们来看一下代码结构。

所有DrawXXX的js模块都是渲染的模块,我们逐一来简单介绍一下,第一个Drawable.js,这个模块是描述可绘制对象的类,也是我们今天要讨论的类。在osg框架中,渲染管道在准备时期首先要统计管理可绘制对象,我们来看看Drawable模块到底做了什么操作,进行了哪些管理。先贴出代码。

/*
可绘制对象*/
let StateBin = require('./StateBin');
let BoundingBox = require('../util/BoundingBox');
let BoundingSphere = require('../util/BoundingSphere');
let Vec3 = require('../util/Vec3');let Drawable = function (actor) {this._drawActor = actor;//关联的DrawActorthis._geometry = undefined;//渲染的几何Geometrythis._transform = undefined;//世界变换 FloatArray(16)this._statebin = undefined;//状态结点,原始的状态,没有额外功能时的状态this._curStatebin = undefined;//如果状态会动态变化,这里存储每一帧绘制时的状态结点this._depth = 0.0;//场景深度值,透明需要按深度排序绘制this._boundingBox = undefined;//包围盒this._boundingSphere = undefined;//包围球
};
Drawable.prototype = {setGeometry: function (g, transform) {this._geometry = g;this._transform = transform;},getGeometry: function () {return this._geometry;},getTransform: function () {return this._transform;},setStateBin: function (sb) {this._statebin = sb;},getStateBin: function () {return this._statebin;},getCurrentStateBin: function () {return this._curStatebin;},// setDepth: function (d) {//     this._depth = d;// },getDepth: function () {return this._depth;},reset: function () {this._geometry = undefined;this._transform = undefined;this._statebin = undefined;this._curStatebin = undefined;this._depth = 0.0;this._boundingBox = undefined;this._boundingSphere = undefined;},valid: function () {if (this._drawActor.getBaseCamera().isBoundingBoxCulled(this.getBoundingBox())) {return false;}return true;},isTransparent: function () {return false;},//计算深度值computeDepth: function () {//根据包围盒和相机变换矩阵,确认中心点的Z值let mvmatrix = this._drawActor.getBaseCamera().getModelViewMatrix();let temp = Vec3.MemoryPool.alloc();this._depth = this.distanceZ(this.getBoundingBox().getCenter(temp), mvmatrix);Vec3.MemoryPool.free(temp);//drawable.setDepth(depth);
    },//相机的矩阵要取反distanceZ: function (coord, matrix) {return -(coord[0] * matrix[2] + coord[1] * matrix[6] + coord[2] * matrix[10] + matrix[14]);},getBoundingBox: function () {if(this._boundingBox === undefined){this._boundingBox = new BoundingBox();this._boundingBox.copy(this._geometry.getBoundingBox(true));if(this._transform){this._boundingBox.transformMat4(this._transform);}}return this._boundingBox;},getBoundingSphere: function () {if(this._boundingSphere === undefined) {this._boundingSphere = new BoundingSphere();let bb = this.getBoundingBox();this._boundingSphere.expandByBoundingBox(bb);}return this._boundingSphere;},getRadius: function () {return this.getBoundingSphere().getRadius();},// There are 3 cases when there is a prev / current render leaf// pSG: previousStateGraph// cSG: currentStateGraph// pRL: previousRenderLeaf// cRL: currentRenderLeaf//
    //          A                        B                       C// +-----+     +-----+            +-----+                 +-----+// | pSG |     | cSG |         +--+ SG  +--+              | SG  |// +--+--+     +--+--+         |  +-----+  |              +--+--+//    |           |            |           |                 |// +--v--+     +--v--+      +--v--+     +--v--+           +--v--+// | pSG |     | cSG |      | pSG |     | cSG |        +--+ SG  +--+// +--+--+     +--+--+      +--+--+     +--+--+        |  +-----+  |//    |           |            |           |           |           |// +--v--+     +--v--+      +--v--+     +--v--+     +--v--+     +--v--+// | pRL |     | cRL |      | pRL |     | cRL |     | pRL |     | cRL |// +-----+     +-----+      +-----+     +-----+     +-----+     +-----+//
    //
    // Case A// no common parent StateGraphNode we need to// popStateSet until we find the common parent and then// pushStateSet from the common parent to the current RenderLeaf//
    // Case B// common parent StateGraphNode so we apply the current stateSet//
    // Case C// the StateGraphNode is common to the previous RenderLeaf so we dont need// to do anything except if we used an insertStateSetdraw: function (glstate, preDrawable) {//先接受状态,再渲染几何let curStateGraph = this._statebin;let curStateGraphStateSet = curStateGraph.getStateSet();let curStateGraphParent = curStateGraph.getParent();let preStateGraph;let preStateGraphParent;if(preDrawable !== undefined){preStateGraph = preDrawable._statebin;preStateGraphParent = preStateGraph.getParent();if(preStateGraphParent !== curStateGraphParent){//A
                StateBin.moveStateBin(glstate, preStateGraphParent, curStateGraphParent);glstate.applyStateSet(curStateGraphStateSet);}else if(preStateGraph !== curStateGraph){//B
                glstate.applyStateSet(curStateGraphStateSet);}else{// in osg we call apply but actually we dont need// except if the stateSetStack changed.// for example if insert/remove StateSet has been used// if (glstate._stateSetStackChanged(idLastDraw, lastStateSetStackSize )) {//     glstate.applyStateSet(curStateGraphStateSet);// }
            }}else{//如果preLeaf为空,第一个绘制的几何,状态遍历到根节点全部push到GLState中
            StateBin.moveStateBin(glstate, undefined, curStateGraphParent);glstate.applyStateSet(curStateGraphStateSet);}let camera = this._drawActor.getBaseCamera();glstate.applyModelMatrix(this._transform, camera.getModelViewMatrix(), camera.getProjectionMatrix());this._geometry.draw(glstate);return true;},
};
module.exports = Drawable;// set: function (stateGraph, geometry, , depth) {
//     this._statebin = stateGraph;
//     this._geometry = geometry;
//
//     this._depth = depth;
// },
// drawGeometry: function (glstate) {
//     //let program = glstate.getLastProgramApplied();
//     //let programID = program.getID();
//     //let programCaches = glstate.getProgramCaches();
//     //let obj = programCaches[programID];
//     // if(!obj){//程序不存在,创建一个新的
//     //     obj = new CacheUniformApply(glstate, program);
//     //     programCaches[programID] = obj;
//     // }
//
//     //从相机获取modelview和projection
//     //着色器暂时不需要透视矩阵
//     //let modelview = this._camera.get
//
//
//
//     //glstate.applyModelViewMatrix(this._modelview);
//     //glstate.applyProjectionMatrix(this._projection);
//     glstate.applyTransformMatrix(this._transform);
//     //this._modelview = this._camera.getModelViewMatrix();
//     //Mat4.mul(this._modelview, this._modelview, this._transform);
//     //this._projection = this._camera.getProjectionMatrix();
//     glstate.applyModelMatrix(this._transform, this._camera.getModelViewMatrix(), this._camera.getProjectionMatrix());
//
//
//     //let gluniforms = program.getGLUniformsCache();
//     //let modelviewloc = gluniforms[glstate._modelViewMatrixUniform.getName()];
//     //let viewloc = gluniforms[glstate._viewMatrixUniform.getName()];
//
//     //obj.apply(glstate, this._modelview, this._modelworld, this._view, this._projection, this._normal);
//     this._geometry.draw(glstate);
// },

我们先来看看Drawable的构造函数,截取构造函数代码

let Drawable = function (actor) {this._drawActor = actor;//关联的DrawActorthis._geometry = undefined;//渲染的几何Geometrythis._transform = undefined;//世界变换 FloatArray(16)this._statebin = undefined;//状态结点,原始的状态,没有额外功能时的状态this._curStatebin = undefined;//如果状态会动态变化,这里存储每一帧绘制时的状态结点this._depth = 0.0;//场景深度值,透明需要按深度排序绘制this._boundingBox = undefined;//包围盒this._boundingSphere = undefined;//包围球
};

首先我们看到第一个私有属性是DrawActor,我们看看DrawActor是个什么模块,先贴出DrawActor类代码。

/*
绘制对象角色每个DrawActor管理自己的渲染数据,自己的状态树,自己的相机树
如果该功能销毁直接销毁对应的DrawActor资源
但是他引用的状态,相机并不是他管理
不想把各个DrawActor搅和在一起,逻辑混乱绘制分几种情况,全自动,全手动,半自动
全自动-所有的绘制流程从一开始数据构造好后就不会再变更,只需在初始化时确认好 后续直接渲染即可
半自动-部分绘制流程是固定的,部分绘制流程是动态的,比如构件场景下,需要点选高亮等功能变化
全手动-所有的绘制流程都是动态的,每一帧都需要重新构造每个drawable,部分功能数据*/
let Drawable = require('./Drawable');
let StateBin = require('./StateBin');
let NodeVisitor = require('../util/NodeVisitor');
let CullStack = require('./CullStack');
let Mat4 = require('../util/Mat4');
let Group = require('../core/Group');
let Geode = require('../core/Geode');
let MatrixTrasform = require('../core/MatrixTransform');
let SceneRoot = require('../scene/SceneRoot');
let Geometry = require('../core/Geometry');let DrawActor = function (renderer) {NodeVisitor.call(this, NodeVisitor.TRAVERSE_CHILDREN);CullStack.call(this);//为正确渲染准备的数据this._renderer = renderer;//所属的渲染器,固有资产,不会变更this._baseCamera = this._renderer.getMainCamera();//相机,默认为主相机this._baseState = new StateBin();//状态this._sceneRoot = undefined;//所属的场景根节点//渲染的对象this._drawables = [];//
    this._drawIndex = 0;//当前绘制的索引数,需要固定帧率渲染的地方使用//
    this._currentStateBin = undefined;//当前处理的状态树结点,临时数据//
    //this._fixed = false;//是否启用固定帧率this._valid = true;//直接屏蔽渲染的标记
};DrawActor.prototype = Object.create(NodeVisitor.prototype);
Object.assign(DrawActor.prototype, CullStack.prototype);
DrawActor.prototype.constructor = DrawActor;
Object.assign(DrawActor.prototype, {setBaseCamera: function (camera) {//设置当前相机,可以自由设置自己独特的相机this._baseCamera = camera;},getBaseCamera: function () {return this._baseCamera;},getBaseStateBin: function () {return this._baseState;},getBaseStateSet: function () {return this._baseState.getStateSet();},//复原场景根节点的状态revertBaseState: function () {if (this._sceneRoot) {this._baseState.setStateSet(this._sceneRoot.getStateSet());}},//与场景的唯一联系,一定要先定义好场景的几何和状态再调用setSceneRootsetSceneRoot: function (root) {this._sceneRoot = root;//一定要先定义好场景根节点的状态,否则会出错!!!//this._baseState.setStateSet(root.getStateSet());
    },createDrawable: function () {//base overridereturn new Drawable(this);//创建对应类型的Drawable
    },addDrawable: function (drawable) {this._drawables.push(drawable);},getDrawables: function () {return this._drawables;},valid: function (valid) {if (valid !== undefined) {this._valid = valid;}return this._valid;},//遍历apply: function (node) {this[node.typeID](node);},//重载,压入一个状态pushStateSet: function (stateset) {if (stateset) {//添加StateGraph子节点,更新当前活动的StateGraph为新的状态this._currentStateBin = this._currentStateBin.addStateSetChild(stateset);}},//重载,弹出一个状态popStateSet: function (stateset) {if (stateset) {this._currentStateBin = this._currentStateBin.getParent();}},//重载pushDrawable: function (geometry) {let drawable = this.createDrawable();drawable.setStateBin(this._currentStateBin);drawable.setGeometry(geometry, this.getCurrentTransformMatrix());this.addDrawable(drawable);},//根重载,绘制当前Actor下的drawables,绘制不需要固定帧率,永远在第一帧里绘制完毕draw: function (glstate, preCamera) {if (!this._valid) {//不再绘制return preCamera;}this.drawCamera(preCamera);//循环遍历一遍drawables,绘制实体let preDrawable = undefined;let l = this._drawables.length;for (let i = this._drawIndex; i < l; i++) {let drawable = this._drawables[i];if (drawable.valid()) {drawable.draw(glstate, preDrawable);//成功绘制的preDrawable = drawable;}this._drawIndex++;}return this._baseCamera;},//每个新帧绘制之前的重置工作drawReset: function () {this._baseCamera.setClearFlag(false);this._drawIndex = 0;},//当前Actor的对象是否全部绘制完毕drawFinished: function () {return this._drawables.length === this._drawIndex;},//绘制相机状态(视口,清空)drawCamera: function (preCamera) {if (preCamera === this._baseCamera) {//重复的不再处理return;}//视口何时都需要设置let glstate = this._renderer.getGLState();glstate.applyAttribute(this._baseCamera.getViewport());//以下是每个相机只需要处理一次的事情if (!this._baseCamera.getClearFlag()) {//更新视锥体,确保剔除正确,每帧相机的投影矩阵和视图矩阵可能都会变化this._baseCamera.updateCullFrustum();//清空颜色和深度,但如果是主相机不再需要,在最开始就已经清空let clearmask = this._baseCamera.getClearMask();if (clearmask !== 0x0) {let gl = glstate.getWebGLContext();if (clearmask & gl.COLOR_BUFFER_BIT) {let color = this._baseCamera.getClearColor();//清空颜色gl.clearColor(color[0], color[1], color[2], color[3]);}if (clearmask & gl.DEPTH_BUFFER_BIT) {let depth = this._baseCamera.getClearDepth();//清空深度gl.depthMask(true);gl.clearDepth(depth);}gl.clear(clearmask);}this._baseCamera.setClearFlag(true);}},//根重载,线段求交,返回相交的drawable对象linesegmentIntersect: function (start, end, threshold) {let LineSegmentIntersector = require('../util/LineSegmentIntersector');let intersector = new LineSegmentIntersector();intersector.initialize(start, end, threshold);let length = this._drawables.length;for (let i = 0; i < length; i++) {let drawable = this._drawables[i];if (drawable.valid()) {//没有隐藏,没有被剔除的drawable进行相交运算
                intersector.intersect(drawable);}}//线段求交结果需要根据ratio排序return intersector.getIntersections();},
});DrawActor.prototype[SceneRoot.typeID] = function (root) {this._baseState.removeChildren();this._baseState.setStateSet(root.getStateSet());this._currentStateBin = this._baseState;this.pushTransformMatrix(root.getRootTransform());//变换矩阵中先推入一个单位矩阵作为根节点,非常重要this.traverse(root);this.popTransformMatrix();this._currentStateBin = undefined;
};
DrawActor.prototype[MatrixTrasform.typeID] = function (node) {//模型矩阵变换let lastModelMatrix = this.getCurrentTransformMatrix();let mmatrix = undefined;if (lastModelMatrix) {mmatrix = Mat4.clone(lastModelMatrix);} else {mmatrix = Mat4.new();}node.computeLocalToWorldMatrix(mmatrix);this.pushTransformMatrix(mmatrix);//状态let stateset = node.getStateSet();this.pushStateSet(stateset);this.traverse(node);this.popStateSet(stateset);this.popTransformMatrix();
};
DrawActor.prototype[Geode.typeID] = function (geode) {this[Group.typeID](geode);
};
DrawActor.prototype[Group.typeID] = function (group) {let stateset = group.getStateSet();this.pushStateSet(stateset);this.traverse(group);this.popStateSet(stateset);
};
DrawActor.prototype[Geometry.typeID] = function (geometry) {//Geometry已经是叶子,不需要继续递归了let stateset = geometry.getStateSet();this.pushStateSet(stateset);this.pushDrawable(geometry);this.popStateSet(stateset);
};
module.exports = DrawActor;
// reset: function () {
//     this._drawables.length = 0;//置空
//     this._sceneRoot = undefined;
// },
// polytopeIntersect: function () {
//
// },
// sphereIntersect: function () {
//
// },

  我们可以看到,DrawActor是将要被绘制的对象,分成全自动(初始化模型数据就构造Drawable,准备渲染)、半自动(事件触发后构造Drawable,等待渲染)、全手动(用户自己构造Drawable,用户自己将Drawable排入渲染队列)。我们看到,DrawActor的构造函数包含的私有属性有this._renderer渲染器、this._baseCamera待渲染模块所属相机、this._baseState状态(对应shader里的uniform参数)、this._sceneRoot所属场景根节点、this._drawables包含的渲染对象、this._drawIndex当前绘制的索引数(代表本次绘制是第几次绘制,如果一此不能全部绘制完,就分多次绘制,例如模型增长)、this._currentStateBin当前处理的状态树节点、this._valid屏蔽渲染的标记(true:加入渲染队列,false:不加入渲染队列)。

  我们再来看看DrawActor的成员函数都做了什么,我们依次来看。

1.setBaseCamera设置参考相机,这就是设置当前绘制对象的观察相机。2.getBaseCamera获取参考相机。3.getbaseStateBin获取状态信息,包括当前渲染对象绑定的shader,uniform参数以及frameBuffer材质。4.getBaseStateSet同样是获取当前渲染对象的状态信息,shader,uniform,材质信息,depth深度缓冲等。5.revertBaseState恢复场景根节点状态,包括shader,uniform参数,材质信息,depth深度缓存。6.setSceneRoot设置场景根节点。7.createDrawable创建渲染对象。8.addDrawable追加渲染对象进入绘制对象数组。9.getDrawable返回渲染对象数组。10.valid标记当前渲染对象是否被屏蔽。11.apply取出每个渲染节点。12.pushStateSet向stateBin中加入stateSet,这里说明一点,stateSet是stateBin的属性。13.popStateSet从stateBin中取出stateSet属性参数。14.pushDrawable创建渲染对象drawable然后加入drawActor的drawable渲染对象数组。15.draw这才是drawActor的核心功能函数,同学们,鲫鱼为大家隆重介绍绘制函数,或者叫渲染函数,这就是将所有的drawable渲染对象进行遍历渲染的功能函数。16.drawReset每一帧绘制之前的整理重置。17.drawFinished判断当前drawActor绘制对象是否全部将drawable数组中的渲染对象绘制完毕。18.drawCamera绘制相机状态(视口,深度缓冲),如果没有渲染对象私有独立的相机,就操作主相机。19.linesegmentIntersection射线碰撞,重载父类方法。

  依次看一下上面的函数,我们大致了解了DrawActor类处理的是渲染流程管理的工作。我们接下来继续看Drawable类的其他属性。我们再贴出一次drawable的构造函数。

let Drawable = function (actor) {this._drawActor = actor;//关联的DrawActorthis._geometry = undefined;//渲染的几何Geometrythis._transform = undefined;//世界变换 FloatArray(16)this._statebin = undefined;//状态结点,原始的状态,没有额外功能时的状态this._curStatebin = undefined;//如果状态会动态变化,这里存储每一帧绘制时的状态结点this._depth = 0.0;//场景深度值,透明需要按深度排序绘制this._boundingBox = undefined;//包围盒this._boundingSphere = undefined;//包围球
};

我们已经看过了this._drawActor,也知道了drawActor是管理渲染的类。接下来我们看this._geometry渲染的几何体成员对象。this._transform空间变换矩阵。this._statebin状态节点,用来添加stateSet状态参数对象。this._currStatebin,保存每一帧的临时状态stateSet。this._depth场景深度值,作用于主相机或渲染对象私有相机(如果有私有相机的话)。this._boundingBox包围盒。this._boundingSphere包围球。这些就是Drawable类的成员。我们马上来看一下Drawable的成员函数。
  Drawable成员函数。贴出代码。

setGeometry: function (g, transform) {this._geometry = g;this._transform = transform;},getGeometry: function () {return this._geometry;},getTransform: function () {return this._transform;},setStateBin: function (sb) {this._statebin = sb;},getStateBin: function () {return this._statebin;},getCurrentStateBin: function () {return this._curStatebin;},// setDepth: function (d) {//     this._depth = d;// },getDepth: function () {return this._depth;},reset: function () {this._geometry = undefined;this._transform = undefined;this._statebin = undefined;this._curStatebin = undefined;this._depth = 0.0;this._boundingBox = undefined;this._boundingSphere = undefined;},valid: function () {if (this._drawActor.getBaseCamera().isBoundingBoxCulled(this.getBoundingBox())) {return false;}return true;},isTransparent: function () {return false;},//计算深度值computeDepth: function () {//根据包围盒和相机变换矩阵,确认中心点的Z值let mvmatrix = this._drawActor.getBaseCamera().getModelViewMatrix();let temp = Vec3.MemoryPool.alloc();this._depth = this.distanceZ(this.getBoundingBox().getCenter(temp), mvmatrix);Vec3.MemoryPool.free(temp);//drawable.setDepth(depth);
    },//相机的矩阵要取反distanceZ: function (coord, matrix) {return -(coord[0] * matrix[2] + coord[1] * matrix[6] + coord[2] * matrix[10] + matrix[14]);},getBoundingBox: function () {if(this._boundingBox === undefined){this._boundingBox = new BoundingBox();this._boundingBox.copy(this._geometry.getBoundingBox(true));if(this._transform){this._boundingBox.transformMat4(this._transform);}}return this._boundingBox;},getBoundingSphere: function () {if(this._boundingSphere === undefined) {this._boundingSphere = new BoundingSphere();let bb = this.getBoundingBox();this._boundingSphere.expandByBoundingBox(bb);}return this._boundingSphere;},getRadius: function () {return this.getBoundingSphere().getRadius();},

都是设置和获取属性的函数,包括包围盒和包围球。接下来我们来看看最核心的部分,隆重介绍draw绘制函数,请看代码。

// There are 3 cases when there is a prev / current render leaf// pSG: previousStateGraph// cSG: currentStateGraph// pRL: previousRenderLeaf// cRL: currentRenderLeaf//
    //          A                        B                       C// +-----+     +-----+            +-----+                 +-----+// | pSG |     | cSG |         +--+ SG  +--+              | SG  |// +--+--+     +--+--+         |  +-----+  |              +--+--+//    |           |            |           |                 |// +--v--+     +--v--+      +--v--+     +--v--+           +--v--+// | pSG |     | cSG |      | pSG |     | cSG |        +--+ SG  +--+// +--+--+     +--+--+      +--+--+     +--+--+        |  +-----+  |//    |           |            |           |           |           |// +--v--+     +--v--+      +--v--+     +--v--+     +--v--+     +--v--+// | pRL |     | cRL |      | pRL |     | cRL |     | pRL |     | cRL |// +-----+     +-----+      +-----+     +-----+     +-----+     +-----+//
    //
    // Case A// no common parent StateGraphNode we need to// popStateSet until we find the common parent and then// pushStateSet from the common parent to the current RenderLeaf//
    // Case B// common parent StateGraphNode so we apply the current stateSet//
    // Case C// the StateGraphNode is common to the previous RenderLeaf so we dont need// to do anything except if we used an insertStateSetdraw: function (glstate, preDrawable) {//先接受状态,再渲染几何let curStateGraph = this._statebin;let curStateGraphStateSet = curStateGraph.getStateSet();let curStateGraphParent = curStateGraph.getParent();let preStateGraph;let preStateGraphParent;if(preDrawable !== undefined){preStateGraph = preDrawable._statebin;preStateGraphParent = preStateGraph.getParent();if(preStateGraphParent !== curStateGraphParent){//A
                StateBin.moveStateBin(glstate, preStateGraphParent, curStateGraphParent);glstate.applyStateSet(curStateGraphStateSet);}else if(preStateGraph !== curStateGraph){//B
                glstate.applyStateSet(curStateGraphStateSet);}else{// in osg we call apply but actually we dont need// except if the stateSetStack changed.// for example if insert/remove StateSet has been used// if (glstate._stateSetStackChanged(idLastDraw, lastStateSetStackSize )) {//     glstate.applyStateSet(curStateGraphStateSet);// }
            }}else{//如果preLeaf为空,第一个绘制的几何,状态遍历到根节点全部push到GLState中
            StateBin.moveStateBin(glstate, undefined, curStateGraphParent);glstate.applyStateSet(curStateGraphStateSet);}let camera = this._drawActor.getBaseCamera();glstate.applyModelMatrix(this._transform, camera.getModelViewMatrix(), camera.getProjectionMatrix());this._geometry.draw(glstate);return true;},

我将注释也贴了出来,我们可以看到,渲染绘制是分三种情况的,首先我们要了解一下StateGraph渲染属性这个来源于osg的概念。stateGraph是渲染的状态属性,包括本次渲染绑定的shader,uniform参数,frameBuffer材质属性,depth深度属性。好了,大致了解了StateGraph后我们再来了解一下RenderLeaf渲染叶这个同样来自osg的概念。RenderLeaf是渲染叶,需要注意的是渲染叶保存的是sceneTree的节点状态,而不是场景树的几何和transform信息。好了,了解了这两个概念我们来看看这三种情况。A.前一帧stateGraph和后一帧stateGraph没有同一个父节点;B.前后两帧stateGraph有同一个父节点;C.前后两帧renderLeaf有共同父节点。针对这三种情况,处理的方式不同,需要注意。鲫鱼也才开始逐步研究osg框架,理解不到位之处请各位方家海涵。
  好了,今天讲述的是osg的渲染模块中的一部分DrawActor和Drawable两个模块。下一篇会进一步讲述渲染模块。欢迎大家讨论,祝大家元旦快乐。本文系原创,如需引用,请注明出处:https://www.cnblogs.com/ccentry/p/10199157.html                       

 

转载于:https://www.cnblogs.com/ccentry/p/10199157.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/387669.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

EmguCV 一些基本操作

一、先是在程序中图像的导入&#xff0c;我是根据图像路径实现&#xff0c;其中path是string类型&#xff0c;是图像路径。 IntPtr imgCvInvoke.cvLoadImage(path, Emgu.CV.CvEnum.LOAD_IMAGE_TYPE.CV_LOAD_IMAGE_ANYCOLOR); 二、图像灰度化处理&#xff0c;先创建一幅尺寸大小…

Java字符串分割

java中字符串的分割函数&#xff0c;split("你想要分割的字符", 你想要最多分割为多少段&#xff0c;正整数&#xff09; 注意事项&#xff1a; 1.分割特殊字符考虑转义字符的使用。如&#xff1a; . \ | 2.第二个参数&#xff1a; 无&#xff1a; 不传默认分割全部…

OpenCV人脸识别的原理 .

在之前讲到的人脸测试后&#xff0c;提取出人脸来&#xff0c;并且保存下来&#xff0c;以供训练或识别是用&#xff0c;提取人脸的代码如下&#xff1a; [html] view plaincopy print?void GetImageRect(IplImage* orgImage, CvRect rectInImage, IplImage* imgRect,double s…

说一下SEO和SEM到底有哪些区别?

开场白免了&#xff0c;我们直接说与主题相关的。 SEO和SEM到底有什么区别&#xff1f; SEO和SEM到底有什么区别 我们先理解字面意思&#xff1a; SEO&#xff08;Search Engine Optimization&#xff09;&#xff1a;汉译为搜索引擎优化。 SEM&#xff08;Search Engine Marke…

django模型的继承

很多时候&#xff0c;我们都不是从‘一穷二白’开始编写模型的&#xff0c;有时候可以从第三方库中继承&#xff0c;有时候可以从以前的代码中继承&#xff0c;甚至现写一个模型用于被其它模型继承。这样做的好处&#xff0c;我就不赘述了&#xff0c;每个学习Django的人都非常…

SpringBoot部署项目到Docker仓库

SpringBoot部署项目到Docker仓库1.开启远程控制端口Centos7开启方式&#xff1a; vim /lib/systemd/system/docker.service找到ExecStart行 ExecStart/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock 重启docker 启动 systemctl start docker守护进程…

人脸识别经典方法

这篇文章是撸主要介绍人脸识别经典方法的第一篇&#xff0c;后续会有其他方法更新。特征脸方法基本是将人脸识别推向真正可用的第一种方法&#xff0c;了解一下还是很有必要的。特征脸用到的理论基础PCA在另一篇博客里&#xff1a;特征脸(Eigenface)理论基础-PCA(主成分分析法)…

Jquery常用正则验证

常用校验的正则表达式var rulesConfig { /** * str.replace(/^\s|\s$/g, ) 解析&#xff1a; str&#xff1a;要替换的字符串 \s : 表示 space &#xff0c;空格 &#xff1a; 一个或多个 ^&#xff1a; 开始&#xff0c;^\s&#xff0c;以空格开始 $&#xff1a; 结束&#x…

svm参数说明

svm参数说明---------------------- 如果你要输出类的概率&#xff0c;一定要有-b参数 svm-train training_set_file model_file svm-predict test_file model_fileoutput_file 自动脚本&#xff1a;Python easy.py train_data test_data 自动选择最优参数&#xff0c;自动进行…

poj-3667(线段树区间合并)

题目链接&#xff1a;传送门 参考文章&#xff1a;传送门 思路&#xff1a;线段树区间合并问题&#xff0c;每次查询到满足线段树的区间最左值&#xff0c;然后更新线段树。 #include<iostream> #include<cstdio> #include<cstring> using namespace std; co…

面试题编程题11-python 生成随机数

随机整数&#xff1a; random.randint(a,b), [a,b] random.randrange(a,b,step) [a,b) 随机实数 random.random()返回0 到1 之间的浮点数转载于:https://www.cnblogs.com/feihujiushiwo/p/10922454.html

车牌识别之颜色选取

车牌定位是车牌识别中第一步&#xff0c;也是最重要的一步。 由于中国车牌种类多样&#xff0c;颜色不一&#xff0c; 再加上车牌经常有污损&#xff0c;以及车牌周围干扰因素太多&#xff0c;都成为了车牌定位的难点。 这里首先使用最简单算法来描述车牌定位&#xff0c;以及他…

Python - 排序( 插入, 冒泡, 快速, 二分 )

插入排序 算法分析 两次循环, 大循环对队列中的每一个元素拿出来作为小循环的裁定对象 小循环对堆当前循环对象在有序队列中寻找插入的位置 性能参数 空间复杂度  O(1) 时间复杂度  O(n^2) 详细代码解读 import randomdef func(l):# 外层循环: 对应遍历所有的无序数据for i…

[EmguCV|C#]使用CvInvoke自己繪製色彩直方圖-直方圖(Hitsogram)系列(4)

2014-02-0610325 0C# 檢舉文章 過年結束了&#xff0c;雖然還是學生所以其實還有兩個禮拜的假期&#xff0c;不過為了不讓自己發慌&#xff0c;趁著假期多利用充實自己&#xff0c;所以提早回到開工狀態&#xff0c;而這次總算要把一直說的自己動手繪製猜色直方圖文章寫出。 …

G.点我

链接&#xff1a;https://ac.nowcoder.com/acm/contest/903/G 题意&#xff1a; X腿与队友到河北省来参加2019河北省大学生程序设计竞赛&#xff0c;然而这场比赛的题目难度实在是太高了。比赛开始一个小时后&#xff0c;X腿仍然没有做出一个题。这时候&#xff0c;X腿惊讶的发…

轮廓的查找、表达、绘制、特性及匹配(How to Use Contour? Find, Component, Construct, Features Match)

前言 轮廓是构成任何一个形状的边界或外形线。前面讲了如何根据色彩及色彩的分布&#xff08;直方图对比和模板匹配&#xff09;来进行匹配&#xff0c;现在我们来看看如何利用物体的轮廓。包括以下内容&#xff1a;轮廓的查找、表达方式、组织方式、绘制、特性、匹配。 查…

Android:IntentService的学习

在Android的四大组件中&#xff0c;Service排行老二&#xff0c;在Android中的主要作用是后台服务&#xff0c;进行与界面无关的操作。由于Service运行在主线程&#xff0c;所以进行异步操作需要在子线进行。为此Android为我们提供了IntentService。 IntentService是一个抽象类…

智能商业大会构造信息化交流平台

在快速发展的当今社会&#xff0c;所有事物都在日新月异地变化着&#xff0c;相较于过去的传统商业的变化速度&#xff0c;现今基于数据的互联网商业变化速度高出了一个量级&#xff0c;同时市场对于企业的应对速度也有了更高的要求&#xff0c;然而面对大体量的数据&#xff0…

itcast-ssh-crm实践

分析 BaseDao 文件上传 转载于:https://www.cnblogs.com/hellowq/p/10209761.html

分类器大牛们

David Lowe&#xff1a;Sift算法的发明者&#xff0c;天才。 Rob Hess&#xff1a;sift的源码OpenSift的作者&#xff0c;个人主页上有openSift的下载链接&#xff0c;Opencv中sift的实现&#xff0c;也是参考这个。 Koen van de Sande&#xff1a;作者给出了sift,densesift,co…