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

 

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 οnlοad='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>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值