您的位置:首页 > Web前端

WebGL自学教程——WebGL示例:7.1 改进的立方体程序(上)

2011-10-24 14:25 447 查看

 
7.1 改进的立方体程序(上)
 

    如上一章所提,直接以顶点的方式提供数据,让后用WebGL函数drawArrays绘图,会导致很大的内容占用和带宽。比如,立方体只有8个顶点,但为了使用drawArrays绘画它,我们足足提供了36个顶点的数据。一种改善的做法是,仅将立方体的8个顶点保存在缓冲中,在绘图时,我们通过指定各个顶点在该缓冲中的索引进行。以立方体为例,我们首先定义它的8个顶点:

 

    那么,立方体的六个面就可以通过下面的这些三角形来表示:

        前:123、341

        后:587、765

        左:437、784

        右:562、215

        上:514、485

        下:267、732

    那么,我们在绘画这个立方体时,只需要传递这36个索引,每个索引用BYTE就可以表示。

    通过索引绘图的WebGL函数为drawElements(mode, count, type, offset)。第一个参数mode和drawArray相同,都用来指明绘画类型,比如线、三角形、三角带等。count指明要绘画索引的个数。type指明保存索引的数据类型,可以是UNSIGNED_BYTE、UNSIGNED_SHORT。offset指明起始元素索引的偏移,以字节为单位。当type为UNSIGNED_SHORT时,该值必须是2的整数倍。该值指明我们要从哪个索引开始画起。比如,我们把三角形的索引和立方体的索引保存在了同一个索引缓冲中,并以BYTE类型保存索引数据,用三个索引表示一个顶点。三角形索引在前,立方体索引在后,那么,我们在绘画立方体时,就要跳过前面的三角形索引。相应地,offset的值如下计算:1字节/数据*3个数据/顶点*3个顶点=9个字节。

下面是完整的源码:

<html>

<head>

<meta http-equiv="content-type" content="text/html; charset=gb2312">

<script type="text/javascript" src="glMatrix-0.9.5.js"></script>

<script id="shader-vs" type="x-shader/x-vertex">

attribute vec3 v3Position;

attribute vec2 v2CubeTexST;

uniform mat4 um4Rotate;

varying vec2 v_texCoord;

void main(void)

{

    v_texCoord = vec2((v2CubeTexST.x+1.0)/2.0, 1.0-(v2CubeTexST.y+1.0)/2.0);

    gl_Position = um4Rotate * vec4(v3Position, 1.0);

}

</script>

<script id="shader-fs" type="x-shader/x-fragment">

#ifdef GL_FRAGMENT_PRECISION_HIGH

    precision highp float;

#else

    precision mediump float;

#endif

uniform sampler2D s_texture;

varying vec2 v_texCoord;

void main(void)

{

    gl_FragColor = texture2D(s_texture, v_texCoord);

}

</script>

<script>

function ShaderSourceFromScript(scriptID)

{

    var shaderScript = document.getElementById(scriptID);

    if (shaderScript == null) return "";

    var sourceCode = "";

    var child = shaderScript.firstChild;

    while (child)

    {

        if (child.nodeType == child.TEXT_NODE ) sourceCode += child.textContent;

        child = child.nextSibling;

    }

    return sourceCode;

}

var webgl = null;

var vertexShaderObject = null;

var fragmentShaderObject = null;

var programObject = null;

var cubeBuffer = null;
var cubeIndexBuffer = null;

var v3PositionIndex = 0;

var textureObject = null;

var samplerIndex = -1;

var interval = 300;

var angle = 0;

var um4RotateIndex = -1;

var leftKeyDown = false;

var rightKeyDown = false;

var cubeTexSTBuffer = null;

var cubeTexSTIndex = 1;

var angleX = 0;

var upKeyDown = false;

var downKeyDown = false;

function LoadData()

{

    var jsCubeData = [

        0.5, 0.5, 0.5,

        0.5, -0.5, 0.5,

        -0.5, -0.5, 0.5,

        -0.5, 0.5, 0.5,

        0.5, 0.5, -0.5,

        0.5, -0.5, -0.5,

        -0.5, -0.5, -0.5,

        -0.5, 0.5, -0.5

    ];

    cubeBuffer = webgl.createBuffer();

    webgl.bindBuffer(webgl.ARRAY_BUFFER, cubeBuffer);

    webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(jsCubeData), webgl.STATIC_DRAW);

    var jsCubeIndex = [

        //前

        1,2,3,

        3,4,1,

        //后

        5,8,7,

        7,6,5,

       

        //左

        4,3,7,

        7,8,4,

       

        //右

        5,6,2,

        2,1,5,

       

        //上

        5,1,4,

        4,8,5,

        //下

        2,6,7,

        7,3,2

    ];

    //数组中的是1-8的编号,要处理为0-7的索引

    for(var i=0; i<jsCubeIndex.length; ++i) --jsCubeIndex[i];

   

    cubeIndexBuffer = webgl.createBuffer();

    webgl.bindBuffer(webgl.ELEMENT_ARRAY_BUFFER, cubeIndexBuffer);

    webgl.bufferData(webgl.ELEMENT_ARRAY_BUFFER, new Uint8Array(jsCubeIndex), webgl.STATIC_DRAW);

   

   

    var jsCubeTexST = [

        //前

        0.5, 0.5,

        0.5, -0.5,

        -0.5, -0.5,

 

        -0.5, -0.5,

        -0.5, 0.5,

        0.5, 0.5,

 

        //后

        0.5, 0.5,

        -0.5, 0.5,

        -0.5, -0.5,

 

        -0.5, -0.5,

        0.5, -0.5,

        0.5, 0.5,

 

        //左

        0.5, 0.5,

        -0.5, 0.5,

        -0.5, -0.5,

 

        -0.5, -0.5,

        0.5, -0.5,

        0.5, 0.5,

 

        //右

        0.5, -0.5,

        -0.5, -0.5,

        -0.5, 0.5,

 

        -0.5, 0.5,

        0.5, 0.5,

        0.5, -0.5,

 

        //上

        0.5, -0.5,

        0.5, 0.5,

        -0.5, 0.5,

 

        -0.5, 0.5,

        -0.5, -0.5,

        0.5, -0.5,

 

        //下

        0.5, 0.5,

        0.5, -0.5,

        -0.5, -0.5,

 

        -0.5, -0.5,

        -0.5, 0.5,

        0.5, 0.5

    ];

    cubeTexSTBuffer = webgl.createBuffer();

    webgl.bindBuffer(webgl.ARRAY_BUFFER, cubeTexSTBuffer);

    webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(jsCubeTexST), webgl.STATIC_DRAW);

 

 

    textureObject = webgl.createTexture();

    webgl.bindTexture(webgl.TEXTURE_2D, textureObject);

    var img = document.getElementById('myTexture');

    webgl.texImage2D(webgl.TEXTURE_2D, 0, webgl.RGB, webgl.RGB, webgl.UNSIGNED_BYTE, img);

 

    return 0;

}

function RenderScene()

{

    webgl.clearColor(0.0, 0.0, 0.0, 1.0);

    webgl.clearDepth(1.0);

    webgl.clear(webgl.COLOR_BUFFER_BIT|webgl.DEPTH_BUFFER_BIT);

   

    webgl.bindBuffer(webgl.ARRAY_BUFFER, cubeTexSTBuffer);

    webgl.enableVertexAttribArray(cubeTexSTIndex);

    webgl.vertexAttribPointer(cubeTexSTIndex, 2, webgl.FLOAT, false, 0, 0);

 

    webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_MIN_FILTER, webgl.NEAREST);

    webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_MAG_FILTER, webgl.NEAREST);

    webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_S, webgl.CLAMP_TO_EDGE);//注意,部分数值不被支持,如REPEAT,因为我们的纹理的宽度和高度不是2的整数次方

    webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_T, webgl.CLAMP_TO_EDGE);

    webgl.activeTexture(webgl.TEXTURE0);

    webgl.bindTexture(webgl.TEXTURE_2D, textureObject);

    webgl.uniform1i(samplerIndex, 0);

    var m4Rotate = mat4.create();

    mat4.identity(m4Rotate);

    mat4.rotateZ(m4Rotate, angle*Math.PI/180);

    mat4.rotateX(m4Rotate, angleX*Math.PI/180);

    webgl.uniformMatrix4fv(um4RotateIndex, false, m4Rotate);

   

    webgl.enable(webgl.DEPTH_TEST);

    webgl.frontFace(webgl.CW);

    webgl.bindBuffer(webgl.ARRAY_BUFFER, cubeBuffer);

    webgl.bindBuffer(webgl.ELEMENT_ARRAY_BUFFER, cubeIndexBuffer);

    webgl.enableVertexAttribArray(v3PositionIndex);

    webgl.vertexAttribPointer(v3PositionIndex, 3, webgl.FLOAT, false, 0, 0);

   

    webgl.drawElements(webgl.TRIANGLES, 36, webgl.UNSIGNED_BYTE, 0);

}

function RotateTriangle()

{

    if(leftKeyDown) angle += 10;

    if(rightKeyDown) angle -= 10;

    if(angle >= 360) angle -= 360;

    if(angle < 0) angle += 360;

 

    if(upKeyDown) angleX += 10;

    if(downKeyDown) angleX -= 10;

    if(angleX >= 360) angleX -= 360;

    if(angleX < 0) angleX += 360;

    RenderScene();

}

document.onkeydown = function(e)

{

    if(e.keyCode == 37) leftKeyDown = true;

    if(e.keyCode == 39) rightKeyDown = true;

    if(e.keyCode == 38) upKeyDown = true;

    if(e.keyCode == 40) downKeyDown = true;

}

document.onkeyup = function(e)

{

    if(e.keyCode == 37) leftKeyDown = false;

    if(e.keyCode == 39) rightKeyDown = false;

    if(e.keyCode == 38) upKeyDown = false;

    if(e.keyCode == 40) downKeyDown = false;

}

function Init()

{

    var myCanvasObject = document.getElementById('myCanvas');

    webgl = myCanvasObject.getContext("experimental-webgl");

    webgl.viewport(0, 0, myCanvasObject.clientWidth, myCanvasObject.clientHeight);

    vertexShaderObject = webgl.createShader(webgl.VERTEX_SHADER);

    fragmentShaderObject = webgl.createShader(webgl.FRAGMENT_SHADER);

    webgl.shaderSource(vertexShaderObject, ShaderSourceFromScript("shader-vs"));

    webgl.shaderSource(fragmentShaderObject, ShaderSourceFromScript("shader-fs"));

    webgl.compileShader(vertexShaderObject);

    webgl.compileShader(fragmentShaderObject);

    if(!webgl.getShaderParameter(vertexShaderObject, webgl.COMPILE_STATUS)){alert(webgl.getShaderInfoLog(vertexShaderObject));return;}

    if(!webgl.getShaderParameter(fragmentShaderObject, webgl.COMPILE_STATUS)){alert(webgl.getShaderInfoLog(fragmentShaderObject));return;}

    programObject = webgl.createProgram();

    webgl.attachShader(programObject, vertexShaderObject);

    webgl.attachShader(programObject, fragmentShaderObject);

    webgl.bindAttribLocation(programObject, v3PositionIndex, "v3Position");

    webgl.bindAttribLocation(programObject, cubeTexSTIndex, "v2CubeTexST");

    webgl.linkProgram(programObject);

    if(!webgl.getProgramParameter(programObject, webgl.LINK_STATUS)){alert(webgl.getProgramInfoLog(programObject));return;}

    samplerIndex = webgl.getUniformLocation(programObject, "s_texture");

    um4RotateIndex = webgl.getUniformLocation(programObject, "um4Rotate");

    webgl.useProgram(programObject);

    if(LoadData() != 0){alert("error:LoadData()!");return;}

    window.setInterval("RotateTriangle()", interval);

}

</script>

</head>

<body onload='Init()'>

<canvas id="myCanvas" style="border:1px solid red;" width='600px' height='450px'></canvas>

<img id="myTexture" src='texture.bmp'>

</body>

</html>
运行起来之后,发现贴图上有些问题,可能是我使用的FF浏览器的bug。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息