WebGL自学教程——WebGL示例:6. 第一个三维的物体:立方体

 

6. 第一个三维的物体:立方体

 

    前面几章都在讲二维的三角形,本章开始接触真正的3D物体。我们从一个简单的立方体开始。

    立方体有六个面;每个面分为两个三角形,每个三角形3个顶点;就是说,每个面需要6个顶点才能绘画出来,而一个立方体总共需要36个顶点。但是,实际上,立方体的顶点只有8个。在表达立方体的36个顶点当中,有很多是重复的。这造成了严重的内存的浪费。我们可以使用元素索引数组来改善这种情况,不过这是下一章的内容。本章暂时就直接使用24个顶点数据。为了WebGL能够分清立方体的里面和外面,我们对组成六个面的三角形使用相同的绕向:顺时针。由于总共有36个顶点要绘画,所以在调用WebGL函数drawArrays时,第三个参数要修改为36,而不是前几章中的3。

    注:在WebGL的3D坐标系中的,x轴向右为正向,y轴向上为正向,z轴垂直于屏幕、以指向观察者的方向为正向。

    我们保留前章中的贴图和按键控制旋转的功能,并扩展了按键控制:增加对上、下按键的响应处理(绕x轴旋转),并且,上下键和左右键可同时按下、同时生效。

    我们需要改变原先的贴图方法,因为立方体的六个面到图片的映射已经不再是简单的2D坐标变换。我们当然可以使用3D纹理,但这是后面章节的内容。现在,我只是演示如何使用2D纹理,将图像贴到立方体上。在着色器中,我们无法直接判断所处理的顶点来自哪个面,那我们就告诉它,通过顶点属性;或者,我们直接通过顶点属性,指定每个顶点的纹理坐标。本章示例中,使用的是后者。具体点,就是在顶点着色器中定义的一个vec2类型的属性变量v2CubeTexST。

完整代码如下:

<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 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,
        -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, 
        -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 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.clear(webgl.COLOR_BUFFER_BIT);

    webgl.bindBuffer(webgl.ARRAY_BUFFER, cubeBuffer);
    webgl.enableVertexAttribArray(v3PositionIndex);
    webgl.vertexAttribPointer(v3PositionIndex, 3, webgl.FLOAT, false, 0, 0);

    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);
    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.drawArrays(webgl.TRIANGLES, 0, 36);
}

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 οnlοad='Init()'>
<canvas id="myCanvas" style="border:1px solid red;" width='600px' height='450px'></canvas>
<img id="myTexture" src='texture.bmp'>
</body>
</html>

    运行看看,厄,虽然程序上没有问题,但旋转起来之后,感觉怪怪的,因为我们分不清立方体的这个面和那个面。这是因为我们没有使用深度测试,导致本该被遮挡的内容被绘画到了前面。于是,我修改了一下代码:

<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 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,
        -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,
        -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 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);
    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.enableVertexAttribArray(v3PositionIndex);
    webgl.vertexAttribPointer(v3PositionIndex, 3, webgl.FLOAT, false, 0, 0);
   
    webgl.drawArrays(webgl.TRIANGLES, 0, 36);
}

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 οnlοad='Init()'>
<canvas id="myCanvas" style="border:1px solid red;" width='600px' height='450px'></canvas>
<img id="myTexture" src='texture.bmp'>
</body>
</html>

    现在,运行起来之后,虽然还是有点分不清立方体的面,但这是贴图的原因,也能凑合着看。如果你不愿意凑合着看,可以自行将立方体的12条边画出来。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值