13. 混合
混合和光照计算相似,都是对颜色进行操作。不同的是,光照计算中进行的操作,针对的是当前的片段,通过在着色器中的指令完成;而混合,针对的是同一个像素处的两个或多个片段,通过WebGL指令完成。举个例子,一个不透明的物体和一个半透明的物体。当它们在同一个场景时,如果半透明的物体在前面,那么我们不仅能够看到半透明的物体,还应当能够透过它看到后面的不透明物体。对这两个物体的绘画,我们不是一次完成,而是调用了两次drawElements或drawArrays,并且中间还可能隔了N个其他绘图操作。
假设现在屏幕上某像素的颜色为Cp,半透明物体中与该像素对应的片段的颜色为Cf。混合所执行的操作就是将Cp和Cf各乘以一个比例因子,然后将得到的两个值按指定的方式(比如相加)进行组合,组合后的值成为该像素的新的颜色值。混合系数(比例因子)可以通过WebGL函数blendFunc和blendFuncSeparate指定。混合方式(混合操作符、混合方程)通过WebGL函数blendEquation和blendEquationSeparate指定。有一些混合系数还需要额外的颜色参数,通过WebGL函数blendColor指定。注意,在使用混合之前,首先要启用混合:enable(BLEND)。
示例中,我将立方体缩小了一半,在其原先的位置上渲染了一个半透明的立方体。
<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 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>
var textureObject = null;
var shadowMat4 = null;//预先计算好的阴影矩阵
var mouseDown = false;
var mousePosition = [0, 0];
var perspectiveMat4 = null;//预先计算好的投影矩阵
var rotateMat4 = null;
var rotatePosV3 = [0, 0, 0];;//记录离圆心的偏移,2D,弄成vec3只是为了方便执行加减操作
var jsConeMat4 = null;
function LoadData()
{
var jsCubeData = [
// Front face
-0.3, -0.3, 0.3,
0.3, -0.3, 0.3,
0.3, 0.3, 0.3,
-0.3, 0.3, 0.3,
// Back face
-0.3, -0.3, -0.3,
-0.3, 0.3, -0.3,
0.3, 0.3, -0.3,
0.3, -0.3, -0.3,
// Top face
-0.3, 0.3, -0.3,
-0.3, 0.3, 0.3,
0.3, 0.3, 0.3,
0.3, 0.3, -0.3,
// Bottom face
-0.3, -0.3, -0.3,
0.3, -0.3, -0.3,
0.3, -0.3, 0.3,
-0.3, -0.3, 0.3,
// Right face
0.3, -0.3, -0.3,
0.3, 0.3, -0.3,
0.3, 0.3, 0.3,
0.3, -0.3, 0.3,
// Left face
-0.3, -0.3, -0.3,
-0.3, -0.3, 0.3,
-0.3, 0.3, 0.3,
-0.3, 0.3, -0.3,
];
oJSWebGL.JS_LoadArrayBuffer("cube", new Float32Array(jsCubeData), webgl.STATIC_DRAW, 3, webgl.FLOAT, false);//加载之后,可以通过oJSWebGL.jsBuffers.cube进行访问
var jsCubeIndex = [
0, 1, 2, 0, 2, 3, // Front face
4, 5, 6, 4, 6, 7, // Back face
8, 9, 10, 8, 10, 11, // Top face
12, 13, 14, 12, 14, 15, // Bottom face
16, 17, 18, 16, 18, 19, // Right face
20, 21, 22, 20, 22, 23 // Left face
];
oJSWebGL.JS_LoadElementArrayBuffer("cubeIndex", new Uint8Array(jsCubeIndex), webgl.STATIC_DRAW, 3, webgl.UNSIGNED_BYTE, false);//加载之后,可以通过oJSWebGL.jsBuffers.cubeIndex进行访问
var jsCubeNormals = [
// Front face
0.0, 0.0, 1.0,
0.0, 0.0, 1.0,
0.0, 0.0, 1.0,
0.0, 0.0, 1.0,
// Back face
0.0, 0.0, -1.0,
0.0, 0.0, -1.0,
0.0, 0.0, -1.0,
0.0, 0.0, -1.0,
// Top face
0.0, 1.0, 0.0,
0.0, 1.0, 0.0,
0.0, 1.0, 0.0,
0.0, 1.0, 0.0,
// Bottom face
0.0, -1.0, 0.0,
0.0, -1.0, 0.0,
0.0, -1.0, 0.0,
0.0, -1.0, 0.0,
// Right face
1.0, 0.0, 0.0,
1.0, 0.0, 0.0,
1.0, 0.0, 0.0,
1.0, 0.0, 0.0,
// Left face
-1.0, 0.0, 0.0,
-1.0, 0.0, 0.0,
-1.0, 0.0, 0.0,
-1.0, 0.0, 0.0,
];
oJSWebGL.JS_LoadArrayBuffer("cubeNormals", new Float32Array(jsCubeNormals), webgl.STATIC_DRAW, 3, webgl.FLOAT, false);//加载之后,可以通过oJSWebGL.jsBuffers.cubeNormals进行访问
textureObject = webgl.createTexture();
webgl.bindTexture(webgl.TEXTURE_CUBE_MAP, textureObject);
webgl.texImage2D(webgl.TEXTURE_CUBE_MAP_POSITIVE_X, 0, webgl.RGB, webgl.RGB, webgl.UNSIGNED_BYTE, document.getElementById('myTexture1'));
webgl.texImage2D(webgl.TEXTURE_CUBE_MAP_NEGATIVE_X, 0, webgl.RGB, webgl.RGB, webgl.UNSIGNED_BYTE, document.getElementById('myTexture2'));
webgl.texImage2D(webgl.TEXTURE_CUBE_MAP_POSITIVE_Y, 0, webgl.RGB, webgl.RGB, webgl.UNSIGNED_BYTE, document.getElementById('myTexture3'));
webgl.texImage2D(webgl.TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, webgl.RGB, webgl.RGB, webgl.UNSIGNED_BYTE, document.getElementById('myTexture4'));
webgl.texImage2D(webgl.TEXTURE_CUBE_MAP_POSITIVE_Z, 0, webgl.RGB, webgl.RGB, webgl.UNSIGNED_BYTE, document.getElementById('myTexture5'));
webgl.texImage2D(webgl.TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, webgl.RGB, webgl.RGB, webgl.UNSIGNED_BYTE, document.getElementById('myTexture6'));
var jsShadowPlaneData = [-1.0, -1.0, 1.0, -1.0,-0.25,-1.0, 1.0,-0.25,-1.0, 1.0, -1.0, 1.0];
oJSWebGL.JS_LoadArrayBuffer("shadowPlane", new Float32Array(jsShadowPlaneData), webgl.STATIC_DRAW, 3, webgl.FLOAT, false);
shadowMat4 = mat4.create();
gltMakeShadowMatrix([[-1, -1, 1], [1, -1, 1], [0, -0.25, -1]], [-1, 1, 0, 1], shadowMat4);//预先计算好阴影矩阵,这样就不用重复计算
var lookAtMat4 = mat4.create();
mat4.lookAt([0, 0, 1], [0,0,0], [0,1,0], lookAtMat4);
perspectiveMat4 = mat4.create();
var wh = oJSWebGL.Viewport();
mat4.perspective(60, wh[0]/wh[1], 0.1, 2, perspectiveMat4);
mat4.multiply(perspectiveMat4, lookAtMat4, perspectiveMat4);
var jsConeData = [0, 0.2, 0];//圆锥的顶部顶点为(0, 0.2, 0)
for(var i=0; i<13; ++i)//计算圆锥顶部圆面上的12个点;圆的半径为0.1
{
//保证各个顶点为顺时针顺序
var radians = -i*30*Math.PI/180;
jsConeData[jsConeData.length] = parseInt(0.1 * Math.cos(radians)*1000)/1000;
jsConeData[jsConeData.length] = 0;
jsConeData[jsConeData.length] = -parseInt(0.1 * Math.sin(radians)*1000)/1000;
}
oJSWebGL.JS_LoadArrayBuffer("cone", new Float32Array(jsConeData), webgl.STATIC_DRAW, 3, webgl.FLOAT, false);
jsConeMat4 = new CJSMat4();
jsConeMat4.Identity();
jsConeMat4.RotateX(Math.PI/2);
jsConeMat4.Translate([0, 0, 0.3]);
rotateMat4 = mat4.create();
mat4.identity(rotateMat4);
return 0;
}
function RenderScene()
{
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.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);
//只需要透视投影
webgl.uniformMatrix4fv(oJSWebGLProgram.u_m4ModelView, false, perspectiveMat4);
webgl.drawArrays(webgl.TRIANGLE_FAN, 0, 4);
var jsMatrix = new CJSMat4();
//画立方体
//先旋转,再透视投影
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);
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);
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);
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);
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);
}
//这些事件代码仍然只适合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 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", true);
if(oJSWebGLProgram == null) throw oJSWebGL.error;//如果oJSWebGLProgram生成成功,则各个活动属性和活动uniform的位置/索引都将自动保存到oJSWebGLProgram对象下的同名变量
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()'>
<canvas id="myCanvas" style="border:1px solid red;" width='600px' height='450px'></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>
<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 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>
var textureObject = null;
var shadowMat4 = null;//预先计算好的阴影矩阵
var mouseDown = false;
var mousePosition = [0, 0];
var perspectiveMat4 = null;//预先计算好的投影矩阵
var rotateMat4 = null;
var rotatePosV3 = [0, 0, 0];;//记录离圆心的偏移,2D,弄成vec3只是为了方便执行加减操作
var jsConeMat4 = null;
function LoadData()
{
var jsCubeData = [
// Front face
-0.3, -0.3, 0.3,
0.3, -0.3, 0.3,
0.3, 0.3, 0.3,
-0.3, 0.3, 0.3,
// Back face
-0.3, -0.3, -0.3,
-0.3, 0.3, -0.3,
0.3, 0.3, -0.3,
0.3, -0.3, -0.3,
// Top face
-0.3, 0.3, -0.3,
-0.3, 0.3, 0.3,
0.3, 0.3, 0.3,
0.3, 0.3, -0.3,
// Bottom face
-0.3, -0.3, -0.3,
0.3, -0.3, -0.3,
0.3, -0.3, 0.3,
-0.3, -0.3, 0.3,
// Right face
0.3, -0.3, -0.3,
0.3, 0.3, -0.3,
0.3, 0.3, 0.3,
0.3, -0.3, 0.3,
// Left face
-0.3, -0.3, -0.3,
-0.3, -0.3, 0.3,
-0.3, 0.3, 0.3,
-0.3, 0.3, -0.3,
];
oJSWebGL.JS_LoadArrayBuffer("cube", new Float32Array(jsCubeData), webgl.STATIC_DRAW, 3, webgl.FLOAT, false);//加载之后,可以通过oJSWebGL.jsBuffers.cube进行访问
var jsCubeIndex = [
0, 1, 2, 0, 2, 3, // Front face
4, 5, 6, 4, 6, 7, // Back face
8, 9, 10, 8, 10, 11, // Top face
12, 13, 14, 12, 14, 15, // Bottom face
16, 17, 18, 16, 18, 19, // Right face
20, 21, 22, 20, 22, 23 // Left face
];
oJSWebGL.JS_LoadElementArrayBuffer("cubeIndex", new Uint8Array(jsCubeIndex), webgl.STATIC_DRAW, 3, webgl.UNSIGNED_BYTE, false);//加载之后,可以通过oJSWebGL.jsBuffers.cubeIndex进行访问
var jsCubeNormals = [
// Front face
0.0, 0.0, 1.0,
0.0, 0.0, 1.0,
0.0, 0.0, 1.0,
0.0, 0.0, 1.0,
// Back face
0.0, 0.0, -1.0,
0.0, 0.0, -1.0,
0.0, 0.0, -1.0,
0.0, 0.0, -1.0,
// Top face
0.0, 1.0, 0.0,
0.0, 1.0, 0.0,
0.0, 1.0, 0.0,
0.0, 1.0, 0.0,
// Bottom face
0.0, -1.0, 0.0,
0.0, -1.0, 0.0,
0.0, -1.0, 0.0,
0.0, -1.0, 0.0,
// Right face
1.0, 0.0, 0.0,
1.0, 0.0, 0.0,
1.0, 0.0, 0.0,
1.0, 0.0, 0.0,
// Left face
-1.0, 0.0, 0.0,
-1.0, 0.0, 0.0,
-1.0, 0.0, 0.0,
-1.0, 0.0, 0.0,
];
oJSWebGL.JS_LoadArrayBuffer("cubeNormals", new Float32Array(jsCubeNormals), webgl.STATIC_DRAW, 3, webgl.FLOAT, false);//加载之后,可以通过oJSWebGL.jsBuffers.cubeNormals进行访问
textureObject = webgl.createTexture();
webgl.bindTexture(webgl.TEXTURE_CUBE_MAP, textureObject);
webgl.texImage2D(webgl.TEXTURE_CUBE_MAP_POSITIVE_X, 0, webgl.RGB, webgl.RGB, webgl.UNSIGNED_BYTE, document.getElementById('myTexture1'));
webgl.texImage2D(webgl.TEXTURE_CUBE_MAP_NEGATIVE_X, 0, webgl.RGB, webgl.RGB, webgl.UNSIGNED_BYTE, document.getElementById('myTexture2'));
webgl.texImage2D(webgl.TEXTURE_CUBE_MAP_POSITIVE_Y, 0, webgl.RGB, webgl.RGB, webgl.UNSIGNED_BYTE, document.getElementById('myTexture3'));
webgl.texImage2D(webgl.TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, webgl.RGB, webgl.RGB, webgl.UNSIGNED_BYTE, document.getElementById('myTexture4'));
webgl.texImage2D(webgl.TEXTURE_CUBE_MAP_POSITIVE_Z, 0, webgl.RGB, webgl.RGB, webgl.UNSIGNED_BYTE, document.getElementById('myTexture5'));
webgl.texImage2D(webgl.TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, webgl.RGB, webgl.RGB, webgl.UNSIGNED_BYTE, document.getElementById('myTexture6'));
var jsShadowPlaneData = [-1.0, -1.0, 1.0, -1.0,-0.25,-1.0, 1.0,-0.25,-1.0, 1.0, -1.0, 1.0];
oJSWebGL.JS_LoadArrayBuffer("shadowPlane", new Float32Array(jsShadowPlaneData), webgl.STATIC_DRAW, 3, webgl.FLOAT, false);
shadowMat4 = mat4.create();
gltMakeShadowMatrix([[-1, -1, 1], [1, -1, 1], [0, -0.25, -1]], [-1, 1, 0, 1], shadowMat4);//预先计算好阴影矩阵,这样就不用重复计算
var lookAtMat4 = mat4.create();
mat4.lookAt([0, 0, 1], [0,0,0], [0,1,0], lookAtMat4);
perspectiveMat4 = mat4.create();
var wh = oJSWebGL.Viewport();
mat4.perspective(60, wh[0]/wh[1], 0.1, 2, perspectiveMat4);
mat4.multiply(perspectiveMat4, lookAtMat4, perspectiveMat4);
var jsConeData = [0, 0.2, 0];//圆锥的顶部顶点为(0, 0.2, 0)
for(var i=0; i<13; ++i)//计算圆锥顶部圆面上的12个点;圆的半径为0.1
{
//保证各个顶点为顺时针顺序
var radians = -i*30*Math.PI/180;
jsConeData[jsConeData.length] = parseInt(0.1 * Math.cos(radians)*1000)/1000;
jsConeData[jsConeData.length] = 0;
jsConeData[jsConeData.length] = -parseInt(0.1 * Math.sin(radians)*1000)/1000;
}
oJSWebGL.JS_LoadArrayBuffer("cone", new Float32Array(jsConeData), webgl.STATIC_DRAW, 3, webgl.FLOAT, false);
jsConeMat4 = new CJSMat4();
jsConeMat4.Identity();
jsConeMat4.RotateX(Math.PI/2);
jsConeMat4.Translate([0, 0, 0.3]);
rotateMat4 = mat4.create();
mat4.identity(rotateMat4);
return 0;
}
function RenderScene()
{
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.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);
//只需要透视投影
webgl.uniformMatrix4fv(oJSWebGLProgram.u_m4ModelView, false, perspectiveMat4);
webgl.drawArrays(webgl.TRIANGLE_FAN, 0, 4);
var jsMatrix = new CJSMat4();
//画立方体
//先旋转,再透视投影
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);
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);
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);
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);
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);
}
//这些事件代码仍然只适合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 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", true);
if(oJSWebGLProgram == null) throw oJSWebGL.error;//如果oJSWebGLProgram生成成功,则各个活动属性和活动uniform的位置/索引都将自动保存到oJSWebGLProgram对象下的同名变量
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()'>
<canvas id="myCanvas" style="border:1px solid red;" width='600px' height='450px'></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>
运行效果如下: