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>
相关文章推荐
- WebGL自学教程——WebGL示例:7.2 改进的立方体程序(下):立方图纹理
- WebGL自学教程——WebGL示例:7.1 改进的立方体程序(上)
- WebGL自学教程——WebGL示例:3. 纹理三角形
- WebGL自学教程——WebGL示例:9. 交互:这次我们用鼠标
- WebGL自学教程——WebGL示例:gl-0.1.js
- WebGL自学教程--示例:图表控件
- WebGL自学教程——WebGL示例:为三角形加上颜色
- OpenGL ES 学习教程(十四) 帧缓冲区对象(FBO) 实现渲染到纹理(Render To Texture/RTT)
- WebGL自学教程——WebGL示例:4. 让三角形动起来
- WebGL自学教程——WebGL示例:8. 带阴影的立方体
- WebGL自学教程——WebGL示例:11. 交互:更好的鼠标控制
- WebGL自学教程——WebGL示例:12. 要有光
- WebGL自学教程——WebGL示例:悲摧了,浏览器升级后,有些功能我的本本的显卡不支持了
- WebGL自学教程——WebGL示例:10. 透视投影:看起来更真实
- WebGL自学教程——WebGL示例:6. 第一个三维的物体:立方体
- WebGL自学教程——WebGL示例:glMatrix-0.9.5.js
- WebGL自学教程——WebGL示例:14.0 代码整理
- WebGL自学教程——WebGL示例:13. 混合
- WebGL自学教程——WebGL示例:测试了一下模板缓冲,目前FF还不支持。。。
- WebGL自学课程(14):WebGL使用Mipmap纹理