o3d教程2 - shapes

来源:互联网 发布:mac 指令删除文件夹 编辑:程序博客网 时间:2024/05/16 14:35

 

这一章介绍怎么用顶点数组创建一个3D模型,如何创建一个shape对象,缓冲(buffers),域(fields)等等。


    由于要定义这个3D模型的每个顶点,然后存入顶点数组,所以这章不会画出一个比较复杂的3D模型,我们只是画一个立方体来说明如何创建一个3D模型,如果对于一个复杂的3D模型还是一个个顶点画的话,只能说太牛逼了,那时候就要用到3dmaxmaya等软件来建模了,经过传换成一定格式的文件后再在程序中导入。


    上一章中直接用了一个createSphere()函数创建了一个球体,这个函数是o3d自带的,其实也有自带的创建立方体的函数,当然这章是不会用的,否则就没东西可以讲了。我们要做的就是自己写一个。


    function createCube(material, size){

   

    这里的两个参数,前一个是这个立方体要用到的材质,材质这块下一章会讲到。Size顾名思义就是这个立方体的大小了。


    o3d中首先要做的便是创建一个shape对象,一个图元(primitive)对象和一个streamBank对象,


var cubeShape = g_pack.createObject('Shape');
    var cubePrimitive = g_pack.createObject('Primitive');
    var streamBank = g_pack.createObject('StreamBank');

 

cubePrimitive.material = material;      //定义这个形状的材质
    cubePrimitive.owner = cubeShape;            //
定义shape和图元的关系
    cubePrimitive.streamBank = streamBank;
·· //定义这个shapestreamBank

 

    其中shape是许多图元的集合,而一个图元则是一个点或者多个点通过一定规律连接在一起组成的图形(不像纸上画画最重要的是把每条线画出来,在O3d或者说是传统的openGLD3D中画一个模型是把这个模型的每个顶点的坐标定义好,然后系统便会自己把这些点连接起来)其中点与点之间的连接可以有多种规律,比如说比较常用的每三个点通过连线组成三角形,多个点连成多个连续的三角形,每四个点组成的四边形,多个点连成的多个连续的四边形等等。

   

             下面就要定义一下图元中各个点之间连接的规律了

cubePrimitive.primitiveType = g_o3d.Primitive.TRIANGLELIST; //三角形绘制

cubePrimitive.numberPrimitives = 12;        //这个图元中有12个三角形
    cubePrimitive.numberVertices = 8;       //
8个顶点

 

    创建一个绘制元素

cubePrimitive.createDrawElement(g_pack, null);

 

  接下来填充这8个顶点的数据,在js中这些数据都是存放在一个一维数组中的:

var positionArray = [
    -size/2, -size/2,  size/2,  //
顶点0
     size/2, -size/2,  size/2,  //
顶点1
    -size/2,  size/2,  size/2,  //
顶点2
     size/2,  size/2,  size/2,  //
顶点3
    -size/2,  size/2, -size/2,  //
顶点4
     size/2,  size/2, -size/2,  //
顶点5
    -size/2, -size/2, -size/2,  //
顶点6
     size/2, -size/2, -size/2   //
顶点7
  ];

 

 这么大串size/2(还记得size是这个函数传进来的参数吧)都是每个顶点的坐标。

 

现在就有个问题,就是size要多大才能够在屏幕当中显示一个身材合适的立方体呢,一开始可能会假象这个size取成10吧,10这个数字不大不小蛮好的,可是你会发现屏幕中除了一片灰色就什么都没有了,因为10对于现在这个情况来说太大了,连摄像机都在那个立方体里面了(当然你不会看到立方体里面那个面,因为一般情况下o3d是只绘制一个面的)。

 

o3d中到底用的是什么单位导致10这个在现实世界中这么小的一个数字能够变得这么大。咳咳,其实貌似在这样的3D世界中是没有单位之说的(至少我还不知道),因为这个物体的大小是相对于镜头的位置的,如果镜头离物体近了,size就算很小那物体还是庞然大物,因此想要调整物体大小,可以调整size的大小,也可以把镜头拉远(还记得第一章中设置照相机的位置吧),也可以把物体放远点,这些都是跟真实世界中差不多的。

 

O3d中这些顶点的坐标数据都会先放入一个缓冲(buffers)中,缓冲是一个对象(object),用来暂时存储顶点的各种数据的,像位置坐标,色彩,贴图坐标。下面就先创建缓冲对象:

    //创建顶点缓冲来存放顶点数据

    var positionsBuffer = g_pack.createObject('VertexBuffer');

   

    然后创建域(field) ,域是一小块buffer

var positionsField = positionsBuffer.createField('FloatField', 3);

positionsBuffer.set(positionArray);     //设置该缓冲所用的数组

   

    在一开始有说到要创建一个streamBank对象,但是没解释为什么,这个真是说来话长啊,因为o3d使用的是可编程渲染管线(即shader),而非固定函数渲染管线(fixed function pipeline)shader(又可以叫做着色器)可以分为vertex shader(顶点着色器)和pixel shader(像素着色器)。有了pixel shader之后,你能决定里面的每个像素该怎么填充。因为GPU浮点运算能力比CPU比显卡强得多,所以把这些计算交给显卡做再合适不过了。

   

    而这次绘制正方形,每个顶点也是通过vertex shader来渲染的,是不是有点大材小用了,其实我也这么觉得,不过我们还好暂时还不用去写这个shader,可以让Js的接口来做这件事,这就是这个streamBank做的了,streamBank把域(field)中的数据作为vertex shader的输入。可以是js中的顶点数据和底层shader交互的通道。下面这段代码就是把域(field)和vertex shader联系起来。

streamBank.setVertexStream(
    g_o3d.Stream.POSITION, // semantic: This stream stores vertex positions
    0,                     // semantic index: First (and only) position stream
    positionsField,        // field: the field this stream uses.
    0);                    // start_index: How many elements to skip in the field.

(注释的英文我就不翻译了- -)

   

          最后你可以创建一个index buffer,这个是用来重排上面vertex buffer里各个点的得排列顺序的。你可以不加这个,不过显示出来的是不是一个立方体就不一定了- -

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
    ];

var indexBuffer = g_pack.createObject('IndexBuffer');

indexBuffer.set(indicesArray);

cubePrimitive.indexBuffer = indexBuffer;

Index buffervertex buffer的创建方式和存储方式是差不多的.是两个差不多的Object,都是用来存储数据的。

   

          函数最后就是把这个已经创建好的shape返回了。

return cubeShape;

}

        

          函数写好了,得试下先,把前一章用来创建一个球体的语句替换掉,替换成

    var shape = createSphere(material,0.5);

   

         然后就会发现原来那个很丑的圆就变成一个很丑的正方形了。为了体现出这是一个立方体,而不是一个正方形,我们还要做透视投影变换,这个得用shading language了,

<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>

 

    这段就是传说中的shading language了,它放在一个textarea中方便js程序获取,可以看到里面有两个函数,vertexShaderFunctionpixelShaderFunction,分别为vertex shaderpixelShader的入口,这个怎么工作,通俗点讲vertexShaderFunction就是把每个顶点的数据传进去后经过一定的计算后再返回进行绘制。pixelShaderFunction也是这样,在这里vertexShaderFunction就一句代码:output.position = mul(input.position, worldViewProjection);就是进行了一次投影变换.pixelShaderFunction就只是把这个立方体的颜色变成了红色。最后三句不要看成是注释啊,前两句申明了vertex shader的入口函数是vertexShaderFunctionpixelShader的入口函数是pixelShaderFunction

   

          接下来就要用这段代码作为这个立方体的shader string,还记不记得在前面创建过一个effect,在后面加上下面两句代码

    var shaderString = document.getElementById('effect').value;

  effect.loadFromFXString(shaderString);              //载入shading language

   

            然后就会发现这个图形已经有立方体的样子了(我们已经能够脑淫出这是个立方体了- -)。

   

            换个视角看会更像,还记得上一章设置摄像机参数吧。

viewInfo.drawContext.view = g_math.matrix4.lookAt([2, 2, 2],  //将摄像机放置在(222)位置上

                                                      [0, 0, 0],  // 目标仍是立方体

                                                      [0, 1, 0]); // up

   

          关于shading languagematerial这里只是出于需要稍微涉及下,以后会详细地介绍,就像cg的强大一样,o3d shading language对于o3d的渲软能力起到了至关重要的作用。

 

转载自:http://techblog.qsctech.com/2009/10/o3d2---shapes.html