??xml version="1.0" encoding="utf-8" standalone="yes"?> 如下是最新公布的一D?#8220;CryEngine 3”引擎制作FPS游戏地图视频Q从制作到演CZ?分钟Q看了让人瞠目结舌,唏嘘不已?/p>
Introduction
Hidden surface removal is among the biggest problems when writing a 3D engine.
I struggled with it since the very beginning of writing 3D engines and still have no satisfactory solution to it. The ideal visibility detection scheme would allow unlimited data, extremely dynamic worlds and would have zero overdraw. The first 3d engine which implements these three demands still has to be written.
The Z buffer for example allows dynamical worlds and even crossing faces, but it suffers from immense overdraw. The BSP-tree on the other hand, if well implemented, has no overdraw at all but needs that much pre-processing that dynamical worlds are a definite nono.
It wasn't until recently i first heard of the octree, and I must admit i was struck by it's simplicity. I never actually implemented this structure and therefore I will present no pseudo code at all. This explanation is merely an attempt to make it more clear to people who have never heard of it. Besides, if you really understand the structure, then implementation is a piece of cake.
The Octree Structure
Our virtual world or level is actually nothing more then a soup of polygons. Some of you people might have thrown in a couple of curves and voxels but most of it will be polygons. Here it is:
Fig 1. Our little level.
In the picture I just built a simple level containing no more than 250 polys. Now imagine a cube surrounding the world like in the next image:
Fig. 2. Our little level surrounded by a cube.
Now it isn't hard to see that this cube can be divided into eight smaller cubes, hence the name octree. Take a look at this picture:
Fig 3. Our little level with the surrounding cube subdivided.
Call the larger cube the parent and the smaller cubes the children. On their turn subdivide each children into eight smaller cubes, and you will notice we are creating a tree where each node has eight children.
There is still one little problem left. When should we stop dividing cubes into smaller ones? There are two possible solutions. The first one is to stop when a cube has some size smaller then a fixed number. The second one is more common. You might have noticed that every child has less polygons then it's parent. The trick is to stop subdividing when the number of polygons in a cube is smaller then some fixed number.
Creating The Octree
Trees are recursion, recursion is trees. It is as simple as that. If you have a correct definition of you cubeNode it is very easy to create an octree recursively. First of all you check all polygons against the boundarys of the cube. This is very simple cause these boundaries are all axis aligned. This means that the cube has six plane equations, which are:
Where Q is the position of one corner of the cube. This are very easy equations and the all parent polygons can very easily be checked against them.
It could occur that a polygon crosses a cube boundary. Again two possible solution are at hand. First of all we could clip the polygon against the cube, which is simple, because of the axis aligned boundarys. On the other hand we could put the polygon in all cubes it is in. This means that some cubes can contain the same polygons. In order to prevent us from drawing one poly more than one time we should have a flag on each polygon which will be set if the poly is drawn for the first time.
The implementation of an octree is very straight forward. I haven't done it myself yet, but I will soon. It is all matter of recursion. In order to construct a tree, the first thing you should think of is recursion. Whether we are talking about binary trees, quad trees or octrees, it doesnt matter, just build the darn thing recursively. So have a class definition of one cubeNode and put the creation of the tree in it's constructor. In this constructor you will call the constructor itself for smaller cubes.
The Purpose Of The Octree
An octree is actually nothing more then a data structure. It can be used for very different things. It is not only handy for visibility detection but also for collision detection, realtime shadows and many more things. The most important thing to understand about octrees is that if a parent is not important then it's children aren't either. Let's makes this a little bit more clear with an example.
We will do this in 2d, which therefore resembles a quadtree, but with some imagination it can very easily be extended to 3d. Here we test the cubes (squares) against the viewing frustrum. Take a look at the next picture:
Fig 4. An octree from the top and a viewing frustrum.
In this picture a colored square that has one color was dumped without checking itKs children. As you can see some squares had to be checked all the way to the final node, but some large squares could be dumped at once. The colored squares are the ones that are outside the viewing frustrum and the greyscale ones are the one inside the viewing frustrum. As you can see this is actually a worst case scenario because the viewing frustrum crosses the middle of the surrounding square and therefore all the four children have to be checked.
You could also apply the octree to many other things. Collision detection for example. Just check in which cube your bounding sphere or box is and you will only have to check against those faces. There are many more examples.
Conclusion
There is already a lot written about octrees. I tried to give my view on them in the hope somebody might read this. As you can see octrees are way easier to implement than BSP-trees (although some disagree) while offering a better structure. The main point about an octrees is that when a parent is discarded so are it's children. Actually that is all there is to it.
Code clean, play Goldeneye and go vegetarian.
Jaap Suter a.k.a .........
译文Q?br>1、引aQ?span class=English>IntroductionQ?br> 隐面U除是写3D引擎时候最大的问题之一?br> Hidden surface removal is among the biggest problems when writing a 3D engine.
在我?D引擎的开始阶D就开始和它斗争ƈ且一直没有满意的解决Ҏ。理想的可见方案应当做到允许(使用Q无限的数据、大动态的世界和零重画。第一的3D引擎一直在q求实现q?个要求?br> I struggled with it since the very beginning of writing 3D engines and still have no satisfactory solution to it. The ideal visibility detection scheme would allow unlimited data, extremely dynamic worlds and would have zero overdraw. The first 3d engine which implements these three demands still has to be written.
Z buffer允许动态世界以及即使是交叉的面Q但是得忍受大量的重甅R另一斚wQBSP树如果能够被很好的实玎ͼ可以避免重画Q但是它需要大量的预处理计而且不适合动态世界?br> The Z buffer for example allows dynamical worlds and even crossing faces, but it suffers from immense overdraw. The BSP-tree on the other hand, if well implemented, has no overdraw at all but needs that much pre-processing that dynamical worlds are a definite nono.
直到最q我W一ơ听说了八叉树(OctreeQ,我必L认我被它的简易?#8220;狠狠地打了一下儿”。我从来没有真正地去实现q个l构Q因此我不会dC些代码。这个解释只是想让从没有听说q八叉树的h感到更清晰易懂。如果你真的了解了这个结构(OctreeQ,实现它只是一个小意思?br> It wasn't until recently i first heard of the octree, and I must admit i was struck by it's simplicity. I never actually implemented this structure and therefore I will present no pseudo code at all. This explanation is merely an attempt to make it more clear to people who have never heard of it. Besides, if you really understand the structure, then implementation is a piece of cake.
2、八叉树的结?/strong>Q?span class=English>The Octree StructureQ?br> 我们的实?#8216;世界’或者说是关卡只不过是一些多边Ş。你们或许在其中加入了一些曲面(CurvesQ和VoxelsQ地形实现方法的一U)Q但是大多数也都是多边Ş?br> Our virtual world or level is actually nothing more then a soup of polygons. Some of you people might have thrown in a couple of curves and voxels but most of it will be polygons. Here it is:
?
?Q我们设计的型兛_?br> Fig 1. Our little level.
在这张图片中我徏立了一个不过250个多边Ş?#8216;兛_’。现在想象有一个包围了q个世界的立方体Q像下一张图片那栗?br> In the picture I just built a simple level containing no more than 250 polys. Now imagine a cube surrounding the world like in the next image:
?
?Q我们设计的兛_被一个立方体所包围着?br> Fig. 2. Our little level surrounded by a cube.
现在不难看出q个立方体可以被八等分ؓ八个些的立方体Q所以才叫八叉树。看看这张图Q?br> Now it isn't hard to see that this cube can be divided into eight smaller cubes, hence the name octree. Take a look at this picture:
?
?Q对围绕着兛_的立方体q行l分?br> Fig 3. Our little level with the surrounding cube subdivided.
大点儿的q个立方体被叫做‘父亲’些的立方体叫做‘孩子’。处理每?#8216;孩子’的时候,再ؓ更小的八个立方体Q你会发现我们在创徏一个每个节Ҏ八个子节点(孩子Q的树?br> Call the larger cube the parent and the smaller cubes the children. On their turn subdivide each children into eight smaller cubes, and you will notice we are creating a tree where each node has eight children.
q有一个小问题。什么时候我们该l束l分的过E呢Q有两个可行的解x法。第一个是当一个立方体的尺寸已l比一个预定的固定值小的时候。第二个更普通。你可以注意到每?#8216;孩子’都比他的‘父亲’有更的多边形。这是_当一个立方体包含的多边Ş数量比一个预定的固定值少的时候停止细分?br> There is still one little problem left. When should we stop dividing cubes into smaller ones? There are two possible solutions. The first one is to stop when a cube has some size smaller then a fixed number. The second one is more common. You might have noticed that every child has less polygons then it's parent. The trick is to stop subdividing when the number of polygons in a cube is smaller then some fixed number.
3、创建八叉树Q?span class=English>Creating The OctreeQ?br> 树是递归的,递归的是树。就那么单。如果你正确的定义了你的立方体节点,那么递归地创Z个八叉树是很Ҏ的。首先,查所有的多边形(多边形上位置最外面的点作ؓQ立方体的边界。这很简单因为所有边界都是和坐标轴对其的。这意味着立方体有六个q面方程Q分别是Q?br> Trees are recursion, recursion is trees. It is as simple as that. If you have a correct definition of you cubeNode it is very easy to create an octree recursively. First of all you check all polygons against the boundarys of the cube. This is very simple cause these boundaries are all axis aligned. This means that the cube has six plane equations, which are:
在这里Q是立方体一个顶角的位置。这是个很简单的方程Q而且所有的父亲多边形可以容易地用这些方E来?br> WhereQ is the position of one corner of the cube. This are very easy equations and the all parent polygons can very easily be checked against them.
一个多边ŞI过了一个立方体边界q样的事情是会发生的。再一ơ,有两个解x案可用。首先我们可以沿着立方体剪切这个多边ŞQ这也是很简单的Q因为(立方体的Q边界是与坐标u寚w的。还有,我们可以把一个多边Ş攑֜所有它所在的立方体里面。这意味着一些立方体可能包含着同一个多边Ş。ؓ了防止重M个多边ŞQ我们应当在每一个多边Ş上做一个标讎ͼ当多边ŞW一ơ被ȝ时候,讄q个标记Q当然画之前也要查这个标记啦Q不q肯定媄响效率?br> It could occur that a polygon crosses a cube boundary. Again two possible solution are at hand. First of all we could clip the polygon against the cube, which is simple, because of the axis aligned boundarys. On the other hand we could put the polygon in all cubes it is in. This means that some cubes can contain the same polygons. In order to prevent us from drawing one poly more than one time we should have a flag on each polygon which will be set if the poly is drawn for the first time.
实现一个八叉树是很单明了的事情Q我q没有自己做q,但是我不久会做的。只不过是递归而已。ؓ了构造一个树Q第一件你该想的事情就是递归。不我们讨论的是二叉、四叉还是八叉树Q都没有区别Q都是递归的方法。所以定义一个立方体节点的类Q在构造函数里面写上创建树的代码。在q个构造函数里面,你会调用到更立方体的构造函数?br> The implementation of an octree is very straight forward. I haven't done it myself yet, but I will soon. It is all matter of recursion. In order to construct a tree, the first thing you should think of is recursion. Whether we are talking about binary trees, quad trees or octrees, it doesnt matter, just build the darn thing recursively. So have a class definition of one cubeNode and put the creation of the tree in it's constructor. In this constructor you will call the constructor itself for smaller cubes.
4、八叉树的用?/strong>Q?span class=English>The Purpose Of The OctreeQ?br> 一个八叉树实际上就是一个数据结构。它可以用做不同的事情。不仅仅对于‘可见?#8217;来说很方便,q有实时阴媄以及其它斚w。理解八叉树q程中最重要的事情是Q如果一?#8216;父亲’节点?#8216;没用’的,那么他的‘孩子’节点也同栗让我们举个例子让它更清楚?br> An octree is actually nothing more then a data structure. It can be used for very different things. It is not only handy for visibility detection but also for collision detection, realtime shadows and many more things. The most important thing to understand about octrees is that if a parent is not important then it's children aren't either. Let's makes this a little bit more clear with an example.
我们?D来表C它Q这样就很像一个四叉树Q但是想象一下它也可以很Ҏ的扩展到3D上。在q里我们沿着视锥Q?span class=English>viewing frustrumQ检立方体Q正方ŞQ?br> We will do this in 2d, which therefore resembles a quadtree, but with some imagination it can very easily be extended to 3d. Here we test the cubes (squares) against the viewing frustrum. Take a look at the next picture:
?
?Q顶部视锥内的八叉树?br> Fig 4. An octree from the top and a viewing frustrum.
在这个图片中Q彩色的正方形是没有Q没必要Q检它?#8216;孩子’的。就像你看到的那样一些正方ŞQ灰色的一些)被检直到最l节点,但是一些大的正方Ş只被涂了一U颜艌Ӏ那些彩色的正方形就是超Z视锥范围的,灰色的是在视锥范围内的。就像你看到的那Pq确实是最p糕的一U情况,因ؓ视锥I过了包围正方Ş的中央,所以所有的四个‘孩子’都得被检?br> In this picture a colored square that has one color was dumped without checking it?children. As you can see some squares had to be checked all the way to the final node, but some large squares could be dumped at once. The colored squares are the ones that are outside the viewing frustrum and the greyscale ones are the one inside the viewing frustrum. As you can see this is actually a worst case scenario because the viewing frustrum crosses the middle of the surrounding square and therefore all the four children have to be checked.
你可以应用八叉树到很多别的地斏V例如碰撞检。只查你的碰撞球或者碰撞盒子所在的立方体,而且只需要检那些面。还有更多的例子?br> You could also apply the octree to many other things. Collision detection for example. Just check in which cube your bounding sphere or box is and you will only have to check against those faces. There are many more examples.
5、结?/strong>Q?span class=English>ConclusionQ?br> 关于八叉树已l写了很多了。我设法表述自己对它的看法,q且希望有h会读到它。就像你看到的那P当提供好的结构的时候,八叉树比BSP树更Ҏ实现Q有人持异议Q。主要的一ҎQ当一?#8216;父亲’节点被丢弃的时候,他的‘孩子’节点也同栯丢弃。实际上q就是全部?br> There is already a lot written about octrees. I tried to give my view on them in the hope somebody might read this. As you can see octrees are way easier to implement than BSP-trees (although some disagree) while offering a better structure. The main point about an octrees is that when a parent is discarded so are it's children. Actually that is all there is to it.
Code clean, play Goldeneye and go vegetarian. Jaap Suter a.k.a .........
// -------------------------------
// Cel Shading Section
// -------------------------------
vertex_program Ogre/CelShadingVP cg
{
source Example_CelShading.cg
entry_point main_vp
profiles vs_1_1 arbvp1
default_params
{
param_named_auto lightPosition light_position_object_space 0 param_named_auto eyePosition camera_position_object_space
param_named_auto worldViewProj worldviewproj_matrix
param_named shininess float 10
}
}
fragment_program Ogre/CelShadingFP cg
{
source Example_CelShading.cg
entry_point main_fp
profiles ps_1_1 arbfp1 fp20
}
material Examples/CelShading
{
technique
{
pass
{
vertex_program_ref Ogre/CelShadingVP
{
// map shininess from custom renderable param 1
param_named_auto shininess custom 1
// l于知道param_named_autoW三个参数的作用了,用于l程序中赋值的索引
sub->setCustomParameter(CUSTOM_SHININESS, Vector4(
}
fragment_program_ref Ogre/CelShadingFP
{
// map diffuse from custom renderable param 2
param_named_auto diffuse custom 2
// map specular from custom renderable param 2
param_named_auto specular custom 3
}
texture_unit
{
texture cel_shading_diffuse.png 1d
tex_address_mode clamp
filtering none
}
texture_unit
{
texture cel_shading_specular.png 1d
tex_address_mode clamp
filtering none
}
texture_unit
{
texture cel_shading_edge.png 1d
tex_address_mode clamp
filtering none
}
}
}
}
////////////////////////////////////////////////////////////////////////////////////下面?span>cg文g
////////////////////////////////////////////////////////////////////////////////
/* Cel shading vertex program for single-pass rendering
In this program, we want to calculate the diffuse and specular
ramp components, and the edge factor (for doing simple outlining)
For the outlining to look good, we need a pretty well curved model.
*/
void main_vp(float4 position : POSITION,
float3 normal : NORMAL,[U1]
// outputs
out float4 oPosition : POSITION,
out float diffuse : TEXCOORD0,
out float specular : TEXCOORD1,
out float edge : TEXCOORD2,[U2]
// parameters
uniform float3 lightPosition, // object spaceQ世界坐标系
uniform float3 eyePosition, // object space
uniform float4 shininess,
uniform float4x4 worldViewProj)
{
// calculate output position
oPosition = mul(worldViewProj, position);
// calculate light vector
float3 N = normalize(normal);
float
// Calculate diffuse component
diffuse = max(dot(N, L) , 0);
// Calculate specular component
float3 E = normalize(eyePosition - position.xyz);
float3 H = normalize(L + E);
specular = pow(max(dot(N, H), 0), shininess[U3] );
// Mask off specular if diffuse is 0
if (diffuse == 0) specular = 0;
// Edge detection, dot eye and normal vectors
edge = max(dot(N, E), 0); // 法线与视U^行的点认为是标远Q给予黑色沟?/span>
}
void main_fp(float diffuseIn : TEXCOORD0,
float specularIn : TEXCOORD1,
float edge : TEXCOORD2,
out float4 colour : COLOR,
uniform float4 diffuse,
uniform float4 specular,
uniform sampler1D diffuseRamp,
uniform sampler1D specularRamp,
uniform sampler1D edgeRamp[U5] )
{
// Step functions from textures
diffuseIn = tex1D(diffuseRamp, diffuseIn).x;
specularIn = tex1D(specularRamp, specularIn).x;
edge = tex1D(edgeRamp, edge).x;
colour = edge * ((diffuse * diffuseIn) +
(specular * specularIn));
}
[U1]q里?/span>GPU传进来的Q?/span>POSITIONQ顶点坐标,NORMAL法线Q去看看hlsl的语意定?/span>
[U2]out都是输出参数Q改变他们的DZ后被ps获取
[U3]?/span>m下标glsQؓ材料的光泽度
[U4]得到的是vs?/span>out出来的?/span>
[U5]?/span>texture_unit中得刎ͼ序Ҏ定义的顺?/span>texture_unit
{
texture cel_shading_diffuse.png 1d
tex_address_mode clamp
filtering none
}
texture_unit
{
texture cel_shading_specular.png 1d
tex_address_mode clamp
filtering none
}
texture_unit
{
texture cel_shading_edge.png 1d
tex_address_mode clamp
filtering none
}
1Q固定着?span>(constant shading)
固定着色根本不考虑光照模型Q只是根据多边Ş颜色的烦引或RGB值绘制它?/span>
PS:基本已经被游戏淘汎ͼ太简单的着色模?/span>
2Q恒定着Ԍflat shdingQ?/span>
对于每个多边形只需要根据一个顶Ҏ行光照计,然后Ҏ计算l果Ҏ个多边Şq行着Ԍq就是恒定着Ԍ也称为面片着?span>(faceted shading)Q对于由q面l成的多边Şq种Ҏ是可行的Q立方体Q,但对于由曲面l成的多边Şq种着色方式会出现非你惌的结?span>.
3Q?span>Gougraud着?/span>
对于点之间的像素值采用两个顶点之间的插D来定Q顶点之间的颜色q渡自然Q渲染效果也比较qx,没有恒定着色那L兀?/span>
4Q?span>Phong着?/span>
?span>Gougraud着色类|不过优点在于q对每个像素q行了法U的插|对于镜面反射效果比较好?/span>
凹凸映射Bump Mapping | ![]() |
凹凸映射和纹理映非常相伹{然而,U理映射是把颜色加到多边形上Q而凹凸映是把粗p信息加到多边Ş上。这在多边Ş的视觉上会生很吸引人的效果。我们只需要添加一点信息到本来需要用大量多边Ş的物体上。需要注意的是这个物体是q的Q但是它看v来却是粗p不q的。让我们来看看左边的那个立方体。如果你很近地观察它Ӟ你会发现它上面的很多l节。它看v来好像是由成千上万个多边形构成的Q其实它只是?个矩形构成。你或许会问Q?#8220;q和U理映射有什么不同?”它们的不同之处在于——凹凸映是一U负责光方向的纹理映?br>
Q?Q凹凸映背后的原理
让我们来看看一个粗p的表面?br>
从远处看Q你判断q个物体是粗p的的唯一证据是在它表面上下的亮度有改变。你的大脑能够获得这些亮暗不一的图案信息,然后判断出它们是表面中有凹凸的部位?/p>
那么你也怼问:我是怎么知道哪些点要亮,哪些点要暗呢Q这不难。绝大多Ch生活在这样一U环境下——这个环境的大多数光源来自上方(译者注Q比如白天主要的光来自太阻I夜晚主要的光来自天花板上的日光灯Q。所以向上倄地方׃更亮Q而向下倄地方׃更暗。所以这U现象你的眼睛看到一个物体上亮暗区域Ӟ可以判断出它的凹凸情c相对亮的块被判断是面向上的Q相Ҏ的块被判断是面向下的。所以我只需要给物体上的U条单得上色?br>
如果你想要更多的证据Q这里还有一q几乎相同的图,不同于前的是它旋转了180度。所以它是前一q图倒{的图像。那些先前看h是凹q去的区域,现在看v来是凸出来的了?br>
q个时候你的大脑ƈ没有被完全欺骗,你脑中存留的视觉印象使你仍然有能力判断出q是前一q图Q只是它的光源变了,是从往上照的你的大脑可能强q性地判断出它是第一q图。事实上Q你只要始终盯着它,q且努力地想像着光是从右下方向照的Q你׃理解它是凹的Q译者注Q因为日常生zȝ习惯Q你会很Ҏ把这些图形判断成凸出的图形,但是因ؓ有了上一q对照图的印象,你可能才会特别注意到q些囑֝其实q是凹入的,只是判断Ҏ不符合我们日常生zM惯,因ؓq时大多数光不是从上方照,而是从下往上照)?br>
Q?Q什么是凹凸图(Bump MapQ?/strong>
凹凸囑֒U理囑־怼。但是不同的是,凹凸囑含的不是颜色信息Q而是凹凸信息。最通常的方法是通过存储高度值实现。我们要用到一个灰色的U理图,灰色的亮度体现出每个点分别凸出多(见右图)。这是一个非常方便的保存凹凸囄ҎQ而且q种囑־Ҏ制作。这副图具体又是怎样被渲染器使用的呢Q你接着往下看׃明白了?br>
当然Q你q不一定要把自己局限于q些单的囑ŞQ你可以扩展Q用它来做木材,做石_做脱了漆的墙面,做Q何你惛_的物体?br>
Q?Q那么它是怎么工作?/strong>
凹凸映射是补色渲染技术(Phong Shading TechniqueQ的一Ҏ展,只是在补色渲染里Q多边Ş表面上的法线被改变Q这个向量用来计该点的亮度。当你加入了凹凸映射Q法U向量会略微地改变,怎么改变则基于凹凸图。改变法U向量就会改变多边Ş的点的颜色倹{就q么单?br>
现在Q有几种Ҏ来达到这个目的(译者注Q这个目的指改变法线向量Q。我q没有实际编写补色渲染和凹凸映射的程序,但是我在q里介l一U我喜欢的方法来实现Q?br>
现在我们需要将凹凸图中的高度信息{换成补色渲染用到的法U的调节信息。这个做h不难Q但是解释v来比较费劌Ӏ?br>
好的Q我们现在将凹凸位图的信息{换成一些小向量——一个向量对应于一个点。请看左边一副放大的凹凸图。相对亮的点比相Ҏ的点更ؓ凸出。看清楚了吗Q现在计每个点的向量,q些向量表征了每个点的倾斜情况Q请看下囄描绘。图中红色小圆点表示向量是向下的Q?br>
有很多计向量的ҎQ不同的Ҏ_度不同,但是选择什么方法要取决于你所要求的精度是个什么层ơ。最通常的方法是分别计算每个点上X和Y的倾斜度:
x_gradient = pixel(x-1, y) - pixel(x+1, y)
y_gradient = pixel(x, y-1) - pixel(x, y+1)
在得Zq两个倾斜度后Q你可以计多边Ş点的法线了?br>
q里有一个多边ŞQ图上绘Z它的一条法U向量——n。除此,q有两条向量Q它们将用来调节该点法线向量。这两条向量必须与当前被渲染的多边Ş的凹凸图寚wQ换句话_它们要与凹凸图用同一U坐标u。下边的囑ֈ别是凹凸囑֒多边形,两副N昄了U、V两条向量Q译者注Q也是q面2D坐标的两条uQ:
现在你可以看到被调节后的新法U向量了。这个调节公式很单:
New_Normal = Normal + (U * x_gradient) + (V * y_gradient)
有了新法U向量后Q你可以通过补色渲染技术计出多边形每个点的亮度了
|
现在Ҏ点存储了世界中的全部点。这是,他还是不能给我们M好处Q因Zq会L有的东西。我们想把这个接点分?个部分。一ơ,我们划分Ӟ?个立方体包含那最初的Ҏ点的立方体。那意味着?个立方体在上面,4个立方体在下面。请看下图:
|
C图中黄色轮廓U没有?br> 我们刚刚把这个世界分?个部分。想象一下如果我们有2Q?Q?个部分,我们的世界将是怎样Q我们的摄象机在世界的中_向着后面靠右的角落。如果你看这些线Q你注意到在OCTREE?个结点中的第4个。这些结点包?个后面的和底结炏V这意味着我们只用d储在q些l点中的点?/font>
|
我们如何出我们要画的结点?如果你学q破片拣选(frustum culling),q将是很单的。你能得到这些破片的大小q检每个结点,看他是否被截断或在你的视觉破片中。有一个关于破片拣选的教程?a >http://www.gametutorials.com. 如果一个立方体截断破片Q我们就画q些l点。在q个例子中,我们切的数量是我们需要画?0%。记住,q只是我们世界中的一个部分。部分越多,我们将精。当Ӟ我们不会需要太多的炏V下面,我们来看一下下面的图:
|
在上面这q图中,你将发现许多的不同。这个例子中Q不是在原始?个立方体的每一个结点中建立8个新的立方体。原始的8个结点的和底面没有l分。你L把一个结点分?个或更多的结点,但是Q如果在q个面没有三角Ş可以存储Q我们将忽视q个l点q不l他分配内存。如果我们进一步的l分Q更多的l点Ş成原始的世界。如果我们变换到另一个部分,那立方体相似的变换。ؓ了说明这一点,L图:
![]() |
![]() |
在图中,有两个球Q但是方向相反。注意左边的部分Q他那世界分成2个结点,不是8个。这是因为球只要2个结炏V请看右边的球是不是和左边的很相伹{这q图告诉我们Q只昄需要的部分l点。如果没有三角Ş占用I间Q结点将不会建立?br>
何时停止l分
现在Q我们明白了如何l分Q但是我们也需要知道怎样停止l分。有许多的方法:
1。如果当时的三角形数量少于我们定义的最大的三角形数量,我们可以停止l分当前的结炏VD个例子,我们定义三角形的最大数量ؓ100。这意味着我们在细分结点之前,要检一下当前三角Ş的L量是否小于或{于我们定义的最大数量,然后再做军_。如果数量少于或{于Q我们将停止l分q指定这些三角Ş到那个结炏V请注意我们从不指定M三角形到Ml点Q除非他是末l点。如果我们划分一个结点,我们不能存储三角形到那个l点Q但是可以存储在他的子结Ҏ他们的子l点中,甚至C们的子中Q等{。当我们复习了怎样?C树后Q将会有更多的了解?br>
2。另一个方法是在停止细分时Q看我们是否l分的数目是否超q了l分的标准。例如,我们可以先徏立细分的标准?0Q如果细分的数量大于q个标准Q我们将停止q指定这些在立方体中q个面的点到那个结炏V当我们?#8220;大于q个标准”意味着l分的数量有11个?br>
3。最后的Ҏ是看l点数是否超q结点变量定义时的倹{例如,我们讄l点变量的gؓ500。每ơ,我们建立一个结点,相应的结Ҏ增加1。然后在我们每次建立另一个结ҎQ检一下我们当前的l点数是否超q结点变量。如果结Ҏ?01Q我们将不会l分q个l点Q但是指定他的当前顶点给他。我自己?1?的方法,但是1?也很好,因ؓ你可以测试不同的l分的标准?br>
怎样画OCTREE
OCTREE建立后,我们可以画我们需要的l点。那些立方体不是全部包含在我们的视觉中的Q只有一部分。这是我们Z么要计算每个l点的三角Ş的数量,如果我们只需要一个结点中的一部分Q这h们就不需要画成千上万个三角Ş。我们从根目录开始画OCTREE。对于每个结炚w存储着一个中心点。这是非常好的,比如在下面这个函CQ?br>
//q个函数取走立方体(XQYQZQ的中心点和他的大小Qwidth/2)
bool CubeInFrustum(float x,float y,float z,float size);
q个函数q回true or false,是由立方体是否在破片中决定的。如果立方体在破片中Q我们将所有他的结点,看他们是否在破片中,否则我们忽U树中的整个分枝。当我们得到了破片中的结点,但是没有Ml点在他下面。我们想ȝ点存储在末l点中。记住,只有末结Ҏ点存储。看下面的图Q?/font>
|
有阴q立方体是在破片中。白色的立方体不是在破片中。这里显CZl分?层?br>
OCTREE 中的冲突
OCTREE 不是用于渲染Q但是用于冲H检很好。随着游戏的发展,冲突也在改变,你将不得不在游戏世界中运用自q式你的角色或目标是否冲突。下面是一些冲H检的例子Q?br> 1。你徏立一个函数允怽转?DI间中的点到你的OCTREEq返回在q个点周围的点。你{递的那个Ҏ你的角色和目标的中心炏V这虽然可以工作一端时_但如果这个点靠近一个立方体的边时呢Q你的角色或目标可能和另一个立方体的顶点相冲突。ؓ了解册个问题,你将做一些事情。你可以转递角?目标的半径或一定的I间Q然后检半径或一定的I间是否和周围的l点相冲H。这׃的角?目标的Ş状决定。下面是一些典型的函数Q?br>
//q个函数取走角色/目标QXQYQZQ的中心点ƈq回靠近他的点
CVector3 *GetVerticesFromPoint(float x,float y,float z);
//q个函数取走角色/目标QXQYQZQ的中心点和半径Q然后返回和他冲H的l点中的点
CVector3 *GetVerticesFromPointAndRadius(float x,float y,float z,float radius);
//q个函数取走角色/目标QXQYQZQ的中心点和立方体的大小Q然后返回和他冲H的l点中的点
CVector3 *GetVerticesFromPointAndCube(float x,float y,float z,float size);
我相信你有更好的Ҏ快速的,在这里只是给你一点基?br>
ȝ
q篇教程只是l你讲截如何建立自己的OCTREE。关于这文章中的代码在www.gametutorials.comQ我希望q篇文章对你有用?br>
中文译者:antking
http://akinggame.gameres.com
q篇文章的英文版?a >http://www.gametutorials.com/Tutorials/OpenGL/Octree.htm
Ben Humphrey (DigiBen)
Game Programmer
DigiBen@GameTutorials.com
Co-Web Host of www.GameTutorials.com