上一篇我们调用O3D的createSphere函数绘制了一个球体,这一次我们将自己来完成这个几何体绘制过程,这将加深我们对O3D渲染过程的了解。

如同OpenGL绘制过程一样,在O3D中我们也需要定义3D模型的各个顶点坐标和连接类型(如三角形、四边形等),下面对具体的代码进行分析,这个代码将创建一个立方体。大家如果和OpenGL的代码做类比,可以发现有很多共同点。

首先还是一些初始化工作,这里的工作主要是创建primitives绘制图元。

var cubeShape = g_pack.createObject(‘Shape’);
// 创建Primitive图元,这个图元里将包含创建3D物体(立方体)的几何数据(顶点)
var cubePrimitive = g_pack.createObject(‘Primitive’);
// StreamBank用于存储顶点数据流
var streamBank = g_pack.createObject(‘StreamBank’);

// 定义该图元的材质
cubePrimitive.material = material;
// 将Primitive和streamBank 指派给cubeShape
cubePrimitive.owner = cubeShape;
cubePrimitive.streamBank = streamBank;

下面初始化cube 的顶点数据:

cubePrimitive.primitiveType = g_o3d.Primitive.TRIANGLELIST;
cubePrimitive.numberPrimitives = 12; // 12 个三角形
cubePrimitive.numberVertices = 8;    // 8 个顶点
// 生成cube的绘制元素
cubePrimitive.createDrawElement(g_pack, null);
// 生成8个顶点的坐标
var positionArray = [
-0.5, -0.5, 0.5, // vertex 0
0.5, -0.5, 0.5// vertex 1
-0.5, 0.5, 0.5// vertex 2
0.5, 0.5, 0.5,   // vertex 3
-0.5, 0.5, -0.5, // vertex 4
0.5, 0.5, -0.5// vertex 5
-0.5, -0.5, -0.5,// vertex 6
0.5, -0.5, -0.5 // vertex 7
];

var indicesArray = [
0, 1, 2, // face 1
2, 1, 3,
2, 3, 4, // face 2
4, 3, 5,
4, 5, 6, // face 3
6, 5, 7,
6, 7, 0, // face 4
0, 7, 1,
1, 7, 3, // face 5
3, 7, 5,
6, 0, 4, // face 6
4, 0, 2
];

第一句定义cube的图元类型为TRIANGLELIST,即三角形,这个含义是后面的顶点按顺序每三个点通过连线组成三角形。立方体共有6个面、8个顶点,每个面由两个三角形组成,所以共有12个三角形。这么一说熟悉OpenGl的朋友就很明白了。后面定义的positionArray 就相当于OpenGL的绘制图形的顶点队列,含义和用法都是一样的。
而最后的indicesArray 则用于声明如何将上面提到的三角形组装成cube的面,用法如下:每三个顶点定义了一个三角形(triangle),然后每两个三角形组成一个面,如(0, 1, 2)和(2, 1, 3)分别定义了两个三角形,这两个三角形就组成了一个面face1,后面的类比。
从这里可以看出,O3D的图元绘制和OpenGL基本还是一样的。只是O3D会将所有的顶点数据放入到一个缓冲(buffers)中,用来暂时存储顶点的各种数据,像位置坐标,色彩,贴图坐标。下面是创建一个存储顶点数据的Buffer和Field:

var positionsBuffer = g_pack.createObject(‘VertexBuffer’);
var positionsField = positionsBuffer.createField(‘FloatField’, 3); // Field是一个Buffer,类型为FloatField,大小为3
positionsBuffer.set(positionArray);  // 用顶点数组填充Buffer
下面把positionBufferStreamBank 联系起来:

streamBank.setVertexStream(
g_o3d.Stream.POSITION, // 声明这个stream 存储的是顶点位置(POSITION)
0,                     // First (and only) position stream
positionsField,        // field: stream使用的field
0);                    // start_index: 跳过field中多少个elements

这里streamBank的作用是将域(field)中的数据作为vertex shader(顶点着色器)的输入,关于着色器,以后再讨论。
最后,我们还要做一个工作,前面完成了顶点相关的工作,但是我们还没有定义绘制顺序,也就是上面提到的indicesArray 数组还没发挥作用。

var indexBuffer = g_pack.createObject(‘IndexBuffer’);
indexBuffer.set(indicesArray); // Buffer数据填充
cubePrimitive.indexBuffer = indexBuffer;
这样cube将按照indicesArray 定义的顺序绘制,这样我们就可以得到一个立方体了,然后可以将cubeShape 返回。
我们将这部分代码封装成createCube(material) 函数,然后替代上一篇的createSphere:

//var shape = o3djs.primitives.createSphere(g_pack, material, 0.5, 20, 20);
var shape = createCube(material);
不过到这里cube的显示效果仍不理想,O3D的官方Demo中有一段做透视投影变换代码,这样做之后显示效果就非常理想了。
首先要在HTML中定义一个一段文本域,这是为了便于JS获取,其实这段文本域里定义的就是shading language:

<div style=“display:none”>
<! Start of effect >
<textarea id=“effect”>
// World View Projection matrix that will transform the input vertices
// to screen space.
float4x4 worldViewProjection : WorldViewProjection;
// input parameters for our vertex shader
struct VertexShaderInput {
float4 position : POSITION;
};
// input parameters for our pixel shader
struct PixelShaderInput {
float4 position : POSITION;
};
/**
* The vertex shader simply transforms the input vertices to screen space.
*/

PixelShaderInput vertexShaderFunction(VertexShaderInput input) {
PixelShaderInput output;
// Multiply the vertex positions by the worldViewProjection matrix to
// transform them to screen space.
output.position = mul(input.position, worldViewProjection);
return output;
}
/**
* This pixel shader just returns the color red.
*/

float4 pixelShaderFunction(PixelShaderInput input): COLOR {
return float4(1, 0, 0, 1); // Red.
}
// Here we tell our effect file *which* functions are our vertex and pixel shaders.
// #o3d VertexShaderEntryPoint vertexShaderFunction
// #o3d PixelShaderEntryPoint pixelShaderFunction
// #o3d MatrixLoadOrder RowMajor
</textarea>
<! End of effect >
</div>
关于shading language本人也理解不多。这段代码里面有两个函数vertexShaderFunctionpixelShaderFunction,最后三行不是注释,而是定义了VertexShaderEntryPoint 、PixelShaderEntryPoint 、MatrixLoadOrder ,其中VertexShaderEntryPoint 就是把每个顶点的数据传进去后经过一定的计算后再返回进行绘制,而PixelShaderEntryPoint 则对像素进行操作,在这里只返回一个颜色值。
定义了这段shading language后,我们可以用它来重新创建Effect,将下面的代码替换原来的effect:

var redeffect = g_pack.createObject(‘Effect’);
var shaderString = document.getElementById(‘effect’).value;
redeffect.loadFromFXString(shaderString);
好了,这就是最终的结果,终于可以看出这是一个立方体了。
O3D_2_1

暂无相关产品

发表评论