您的位置:首页 > Web前端

WebGL自学教程——WebGL示例:14. 渲染到纹理和多程序对象

2011-11-01 17:30 513 查看
 
14. 渲染到纹理和多程序对象
 

    有的时候(比如实现镜像效果),需要将一些场景作为纹理。我们可以使用离屏渲染,使用帧缓冲来达到目的。具体点,是利用帧缓冲对象的附件。在帧缓冲对象中:

    可以附加为颜色附件的有:保存颜色值的渲染缓冲对象;2D纹理;单一mip级别的立方图纹理面。

    可以附加为深度附件的有:包含深度值的渲染缓冲,保存深度值的2D纹理;单一mip级别的立方图纹理面。

    可以附加为模板附件的有:保存模板值的渲染缓冲对象。

    在本章示例中,使用2D纹理作为颜色附件,渲染缓冲作为深度附件。至于模板缓冲,暂时还没有用到。

    为了方便,将原来的绘图代码移动到了js函数DrawScene中。在RenderScene中,首先创建和配置帧缓冲对象,然后调用DrawScene函数,将内容绘画到了帧缓冲对象中。然后,又调用了一次DrawScene函数,不过这次,是绘画到屏幕上。然后,我绘画了一个三角形,该三角形在第一章的示例中有定义,但我把其绕点顺序改为了顺时针。三角形所使用的纹理,正是帧缓冲中作为颜色附件作为纹理对象。

    本示例还演示了如何在一个应用中使用多个程序对象。

<html>

<head>

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

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

<script type="text/javascript" src="glt.js"></script>

<script type="text/javascript" src="gl-0.1.js"></script>

<script type="text/javascript" src="loadData.js"></script><!--加载数据的代码移动到了这个js文件中-->

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

attribute vec3 a_v3Position;

attribute vec3 a_v3Normal;

uniform mat4 u_m4ModelView;

varying vec3 v_v3TexCoord;

uniform int u_iShadowMode;

varying vec3 v_v3Position;

varying vec3 v_v3Normal;

void main(void)

{

    gl_Position = u_m4ModelView * vec4(a_v3Position, 1.0);

   

    if(u_iShadowMode == 0)

    {

        v_v3TexCoord = a_v3Position;

        v_v3Position = a_v3Position;

        v_v3Normal = vec3(u_m4ModelView * vec4(a_v3Normal, 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 samplerCube u_samplerCube;

varying vec3 v_v3TexCoord;

uniform int u_iShadowMode;

varying vec3 v_v3Position;

varying vec3 v_v3Normal;

uniform bool u_bUseLighting;

uniform vec3 u_v3AmbientScale;

uniform vec3 u_v3PointLightPosition;

uniform vec3 u_v3PointLightScale;

void main(void)

{

    if(u_iShadowMode == 0)

    {

        vec3 v3ColorScale;

        if(!u_bUseLighting) v3ColorScale = vec3(1.0, 1.0, 1.0);

        else

        {

            vec3 v3PLV = u_v3PointLightPosition - v_v3Position;

            if(dot(v_v3Normal, v3PLV) > 0.0)//可以被点光源照射到

            {

                //求出顶点到点光源的距离,假设在距离0-1.5范围内线性衰减;当然也可以使用其他的光照公式

                float f = max(0.0, 1.0 - length(v3PLV) / 2.0);//要注意的是,示例中旋转的只有立方体,光源的位置并没有变动

               

                v3ColorScale = u_v3AmbientScale + u_v3PointLightScale * f;//将衰减后的点光源颜色比例合并到环境光

            }

            else v3ColorScale = u_v3AmbientScale;

        }

       

        vec4 color = textureCube(u_samplerCube, v_v3TexCoord);

        gl_FragColor = vec4(color.rgb * v3ColorScale, color.a);

    }

    else if(u_iShadowMode == 1) gl_FragColor = vec4(0.7, 0.7, 0.7, 1.0);

    else if(u_iShadowMode == 2) gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);

    else if(u_iShadowMode == 3) gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);

    else if(u_iShadowMode == 4) gl_FragColor = vec4(0.0, 0.5, 0.5, 1.0);

}

</script>

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

attribute vec3 a_v3Position;

uniform mat4 u_m4ModelView;

varying vec2 v_v2TexCoord;

void main(void)

{

    //v_v2TexCoord = vec2((a_v3Position.x+1.0)/2.0, 1.0-(a_v3Position.y+1.0)/2.0);

    gl_Position = u_m4ModelView * vec4(a_v3Position, 1.0);

    v_v2TexCoord = vec2(gl_Position.x/2.0, gl_Position.y);

}

</script>

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

#ifdef GL_FRAGMENT_PRECISION_HIGH

    precision highp float;

#else

    precision mediump float;

#endif

uniform sampler2D u_sampler2D;

varying vec2 v_v2TexCoord;

void main(void)

{

    gl_FragColor = texture2D(u_sampler2D, v_v2TexCoord);

}

</script>

<script>

var mouseDown = false;

var mousePosition = [0, 0];

var rotateMat4 = null;

var rotatePosV3 =  [0, 0, 0];;//记录离圆心的偏移,2D,弄成vec3只是为了方便执行加减操作

var textureObject = null;

var shadowMat4 = null;//预先计算好的阴影矩阵

var jsConeMat4 = null;

var perspectiveMat4 = null;//预先计算好的投影矩阵

function LoadData()

{

    LoadCubeData("cube", "cubeIndex", "cubeNormals");

    LoadTextureData();

    LoadShadowPlaneData("shadowPlane", "shadowMat4");

    LoadConeData("cone", "jsConeMat4");

    LoadPerspectiveMat4("perspectiveMat4");

    LoadTriangleData("triangle");

    rotateMat4 = mat4.create();

    mat4.identity(rotateMat4);

    return 0;

}

function DrawScene()

{

    oJSWebGL.JS_UseProgram(oJSWebGLProgram);

   

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

    webgl.clearDepth(1.0);

    webgl.depthMask(true);

    webgl.disable(webgl.BLEND);

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

   

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

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

    webgl.texParameteri(webgl.TEXTURE_CUBE_MAP, webgl.TEXTURE_WRAP_S, webgl.CLAMP_TO_EDGE);

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

    oJSWebGL.BindTextureUnit(webgl.TEXTURE0, webgl.TEXTURE_CUBE_MAP, textureObject);//直观

    webgl.uniform1i(oJSWebGLProgram.u_samplerCube, 0);

   

    webgl.frontFace(webgl.CW);

    webgl.cullFace(webgl.BACK);

    webgl.enable(webgl.CULL_FACE);

    //为了演示需要,将屏幕分为了左右两个部分,左边的一半用来绘画原效果,右边的一半绘画实际演示

    var jsMatrix = new CJSMat4();

    //画平面

    webgl.disable(webgl.DEPTH_TEST);//相当于地面,禁止深度测试,此时深度缓冲中值保持1.0不变,表示可以被任何其它物体所遮挡

    oJSWebGL.JS_VertexAttribPointer(oJSWebGLProgram.a_v3Position, "shadowPlane", 0, 0);

    webgl.enableVertexAttribArray(oJSWebGLProgram.a_v3Position);

    webgl.disableVertexAttribArray(oJSWebGLProgram.a_v3Normal);

    webgl.uniform1i(oJSWebGLProgram.u_iShadowMode, 2);

    jsMatrix.Identity();

    jsMatrix.Transform(perspectiveMat4);

    jsMatrix.Translate([-0.5, 0, 0]);

    webgl.uniformMatrix4fv(oJSWebGLProgram.u_m4ModelView, false, jsMatrix.GetValue());

    webgl.drawArrays(webgl.TRIANGLE_FAN, 0, 4);

   

    //画立方体

    //先旋转,再透视投影

    oJSWebGL.JS_VertexAttribPointer(oJSWebGLProgram.a_v3Position, "cube", 0, 0);

    oJSWebGL.JS_SetCurrentBuffer("cubeIndex");

    webgl.enableVertexAttribArray(oJSWebGLProgram.a_v3Position);

    //画阴影

    webgl.disable(webgl.DEPTH_TEST);

    webgl.uniform1i(oJSWebGLProgram.u_iShadowMode, 1);

    webgl.disableVertexAttribArray(oJSWebGLProgram.a_v3Normal);

    jsMatrix.Identity();

    jsMatrix.Scale([0.5, 0.5, 0.5]);

    jsMatrix.Transform(rotateMat4, shadowMat4, perspectiveMat4);

    jsMatrix.Translate([-0.5, 0, 0]);

    webgl.uniformMatrix4fv(oJSWebGLProgram.u_m4ModelView, false, jsMatrix.GetValue());

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

    //画立方体

    webgl.uniform1i(oJSWebGLProgram.u_bUseLighting, document.getElementById("lighting").checked);

    webgl.uniform3f(oJSWebGLProgram.u_v3AmbientScale,

                parseFloat(document.getElementById("ambientR").value),

                parseFloat(document.getElementById("ambientG").value),

                parseFloat(document.getElementById("ambientB").value));

     webgl.uniform3fv(oJSWebGLProgram.u_v3PointLightPosition, [-1, 1, 0]);

     webgl.uniform3f(oJSWebGLProgram.u_v3PointLightScale,

                parseFloat(document.getElementById("pointR").value),

                parseFloat(document.getElementById("pointG").value),

                parseFloat(document.getElementById("pointB").value));

    webgl.enable(webgl.DEPTH_TEST);

    webgl.depthFunc(webgl.LEQUAL);

    webgl.depthMask(true);

    webgl.uniform1i(oJSWebGLProgram.u_iShadowMode, 0);

    jsMatrix.Identity();

    jsMatrix.Scale([0.5, 0.5, 0.5]);

    jsMatrix.Transform(rotateMat4, perspectiveMat4);

    jsMatrix.Translate([-0.5, 0, 0]);

    webgl.uniformMatrix4fv(oJSWebGLProgram.u_m4ModelView, false, jsMatrix.GetValue());

    oJSWebGL.JS_VertexAttribPointer(oJSWebGLProgram.a_v3Normal, "cubeNormals", 0, 0);

    webgl.enableVertexAttribArray(oJSWebGLProgram.a_v3Normal);

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

   

    //画圆锥

    webgl.enable(webgl.DEPTH_TEST);

    webgl.depthFunc(webgl.LEQUAL);

    webgl.uniform1i(oJSWebGLProgram.u_iShadowMode, 3);   

    jsMatrix.Identity();

    jsMatrix.Transform(jsConeMat4, rotateMat4, perspectiveMat4);

    jsMatrix.Translate([-0.5, 0, 0]);

    webgl.uniformMatrix4fv(oJSWebGLProgram.u_m4ModelView, false, jsMatrix.GetValue());

    oJSWebGL.JS_VertexAttribPointer(oJSWebGLProgram.a_v3Position, "cone", 0, 0);

    webgl.enableVertexAttribArray(oJSWebGLProgram.a_v3Position);

    webgl.disableVertexAttribArray(oJSWebGLProgram.a_v3Normal);

    webgl.drawArrays(webgl.TRIANGLE_FAN, 0, 14);

   

    //画大立方体,除了混合指令,其他基本上和画小立方体相同

    oJSWebGL.JS_VertexAttribPointer(oJSWebGLProgram.a_v3Position, "cube", 0, 0);

    oJSWebGL.JS_SetCurrentBuffer("cubeIndex");

    webgl.enableVertexAttribArray(oJSWebGLProgram.a_v3Position);

    webgl.disableVertexAttribArray(oJSWebGLProgram.a_v3Normal);

    webgl.enable(webgl.DEPTH_TEST);

    webgl.depthFunc(webgl.LEQUAL);

    webgl.depthMask(false);

    webgl.uniform1i(oJSWebGLProgram.u_iShadowMode, 4);

    jsMatrix.Identity();

    jsMatrix.Transform(rotateMat4, perspectiveMat4);

    jsMatrix.Translate([-0.5, 0, 0]);

    webgl.uniformMatrix4fv(oJSWebGLProgram.u_m4ModelView, false, jsMatrix.GetValue());

    oJSWebGL.JS_VertexAttribPointer(oJSWebGLProgram.a_v3Normal, "cubeNormals", 0, 0);

    //设置混合

    webgl.enable(webgl.BLEND);//首先要启用混合

    webgl.blendEquation(webgl.FUNC_ADD);//相加

    webgl.blendFunc(webgl.SRC_COLOR, webgl.DST_COLOR);//源色和现有色的相加

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

}

function RenderScene()

{

    //需要将内容画到帧缓冲中,需要用到深度缓冲、颜色缓冲

    //在帧缓冲附件中,分别对应深度附件和颜色附件

    //深度附件使用渲染缓冲;

    //由于要把内容作为纹理,所以颜色附件是一个2D纹理

   

    //首先判断,要创建的纹理大小是否合法

    var size = webgl.getParameter(webgl.MAX_RENDERBUFFER_SIZE);

    var textureWidth = 300;

    var textureHeight = 300;

    if(size <= textureWidth || size <= textureHeight)

    {

        alert("纹理大小超出范围!");

        return;

    }

   

    //创建帧缓冲,渲染缓冲和纹理对象

    var framebuffer = webgl.createFramebuffer();

    var depthRenderbuffer = webgl.createRenderbuffer();

    var texture = webgl.createTexture();

    //用相应的大小配置纹理对象,由于纹理图像的内容将由我们绘画填充,所以此处我们无需设置初始纹理数据

    webgl.bindTexture(webgl.TEXTURE_2D, texture);

    webgl.texImage2D(webgl.TEXTURE_2D, 0, webgl.RGB, textureWidth, textureHeight,

                 0, webgl.RGB, webgl.UNSIGNED_BYTE, null);

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

    webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_MIN_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.bindRenderbuffer(webgl.RENDERBUFFER, depthRenderbuffer);

    webgl.renderbufferStorage(webgl.RENDERBUFFER, webgl.DEPTH_COMPONENT16, textureWidth, textureHeight);

    //配置帧缓冲:将渲染缓冲和纹理附加为附件

    webgl.bindFramebuffer(webgl.FRAMEBUFFER, framebuffer);

    webgl.framebufferRenderbuffer(webgl.FRAMEBUFFER, webgl.DEPTH_ATTACHMENT, webgl.RENDERBUFFER, depthRenderbuffer);

    webgl.framebufferTexture2D(webgl.FRAMEBUFFER, webgl.COLOR_ATTACHMENT0, webgl.TEXTURE_2D, texture, 0);

    if(webgl.checkFramebufferStatus(webgl.FRAMEBUFFER) != webgl.FRAMEBUFFER_COMPLETE)//帧缓冲不完整,无法继续

    {

        webgl.deleteRenderbuffer(depthRenderbuffer);depthRenderbuffer = null;

        webgl.deleteFramebuffer(framebuffer);framebuffer = null;

        webgl.deleteTexture(texture);texture = null;

        alert("帧缓冲不完整,无法继续!");

        return;

    }

    DrawScene();//先前已经bindFramebuffer到framebuffer,此处,绘画的内容将保存到framebuffer中

    //可以开始使用纹理对象绘画到屏幕了

    webgl.bindFramebuffer(webgl.FRAMEBUFFER, null);//传递null,表示要绘画到屏幕

    DrawScene();

    oJSWebGL.JS_UseProgram(oJSWebGLProgram02);

   

    oJSWebGL.BindTextureUnit(webgl.TEXTURE0, webgl.TEXTURE_2D, texture);

    webgl.uniform1i(oJSWebGLProgram.u_sampler2D, 0);

   

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

    webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_MIN_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);

    oJSWebGL.JS_VertexAttribPointer(oJSWebGLProgram02.a_v3Position, "triangle", 0, 0);

    webgl.enableVertexAttribArray(oJSWebGLProgram02.a_v3Position);

   

    var jsMatrix = new CJSMat4();

    jsMatrix.Identity();

    jsMatrix.Transform(perspectiveMat4);

    jsMatrix.Scale([0.5, 0.5, 0.5]);

    jsMatrix.Translate([0.5, 0, 0]);

    webgl.uniformMatrix4fv(oJSWebGLProgram02.u_m4ModelView, false, jsMatrix.GetValue());

   

    webgl.drawArrays(webgl.TRIANGLES, 0, 3);

   

    //清理,千万不能漏掉,不然就等着崩溃吧

    webgl.deleteRenderbuffer(depthRenderbuffer);depthRenderbuffer = null;

    webgl.deleteFramebuffer(framebuffer);framebuffer = null;

    webgl.deleteTexture(texture);texture = null;

}

//这些事件代码仍然只适合FF浏览器

function OnMouseDown(e)

{

    mouseDown = true;

    mousePosition = [parseInt(e.screenX), parseInt(e.screenY)];

}

function OnMouseUp(e)

{

    mouseDown = false;

    vec3.add(rotatePosV3, [parseInt(e.screenX)-mousePosition[0], parseInt(e.screenY)-mousePosition[1], 0]);

}

function OnMouseMove(e)

{

    if(!mouseDown) return;

    var tV3 = [];

    vec3.add(rotatePosV3, [parseInt(e.screenX)-mousePosition[0], parseInt(e.screenY)-mousePosition[1], 0], tV3);//上次偏移加拖动形成的临时偏移成为本次拖动的最终偏移

    mat4.identity(rotateMat4);//首先设置为单位矩阵,然后再构造成旋转矩阵

    if(tV3[0] == 0 && tV3[1] == 0) return;

    //根据位移,计算出当前的方向

    var v3 = null;

    var wh = oJSWebGL.Viewport();

    var r = Math.min(wh[0], wh[1])/2;

    var l = Math.sqrt(tV3[0]*tV3[0] + tV3[1] * tV3[1]);

    if(l <= r) v3 = [tV3[0], tV3[1], Math.sqrt(r*r - l*l)];

    else v3 = [r*tV3[0]/l, r*tV3[1]/l, 0];

    v3[1] = - v3[1];

    var radians = Math.acos(vec3.dot([0,0,1], v3)/r);

    var vn = [];

    gltGetNormalVector([0,0,0], [0,0,1], v3, vn);//或vec3.cross([0,0,1], v3, vn);

   

    mat4.rotate(rotateMat4, radians, vn);

}

var oJSWebGL = null;

var oJSWebGLProgram = null;
var oJSWebGLProgram02 = null;

var webgl = null;

function Init()

{

    oJSWebGL  = new CJSWebGL ('myCanvas');

    try

    {

        if(!oJSWebGL.Init()) throw oJSWebGL.error;

        webgl = oJSWebGL.webgl;

       

        oJSWebGLProgram = oJSWebGL.JS_SetupProgramObject("shader-vs", "shader-fs", false);

        if(oJSWebGLProgram  == null) throw oJSWebGL.error;//如果oJSWebGLProgram生成成功,则各个活动属性和活动uniform的位置/索引都将自动保存到oJSWebGLProgram对象下的同名变量

       

        oJSWebGLProgram02 = oJSWebGL.JS_SetupProgramObject("shader-vs-02", "shader-fs-02", false);

        if(oJSWebGLProgram02  == null) throw oJSWebGL.error;

       

        if(LoadData() != 0) throw "error:LoadData()!";

       

        oJSWebGL.SetMouseCallback(OnMouseDown, OnMouseMove, OnMouseUp);

        oJSWebGL.SetRender(RenderScene, 300);

    }

    catch(e){alert(e);}

}

</script>

</head>

<body onload='Init()'>

<p>将加载数据的代码转到了loadData.js文件中。<br>

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

<tr><td>光照控制:</td><td colspan=3><input type="checkbox" id="lighting" checked />启用光照</td></tr>

<tr><td>环境光(比例):</td>

            <td>R: <input type="text" id="ambientR" value="0.5"></td>

            <td>G: <input type="text" id="ambientG" value="0.5"></td>

            <td>B: <input type="text" id="ambientB" value="0.5"></td></tr>

<tr><td>点光源(比例):</td>

            <td>R: <input type="text" id="pointR" value="1.0"></td>

            <td>G: <input type="text" id="pointG" value="1.0"></td>

            <td>B: <input type="text" id="pointB" value="1.0"></td></tr>

</table><br>

<img id="myTexture1" src='cubeTexture1.bmp'>

<img id="myTexture2" src='cubeTexture2.bmp'>

<img id="myTexture3" src='cubeTexture3.bmp'><br>

<img id="myTexture4" src='cubeTexture4.bmp'>

<img id="myTexture5" src='cubeTexture5.bmp'>

<img id="myTexture6" src='cubeTexture6.bmp'>   

</body>

</html>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息