├── 1 ├── HelloPoint1.html └── HelloPoint1.js ├── 2 ├── HelloPoint1.html └── HelloPoint1.js ├── 3 ├── HelloTriangle.html └── HelloTriangle.js ├── 4 ├── ColoredTriangle.html └── ColoredTriangle.js ├── 6 ├── Triangle_MVPMatrix.html └── Triangle_MVPMatrix.js ├── 7 ├── Cuboid.html └── Cuboid.js ├── 8 ├── Cuboid_UI.html └── Cuboid_UI.js ├── 9 ├── TerrainViewer.html └── TerrainViewer.js ├── 10 ├── TerrainViewer.html └── TerrainViewer.js ├── 11 ├── TerrainViewer.html ├── TerrainViewer.js └── tex.jpg ├── 12 ├── TerrainViewer.html └── TerrainViewer.js ├── 13 ├── TerrainViewer.html └── TerrainViewer.js ├── 14 ├── TerrainViewer.html └── TerrainViewer.js ├── 15 ├── TerrainViewer.html └── TerrainViewer.js ├── 1.gif ├── 2.gif ├── Data ├── DEM.dem ├── DEM.prj ├── DEM.tfw ├── DEM.tif ├── DOM.prj ├── DOM.tfw ├── DOM.tif ├── DOM.tif.ovr ├── new.bin ├── new.gltf ├── sky_cloud.jpg └── tex.jpg ├── README.md └── lib ├── cuon-matrix.js ├── cuon-utils.js ├── webgl-debug.js └── webgl-utils.js /1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fafa1899/WebGLTutorial/4431734a5550fd5a33b19d91fedda478f7454804/1.gif -------------------------------------------------------------------------------- /1/HelloPoint1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Draw a point (1) 6 | 7 | 8 | 9 | 10 | Please use a browser that supports "canvas" 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /1/HelloPoint1.js: -------------------------------------------------------------------------------- 1 | // 顶点着色器程序 2 | var VSHADER_SOURCE = 3 | 'void main() {\n' + 4 | ' gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n' + // Set the vertex coordinates of the point 5 | ' gl_PointSize = 10.0;\n' + // Set the point size 6 | '}\n'; 7 | 8 | // 片元着色器程序 9 | var FSHADER_SOURCE = 10 | 'void main() {\n' + 11 | ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' + // Set the point color 12 | '}\n'; 13 | 14 | function main() { 15 | // 获取 元素 16 | var canvas = document.getElementById('webgl'); 17 | 18 | // 获取WebGL渲染上下文 19 | var gl = getWebGLContext(canvas); 20 | if (!gl) { 21 | console.log('Failed to get the rendering context for WebGL'); 22 | return; 23 | } 24 | 25 | // 初始化着色器 26 | if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) { 27 | console.log('Failed to intialize shaders.'); 28 | return; 29 | } 30 | 31 | // 指定清空的颜色 32 | gl.clearColor(0.0, 0.0, 0.0, 1.0); 33 | 34 | // 清空 35 | gl.clear(gl.COLOR_BUFFER_BIT); 36 | 37 | // 绘制一个点 38 | gl.drawArrays(gl.POINTS, 0, 1); 39 | } 40 | -------------------------------------------------------------------------------- /10/TerrainViewer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 显示地形 7 | Hello Triangle 8 | 9 | 10 | 11 |
12 |
13 | 14 | 请使用支持WebGL的浏览器 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /10/TerrainViewer.js: -------------------------------------------------------------------------------- 1 | // 顶点着色器程序 2 | var VSHADER_SOURCE = 3 | 'attribute vec4 a_Position;\n' + //位置 4 | 'attribute vec4 a_Color;\n' + //颜色 5 | 'attribute vec4 a_Normal;\n' + //法向量 6 | 'uniform mat4 u_MvpMatrix;\n' + 7 | 'varying vec4 v_Color;\n' + 8 | 'varying vec4 v_Normal;\n' + 9 | 'void main() {\n' + 10 | ' gl_Position = u_MvpMatrix * a_Position;\n' + //设置顶点的坐标 11 | ' v_Color = a_Color;\n' + 12 | ' v_Normal = a_Normal;\n' + 13 | '}\n'; 14 | 15 | // 片元着色器程序 16 | var FSHADER_SOURCE = 17 | 'precision mediump float;\n' + 18 | 'uniform vec3 u_DiffuseLight;\n' + // 漫反射光颜色 19 | 'uniform vec3 u_LightDirection;\n' + // 漫反射光的方向 20 | 'uniform vec3 u_AmbientLight;\n' + // 环境光颜色 21 | 'varying vec4 v_Color;\n' + 22 | 'varying vec4 v_Normal;\n' + 23 | 'void main() {\n' + 24 | //对法向量归一化 25 | ' vec3 normal = normalize(v_Normal.xyz);\n' + 26 | //计算光线向量与法向量的点积 27 | ' float nDotL = max(dot(u_LightDirection, normal), 0.0);\n' + 28 | //计算漫发射光的颜色 29 | ' vec3 diffuse = u_DiffuseLight * v_Color.rgb * nDotL;\n' + 30 | //计算环境光的颜色 31 | ' vec3 ambient = u_AmbientLight * v_Color.rgb;\n' + 32 | ' gl_FragColor = vec4(diffuse+ambient, v_Color.a);\n' + 33 | '}\n'; 34 | 35 | //定义一个矩形体:混合构造函数原型模式 36 | function Cuboid(minX, maxX, minY, maxY, minZ, maxZ) { 37 | this.minX = minX; 38 | this.maxX = maxX; 39 | this.minY = minY; 40 | this.maxY = maxY; 41 | this.minZ = minZ; 42 | this.maxZ = maxZ; 43 | } 44 | 45 | Cuboid.prototype = { 46 | constructor: Cuboid, 47 | CenterX: function () { 48 | return (this.minX + this.maxX) / 2.0; 49 | }, 50 | CenterY: function () { 51 | return (this.minY + this.maxY) / 2.0; 52 | }, 53 | CenterZ: function () { 54 | return (this.minZ + this.maxZ) / 2.0; 55 | }, 56 | LengthX: function () { 57 | return (this.maxX - this.minX); 58 | }, 59 | LengthY: function () { 60 | return (this.maxY - this.minY); 61 | } 62 | } 63 | 64 | //定义DEM 65 | function Terrain() {} 66 | Terrain.prototype = { 67 | constructor: Terrain, 68 | setWH: function (col, row) { 69 | this.col = col; 70 | this.row = row; 71 | } 72 | } 73 | 74 | var currentAngle = [0.0, 0.0]; // 绕X轴Y轴的旋转角度 ([x-axis, y-axis]) 75 | var curScale = 1.0; //当前的缩放比例 76 | 77 | function main() { 78 | var demFile = document.getElementById('demFile'); 79 | if (!demFile) { 80 | console.log("Failed to get demFile element!"); 81 | return; 82 | } 83 | 84 | demFile.addEventListener("change", function (event) { 85 | //判断浏览器是否支持FileReader接口 86 | if (typeof FileReader == 'undefined') { 87 | console.log("你的浏览器不支持FileReader接口!"); 88 | return; 89 | } 90 | 91 | var input = event.target; 92 | var reader = new FileReader(); 93 | reader.onload = function () { 94 | if (reader.result) { 95 | 96 | //读取 97 | var terrain = new Terrain(); 98 | if (!readDEMFile(reader.result, terrain)) { 99 | console.log("文件格式有误,不能读取该文件!"); 100 | } 101 | 102 | //绘制 103 | onDraw(gl, canvas, terrain); 104 | } 105 | } 106 | 107 | reader.readAsText(input.files[0]); 108 | }); 109 | 110 | // 获取 元素 111 | var canvas = document.getElementById('webgl'); 112 | 113 | // 获取WebGL渲染上下文 114 | var gl = getWebGLContext(canvas); 115 | if (!gl) { 116 | console.log('Failed to get the rendering context for WebGL'); 117 | return; 118 | } 119 | 120 | // 初始化着色器 121 | if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) { 122 | console.log('Failed to intialize shaders.'); 123 | return; 124 | } 125 | 126 | // 指定清空的颜色 127 | gl.clearColor(0.0, 0.0, 0.0, 1.0); 128 | 129 | // 开启深度测试 130 | gl.enable(gl.DEPTH_TEST); 131 | 132 | //清空颜色和深度缓冲区 133 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 134 | } 135 | 136 | //绘制函数 137 | function onDraw(gl, canvas, terrain) { 138 | // 设置顶点位置 139 | var n = initVertexBuffers(gl, terrain); 140 | if (n < 0) { 141 | console.log('Failed to set the positions of the vertices'); 142 | return; 143 | } 144 | 145 | //注册鼠标事件 146 | initEventHandlers(canvas); 147 | 148 | //设置灯光 149 | setLight(gl); 150 | 151 | //绘制函数 152 | var tick = function () { 153 | //设置MVP矩阵 154 | setMVPMatrix(gl, canvas, terrain.cuboid); 155 | 156 | //清空颜色和深度缓冲区 157 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 158 | 159 | //绘制矩形体 160 | gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_SHORT, 0); 161 | 162 | //请求浏览器调用tick 163 | requestAnimationFrame(tick); 164 | }; 165 | 166 | //开始绘制 167 | tick(); 168 | } 169 | 170 | //设置灯光 171 | function setLight(gl) { 172 | var u_AmbientLight = gl.getUniformLocation(gl.program, 'u_AmbientLight'); 173 | var u_DiffuseLight = gl.getUniformLocation(gl.program, 'u_DiffuseLight'); 174 | var u_LightDirection = gl.getUniformLocation(gl.program, 'u_LightDirection'); 175 | if (!u_DiffuseLight || !u_LightDirection || !u_AmbientLight) { 176 | console.log('Failed to get the storage location'); 177 | return; 178 | } 179 | 180 | //设置漫反射光 181 | gl.uniform3f(u_DiffuseLight, 1.0, 1.0, 1.0); 182 | 183 | // 设置光线方向(世界坐标系下的) 184 | var solarAltitude = 45.0; 185 | var solarAzimuth = 315.0; 186 | var fAltitude = solarAltitude * Math.PI / 180; //光源高度角 187 | var fAzimuth = solarAzimuth * Math.PI / 180; //光源方位角 188 | 189 | var arrayvectorX = Math.cos(fAltitude) * Math.cos(fAzimuth); 190 | var arrayvectorY = Math.cos(fAltitude) * Math.sin(fAzimuth); 191 | var arrayvectorZ = Math.sin(fAltitude); 192 | 193 | var lightDirection = new Vector3([arrayvectorX, arrayvectorY, arrayvectorZ]); 194 | lightDirection.normalize(); // Normalize 195 | gl.uniform3fv(u_LightDirection, lightDirection.elements); 196 | 197 | //设置环境光 198 | gl.uniform3f(u_AmbientLight, 0.2, 0.2, 0.2); 199 | } 200 | 201 | //读取DEM函数 202 | function readDEMFile(result, terrain) { 203 | var stringlines = result.split("\n"); 204 | if (!stringlines || stringlines.length <= 0) { 205 | return false; 206 | } 207 | 208 | //读取头信息 209 | var subline = stringlines[0].split("\t"); 210 | if (subline.length != 6) { 211 | return false; 212 | } 213 | var col = parseInt(subline[4]); //DEM宽 214 | var row = parseInt(subline[5]); //DEM高 215 | var verticeNum = col * row; 216 | if (verticeNum + 1 > stringlines.length) { 217 | return false; 218 | } 219 | terrain.setWH(col, row); 220 | 221 | //读取点信息 222 | var ci = 0; 223 | var pSize = 9; 224 | terrain.verticesColors = new Float32Array(verticeNum * pSize); 225 | for (var i = 1; i < stringlines.length; i++) { 226 | if (!stringlines[i]) { 227 | continue; 228 | } 229 | 230 | var subline = stringlines[i].split(','); 231 | if (subline.length != pSize) { 232 | continue; 233 | } 234 | 235 | for (var j = 0; j < pSize; j++) { 236 | terrain.verticesColors[ci] = parseFloat(subline[j]); 237 | ci++; 238 | } 239 | } 240 | 241 | if (ci !== verticeNum * pSize) { 242 | return false; 243 | } 244 | 245 | //包围盒 246 | var minX = terrain.verticesColors[0]; 247 | var maxX = terrain.verticesColors[0]; 248 | var minY = terrain.verticesColors[1]; 249 | var maxY = terrain.verticesColors[1]; 250 | var minZ = terrain.verticesColors[2]; 251 | var maxZ = terrain.verticesColors[2]; 252 | for (var i = 0; i < verticeNum; i++) { 253 | minX = Math.min(minX, terrain.verticesColors[i * pSize]); 254 | maxX = Math.max(maxX, terrain.verticesColors[i * pSize]); 255 | minY = Math.min(minY, terrain.verticesColors[i * pSize + 1]); 256 | maxY = Math.max(maxY, terrain.verticesColors[i * pSize + 1]); 257 | minZ = Math.min(minZ, terrain.verticesColors[i * pSize + 2]); 258 | maxZ = Math.max(maxZ, terrain.verticesColors[i * pSize + 2]); 259 | } 260 | 261 | terrain.cuboid = new Cuboid(minX, maxX, minY, maxY, minZ, maxZ); 262 | 263 | return true; 264 | } 265 | 266 | 267 | //注册鼠标事件 268 | function initEventHandlers(canvas) { 269 | var dragging = false; // Dragging or not 270 | var lastX = -1, 271 | lastY = -1; // Last position of the mouse 272 | 273 | //鼠标按下 274 | canvas.onmousedown = function (ev) { 275 | var x = ev.clientX; 276 | var y = ev.clientY; 277 | // Start dragging if a moue is in 278 | var rect = ev.target.getBoundingClientRect(); 279 | if (rect.left <= x && x < rect.right && rect.top <= y && y < rect.bottom) { 280 | lastX = x; 281 | lastY = y; 282 | dragging = true; 283 | } 284 | }; 285 | 286 | //鼠标离开时 287 | canvas.onmouseleave = function (ev) { 288 | dragging = false; 289 | }; 290 | 291 | //鼠标释放 292 | canvas.onmouseup = function (ev) { 293 | dragging = false; 294 | }; 295 | 296 | //鼠标移动 297 | canvas.onmousemove = function (ev) { 298 | var x = ev.clientX; 299 | var y = ev.clientY; 300 | if (dragging) { 301 | var factor = 100 / canvas.height; // The rotation ratio 302 | var dx = factor * (x - lastX); 303 | var dy = factor * (y - lastY); 304 | currentAngle[0] = currentAngle[0] + dy; 305 | currentAngle[1] = currentAngle[1] + dx; 306 | } 307 | lastX = x, lastY = y; 308 | }; 309 | 310 | //鼠标缩放 311 | canvas.onmousewheel = function (event) { 312 | if (event.wheelDelta > 0) { 313 | curScale = curScale * 1.1; 314 | } else { 315 | curScale = curScale * 0.9; 316 | } 317 | }; 318 | } 319 | 320 | //设置MVP矩阵 321 | function setMVPMatrix(gl, canvas, cuboid) { 322 | // Get the storage location of u_MvpMatrix 323 | var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix'); 324 | if (!u_MvpMatrix) { 325 | console.log('Failed to get the storage location of u_MvpMatrix'); 326 | return; 327 | } 328 | 329 | //模型矩阵 330 | var modelMatrix = new Matrix4(); 331 | modelMatrix.scale(curScale, curScale, curScale); 332 | modelMatrix.rotate(currentAngle[0], 1.0, 0.0, 0.0); // Rotation around x-axis 333 | modelMatrix.rotate(currentAngle[1], 0.0, 1.0, 0.0); // Rotation around y-axis 334 | modelMatrix.translate(-cuboid.CenterX(), -cuboid.CenterY(), -cuboid.CenterZ()); 335 | 336 | //投影矩阵 337 | var fovy = 60; 338 | var near = 1; 339 | var projMatrix = new Matrix4(); 340 | projMatrix.setPerspective(fovy, canvas.width / canvas.height, 1, 10000); 341 | 342 | //计算lookAt()函数初始视点的高度 343 | var angle = fovy / 2 * Math.PI / 180.0; 344 | var eyeHight = (cuboid.LengthY() * 1.2) / 2.0 / angle; 345 | 346 | //视图矩阵 347 | var viewMatrix = new Matrix4(); // View matrix 348 | viewMatrix.lookAt(0, 0, eyeHight, 0, 0, 0, 0, 1, 0); 349 | 350 | //MVP矩阵 351 | var mvpMatrix = new Matrix4(); 352 | mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatrix); 353 | 354 | //将MVP矩阵传输到着色器的uniform变量u_MvpMatrix 355 | gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements); 356 | } 357 | 358 | // 359 | function initVertexBuffers(gl, terrain) { 360 | //DEM的一个网格是由两个三角形组成的 361 | // 0------1 1 362 | // | | 363 | // | | 364 | // col col------col+1 365 | var col = terrain.col; 366 | var row = terrain.row; 367 | 368 | var indices = new Uint16Array((row - 1) * (col - 1) * 6); 369 | var ci = 0; 370 | for (var yi = 0; yi < row - 1; yi++) { 371 | //for (var yi = 0; yi < 10; yi++) { 372 | for (var xi = 0; xi < col - 1; xi++) { 373 | indices[ci * 6] = yi * col + xi; 374 | indices[ci * 6 + 1] = (yi + 1) * col + xi; 375 | indices[ci * 6 + 2] = yi * col + xi + 1; 376 | indices[ci * 6 + 3] = (yi + 1) * col + xi; 377 | indices[ci * 6 + 4] = (yi + 1) * col + xi + 1; 378 | indices[ci * 6 + 5] = yi * col + xi + 1; 379 | ci++; 380 | } 381 | } 382 | 383 | // 384 | var verticesColors = terrain.verticesColors; 385 | var FSIZE = verticesColors.BYTES_PER_ELEMENT; //数组中每个元素的字节数 386 | 387 | // 创建缓冲区对象 388 | var vertexColorBuffer = gl.createBuffer(); 389 | var indexBuffer = gl.createBuffer(); 390 | if (!vertexColorBuffer || !indexBuffer) { 391 | console.log('Failed to create the buffer object'); 392 | return -1; 393 | } 394 | 395 | // 将缓冲区对象绑定到目标 396 | gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer); 397 | // 向缓冲区对象写入数据 398 | gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW); 399 | 400 | //获取着色器中attribute变量a_Position的地址 401 | var a_Position = gl.getAttribLocation(gl.program, 'a_Position'); 402 | if (a_Position < 0) { 403 | console.log('Failed to get the storage location of a_Position'); 404 | return -1; 405 | } 406 | // 将缓冲区对象分配给a_Position变量 407 | gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE * 9, 0); 408 | 409 | // 连接a_Position变量与分配给它的缓冲区对象 410 | gl.enableVertexAttribArray(a_Position); 411 | 412 | //获取着色器中attribute变量a_Color的地址 413 | var a_Color = gl.getAttribLocation(gl.program, 'a_Color'); 414 | if (a_Color < 0) { 415 | console.log('Failed to get the storage location of a_Color'); 416 | return -1; 417 | } 418 | // 将缓冲区对象分配给a_Color变量 419 | gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 9, FSIZE * 3); 420 | // 连接a_Color变量与分配给它的缓冲区对象 421 | gl.enableVertexAttribArray(a_Color); 422 | 423 | // 向缓冲区对象分配a_Normal变量,传入的这个变量要在着色器使用才行 424 | var a_Normal = gl.getAttribLocation(gl.program, 'a_Normal'); 425 | if (a_Normal < 0) { 426 | console.log('Failed to get the storage location of a_Normal'); 427 | return -1; 428 | } 429 | gl.vertexAttribPointer(a_Normal, 3, gl.FLOAT, false, FSIZE * 9, FSIZE * 6); 430 | //开启a_Normal变量 431 | gl.enableVertexAttribArray(a_Normal); 432 | 433 | // 将顶点索引写入到缓冲区对象 434 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); 435 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); 436 | 437 | return indices.length; 438 | } -------------------------------------------------------------------------------- /11/TerrainViewer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 显示地形 7 | Hello Triangle 8 | 9 | 10 | 11 |
12 |
13 | 14 | 请使用支持WebGL的浏览器 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /11/TerrainViewer.js: -------------------------------------------------------------------------------- 1 | // 顶点着色器程序 2 | var VSHADER_SOURCE = 3 | 'attribute vec4 a_Position;\n' + //位置 4 | 'attribute vec4 a_Color;\n' + //颜色 5 | 'uniform mat4 u_MvpMatrix;\n' + 6 | 'varying vec4 v_Color;\n' + 7 | 'varying vec4 v_position;\n' + 8 | 'void main() {\n' + 9 | ' v_position = a_Position;\n' + 10 | ' gl_Position = u_MvpMatrix * a_Position;\n' + // 设置顶点坐标 11 | ' v_Color = a_Color;\n' + 12 | '}\n'; 13 | 14 | // 片元着色器程序 15 | var FSHADER_SOURCE = 16 | 'precision mediump float;\n' + 17 | 'uniform vec2 u_RangeX;\n' + //X方向范围 18 | 'uniform vec2 u_RangeY;\n' + //Y方向范围 19 | 'uniform sampler2D u_Sampler;\n' + 20 | 'varying vec4 v_Color;\n' + 21 | 'varying vec4 v_position;\n' + 22 | 'void main() {\n' + 23 | ' vec2 v_TexCoord = vec2((v_position.x-u_RangeX[0]) / (u_RangeX[1]-u_RangeX[0]), 1.0-(v_position.y-u_RangeY[0]) / (u_RangeY[1]-u_RangeY[0]));\n' + 24 | ' gl_FragColor = texture2D(u_Sampler, v_TexCoord);\n' + 25 | '}\n'; 26 | 27 | //定义一个矩形体:混合构造函数原型模式 28 | function Cuboid(minX, maxX, minY, maxY, minZ, maxZ) { 29 | this.minX = minX; 30 | this.maxX = maxX; 31 | this.minY = minY; 32 | this.maxY = maxY; 33 | this.minZ = minZ; 34 | this.maxZ = maxZ; 35 | } 36 | 37 | Cuboid.prototype = { 38 | constructor: Cuboid, 39 | CenterX: function () { 40 | return (this.minX + this.maxX) / 2.0; 41 | }, 42 | CenterY: function () { 43 | return (this.minY + this.maxY) / 2.0; 44 | }, 45 | CenterZ: function () { 46 | return (this.minZ + this.maxZ) / 2.0; 47 | }, 48 | LengthX: function () { 49 | return (this.maxX - this.minX); 50 | }, 51 | LengthY: function () { 52 | return (this.maxY - this.minY); 53 | } 54 | } 55 | 56 | //定义DEM 57 | function Terrain() { } 58 | Terrain.prototype = { 59 | constructor: Terrain, 60 | setWH: function (col, row) { 61 | this.col = col; 62 | this.row = row; 63 | } 64 | } 65 | 66 | var currentAngle = [0.0, 0.0]; // 绕X轴Y轴的旋转角度 ([x-axis, y-axis]) 67 | var curScale = 1.0; //当前的缩放比例 68 | var initTexSuccess = false; //纹理图像是否加载完成 69 | 70 | function main() { 71 | var demFile = document.getElementById('demFile'); 72 | if (!demFile) { 73 | console.log("Failed to get demFile element!"); 74 | return; 75 | } 76 | 77 | //加载文件后的事件 78 | demFile.addEventListener("change", function (event) { 79 | //判断浏览器是否支持FileReader接口 80 | if (typeof FileReader == 'undefined') { 81 | console.log("你的浏览器不支持FileReader接口!"); 82 | return; 83 | } 84 | 85 | //读取文件后的事件 86 | var reader = new FileReader(); 87 | reader.onload = function () { 88 | if (reader.result) { 89 | var terrain = new Terrain(); 90 | if (!readDEMFile(reader.result, terrain)) { 91 | console.log("文件格式有误,不能读取该文件!"); 92 | } 93 | 94 | //绘制函数 95 | onDraw(gl, canvas, terrain); 96 | } 97 | } 98 | 99 | var input = event.target; 100 | reader.readAsText(input.files[0]); 101 | }); 102 | 103 | // 获取 元素 104 | var canvas = document.getElementById('webgl'); 105 | 106 | // 获取WebGL渲染上下文 107 | var gl = getWebGLContext(canvas); 108 | if (!gl) { 109 | console.log('Failed to get the rendering context for WebGL'); 110 | return; 111 | } 112 | 113 | // 初始化着色器 114 | if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) { 115 | console.log('Failed to intialize shaders.'); 116 | return; 117 | } 118 | 119 | // 指定清空的颜色 120 | gl.clearColor(0.0, 0.0, 0.0, 1.0); 121 | 122 | // 开启深度测试 123 | gl.enable(gl.DEPTH_TEST); 124 | 125 | //清空颜色和深度缓冲区 126 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 127 | } 128 | 129 | //绘制函数 130 | function onDraw(gl, canvas, terrain) { 131 | // 设置顶点位置 132 | //var cuboid = new Cuboid(399589.072, 400469.072, 3995118.062, 3997558.062, 732, 1268); 133 | var n = initVertexBuffers(gl, terrain); 134 | if (n < 0) { 135 | console.log('Failed to set the positions of the vertices'); 136 | return; 137 | } 138 | 139 | //设置纹理 140 | if (!initTextures(gl, terrain)) { 141 | console.log('Failed to intialize the texture.'); 142 | return; 143 | } 144 | 145 | //注册鼠标事件 146 | initEventHandlers(canvas); 147 | 148 | //绘制函数 149 | var tick = function () { 150 | if (initTexSuccess) { 151 | //设置MVP矩阵 152 | setMVPMatrix(gl, canvas, terrain.cuboid); 153 | 154 | //清空颜色和深度缓冲区 155 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 156 | 157 | //绘制矩形体 158 | gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_SHORT, 0); 159 | //gl.drawArrays(gl.Points, 0, n); 160 | } 161 | 162 | //请求浏览器调用tick 163 | requestAnimationFrame(tick); 164 | }; 165 | 166 | //开始绘制 167 | tick(); 168 | } 169 | 170 | function initTextures(gl, terrain) { 171 | // 传递X方向和Y方向上的范围到着色器 172 | var u_RangeX = gl.getUniformLocation(gl.program, 'u_RangeX'); 173 | var u_RangeY = gl.getUniformLocation(gl.program, 'u_RangeY'); 174 | if (!u_RangeX || !u_RangeY) { 175 | console.log('Failed to get the storage location of u_RangeX or u_RangeY'); 176 | return; 177 | } 178 | gl.uniform2f(u_RangeX, terrain.cuboid.minX, terrain.cuboid.maxX); 179 | gl.uniform2f(u_RangeY, terrain.cuboid.minY, terrain.cuboid.maxY); 180 | 181 | //创建一个image对象 182 | var image = new Image(); 183 | if (!image) { 184 | console.log('Failed to create the image object'); 185 | return false; 186 | } 187 | //图像加载的响应函数 188 | image.onload = function () { 189 | if (loadTexture(gl, image)) { 190 | initTexSuccess = true; 191 | } 192 | }; 193 | 194 | //浏览器开始加载图像 195 | image.src = 'tex.jpg'; 196 | 197 | return true; 198 | } 199 | 200 | function loadTexture(gl, image) { 201 | // 创建纹理对象 202 | var texture = gl.createTexture(); 203 | if (!texture) { 204 | console.log('Failed to create the texture object'); 205 | return false; 206 | } 207 | 208 | // 开启0号纹理单元 209 | gl.activeTexture(gl.TEXTURE0); 210 | // 绑定纹理对象 211 | gl.bindTexture(gl.TEXTURE_2D, texture); 212 | 213 | // 设置纹理参数 214 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 215 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); 216 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 217 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 218 | 219 | // 配置纹理图像 220 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image); 221 | 222 | // 将0号单元纹理传递给着色器中的取样器变量 223 | var u_Sampler = gl.getUniformLocation(gl.program, 'u_Sampler'); 224 | if (!u_Sampler) { 225 | console.log('Failed to get the storage location of u_Sampler'); 226 | return false; 227 | } 228 | gl.uniform1i(u_Sampler, 0); 229 | 230 | return true; 231 | } 232 | 233 | //读取DEM函数 234 | function readDEMFile(result, terrain) { 235 | var stringlines = result.split("\n"); 236 | if (!stringlines || stringlines.length <= 0) { 237 | return false; 238 | } 239 | 240 | //读取头信息 241 | var subline = stringlines[0].split("\t"); 242 | if (subline.length != 6) { 243 | return false; 244 | } 245 | var col = parseInt(subline[4]); //DEM宽 246 | var row = parseInt(subline[5]); //DEM高 247 | var verticeNum = col * row; 248 | if (verticeNum + 1 > stringlines.length) { 249 | return false; 250 | } 251 | terrain.setWH(col, row); 252 | 253 | //读取点信息 254 | var ci = 0; 255 | terrain.verticesColors = new Float32Array(verticeNum * 6); 256 | for (var i = 1; i < stringlines.length; i++) { 257 | if (!stringlines[i]) { 258 | continue; 259 | } 260 | 261 | var subline = stringlines[i].split(','); 262 | if (subline.length != 9) { 263 | continue; 264 | } 265 | 266 | for (var j = 0; j < 6; j++) { 267 | terrain.verticesColors[ci] = parseFloat(subline[j]); 268 | ci++; 269 | } 270 | } 271 | 272 | if (ci !== verticeNum * 6) { 273 | return false; 274 | } 275 | 276 | //包围盒 277 | var minX = terrain.verticesColors[0]; 278 | var maxX = terrain.verticesColors[0]; 279 | var minY = terrain.verticesColors[1]; 280 | var maxY = terrain.verticesColors[1]; 281 | var minZ = terrain.verticesColors[2]; 282 | var maxZ = terrain.verticesColors[2]; 283 | for (var i = 0; i < verticeNum; i++) { 284 | minX = Math.min(minX, terrain.verticesColors[i * 6]); 285 | maxX = Math.max(maxX, terrain.verticesColors[i * 6]); 286 | minY = Math.min(minY, terrain.verticesColors[i * 6 + 1]); 287 | maxY = Math.max(maxY, terrain.verticesColors[i * 6 + 1]); 288 | minZ = Math.min(minZ, terrain.verticesColors[i * 6 + 2]); 289 | maxZ = Math.max(maxZ, terrain.verticesColors[i * 6 + 2]); 290 | } 291 | 292 | terrain.cuboid = new Cuboid(minX, maxX, minY, maxY, minZ, maxZ); 293 | 294 | return true; 295 | } 296 | 297 | 298 | //注册鼠标事件 299 | function initEventHandlers(canvas) { 300 | var dragging = false; // Dragging or not 301 | var lastX = -1, 302 | lastY = -1; // Last position of the mouse 303 | 304 | //鼠标按下 305 | canvas.onmousedown = function (ev) { 306 | var x = ev.clientX; 307 | var y = ev.clientY; 308 | // Start dragging if a moue is in 309 | var rect = ev.target.getBoundingClientRect(); 310 | if (rect.left <= x && x < rect.right && rect.top <= y && y < rect.bottom) { 311 | lastX = x; 312 | lastY = y; 313 | dragging = true; 314 | } 315 | }; 316 | 317 | //鼠标离开时 318 | canvas.onmouseleave = function (ev) { 319 | dragging = false; 320 | }; 321 | 322 | //鼠标释放 323 | canvas.onmouseup = function (ev) { 324 | dragging = false; 325 | }; 326 | 327 | //鼠标移动 328 | canvas.onmousemove = function (ev) { 329 | var x = ev.clientX; 330 | var y = ev.clientY; 331 | if (dragging) { 332 | var factor = 100 / canvas.height; // The rotation ratio 333 | var dx = factor * (x - lastX); 334 | var dy = factor * (y - lastY); 335 | currentAngle[0] = currentAngle[0] + dy; 336 | currentAngle[1] = currentAngle[1] + dx; 337 | } 338 | lastX = x, lastY = y; 339 | }; 340 | 341 | //鼠标缩放 342 | canvas.onmousewheel = function (event) { 343 | if (event.wheelDelta > 0) { 344 | curScale = curScale * 1.1; 345 | } else { 346 | curScale = curScale * 0.9; 347 | } 348 | }; 349 | } 350 | 351 | //设置MVP矩阵 352 | function setMVPMatrix(gl, canvas, cuboid) { 353 | // Get the storage location of u_MvpMatrix 354 | var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix'); 355 | if (!u_MvpMatrix) { 356 | console.log('Failed to get the storage location of u_MvpMatrix'); 357 | return; 358 | } 359 | 360 | //模型矩阵 361 | var modelMatrix = new Matrix4(); 362 | modelMatrix.scale(curScale, curScale, curScale); 363 | modelMatrix.rotate(currentAngle[0], 1.0, 0.0, 0.0); // Rotation around x-axis 364 | modelMatrix.rotate(currentAngle[1], 0.0, 1.0, 0.0); // Rotation around y-axis 365 | modelMatrix.translate(-cuboid.CenterX(), -cuboid.CenterY(), -cuboid.CenterZ()); 366 | 367 | //投影矩阵 368 | var fovy = 60; 369 | var near = 1; 370 | var projMatrix = new Matrix4(); 371 | projMatrix.setPerspective(fovy, canvas.width / canvas.height, 1, 10000); 372 | 373 | //计算lookAt()函数初始视点的高度 374 | var angle = fovy / 2 * Math.PI / 180.0; 375 | var eyeHight = (cuboid.LengthY() * 1.2) / 2.0 / angle; 376 | 377 | //视图矩阵 378 | var viewMatrix = new Matrix4(); // View matrix 379 | viewMatrix.lookAt(0, 0, eyeHight, 0, 0, 0, 0, 1, 0); 380 | 381 | //MVP矩阵 382 | var mvpMatrix = new Matrix4(); 383 | mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatrix); 384 | 385 | //将MVP矩阵传输到着色器的uniform变量u_MvpMatrix 386 | gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements); 387 | } 388 | 389 | // 390 | function initVertexBuffers(gl, terrain) { 391 | //DEM的一个网格是由两个三角形组成的 392 | // 0------1 1 393 | // | | 394 | // | | 395 | // col col------col+1 396 | var col = terrain.col; 397 | var row = terrain.row; 398 | 399 | var indices = new Uint16Array((row - 1) * (col - 1) * 6); 400 | var ci = 0; 401 | for (var yi = 0; yi < row - 1; yi++) { 402 | //for (var yi = 0; yi < 10; yi++) { 403 | for (var xi = 0; xi < col - 1; xi++) { 404 | indices[ci * 6] = yi * col + xi; 405 | indices[ci * 6 + 1] = (yi + 1) * col + xi; 406 | indices[ci * 6 + 2] = yi * col + xi + 1; 407 | indices[ci * 6 + 3] = (yi + 1) * col + xi; 408 | indices[ci * 6 + 4] = (yi + 1) * col + xi + 1; 409 | indices[ci * 6 + 5] = yi * col + xi + 1; 410 | ci++; 411 | } 412 | } 413 | 414 | // 415 | var verticesColors = terrain.verticesColors; 416 | var FSIZE = verticesColors.BYTES_PER_ELEMENT; //数组中每个元素的字节数 417 | 418 | // 创建缓冲区对象 419 | var vertexColorBuffer = gl.createBuffer(); 420 | var indexBuffer = gl.createBuffer(); 421 | if (!vertexColorBuffer || !indexBuffer) { 422 | console.log('Failed to create the buffer object'); 423 | return -1; 424 | } 425 | 426 | // 将缓冲区对象绑定到目标 427 | gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer); 428 | // 向缓冲区对象写入数据 429 | gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW); 430 | 431 | //获取着色器中attribute变量a_Position的地址 432 | var a_Position = gl.getAttribLocation(gl.program, 'a_Position'); 433 | if (a_Position < 0) { 434 | console.log('Failed to get the storage location of a_Position'); 435 | return -1; 436 | } 437 | // 将缓冲区对象分配给a_Position变量 438 | gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE * 6, 0); 439 | 440 | // 连接a_Position变量与分配给它的缓冲区对象 441 | gl.enableVertexAttribArray(a_Position); 442 | 443 | //获取着色器中attribute变量a_Color的地址 444 | var a_Color = gl.getAttribLocation(gl.program, 'a_Color'); 445 | if (a_Color < 0) { 446 | console.log('Failed to get the storage location of a_Color'); 447 | return -1; 448 | } 449 | // 将缓冲区对象分配给a_Color变量 450 | gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 6, FSIZE * 3); 451 | // 连接a_Color变量与分配给它的缓冲区对象 452 | gl.enableVertexAttribArray(a_Color); 453 | 454 | // 将顶点索引写入到缓冲区对象 455 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); 456 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); 457 | 458 | return indices.length; 459 | } -------------------------------------------------------------------------------- /11/tex.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fafa1899/WebGLTutorial/4431734a5550fd5a33b19d91fedda478f7454804/11/tex.jpg -------------------------------------------------------------------------------- /12/TerrainViewer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 显示地形 7 | Hello Triangle 8 | 9 | 10 | 11 |
12 |
13 | 14 | 请使用支持WebGL的浏览器 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /12/TerrainViewer.js: -------------------------------------------------------------------------------- 1 | // 顶点着色器程序 2 | var VSHADER_SOURCE = 3 | 'attribute vec4 a_Position;\n' + //位置 4 | 'attribute vec4 a_Color;\n' + //颜色 5 | 'attribute vec4 a_Normal;\n' + //法向量 6 | 'uniform mat4 u_MvpMatrix;\n' + 7 | 'varying vec4 v_Color;\n' + 8 | 'varying vec4 v_Normal;\n' + 9 | 'void main() {\n' + 10 | ' gl_Position = u_MvpMatrix * a_Position;\n' + //设置顶点的坐标 11 | ' v_Color = a_Color;\n' + 12 | ' v_Normal = a_Normal;\n' + 13 | '}\n'; 14 | 15 | // 片元着色器程序 16 | var FSHADER_SOURCE = 17 | 'precision mediump float;\n' + 18 | 'uniform vec3 u_DiffuseLight;\n' + // 漫反射光颜色 19 | 'uniform vec3 u_LightDirection;\n' + // 漫反射光的方向 20 | 'uniform vec3 u_AmbientLight;\n' + // 环境光颜色 21 | 'varying vec4 v_Color;\n' + 22 | 'varying vec4 v_Normal;\n' + 23 | 'void main() {\n' + 24 | //对法向量归一化 25 | ' vec3 normal = normalize(v_Normal.xyz);\n' + 26 | //计算光线向量与法向量的点积 27 | ' float nDotL = max(dot(u_LightDirection, normal), 0.0);\n' + 28 | //计算漫发射光的颜色 29 | ' vec3 diffuse = u_DiffuseLight * v_Color.rgb * nDotL;\n' + 30 | //计算环境光的颜色 31 | ' vec3 ambient = u_AmbientLight * v_Color.rgb;\n' + 32 | ' gl_FragColor = vec4(diffuse+ambient, v_Color.a);\n' + 33 | '}\n'; 34 | 35 | //定义一个矩形体:混合构造函数原型模式 36 | function Cuboid(minX, maxX, minY, maxY, minZ, maxZ) { 37 | this.minX = minX; 38 | this.maxX = maxX; 39 | this.minY = minY; 40 | this.maxY = maxY; 41 | this.minZ = minZ; 42 | this.maxZ = maxZ; 43 | } 44 | 45 | Cuboid.prototype = { 46 | constructor: Cuboid, 47 | CenterX: function () { 48 | return (this.minX + this.maxX) / 2.0; 49 | }, 50 | CenterY: function () { 51 | return (this.minY + this.maxY) / 2.0; 52 | }, 53 | CenterZ: function () { 54 | return (this.minZ + this.maxZ) / 2.0; 55 | }, 56 | LengthX: function () { 57 | return (this.maxX - this.minX); 58 | }, 59 | LengthY: function () { 60 | return (this.maxY - this.minY); 61 | }, 62 | LengthZ: function () { 63 | return (this.maxZ - this.minZ); 64 | } 65 | } 66 | 67 | //定义一个球体 68 | function Sphere(cuboid) { 69 | this.centerX = cuboid.CenterX(); 70 | this.centerY = cuboid.CenterY(); 71 | this.centerZ = cuboid.CenterZ(); 72 | this.radius = Math.max(Math.max(cuboid.LengthX(), cuboid.LengthY()), cuboid.LengthZ()) / 2.0; 73 | } 74 | 75 | Sphere.prototype = { 76 | constructor: Sphere 77 | } 78 | 79 | //定义DEM 80 | function Terrain() { } 81 | Terrain.prototype = { 82 | constructor: Terrain, 83 | setWH: function (col, row) { 84 | this.col = col; 85 | this.row = row; 86 | } 87 | } 88 | 89 | var currentAngle = [0.0, 0.0]; // 绕X轴Y轴的旋转角度 ([x-axis, y-axis]) 90 | var curScale = 1.0; //当前的缩放比例 91 | 92 | function main() { 93 | var demFile = document.getElementById('demFile'); 94 | if (!demFile) { 95 | console.log("Failed to get demFile element!"); 96 | return; 97 | } 98 | 99 | demFile.addEventListener("change", function (event) { 100 | //判断浏览器是否支持FileReader接口 101 | if (typeof FileReader == 'undefined') { 102 | console.log("你的浏览器不支持FileReader接口!"); 103 | return; 104 | } 105 | 106 | var input = event.target; 107 | var reader = new FileReader(); 108 | reader.onload = function () { 109 | if (reader.result) { 110 | 111 | //读取 112 | var terrain = new Terrain(); 113 | if (!readDEMFile(reader.result, terrain)) { 114 | console.log("文件格式有误,不能读取该文件!"); 115 | } 116 | 117 | //绘制 118 | onDraw(gl, canvas, terrain); 119 | } 120 | } 121 | 122 | reader.readAsText(input.files[0]); 123 | }); 124 | 125 | // 获取 元素 126 | var canvas = document.getElementById('webgl'); 127 | 128 | // 获取WebGL渲染上下文 129 | var gl = getWebGLContext(canvas); 130 | if (!gl) { 131 | console.log('Failed to get the rendering context for WebGL'); 132 | return; 133 | } 134 | 135 | // 初始化着色器 136 | if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) { 137 | console.log('Failed to intialize shaders.'); 138 | return; 139 | } 140 | 141 | // 指定清空的颜色 142 | gl.clearColor(0.0, 0.0, 0.0, 1.0); 143 | 144 | // 开启深度测试 145 | gl.enable(gl.DEPTH_TEST); 146 | 147 | //清空颜色和深度缓冲区 148 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 149 | } 150 | 151 | //绘制函数 152 | function onDraw(gl, canvas, terrain) { 153 | // 设置顶点位置 154 | var n = initVertexBuffers(gl, terrain); 155 | if (n < 0) { 156 | console.log('Failed to set the positions of the vertices'); 157 | return; 158 | } 159 | 160 | //注册鼠标事件 161 | initEventHandlers(canvas); 162 | 163 | //设置灯光 164 | var lightDirection = setLight(gl); 165 | 166 | //绘制函数 167 | var tick = function () { 168 | //设置MVP矩阵 169 | setMVPMatrix(gl, canvas, terrain.sphere, lightDirection); 170 | 171 | //清空颜色和深度缓冲区 172 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 173 | 174 | //绘制矩形体 175 | gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_SHORT, 0); 176 | 177 | //请求浏览器调用tick 178 | requestAnimationFrame(tick); 179 | }; 180 | 181 | //开始绘制 182 | tick(); 183 | } 184 | 185 | //设置灯光 186 | function setLight(gl) { 187 | var u_AmbientLight = gl.getUniformLocation(gl.program, 'u_AmbientLight'); 188 | var u_DiffuseLight = gl.getUniformLocation(gl.program, 'u_DiffuseLight'); 189 | var u_LightDirection = gl.getUniformLocation(gl.program, 'u_LightDirection'); 190 | if (!u_DiffuseLight || !u_LightDirection || !u_AmbientLight) { 191 | console.log('Failed to get the storage location'); 192 | return; 193 | } 194 | 195 | //设置漫反射光 196 | gl.uniform3f(u_DiffuseLight, 1.0, 1.0, 1.0); 197 | 198 | // 设置光线方向(世界坐标系下的) 199 | var solarAltitude = 45.0; 200 | var solarAzimuth = 315.0; 201 | var fAltitude = solarAltitude * Math.PI / 180; //光源高度角 202 | var fAzimuth = solarAzimuth * Math.PI / 180; //光源方位角 203 | 204 | var arrayvectorX = Math.cos(fAltitude) * Math.cos(fAzimuth); 205 | var arrayvectorY = Math.cos(fAltitude) * Math.sin(fAzimuth); 206 | var arrayvectorZ = Math.sin(fAltitude); 207 | 208 | var lightDirection = new Vector3([arrayvectorX, arrayvectorY, arrayvectorZ]); 209 | lightDirection.normalize(); // Normalize 210 | gl.uniform3fv(u_LightDirection, lightDirection.elements); 211 | 212 | //设置环境光 213 | gl.uniform3f(u_AmbientLight, 0.2, 0.2, 0.2); 214 | 215 | return lightDirection; 216 | } 217 | 218 | //读取DEM函数 219 | function readDEMFile(result, terrain) { 220 | var stringlines = result.split("\n"); 221 | if (!stringlines || stringlines.length <= 0) { 222 | return false; 223 | } 224 | 225 | //读取头信息 226 | var subline = stringlines[0].split("\t"); 227 | if (subline.length != 6) { 228 | return false; 229 | } 230 | var col = parseInt(subline[4]); //DEM宽 231 | var row = parseInt(subline[5]); //DEM高 232 | var verticeNum = col * row; 233 | if (verticeNum + 1 > stringlines.length) { 234 | return false; 235 | } 236 | terrain.setWH(col, row); 237 | 238 | //读取点信息 239 | var ci = 0; 240 | var pSize = 9; 241 | terrain.verticesColors = new Float32Array(verticeNum * pSize); 242 | for (var i = 1; i < stringlines.length; i++) { 243 | if (!stringlines[i]) { 244 | continue; 245 | } 246 | 247 | var subline = stringlines[i].split(','); 248 | if (subline.length != pSize) { 249 | continue; 250 | } 251 | 252 | for (var j = 0; j < pSize; j++) { 253 | terrain.verticesColors[ci] = parseFloat(subline[j]); 254 | ci++; 255 | } 256 | } 257 | 258 | if (ci !== verticeNum * pSize) { 259 | return false; 260 | } 261 | 262 | //包围盒 263 | var minX = terrain.verticesColors[0]; 264 | var maxX = terrain.verticesColors[0]; 265 | var minY = terrain.verticesColors[1]; 266 | var maxY = terrain.verticesColors[1]; 267 | var minZ = terrain.verticesColors[2]; 268 | var maxZ = terrain.verticesColors[2]; 269 | for (var i = 0; i < verticeNum; i++) { 270 | minX = Math.min(minX, terrain.verticesColors[i * pSize]); 271 | maxX = Math.max(maxX, terrain.verticesColors[i * pSize]); 272 | minY = Math.min(minY, terrain.verticesColors[i * pSize + 1]); 273 | maxY = Math.max(maxY, terrain.verticesColors[i * pSize + 1]); 274 | minZ = Math.min(minZ, terrain.verticesColors[i * pSize + 2]); 275 | maxZ = Math.max(maxZ, terrain.verticesColors[i * pSize + 2]); 276 | } 277 | 278 | terrain.cuboid = new Cuboid(minX, maxX, minY, maxY, minZ, maxZ); 279 | terrain.sphere = new Sphere(terrain.cuboid); 280 | 281 | return true; 282 | } 283 | 284 | 285 | //注册鼠标事件 286 | function initEventHandlers(canvas) { 287 | var dragging = false; // Dragging or not 288 | var lastX = -1, 289 | lastY = -1; // Last position of the mouse 290 | 291 | //鼠标按下 292 | canvas.onmousedown = function (ev) { 293 | var x = ev.clientX; 294 | var y = ev.clientY; 295 | // Start dragging if a moue is in 296 | var rect = ev.target.getBoundingClientRect(); 297 | if (rect.left <= x && x < rect.right && rect.top <= y && y < rect.bottom) { 298 | lastX = x; 299 | lastY = y; 300 | dragging = true; 301 | } 302 | }; 303 | 304 | //鼠标离开时 305 | canvas.onmouseleave = function (ev) { 306 | dragging = false; 307 | }; 308 | 309 | //鼠标释放 310 | canvas.onmouseup = function (ev) { 311 | dragging = false; 312 | }; 313 | 314 | //鼠标移动 315 | canvas.onmousemove = function (ev) { 316 | var x = ev.clientX; 317 | var y = ev.clientY; 318 | if (dragging) { 319 | var factor = 100 / canvas.height; // The rotation ratio 320 | var dx = factor * (x - lastX); 321 | var dy = factor * (y - lastY); 322 | currentAngle[0] = currentAngle[0] + dy; 323 | currentAngle[1] = currentAngle[1] + dx; 324 | } 325 | lastX = x, lastY = y; 326 | }; 327 | 328 | //鼠标缩放 329 | canvas.onmousewheel = function (event) { 330 | if (event.wheelDelta > 0) { 331 | curScale = curScale * 1.1; 332 | } else { 333 | curScale = curScale * 0.9; 334 | } 335 | }; 336 | } 337 | 338 | //设置MVP矩阵 339 | function setMVPMatrix(gl, canvas, sphere, lightDirection) { 340 | // Get the storage location of u_MvpMatrix 341 | var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix'); 342 | if (!u_MvpMatrix) { 343 | console.log('Failed to get the storage location of u_MvpMatrix'); 344 | return; 345 | } 346 | 347 | //模型矩阵 348 | var modelMatrix = new Matrix4(); 349 | modelMatrix.scale(curScale, curScale, curScale); 350 | modelMatrix.rotate(currentAngle[0], 1.0, 0.0, 0.0); // Rotation around x-axis 351 | modelMatrix.rotate(currentAngle[1], 0.0, 1.0, 0.0); // Rotation around y-axis 352 | modelMatrix.translate(-sphere.centerX, -sphere.centerY, -sphere.centerZ); 353 | 354 | /* 355 | //----------------------透视--------------------- 356 | //投影矩阵 357 | var fovy = 60; 358 | var projMatrix = new Matrix4(); 359 | projMatrix.setPerspective(fovy, canvas.width / canvas.height, 1, 10000); 360 | 361 | //计算lookAt()函数初始视点的高度 362 | var angle = fovy / 2 * Math.PI / 180.0; 363 | var eyeHight = (sphere.radius * 2 * 1.1) / 2.0 / angle; 364 | 365 | //视图矩阵 366 | var viewMatrix = new Matrix4(); // View matrix 367 | viewMatrix.lookAt(0, 0, eyeHight, 0, 0, 0, 0, 1, 0); 368 | //----------------------透视---------------------*/ 369 | 370 | //----------------------正射--------------------- 371 | //视图矩阵 372 | var viewMatrix = new Matrix4(); 373 | var r = sphere.radius + 10; 374 | viewMatrix.lookAt(lightDirection.elements[0] * r, lightDirection.elements[1] * r, lightDirection.elements[2] * r, 0, 0, 0, 0, 1, 0); 375 | 376 | //投影矩阵 377 | var projMatrix = new Matrix4(); 378 | var diameter = sphere.radius * 2.1; 379 | var ratioWH = canvas.width / canvas.height; 380 | var nearHeight = diameter; 381 | var nearWidth = nearHeight * ratioWH; 382 | projMatrix.setOrtho(-nearWidth / 2, nearWidth / 2, -nearHeight / 2, nearHeight / 2, 1, 10000); 383 | //----------------------正射--------------------- 384 | 385 | //MVP矩阵 386 | var mvpMatrix = new Matrix4(); 387 | mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatrix); 388 | 389 | //将MVP矩阵传输到着色器的uniform变量u_MvpMatrix 390 | gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements); 391 | } 392 | 393 | // 394 | function initVertexBuffers(gl, terrain) { 395 | //DEM的一个网格是由两个三角形组成的 396 | // 0------1 1 397 | // | | 398 | // | | 399 | // col col------col+1 400 | var col = terrain.col; 401 | var row = terrain.row; 402 | 403 | var indices = new Uint16Array((row - 1) * (col - 1) * 6); 404 | var ci = 0; 405 | for (var yi = 0; yi < row - 1; yi++) { 406 | //for (var yi = 0; yi < 10; yi++) { 407 | for (var xi = 0; xi < col - 1; xi++) { 408 | indices[ci * 6] = yi * col + xi; 409 | indices[ci * 6 + 1] = (yi + 1) * col + xi; 410 | indices[ci * 6 + 2] = yi * col + xi + 1; 411 | indices[ci * 6 + 3] = (yi + 1) * col + xi; 412 | indices[ci * 6 + 4] = (yi + 1) * col + xi + 1; 413 | indices[ci * 6 + 5] = yi * col + xi + 1; 414 | ci++; 415 | } 416 | } 417 | 418 | // 419 | var verticesColors = terrain.verticesColors; 420 | var FSIZE = verticesColors.BYTES_PER_ELEMENT; //数组中每个元素的字节数 421 | 422 | // 创建缓冲区对象 423 | var vertexColorBuffer = gl.createBuffer(); 424 | var indexBuffer = gl.createBuffer(); 425 | if (!vertexColorBuffer || !indexBuffer) { 426 | console.log('Failed to create the buffer object'); 427 | return -1; 428 | } 429 | 430 | // 将缓冲区对象绑定到目标 431 | gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer); 432 | // 向缓冲区对象写入数据 433 | gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW); 434 | 435 | //获取着色器中attribute变量a_Position的地址 436 | var a_Position = gl.getAttribLocation(gl.program, 'a_Position'); 437 | if (a_Position < 0) { 438 | console.log('Failed to get the storage location of a_Position'); 439 | return -1; 440 | } 441 | // 将缓冲区对象分配给a_Position变量 442 | gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE * 9, 0); 443 | 444 | // 连接a_Position变量与分配给它的缓冲区对象 445 | gl.enableVertexAttribArray(a_Position); 446 | 447 | //获取着色器中attribute变量a_Color的地址 448 | var a_Color = gl.getAttribLocation(gl.program, 'a_Color'); 449 | if (a_Color < 0) { 450 | console.log('Failed to get the storage location of a_Color'); 451 | return -1; 452 | } 453 | // 将缓冲区对象分配给a_Color变量 454 | gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 9, FSIZE * 3); 455 | // 连接a_Color变量与分配给它的缓冲区对象 456 | gl.enableVertexAttribArray(a_Color); 457 | 458 | // 向缓冲区对象分配a_Normal变量,传入的这个变量要在着色器使用才行 459 | var a_Normal = gl.getAttribLocation(gl.program, 'a_Normal'); 460 | if (a_Normal < 0) { 461 | console.log('Failed to get the storage location of a_Normal'); 462 | return -1; 463 | } 464 | gl.vertexAttribPointer(a_Normal, 3, gl.FLOAT, false, FSIZE * 9, FSIZE * 6); 465 | //开启a_Normal变量 466 | gl.enableVertexAttribArray(a_Normal); 467 | 468 | // 将顶点索引写入到缓冲区对象 469 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); 470 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); 471 | 472 | return indices.length; 473 | } -------------------------------------------------------------------------------- /13/TerrainViewer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 显示地形 7 | Hello Triangle 8 | 9 | 10 | 11 |
12 |
13 | 14 | 请使用支持WebGL的浏览器 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /13/TerrainViewer.js: -------------------------------------------------------------------------------- 1 | // 顶点着色器程序 2 | var VSHADER_SOURCE = 3 | 'attribute vec4 a_Position;\n' + //位置 4 | 'attribute vec4 a_Color;\n' + //颜色 5 | 'attribute vec4 a_Normal;\n' + //法向量 6 | 'uniform mat4 u_MvpMatrix;\n' + 7 | 'varying vec4 v_PositionFromLight;\n' + 8 | 'void main() {\n' + 9 | ' gl_Position = u_MvpMatrix * a_Position;\n' + 10 | ' v_PositionFromLight = gl_Position;\n' + 11 | '}\n'; 12 | 13 | // 片元着色器程序 14 | var FSHADER_SOURCE = 15 | '#ifdef GL_ES\n' + 16 | 'precision mediump float;\n' + 17 | '#endif\n' + 18 | 'uniform sampler2D u_Sampler;\n' + //颜色贴图 19 | 'varying vec4 v_PositionFromLight;\n' + 20 | 'void main() {\n' + 21 | //获取颜色贴图中的值 22 | ' vec3 shadowCoord = (v_PositionFromLight.xyz/v_PositionFromLight.w)/2.0 + 0.5;\n' + 23 | ' gl_FragColor = texture2D(u_Sampler, shadowCoord.xy);\n' + 24 | '}\n'; 25 | 26 | // 顶点着色器程序-绘制到帧缓存 27 | var FRAME_VSHADER_SOURCE = 28 | 'attribute vec4 a_Position;\n' + //位置 29 | 'attribute vec4 a_Color;\n' + //颜色 30 | 'uniform mat4 u_MvpMatrix;\n' + 31 | 'varying vec4 v_Color;\n' + 32 | 'void main() {\n' + 33 | ' gl_Position = u_MvpMatrix * a_Position;\n' + // 设置顶点坐标 34 | ' v_Color = a_Color;\n' + 35 | '}\n'; 36 | 37 | // 片元着色器程序-绘制到帧缓存 38 | var FRAME_FSHADER_SOURCE = 39 | 'precision mediump float;\n' + 40 | 'varying vec4 v_Color;\n' + 41 | 'void main() {\n' + 42 | ' gl_FragColor = v_Color;\n' + //将深度保存在FBO中 43 | '}\n'; 44 | 45 | //定义一个矩形体:混合构造函数原型模式 46 | function Cuboid(minX, maxX, minY, maxY, minZ, maxZ) { 47 | this.minX = minX; 48 | this.maxX = maxX; 49 | this.minY = minY; 50 | this.maxY = maxY; 51 | this.minZ = minZ; 52 | this.maxZ = maxZ; 53 | } 54 | 55 | Cuboid.prototype = { 56 | constructor: Cuboid, 57 | CenterX: function () { 58 | return (this.minX + this.maxX) / 2.0; 59 | }, 60 | CenterY: function () { 61 | return (this.minY + this.maxY) / 2.0; 62 | }, 63 | CenterZ: function () { 64 | return (this.minZ + this.maxZ) / 2.0; 65 | }, 66 | LengthX: function () { 67 | return (this.maxX - this.minX); 68 | }, 69 | LengthY: function () { 70 | return (this.maxY - this.minY); 71 | }, 72 | LengthZ: function () { 73 | return (this.maxZ - this.minZ); 74 | } 75 | } 76 | 77 | //定义一个球体 78 | function Sphere(cuboid) { 79 | this.centerX = cuboid.CenterX(); 80 | this.centerY = cuboid.CenterY(); 81 | this.centerZ = cuboid.CenterZ(); 82 | this.radius = Math.max(Math.max(cuboid.LengthX(), cuboid.LengthY()), cuboid.LengthZ()) / 2.0; 83 | } 84 | 85 | Sphere.prototype = { 86 | constructor: Sphere 87 | } 88 | 89 | //定义DEM 90 | function Terrain() { } 91 | Terrain.prototype = { 92 | constructor: Terrain, 93 | setWH: function (col, row) { 94 | this.col = col; 95 | this.row = row; 96 | } 97 | } 98 | 99 | // Size of off screen 100 | var OFFSCREEN_WIDTH = 1024; 101 | var OFFSCREEN_HEIGHT = 1024; 102 | 103 | var currentAngle = [0.0, 0.0]; // 绕X轴Y轴的旋转角度 ([x-axis, y-axis]) 104 | var curScale = 1.0; //当前的缩放比例 105 | 106 | function main() { 107 | // 获取 元素 108 | var canvas = document.getElementById('webgl'); 109 | 110 | // 获取WebGL渲染上下文 111 | var gl = getWebGLContext(canvas); 112 | if (!gl) { 113 | console.log('Failed to get the rendering context for WebGL'); 114 | return; 115 | } 116 | 117 | //初始化两个着色器,drawProgram绘制到界面,frameProgram绘制到帧缓存 118 | var drawProgram = createProgram(gl, VSHADER_SOURCE, FSHADER_SOURCE); 119 | var frameProgram = createProgram(gl, FRAME_VSHADER_SOURCE, FRAME_FSHADER_SOURCE); 120 | if (!drawProgram || !frameProgram) { 121 | console.log('Failed to intialize shaders.'); 122 | return; 123 | } 124 | 125 | //从着色器中获取地址,保存到对应的变量中 126 | GetProgramLocation(gl, drawProgram, frameProgram); 127 | 128 | // 初始化帧缓冲区对象 (FBO) 129 | var fbo = initFramebufferObject(gl); 130 | if (!fbo) { 131 | console.log('Failed to intialize the framebuffer object (FBO)'); 132 | return; 133 | } 134 | 135 | // 开启深度测试 136 | gl.enable(gl.DEPTH_TEST); 137 | 138 | // 指定清空的颜色 139 | gl.clearColor(0.0, 0.0, 0.0, 1.0); 140 | 141 | //清空颜色和深度缓冲区 142 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 143 | 144 | var demFile = document.getElementById('demFile'); 145 | if (!demFile) { 146 | console.log("Failed to get demFile element!"); 147 | return; 148 | } 149 | 150 | demFile.addEventListener("change", function (event) { 151 | //判断浏览器是否支持FileReader接口 152 | if (typeof FileReader == 'undefined') { 153 | console.log("你的浏览器不支持FileReader接口!"); 154 | return; 155 | } 156 | 157 | var input = event.target; 158 | var reader = new FileReader(); 159 | reader.onload = function () { 160 | if (reader.result) { 161 | 162 | //读取 163 | var terrain = new Terrain(); 164 | if (!readDEMFile(reader.result, terrain)) { 165 | console.log("文件格式有误,不能读取该文件!"); 166 | } 167 | 168 | //注册鼠标事件 169 | //initEventHandlers(canvas); 170 | 171 | //绘制 172 | DrawDEM(gl, canvas, fbo, frameProgram, drawProgram, terrain); 173 | } 174 | } 175 | 176 | reader.readAsText(input.files[0]); 177 | }); 178 | } 179 | 180 | //从着色器中获取地址,保存到对应的变量中 181 | function GetProgramLocation(gl, drawProgram, frameProgram) { 182 | // Get the storage location of attribute variables and uniform variables 183 | drawProgram.a_Position = gl.getAttribLocation(drawProgram, 'a_Position'); 184 | drawProgram.u_MvpMatrix = gl.getUniformLocation(drawProgram, 'u_MvpMatrix'); 185 | if (drawProgram.a_Position < 0 || !drawProgram.u_MvpMatrix) { 186 | console.log('Failed to get the storage location of a_Position, u_MvpMatrix'); 187 | //return; 188 | } 189 | 190 | frameProgram.a_Position = gl.getAttribLocation(frameProgram, 'a_Position'); 191 | frameProgram.a_Color = gl.getAttribLocation(frameProgram, 'a_Color'); 192 | frameProgram.u_MvpMatrix = gl.getUniformLocation(frameProgram, 'u_MvpMatrix'); 193 | if (frameProgram.a_Position < 0 || frameProgram.a_TexCoord < 0 || !frameProgram.u_MvpMatrix) { 194 | console.log('Failed to get the storage location of a_Position, a_Color, u_MvpMatrix'); 195 | //return; 196 | } 197 | } 198 | 199 | //读取DEM函数 200 | function readDEMFile(result, terrain) { 201 | var stringlines = result.split("\n"); 202 | if (!stringlines || stringlines.length <= 0) { 203 | return false; 204 | } 205 | 206 | //读取头信息 207 | var subline = stringlines[0].split("\t"); 208 | if (subline.length != 6) { 209 | return false; 210 | } 211 | var col = parseInt(subline[4]); //DEM宽 212 | var row = parseInt(subline[5]); //DEM高 213 | var verticeNum = col * row; 214 | if (verticeNum + 1 > stringlines.length) { 215 | return false; 216 | } 217 | terrain.setWH(col, row); 218 | 219 | 220 | //读取点信息 221 | var ci = 0; 222 | var pSize = 9; 223 | terrain.vertices = new Float32Array(verticeNum * 3); 224 | terrain.colors = new Float32Array(verticeNum * 3); 225 | terrain.normals = new Float32Array(verticeNum * 3); 226 | for (var i = 1; i < stringlines.length; i++) { 227 | if (!stringlines[i]) { 228 | continue; 229 | } 230 | 231 | var subline = stringlines[i].split(','); 232 | if (subline.length != pSize) { 233 | continue; 234 | } 235 | 236 | for (var j = 0; j < 3; j++) { 237 | terrain.vertices[ci * 3 + j] = parseFloat(subline[j]); 238 | } 239 | 240 | 241 | for (var j = 0; j < 3; j++) { 242 | terrain.colors[ci * 3 + j] = parseFloat(subline[j + 3]); 243 | } 244 | 245 | for (var j = 0; j < 3; j++) { 246 | terrain.normals[ci * 3 + j] = parseFloat(subline[j + 6]); 247 | } 248 | 249 | ci++; 250 | } 251 | 252 | if (ci !== verticeNum) { 253 | return false; 254 | } 255 | 256 | //包围盒 257 | var minX = terrain.vertices[0]; 258 | var maxX = terrain.vertices[0]; 259 | var minY = terrain.vertices[1]; 260 | var maxY = terrain.vertices[1]; 261 | var minZ = terrain.vertices[2]; 262 | var maxZ = terrain.vertices[2]; 263 | for (var i = 0; i < verticeNum; i++) { 264 | minX = Math.min(minX, terrain.vertices[i * 3]); 265 | maxX = Math.max(maxX, terrain.vertices[i * 3]); 266 | minY = Math.min(minY, terrain.vertices[i * 3 + 1]); 267 | maxY = Math.max(maxY, terrain.vertices[i * 3 + 1]); 268 | minZ = Math.min(minZ, terrain.vertices[i * 3 + 2]); 269 | maxZ = Math.max(maxZ, terrain.vertices[i * 3 + 2]); 270 | } 271 | 272 | terrain.cuboid = new Cuboid(minX, maxX, minY, maxY, minZ, maxZ); 273 | terrain.sphere = new Sphere(terrain.cuboid); 274 | 275 | return true; 276 | } 277 | 278 | //绘制 279 | function DrawDEM(gl, canvas, fbo, frameProgram, drawProgram, terrain) { 280 | // 设置顶点位置 281 | var demBufferObject = initVertexBuffersForDrawDEM(gl, terrain); 282 | if (!demBufferObject) { 283 | console.log('Failed to set the positions of the vertices'); 284 | return; 285 | } 286 | 287 | //获取光线:平行光 288 | var lightDirection = getLight(); 289 | 290 | //预先给着色器传递一些不变的量 291 | { 292 | //使用帧缓冲区着色器 293 | gl.useProgram(frameProgram); 294 | //设置MVP矩阵 295 | setMVPMatrix(gl, canvas, terrain.sphere, lightDirection, frameProgram); 296 | 297 | //使用颜色缓冲区着色器 298 | gl.useProgram(drawProgram); 299 | //设置MVP矩阵 300 | setMVPMatrix(gl, canvas, terrain.sphere, lightDirection, drawProgram); 301 | //将绘制在帧缓冲区的纹理传递给颜色缓冲区着色器的0号纹理单元 302 | gl.activeTexture(gl.TEXTURE0); 303 | gl.bindTexture(gl.TEXTURE_2D, fbo.texture); 304 | gl.uniform1i(drawProgram.u_Sampler, 0); 305 | 306 | gl.useProgram(null); 307 | } 308 | 309 | //开始绘制 310 | var tick = function () { 311 | //帧缓存绘制 312 | gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); //将绘制目标切换为帧缓冲区对象FBO 313 | gl.viewport(0, 0, OFFSCREEN_WIDTH, OFFSCREEN_HEIGHT); // 为FBO设置一个视口 314 | 315 | gl.clearColor(0.2, 0.2, 0.4, 1.0); // Set clear color (the color is slightly changed) 316 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // Clear FBO 317 | gl.useProgram(frameProgram); //准备生成纹理贴图 318 | 319 | //分配缓冲区对象并开启连接 320 | initAttributeVariable(gl, frameProgram.a_Position, demBufferObject.vertexBuffer); // 顶点坐标 321 | initAttributeVariable(gl, frameProgram.a_Color, demBufferObject.colorBuffer); // 颜色 322 | 323 | //分配索引并绘制 324 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, demBufferObject.indexBuffer); 325 | gl.drawElements(gl.TRIANGLES, demBufferObject.numIndices, demBufferObject.indexBuffer.type, 0); 326 | 327 | //颜色缓存绘制 328 | gl.bindFramebuffer(gl.FRAMEBUFFER, null); //将绘制目标切换为颜色缓冲区 329 | gl.viewport(0, 0, canvas.width, canvas.height); // 设置视口为当前画布的大小 330 | 331 | gl.clearColor(0.0, 0.0, 0.0, 1.0); 332 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // Clear the color buffer 333 | gl.useProgram(drawProgram); // 准备进行绘制 334 | 335 | //分配缓冲区对象并开启连接 336 | initAttributeVariable(gl, drawProgram.a_Position, demBufferObject.vertexBuffer); // Vertex coordinat 337 | 338 | //分配索引并绘制 339 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, demBufferObject.indexBuffer); 340 | gl.drawElements(gl.TRIANGLES, demBufferObject.numIndices, demBufferObject.indexBuffer.type, 0); 341 | 342 | window.requestAnimationFrame(tick, canvas); 343 | }; 344 | tick(); 345 | } 346 | 347 | //注册鼠标事件 348 | function initEventHandlers(canvas) { 349 | var dragging = false; // Dragging or not 350 | var lastX = -1, 351 | lastY = -1; // Last position of the mouse 352 | 353 | //鼠标按下 354 | canvas.onmousedown = function (ev) { 355 | var x = ev.clientX; 356 | var y = ev.clientY; 357 | // Start dragging if a moue is in 358 | var rect = ev.target.getBoundingClientRect(); 359 | if (rect.left <= x && x < rect.right && rect.top <= y && y < rect.bottom) { 360 | lastX = x; 361 | lastY = y; 362 | dragging = true; 363 | } 364 | }; 365 | 366 | //鼠标离开时 367 | canvas.onmouseleave = function (ev) { 368 | dragging = false; 369 | }; 370 | 371 | //鼠标释放 372 | canvas.onmouseup = function (ev) { 373 | dragging = false; 374 | }; 375 | 376 | //鼠标移动 377 | canvas.onmousemove = function (ev) { 378 | var x = ev.clientX; 379 | var y = ev.clientY; 380 | if (dragging) { 381 | var factor = 100 / canvas.height; // The rotation ratio 382 | var dx = factor * (x - lastX); 383 | var dy = factor * (y - lastY); 384 | currentAngle[0] = currentAngle[0] + dy; 385 | currentAngle[1] = currentAngle[1] + dx; 386 | } 387 | lastX = x, lastY = y; 388 | }; 389 | 390 | //鼠标缩放 391 | canvas.onmousewheel = function (event) { 392 | if (event.wheelDelta > 0) { 393 | curScale = curScale * 1.1; 394 | } else { 395 | curScale = curScale * 0.9; 396 | } 397 | }; 398 | } 399 | 400 | //设置MVP矩阵 401 | function setMVPMatrix(gl, canvas, sphere, lightDirection, drawProgram) { 402 | //模型矩阵 403 | var modelMatrix = new Matrix4(); 404 | modelMatrix.scale(curScale, curScale, curScale); 405 | modelMatrix.rotate(currentAngle[0], 1.0, 0.0, 0.0); // Rotation around x-axis 406 | modelMatrix.rotate(currentAngle[1], 0.0, 1.0, 0.0); // Rotation around y-axis 407 | modelMatrix.translate(-sphere.centerX, -sphere.centerY, -sphere.centerZ); 408 | 409 | /* 410 | //投影矩阵 411 | var fovy = 60; 412 | var projMatrix = new Matrix4(); 413 | projMatrix.setPerspective(fovy, canvas.width / canvas.height, 1, 10000); 414 | 415 | //计算lookAt()函数初始视点的高度 416 | var angle = fovy / 2 * Math.PI / 180.0; 417 | var eyeHight = (sphere.radius * 2 * 1.1) / 2.0 / angle; 418 | 419 | //视图矩阵 420 | var viewMatrix = new Matrix4(); // View matrix 421 | viewMatrix.lookAt(0, 0, eyeHight, 0, 0, 0, 0, 1, 0); 422 | */ 423 | 424 | //视图矩阵 425 | var viewMatrix = new Matrix4(); 426 | var r = sphere.radius + 10; 427 | viewMatrix.lookAt(lightDirection.elements[0] * r, lightDirection.elements[1] * r, lightDirection.elements[2] * r, 0, 0, 0, 0, 1, 0); 428 | 429 | //投影矩阵 430 | var projMatrix = new Matrix4(); 431 | var diameter = sphere.radius * 2.1; 432 | var ratioWH = canvas.width / canvas.height; 433 | var nearHeight = diameter; 434 | var nearWidth = nearHeight * ratioWH; 435 | projMatrix.setOrtho(-nearWidth / 2, nearWidth / 2, -nearHeight / 2, nearHeight / 2, 1, 10000); 436 | 437 | //MVP矩阵 438 | var mvpMatrix = new Matrix4(); 439 | mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatrix); 440 | 441 | //将MVP矩阵传输到着色器的uniform变量u_MvpMatrix 442 | gl.uniformMatrix4fv(drawProgram.u_MvpMatrix, false, mvpMatrix.elements); 443 | } 444 | 445 | //获取光线 446 | function getLight() { 447 | // 设置光线方向(世界坐标系下的) 448 | var solarAltitude = 30.0; 449 | var solarAzimuth = 315.0; 450 | var fAltitude = solarAltitude * Math.PI / 180; //光源高度角 451 | var fAzimuth = solarAzimuth * Math.PI / 180; //光源方位角 452 | 453 | var arrayvectorX = Math.cos(fAltitude) * Math.cos(fAzimuth); 454 | var arrayvectorY = Math.cos(fAltitude) * Math.sin(fAzimuth); 455 | var arrayvectorZ = Math.sin(fAltitude); 456 | 457 | var lightDirection = new Vector3([arrayvectorX, arrayvectorY, arrayvectorZ]); 458 | lightDirection.normalize(); // Normalize 459 | return lightDirection; 460 | } 461 | 462 | function initVertexBuffersForDrawDEM(gl, terrain) { 463 | //DEM的一个网格是由两个三角形组成的 464 | // 0------1 1 465 | // | | 466 | // | | 467 | // col col------col+1 468 | var col = terrain.col; 469 | var row = terrain.row; 470 | 471 | var indices = new Uint16Array((row - 1) * (col - 1) * 6); 472 | var ci = 0; 473 | for (var yi = 0; yi < row - 1; yi++) { 474 | //for (var yi = 0; yi < 10; yi++) { 475 | for (var xi = 0; xi < col - 1; xi++) { 476 | indices[ci * 6] = yi * col + xi; 477 | indices[ci * 6 + 1] = (yi + 1) * col + xi; 478 | indices[ci * 6 + 2] = yi * col + xi + 1; 479 | indices[ci * 6 + 3] = (yi + 1) * col + xi; 480 | indices[ci * 6 + 4] = (yi + 1) * col + xi + 1; 481 | indices[ci * 6 + 5] = yi * col + xi + 1; 482 | ci++; 483 | } 484 | } 485 | 486 | var dem = new Object(); // Create the "Object" object to return multiple objects. 487 | 488 | // Write vertex information to buffer object 489 | dem.vertexBuffer = initArrayBufferForLaterUse(gl, terrain.vertices, 3, gl.FLOAT); 490 | dem.colorBuffer = initArrayBufferForLaterUse(gl, terrain.colors, 3, gl.FLOAT); 491 | dem.normalBuffer = initArrayBufferForLaterUse(gl, terrain.normals, 3, gl.FLOAT); 492 | dem.indexBuffer = initElementArrayBufferForLaterUse(gl, indices, gl.UNSIGNED_SHORT); 493 | if (!dem.vertexBuffer || !dem.colorBuffer || !dem.indexBuffer || !dem.normalBuffer) { 494 | return null; 495 | } 496 | 497 | dem.numIndices = indices.length; 498 | 499 | // Unbind the buffer object 500 | gl.bindBuffer(gl.ARRAY_BUFFER, null); 501 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); 502 | 503 | return dem; 504 | } 505 | 506 | // 初始化帧缓冲区对象 (FBO) 507 | function initFramebufferObject(gl) { 508 | var framebuffer, texture, depthBuffer; 509 | 510 | // Define the error handling function 511 | var error = function () { 512 | if (framebuffer) gl.deleteFramebuffer(framebuffer); 513 | if (texture) gl.deleteTexture(texture); 514 | if (depthBuffer) gl.deleteRenderbuffer(depthBuffer); 515 | return null; 516 | } 517 | 518 | // 创建帧缓冲区对象 (FBO) 519 | framebuffer = gl.createFramebuffer(); 520 | if (!framebuffer) { 521 | console.log('Failed to create frame buffer object'); 522 | return error(); 523 | } 524 | 525 | // 创建纹理对象并设置其尺寸和参数 526 | texture = gl.createTexture(); // 创建纹理对象 527 | if (!texture) { 528 | console.log('Failed to create texture object'); 529 | return error(); 530 | } 531 | gl.bindTexture(gl.TEXTURE_2D, texture); // Bind the object to target 532 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, OFFSCREEN_WIDTH, OFFSCREEN_HEIGHT, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); 533 | // 设置纹理参数 534 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 535 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); 536 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 537 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 538 | framebuffer.texture = texture; // 保存纹理对象 539 | 540 | // 创建渲染缓冲区对象并设置其尺寸和参数 541 | depthBuffer = gl.createRenderbuffer(); //创建渲染缓冲区 542 | if (!depthBuffer) { 543 | console.log('Failed to create renderbuffer object'); 544 | return error(); 545 | } 546 | gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer); // Bind the object to target 547 | gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, OFFSCREEN_WIDTH, OFFSCREEN_HEIGHT); 548 | 549 | // 将纹理和渲染缓冲区对象关联到帧缓冲区对象上 550 | gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); 551 | gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); //关联颜色 552 | gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer); //关联深度 553 | 554 | // 检查帧缓冲区是否被正确设置 555 | var e = gl.checkFramebufferStatus(gl.FRAMEBUFFER); 556 | if (gl.FRAMEBUFFER_COMPLETE !== e) { 557 | console.log('Frame buffer object is incomplete: ' + e.toString()); 558 | return error(); 559 | } 560 | 561 | // Unbind the buffer object 562 | gl.bindFramebuffer(gl.FRAMEBUFFER, null); 563 | gl.bindTexture(gl.TEXTURE_2D, null); 564 | gl.bindRenderbuffer(gl.RENDERBUFFER, null); 565 | 566 | return framebuffer; 567 | } 568 | 569 | //分配缓冲区对象并开启连接 570 | function initAttributeVariable(gl, a_attribute, buffer) { 571 | gl.bindBuffer(gl.ARRAY_BUFFER, buffer); 572 | gl.vertexAttribPointer(a_attribute, buffer.num, buffer.type, false, 0, 0); 573 | gl.enableVertexAttribArray(a_attribute); 574 | } 575 | 576 | //向顶点缓冲区写入数据,留待以后分配 577 | function initArrayBufferForLaterUse(gl, data, num, type) { 578 | // Create a buffer object 579 | var buffer = gl.createBuffer(); 580 | if (!buffer) { 581 | console.log('Failed to create the buffer object'); 582 | return null; 583 | } 584 | // Write date into the buffer object 585 | gl.bindBuffer(gl.ARRAY_BUFFER, buffer); 586 | gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW); 587 | 588 | // Store the necessary information to assign the object to the attribute variable later 589 | buffer.num = num; 590 | buffer.type = type; 591 | 592 | return buffer; 593 | } 594 | 595 | //向顶点缓冲区写入索引,留待以后分配 596 | function initElementArrayBufferForLaterUse(gl, data, type) { 597 | // Create a buffer object 598 | var buffer = gl.createBuffer(); 599 | if (!buffer) { 600 | console.log('Failed to create the buffer object'); 601 | return null; 602 | } 603 | // Write date into the buffer object 604 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer); 605 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data, gl.STATIC_DRAW); 606 | 607 | buffer.type = type; 608 | 609 | return buffer; 610 | } -------------------------------------------------------------------------------- /14/TerrainViewer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 显示地形 7 | Hello Triangle 8 | 9 | 10 | 11 |
12 |
13 | 14 | 请使用支持WebGL的浏览器 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /14/TerrainViewer.js: -------------------------------------------------------------------------------- 1 | // 顶点着色器程序 2 | var VSHADER_SOURCE = 3 | 'attribute vec4 a_Position;\n' + //位置 4 | 'attribute vec4 a_Color;\n' + //颜色 5 | 'attribute vec4 a_Normal;\n' + //法向量 6 | 'uniform mat4 u_MvpMatrix;\n' + //界面绘制操作的MVP矩阵 7 | 'uniform mat4 u_MvpMatrixFromLight;\n' + //光线方向的MVP矩阵 8 | 'varying vec4 v_PositionFromLight;\n' + 9 | 'varying vec4 v_Color;\n' + 10 | 'varying vec4 v_Normal;\n' + 11 | 'void main() {\n' + 12 | ' gl_Position = u_MvpMatrix * a_Position;\n' + 13 | ' v_PositionFromLight = u_MvpMatrixFromLight * a_Position;\n' + 14 | ' v_Color = a_Color;\n' + 15 | ' v_Normal = a_Normal;\n' + 16 | '}\n'; 17 | 18 | // 片元着色器程序 19 | var FSHADER_SOURCE = 20 | '#ifdef GL_ES\n' + 21 | 'precision mediump float;\n' + 22 | '#endif\n' + 23 | 'uniform sampler2D u_Sampler;\n' + //阴影贴图 24 | 'uniform vec3 u_DiffuseLight;\n' + // 漫反射光颜色 25 | 'uniform vec3 u_LightDirection;\n' + // 漫反射光的方向 26 | 'uniform vec3 u_AmbientLight;\n' + // 环境光颜色 27 | 'varying vec4 v_Color;\n' + 28 | 'varying vec4 v_Normal;\n' + 29 | 'varying vec4 v_PositionFromLight;\n' + 30 | 'float unpackDepth(const in vec4 rgbaDepth) {\n' + 31 | ' const vec4 bitShift = vec4(1.0, 1.0/256.0, 1.0/(256.0*256.0), 1.0/(256.0*256.0*256.0));\n' + 32 | ' float depth = dot(rgbaDepth, bitShift);\n' + // Use dot() since the calculations is same 33 | ' return depth;\n' + 34 | '}\n' + 35 | 'void main() {\n' + 36 | //通过深度判断阴影 37 | ' vec3 shadowCoord = (v_PositionFromLight.xyz/v_PositionFromLight.w)/2.0 + 0.5;\n' + 38 | ' vec4 rgbaDepth = texture2D(u_Sampler, shadowCoord.xy);\n' + 39 | ' float depth = unpackDepth(rgbaDepth);\n' + // 将阴影贴图的RGBA解码成浮点型的深度值 40 | ' float visibility = (shadowCoord.z > depth + 0.0015) ? 0.7 : 1.0;\n' + 41 | //获得反射光 42 | ' vec3 normal = normalize(v_Normal.xyz);\n' + 43 | ' float nDotL = max(dot(u_LightDirection, normal), 0.0);\n' + //计算光线向量与法向量的点积 44 | ' vec3 diffuse = u_DiffuseLight * v_Color.rgb * nDotL;\n' + //计算漫发射光的颜色 45 | ' vec3 ambient = u_AmbientLight * v_Color.rgb;\n' + //计算环境光的颜色 46 | //' gl_FragColor = vec4(v_Color.rgb * visibility, v_Color.a);\n' + 47 | ' gl_FragColor = vec4((diffuse+ambient) * visibility, v_Color.a);\n' + 48 | '}\n'; 49 | 50 | // 顶点着色器程序-绘制到帧缓存 51 | var FRAME_VSHADER_SOURCE = 52 | 'attribute vec4 a_Position;\n' + //位置 53 | 'attribute vec4 a_Color;\n' + //颜色 54 | 'uniform mat4 u_MvpMatrix;\n' + 55 | 'varying vec4 v_Color;\n' + 56 | 'void main() {\n' + 57 | ' gl_Position = u_MvpMatrix * a_Position;\n' + // 设置顶点坐标 58 | ' v_Color = a_Color;\n' + 59 | '}\n'; 60 | 61 | // 片元着色器程序-绘制到帧缓存 62 | var FRAME_FSHADER_SOURCE = 63 | 'precision mediump float;\n' + 64 | 'varying vec4 v_Color;\n' + 65 | 'void main() {\n' + 66 | ' const vec4 bitShift = vec4(1.0, 256.0, 256.0 * 256.0, 256.0 * 256.0 * 256.0);\n' + 67 | ' const vec4 bitMask = vec4(1.0/256.0, 1.0/256.0, 1.0/256.0, 0.0);\n' + 68 | ' vec4 rgbaDepth = fract(gl_FragCoord.z * bitShift);\n' + // Calculate the value stored into each byte 69 | ' rgbaDepth -= rgbaDepth.gbaa * bitMask;\n' + // Cut off the value which do not fit in 8 bits 70 | ' gl_FragColor = rgbaDepth;\n' + //将深度保存在FBO中 71 | '}\n'; 72 | 73 | //定义一个矩形体:混合构造函数原型模式 74 | function Cuboid(minX, maxX, minY, maxY, minZ, maxZ) { 75 | this.minX = minX; 76 | this.maxX = maxX; 77 | this.minY = minY; 78 | this.maxY = maxY; 79 | this.minZ = minZ; 80 | this.maxZ = maxZ; 81 | } 82 | 83 | Cuboid.prototype = { 84 | constructor: Cuboid, 85 | CenterX: function () { 86 | return (this.minX + this.maxX) / 2.0; 87 | }, 88 | CenterY: function () { 89 | return (this.minY + this.maxY) / 2.0; 90 | }, 91 | CenterZ: function () { 92 | return (this.minZ + this.maxZ) / 2.0; 93 | }, 94 | LengthX: function () { 95 | return (this.maxX - this.minX); 96 | }, 97 | LengthY: function () { 98 | return (this.maxY - this.minY); 99 | }, 100 | LengthZ: function () { 101 | return (this.maxZ - this.minZ); 102 | } 103 | } 104 | 105 | //定义一个球体 106 | function Sphere(cuboid) { 107 | this.centerX = cuboid.CenterX(); 108 | this.centerY = cuboid.CenterY(); 109 | this.centerZ = cuboid.CenterZ(); 110 | this.radius = Math.max(Math.max(cuboid.LengthX(), cuboid.LengthY()), cuboid.LengthZ()) / 2.0; 111 | } 112 | 113 | Sphere.prototype = { 114 | constructor: Sphere 115 | } 116 | 117 | //定义DEM 118 | function Terrain() { } 119 | Terrain.prototype = { 120 | constructor: Terrain, 121 | setWH: function (col, row) { 122 | this.col = col; 123 | this.row = row; 124 | } 125 | } 126 | 127 | // Size of off screen 128 | var OFFSCREEN_WIDTH = 1024; 129 | var OFFSCREEN_HEIGHT = 1024; 130 | 131 | var currentAngle = [0.0, 0.0]; // 绕X轴Y轴的旋转角度 ([x-axis, y-axis]) 132 | var curScale = 1.0; //当前的缩放比例 133 | 134 | function main() { 135 | // 获取 元素 136 | var canvas = document.getElementById('webgl'); 137 | 138 | // 获取WebGL渲染上下文 139 | var gl = getWebGLContext(canvas); 140 | if (!gl) { 141 | console.log('Failed to get the rendering context for WebGL'); 142 | return; 143 | } 144 | 145 | //初始化两个着色器,drawProgram绘制到界面,frameProgram绘制到帧缓存 146 | var drawProgram = createProgram(gl, VSHADER_SOURCE, FSHADER_SOURCE); 147 | var frameProgram = createProgram(gl, FRAME_VSHADER_SOURCE, FRAME_FSHADER_SOURCE); 148 | if (!drawProgram || !frameProgram) { 149 | console.log('Failed to intialize shaders.'); 150 | return; 151 | } 152 | 153 | //从着色器中获取地址,保存到对应的变量中 154 | GetProgramLocation(gl, drawProgram, frameProgram); 155 | 156 | // 初始化帧缓冲区对象 (FBO) 157 | var fbo = initFramebufferObject(gl); 158 | if (!fbo) { 159 | console.log('Failed to intialize the framebuffer object (FBO)'); 160 | return; 161 | } 162 | 163 | // 开启深度测试 164 | gl.enable(gl.DEPTH_TEST); 165 | 166 | // 指定清空的颜色 167 | gl.clearColor(0.0, 0.0, 0.0, 1.0); 168 | 169 | //清空颜色和深度缓冲区 170 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 171 | 172 | var demFile = document.getElementById('demFile'); 173 | if (!demFile) { 174 | console.log("Failed to get demFile element!"); 175 | return; 176 | } 177 | 178 | demFile.addEventListener("change", function (event) { 179 | //判断浏览器是否支持FileReader接口 180 | if (typeof FileReader == 'undefined') { 181 | console.log("你的浏览器不支持FileReader接口!"); 182 | return; 183 | } 184 | 185 | var input = event.target; 186 | var reader = new FileReader(); 187 | reader.onload = function () { 188 | if (reader.result) { 189 | 190 | //读取 191 | var terrain = new Terrain(); 192 | if (!readDEMFile(reader.result, terrain)) { 193 | console.log("文件格式有误,不能读取该文件!"); 194 | } 195 | 196 | //注册鼠标事件 197 | initEventHandlers(canvas); 198 | 199 | //绘制 200 | DrawDEM(gl, canvas, fbo, frameProgram, drawProgram, terrain); 201 | } 202 | } 203 | 204 | reader.readAsText(input.files[0]); 205 | }); 206 | } 207 | 208 | //从着色器中获取地址,保存到对应的变量中 209 | function GetProgramLocation(gl, drawProgram, frameProgram) { 210 | // Get the storage location of attribute variables and uniform variables 211 | drawProgram.a_Position = gl.getAttribLocation(drawProgram, 'a_Position'); 212 | drawProgram.a_Color = gl.getAttribLocation(drawProgram, 'a_Color'); 213 | drawProgram.a_Normal = gl.getAttribLocation(drawProgram, 'a_Normal'); 214 | drawProgram.u_MvpMatrix = gl.getUniformLocation(drawProgram, 'u_MvpMatrix'); 215 | drawProgram.u_MvpMatrixFromLight = gl.getUniformLocation(drawProgram, 'u_MvpMatrixFromLight'); 216 | if (drawProgram.a_Position < 0 || drawProgram.a_Color < 0 || drawProgram.a_Normal < 0 || !drawProgram.u_MvpMatrix 217 | || !drawProgram.u_MvpMatrixFromLight) { 218 | console.log('Failed to get the storage location of a_Position, a_Color, a_Normal, u_MvpMatrix, u_MvpMatrixFromLight'); 219 | //return; 220 | } 221 | 222 | drawProgram.u_AmbientLight = gl.getUniformLocation(drawProgram, 'u_AmbientLight'); 223 | drawProgram.u_DiffuseLight = gl.getUniformLocation(drawProgram, 'u_DiffuseLight'); 224 | drawProgram.u_LightDirection = gl.getUniformLocation(drawProgram, 'u_LightDirection'); 225 | if (!drawProgram.u_DiffuseLight || !drawProgram.u_LightDirection || !drawProgram.u_AmbientLight) { 226 | console.log('Failed to get the storage location of u_AmbientLight, u_DiffuseLight, u_LightDirection'); 227 | //return; 228 | } 229 | 230 | frameProgram.a_Position = gl.getAttribLocation(frameProgram, 'a_Position'); 231 | frameProgram.a_Color = gl.getAttribLocation(frameProgram, 'a_Color'); 232 | frameProgram.u_MvpMatrix = gl.getUniformLocation(frameProgram, 'u_MvpMatrix'); 233 | if (frameProgram.a_Position < 0 || frameProgram.a_TexCoord < 0 || !frameProgram.u_MvpMatrix) { 234 | console.log('Failed to get the storage location of a_Position, a_Color, u_MvpMatrix'); 235 | //return; 236 | } 237 | } 238 | 239 | //读取DEM函数 240 | function readDEMFile(result, terrain) { 241 | var stringlines = result.split("\n"); 242 | if (!stringlines || stringlines.length <= 0) { 243 | return false; 244 | } 245 | 246 | //读取头信息 247 | var subline = stringlines[0].split("\t"); 248 | if (subline.length != 6) { 249 | return false; 250 | } 251 | var col = parseInt(subline[4]); //DEM宽 252 | var row = parseInt(subline[5]); //DEM高 253 | var verticeNum = col * row; 254 | if (verticeNum + 1 > stringlines.length) { 255 | return false; 256 | } 257 | terrain.setWH(col, row); 258 | 259 | 260 | //读取点信息 261 | var ci = 0; 262 | var pSize = 9; 263 | terrain.vertices = new Float32Array(verticeNum * 3); 264 | terrain.colors = new Float32Array(verticeNum * 3); 265 | terrain.normals = new Float32Array(verticeNum * 3); 266 | for (var i = 1; i < stringlines.length; i++) { 267 | if (!stringlines[i]) { 268 | continue; 269 | } 270 | 271 | var subline = stringlines[i].split(','); 272 | if (subline.length != pSize) { 273 | continue; 274 | } 275 | 276 | for (var j = 0; j < 3; j++) { 277 | terrain.vertices[ci * 3 + j] = parseFloat(subline[j]); 278 | } 279 | 280 | 281 | for (var j = 0; j < 3; j++) { 282 | terrain.colors[ci * 3 + j] = parseFloat(subline[j + 3]); 283 | } 284 | 285 | for (var j = 0; j < 3; j++) { 286 | terrain.normals[ci * 3 + j] = parseFloat(subline[j + 6]); 287 | } 288 | 289 | ci++; 290 | } 291 | 292 | if (ci !== verticeNum) { 293 | return false; 294 | } 295 | 296 | //包围盒 297 | var minX = terrain.vertices[0]; 298 | var maxX = terrain.vertices[0]; 299 | var minY = terrain.vertices[1]; 300 | var maxY = terrain.vertices[1]; 301 | var minZ = terrain.vertices[2]; 302 | var maxZ = terrain.vertices[2]; 303 | for (var i = 0; i < verticeNum; i++) { 304 | minX = Math.min(minX, terrain.vertices[i * 3]); 305 | maxX = Math.max(maxX, terrain.vertices[i * 3]); 306 | minY = Math.min(minY, terrain.vertices[i * 3 + 1]); 307 | maxY = Math.max(maxY, terrain.vertices[i * 3 + 1]); 308 | minZ = Math.min(minZ, terrain.vertices[i * 3 + 2]); 309 | maxZ = Math.max(maxZ, terrain.vertices[i * 3 + 2]); 310 | } 311 | 312 | terrain.cuboid = new Cuboid(minX, maxX, minY, maxY, minZ, maxZ); 313 | terrain.sphere = new Sphere(terrain.cuboid); 314 | 315 | return true; 316 | } 317 | 318 | //绘制 319 | function DrawDEM(gl, canvas, fbo, frameProgram, drawProgram, terrain) { 320 | // 设置顶点位置 321 | var demBufferObject = initVertexBuffersForDrawDEM(gl, terrain); 322 | if (!demBufferObject) { 323 | console.log('Failed to set the positions of the vertices'); 324 | return; 325 | } 326 | 327 | //获取光线:平行光 328 | var lightDirection = getLight(); 329 | 330 | //预先给着色器传递一些不变的量 331 | { 332 | //使用帧缓冲区着色器 333 | gl.useProgram(frameProgram); 334 | //设置在帧缓存中绘制的MVP矩阵 335 | var MvpMatrixFromLight = setFrameMVPMatrix(gl, terrain.sphere, lightDirection, frameProgram); 336 | 337 | //使用颜色缓冲区着色器 338 | gl.useProgram(drawProgram); 339 | //设置在颜色缓冲区中绘制时光线的MVP矩阵 340 | gl.uniformMatrix4fv(drawProgram.u_MvpMatrixFromLight, false, MvpMatrixFromLight.elements); 341 | //设置光线的强度和方向 342 | gl.uniform3f(drawProgram.u_DiffuseLight, 1.0, 1.0, 1.0); //设置漫反射光 343 | gl.uniform3fv(drawProgram.u_LightDirection, lightDirection.elements); // 设置光线方向(世界坐标系下的) 344 | gl.uniform3f(drawProgram.u_AmbientLight, 0.2, 0.2, 0.2); //设置环境光 345 | //将绘制在帧缓冲区的纹理传递给颜色缓冲区着色器的0号纹理单元 346 | gl.activeTexture(gl.TEXTURE0); 347 | gl.bindTexture(gl.TEXTURE_2D, fbo.texture); 348 | gl.uniform1i(drawProgram.u_Sampler, 0); 349 | 350 | gl.useProgram(null); 351 | } 352 | 353 | //开始绘制 354 | var tick = function () { 355 | //帧缓存绘制 356 | gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); //将绘制目标切换为帧缓冲区对象FBO 357 | gl.viewport(0, 0, OFFSCREEN_WIDTH, OFFSCREEN_HEIGHT); // 为FBO设置一个视口 358 | 359 | gl.clearColor(0.2, 0.2, 0.4, 1.0); // Set clear color (the color is slightly changed) 360 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // Clear FBO 361 | gl.useProgram(frameProgram); //准备生成纹理贴图 362 | 363 | //分配缓冲区对象并开启连接 364 | initAttributeVariable(gl, frameProgram.a_Position, demBufferObject.vertexBuffer); // 顶点坐标 365 | initAttributeVariable(gl, frameProgram.a_Color, demBufferObject.colorBuffer); // 颜色 366 | 367 | //分配索引并绘制 368 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, demBufferObject.indexBuffer); 369 | gl.drawElements(gl.TRIANGLES, demBufferObject.numIndices, demBufferObject.indexBuffer.type, 0); 370 | 371 | //颜色缓存绘制 372 | gl.bindFramebuffer(gl.FRAMEBUFFER, null); //将绘制目标切换为颜色缓冲区 373 | gl.viewport(0, 0, canvas.width, canvas.height); // 设置视口为当前画布的大小 374 | 375 | gl.clearColor(0.0, 0.0, 0.0, 1.0); 376 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // Clear the color buffer 377 | gl.useProgram(drawProgram); // 准备进行绘制 378 | 379 | //设置MVP矩阵 380 | setMVPMatrix(gl, canvas, terrain.sphere, lightDirection, drawProgram); 381 | 382 | //分配缓冲区对象并开启连接 383 | initAttributeVariable(gl, drawProgram.a_Position, demBufferObject.vertexBuffer); // Vertex coordinates 384 | initAttributeVariable(gl, drawProgram.a_Color, demBufferObject.colorBuffer); // Texture coordinates 385 | initAttributeVariable(gl, drawProgram.a_Normal, demBufferObject.normalBuffer); // Texture coordinates 386 | 387 | //分配索引并绘制 388 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, demBufferObject.indexBuffer); 389 | gl.drawElements(gl.TRIANGLES, demBufferObject.numIndices, demBufferObject.indexBuffer.type, 0); 390 | 391 | window.requestAnimationFrame(tick, canvas); 392 | }; 393 | tick(); 394 | } 395 | 396 | 397 | //注册鼠标事件 398 | function initEventHandlers(canvas) { 399 | var dragging = false; // Dragging or not 400 | var lastX = -1, 401 | lastY = -1; // Last position of the mouse 402 | 403 | //鼠标按下 404 | canvas.onmousedown = function (ev) { 405 | var x = ev.clientX; 406 | var y = ev.clientY; 407 | // Start dragging if a moue is in 408 | var rect = ev.target.getBoundingClientRect(); 409 | if (rect.left <= x && x < rect.right && rect.top <= y && y < rect.bottom) { 410 | lastX = x; 411 | lastY = y; 412 | dragging = true; 413 | } 414 | }; 415 | 416 | //鼠标离开时 417 | canvas.onmouseleave = function (ev) { 418 | dragging = false; 419 | }; 420 | 421 | //鼠标释放 422 | canvas.onmouseup = function (ev) { 423 | dragging = false; 424 | }; 425 | 426 | //鼠标移动 427 | canvas.onmousemove = function (ev) { 428 | var x = ev.clientX; 429 | var y = ev.clientY; 430 | if (dragging) { 431 | var factor = 100 / canvas.height; // The rotation ratio 432 | var dx = factor * (x - lastX); 433 | var dy = factor * (y - lastY); 434 | currentAngle[0] = currentAngle[0] + dy; 435 | currentAngle[1] = currentAngle[1] + dx; 436 | } 437 | lastX = x, lastY = y; 438 | }; 439 | 440 | //鼠标缩放 441 | canvas.onmousewheel = function (event) { 442 | if (event.wheelDelta > 0) { 443 | curScale = curScale * 1.1; 444 | } else { 445 | curScale = curScale * 0.9; 446 | } 447 | }; 448 | } 449 | 450 | //设置MVP矩阵 451 | function setMVPMatrix(gl, canvas, sphere, lightDirection, drawProgram) { 452 | //模型矩阵 453 | var modelMatrix = new Matrix4(); 454 | modelMatrix.scale(curScale, curScale, curScale); 455 | modelMatrix.rotate(currentAngle[0], 1.0, 0.0, 0.0); // Rotation around x-axis 456 | modelMatrix.rotate(currentAngle[1], 0.0, 1.0, 0.0); // Rotation around y-axis 457 | modelMatrix.translate(-sphere.centerX, -sphere.centerY, -sphere.centerZ); 458 | 459 | //投影矩阵 460 | var fovy = 60; 461 | var projMatrix = new Matrix4(); 462 | projMatrix.setPerspective(fovy, canvas.width / canvas.height, 1, 10000); 463 | 464 | //计算lookAt()函数初始视点的高度 465 | var angle = fovy / 2 * Math.PI / 180.0; 466 | var eyeHight = (sphere.radius * 2 * 1.1) / 2.0 / angle; 467 | 468 | //视图矩阵 469 | var viewMatrix = new Matrix4(); // View matrix 470 | viewMatrix.lookAt(0, 0, eyeHight, 0, 0, 0, 0, 1, 0); 471 | 472 | /* 473 | //视图矩阵 474 | var viewMatrix = new Matrix4(); 475 | var r = sphere.radius + 10; 476 | viewMatrix.lookAt(lightDirection.elements[0] * r, lightDirection.elements[1] * r, lightDirection.elements[2] * r, 0, 0, 0, 0, 1, 0); 477 | 478 | //投影矩阵 479 | var projMatrix = new Matrix4(); 480 | var diameter = sphere.radius * 2.1; 481 | var ratioWH = canvas.width / canvas.height; 482 | var nearHeight = diameter; 483 | var nearWidth = nearHeight * ratioWH; 484 | projMatrix.setOrtho(-nearWidth / 2, nearWidth / 2, -nearHeight / 2, nearHeight / 2, 1, 10000);*/ 485 | 486 | //MVP矩阵 487 | var mvpMatrix = new Matrix4(); 488 | mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatrix); 489 | 490 | //将MVP矩阵传输到着色器的uniform变量u_MvpMatrix 491 | gl.uniformMatrix4fv(drawProgram.u_MvpMatrix, false, mvpMatrix.elements); 492 | } 493 | 494 | //设置MVP矩阵 495 | function setFrameMVPMatrix(gl, sphere, lightDirection, frameProgram) { 496 | //模型矩阵 497 | var modelMatrix = new Matrix4(); 498 | //modelMatrix.scale(curScale, curScale, curScale); 499 | //modelMatrix.rotate(currentAngle[0], 1.0, 0.0, 0.0); // Rotation around x-axis 500 | //modelMatrix.rotate(currentAngle[1], 0.0, 1.0, 0.0); // Rotation around y-axis 501 | modelMatrix.translate(-sphere.centerX, -sphere.centerY, -sphere.centerZ); 502 | 503 | //视图矩阵 504 | var viewMatrix = new Matrix4(); 505 | var r = sphere.radius + 10; 506 | viewMatrix.lookAt(lightDirection.elements[0] * r, lightDirection.elements[1] * r, lightDirection.elements[2] * r, 0, 0, 0, 0, 1, 0); 507 | //viewMatrix.lookAt(0, 0, r, 0, 0, 0, 0, 1, 0); 508 | 509 | //投影矩阵 510 | var projMatrix = new Matrix4(); 511 | var diameter = sphere.radius * 2.1; 512 | var ratioWH = OFFSCREEN_WIDTH / OFFSCREEN_HEIGHT; 513 | var nearHeight = diameter; 514 | var nearWidth = nearHeight * ratioWH; 515 | projMatrix.setOrtho(-nearWidth / 2, nearWidth / 2, -nearHeight / 2, nearHeight / 2, 1, 10000); 516 | 517 | //MVP矩阵 518 | var mvpMatrix = new Matrix4(); 519 | mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatrix); 520 | 521 | //将MVP矩阵传输到着色器的uniform变量u_MvpMatrix 522 | gl.uniformMatrix4fv(frameProgram.u_MvpMatrix, false, mvpMatrix.elements); 523 | 524 | return mvpMatrix; 525 | } 526 | 527 | //获取光线 528 | function getLight() { 529 | // 设置光线方向(世界坐标系下的) 530 | var solarAltitude = 30.0; 531 | var solarAzimuth = 315.0; 532 | var fAltitude = solarAltitude * Math.PI / 180; //光源高度角 533 | var fAzimuth = solarAzimuth * Math.PI / 180; //光源方位角 534 | 535 | var arrayvectorX = Math.cos(fAltitude) * Math.cos(fAzimuth); 536 | var arrayvectorY = Math.cos(fAltitude) * Math.sin(fAzimuth); 537 | var arrayvectorZ = Math.sin(fAltitude); 538 | 539 | var lightDirection = new Vector3([arrayvectorX, arrayvectorY, arrayvectorZ]); 540 | lightDirection.normalize(); // Normalize 541 | return lightDirection; 542 | } 543 | 544 | function initVertexBuffersForDrawDEM(gl, terrain) { 545 | //DEM的一个网格是由两个三角形组成的 546 | // 0------1 1 547 | // | | 548 | // | | 549 | // col col------col+1 550 | var col = terrain.col; 551 | var row = terrain.row; 552 | 553 | var indices = new Uint16Array((row - 1) * (col - 1) * 6); 554 | var ci = 0; 555 | for (var yi = 0; yi < row - 1; yi++) { 556 | //for (var yi = 0; yi < 10; yi++) { 557 | for (var xi = 0; xi < col - 1; xi++) { 558 | indices[ci * 6] = yi * col + xi; 559 | indices[ci * 6 + 1] = (yi + 1) * col + xi; 560 | indices[ci * 6 + 2] = yi * col + xi + 1; 561 | indices[ci * 6 + 3] = (yi + 1) * col + xi; 562 | indices[ci * 6 + 4] = (yi + 1) * col + xi + 1; 563 | indices[ci * 6 + 5] = yi * col + xi + 1; 564 | ci++; 565 | } 566 | } 567 | 568 | var dem = new Object(); // Create the "Object" object to return multiple objects. 569 | 570 | // Write vertex information to buffer object 571 | dem.vertexBuffer = initArrayBufferForLaterUse(gl, terrain.vertices, 3, gl.FLOAT); 572 | dem.colorBuffer = initArrayBufferForLaterUse(gl, terrain.colors, 3, gl.FLOAT); 573 | dem.normalBuffer = initArrayBufferForLaterUse(gl, terrain.normals, 3, gl.FLOAT); 574 | dem.indexBuffer = initElementArrayBufferForLaterUse(gl, indices, gl.UNSIGNED_SHORT); 575 | if (!dem.vertexBuffer || !dem.colorBuffer || !dem.indexBuffer || !dem.normalBuffer) { 576 | return null; 577 | } 578 | 579 | dem.numIndices = indices.length; 580 | 581 | // Unbind the buffer object 582 | gl.bindBuffer(gl.ARRAY_BUFFER, null); 583 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); 584 | 585 | return dem; 586 | } 587 | 588 | // 初始化帧缓冲区对象 (FBO) 589 | function initFramebufferObject(gl) { 590 | var framebuffer, texture, depthBuffer; 591 | 592 | // Define the error handling function 593 | var error = function () { 594 | if (framebuffer) gl.deleteFramebuffer(framebuffer); 595 | if (texture) gl.deleteTexture(texture); 596 | if (depthBuffer) gl.deleteRenderbuffer(depthBuffer); 597 | return null; 598 | } 599 | 600 | // 创建帧缓冲区对象 (FBO) 601 | framebuffer = gl.createFramebuffer(); 602 | if (!framebuffer) { 603 | console.log('Failed to create frame buffer object'); 604 | return error(); 605 | } 606 | 607 | // 创建纹理对象并设置其尺寸和参数 608 | texture = gl.createTexture(); // 创建纹理对象 609 | if (!texture) { 610 | console.log('Failed to create texture object'); 611 | return error(); 612 | } 613 | gl.bindTexture(gl.TEXTURE_2D, texture); // Bind the object to target 614 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, OFFSCREEN_WIDTH, OFFSCREEN_HEIGHT, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); 615 | // 设置纹理参数 616 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 617 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); 618 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 619 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 620 | framebuffer.texture = texture; // 保存纹理对象 621 | 622 | // 创建渲染缓冲区对象并设置其尺寸和参数 623 | depthBuffer = gl.createRenderbuffer(); //创建渲染缓冲区 624 | if (!depthBuffer) { 625 | console.log('Failed to create renderbuffer object'); 626 | return error(); 627 | } 628 | gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer); // Bind the object to target 629 | gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, OFFSCREEN_WIDTH, OFFSCREEN_HEIGHT); 630 | 631 | // 将纹理和渲染缓冲区对象关联到帧缓冲区对象上 632 | gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); 633 | gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); //关联颜色 634 | gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer); //关联深度 635 | 636 | // 检查帧缓冲区是否被正确设置 637 | var e = gl.checkFramebufferStatus(gl.FRAMEBUFFER); 638 | if (gl.FRAMEBUFFER_COMPLETE !== e) { 639 | console.log('Frame buffer object is incomplete: ' + e.toString()); 640 | return error(); 641 | } 642 | 643 | // Unbind the buffer object 644 | gl.bindFramebuffer(gl.FRAMEBUFFER, null); 645 | gl.bindTexture(gl.TEXTURE_2D, null); 646 | gl.bindRenderbuffer(gl.RENDERBUFFER, null); 647 | 648 | return framebuffer; 649 | } 650 | 651 | //分配缓冲区对象并开启连接 652 | function initAttributeVariable(gl, a_attribute, buffer) { 653 | gl.bindBuffer(gl.ARRAY_BUFFER, buffer); 654 | gl.vertexAttribPointer(a_attribute, buffer.num, buffer.type, false, 0, 0); 655 | gl.enableVertexAttribArray(a_attribute); 656 | } 657 | 658 | //向顶点缓冲区写入数据,留待以后分配 659 | function initArrayBufferForLaterUse(gl, data, num, type) { 660 | // Create a buffer object 661 | var buffer = gl.createBuffer(); 662 | if (!buffer) { 663 | console.log('Failed to create the buffer object'); 664 | return null; 665 | } 666 | // Write date into the buffer object 667 | gl.bindBuffer(gl.ARRAY_BUFFER, buffer); 668 | gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW); 669 | 670 | // Store the necessary information to assign the object to the attribute variable later 671 | buffer.num = num; 672 | buffer.type = type; 673 | 674 | return buffer; 675 | } 676 | 677 | //向顶点缓冲区写入索引,留待以后分配 678 | function initElementArrayBufferForLaterUse(gl, data, type) { 679 | // Create a buffer object 680 | var buffer = gl.createBuffer(); 681 | if (!buffer) { 682 | console.log('Failed to create the buffer object'); 683 | return null; 684 | } 685 | // Write date into the buffer object 686 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer); 687 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data, gl.STATIC_DRAW); 688 | 689 | buffer.type = type; 690 | 691 | return buffer; 692 | } -------------------------------------------------------------------------------- /15/TerrainViewer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 显示地形 7 | Hello Triangle 8 | 9 | 10 | 11 |
12 |
13 | 14 | 请使用支持WebGL的浏览器 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /15/TerrainViewer.js: -------------------------------------------------------------------------------- 1 | // 顶点着色器程序 2 | var VSHADER_SOURCE = 3 | 'attribute vec4 a_Position;\n' + //位置 4 | 'attribute vec2 a_TexCoord;\n' + //颜色 5 | 'varying vec2 v_TexCoord;\n' + //纹理坐标 6 | 'uniform mat4 u_MvpMatrix;\n' + 7 | 'void main() {\n' + 8 | ' gl_Position = u_MvpMatrix * a_Position;\n' + // 设置顶点坐标 9 | ' v_TexCoord = a_TexCoord;\n' + //纹理坐标 10 | '}\n'; 11 | 12 | // 片元着色器程序 13 | var FSHADER_SOURCE = 14 | 'precision mediump float;\n' + 15 | 'uniform sampler2D u_Sampler;\n' + 16 | 'varying vec2 v_TexCoord;\n' + //纹理坐标 17 | 'void main() {\n' + 18 | ' gl_FragColor = texture2D(u_Sampler, v_TexCoord);\n' + 19 | '}\n'; 20 | 21 | //定义一个矩形体:混合构造函数原型模式 22 | function Cuboid(minX, maxX, minY, maxY, minZ, maxZ) { 23 | this.minX = minX; 24 | this.maxX = maxX; 25 | this.minY = minY; 26 | this.maxY = maxY; 27 | this.minZ = minZ; 28 | this.maxZ = maxZ; 29 | } 30 | 31 | Cuboid.prototype = { 32 | constructor: Cuboid, 33 | CenterX: function () { 34 | return (this.minX + this.maxX) / 2.0; 35 | }, 36 | CenterY: function () { 37 | return (this.minY + this.maxY) / 2.0; 38 | }, 39 | CenterZ: function () { 40 | return (this.minZ + this.maxZ) / 2.0; 41 | }, 42 | LengthX: function () { 43 | return (this.maxX - this.minX); 44 | }, 45 | LengthY: function () { 46 | return (this.maxY - this.minY); 47 | } 48 | } 49 | 50 | var currentAngle = [0.0, 0.0]; // 绕X轴Y轴的旋转角度 ([x-axis, y-axis]) 51 | var curScale = 1.0; //当前的缩放比例 52 | 53 | //获取文件路径的后缀,为小写 54 | function getFileSuffix(filePath) { 55 | var index = filePath.lastIndexOf("."); 56 | var suffix = filePath.substr(index + 1); 57 | return suffix.toLowerCase(); 58 | } 59 | 60 | function main() { 61 | var demFile = document.getElementById('demFile'); 62 | if (!demFile) { 63 | console.log("Failed to get demFile element!"); 64 | return; 65 | } 66 | 67 | //加载文件后的事件 68 | demFile.addEventListener("change", function (event) { 69 | //判断浏览器是否支持FileReader接口 70 | if (typeof FileReader == 'undefined') { 71 | console.log("你的浏览器不支持FileReader接口!"); 72 | return; 73 | } 74 | 75 | //读取文件后的事件 76 | var reader = new FileReader(); 77 | reader.onload = function () { 78 | if (reader.result) { 79 | var gltfObj = JSON.parse(reader.result); 80 | 81 | for (var fi = 0; fi < input.files.length; fi++) { 82 | //读取bin文件 83 | if (gltfObj.buffers[0].uri === input.files[fi].name) { 84 | var binReader = new FileReader(); 85 | binReader.onload = function () { 86 | if (binReader.result) { 87 | for (var fi = 0; fi < input.files.length; fi++) { 88 | if (gltfObj.images[0].uri === input.files[fi].name) { 89 | //读取纹理图像 90 | var imgReader = new FileReader(); 91 | 92 | imgReader.onload = function () { 93 | //创建一个image对象 94 | var image = new Image(); 95 | if (!image) { 96 | console.log('Failed to create the image object'); 97 | return false; 98 | } 99 | 100 | //图像加载的响应函数 101 | image.onload = function () { 102 | //绘制函数 103 | onDraw(gl, canvas, gltfObj, binReader.result, image); 104 | }; 105 | 106 | //浏览器开始加载图像 107 | image.src = imgReader.result; 108 | } 109 | 110 | imgReader.readAsDataURL(input.files[fi]); //按照base64格式读取 111 | break; 112 | } 113 | } 114 | } 115 | } 116 | binReader.readAsArrayBuffer(input.files[fi]); //按照ArrayBuffer格式读取 117 | break; 118 | } 119 | } 120 | } 121 | } 122 | 123 | var input = event.target; 124 | 125 | var flag = false; 126 | for (var fi = 0; fi < input.files.length; fi++) { 127 | if (getFileSuffix(input.files[fi].name) === "gltf") { 128 | flag = true; 129 | reader.readAsText(input.files[fi]); //按照字符串格式读取 130 | break; 131 | } 132 | } 133 | 134 | if (!flag) { 135 | alert("没有找到gltf"); 136 | } 137 | }); 138 | 139 | // 获取 元素 140 | var canvas = document.getElementById('webgl'); 141 | 142 | // 获取WebGL渲染上下文 143 | var gl = getWebGLContext(canvas); 144 | if (!gl) { 145 | console.log('Failed to get the rendering context for WebGL'); 146 | return; 147 | } 148 | 149 | // 初始化着色器 150 | if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) { 151 | console.log('Failed to intialize shaders.'); 152 | return; 153 | } 154 | 155 | // 指定清空的颜色 156 | gl.clearColor(0.0, 0.0, 0.0, 1.0); 157 | 158 | // 开启深度测试 159 | gl.enable(gl.DEPTH_TEST); 160 | 161 | //清空颜色和深度缓冲区 162 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 163 | } 164 | 165 | //绘制函数 166 | function onDraw(gl, canvas, gltfObj, binBuf, image) { 167 | // 设置顶点位置 168 | var n = initVertexBuffers(gl, gltfObj, binBuf); 169 | if (n < 0) { 170 | console.log('Failed to set the positions of the vertices'); 171 | return; 172 | } 173 | 174 | //设置纹理 175 | if (!loadTexture(gl, image)) { 176 | console.log('Failed to set the Texture!'); 177 | } 178 | 179 | //注册鼠标事件 180 | initEventHandlers(canvas); 181 | 182 | //绘制函数 183 | var tick = function () { 184 | //设置MVP矩阵 185 | setMVPMatrix(gl, canvas, gltfObj.cuboid); 186 | 187 | //清空颜色和深度缓冲区 188 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 189 | 190 | //绘制矩形体 191 | gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_SHORT, 0); 192 | 193 | //请求浏览器调用tick 194 | requestAnimationFrame(tick); 195 | }; 196 | 197 | //开始绘制 198 | tick(); 199 | } 200 | 201 | function loadTexture(gl, image) { 202 | // 创建纹理对象 203 | var texture = gl.createTexture(); 204 | if (!texture) { 205 | console.log('Failed to create the texture object'); 206 | return false; 207 | } 208 | 209 | // 开启0号纹理单元 210 | gl.activeTexture(gl.TEXTURE0); 211 | // 绑定纹理对象 212 | gl.bindTexture(gl.TEXTURE_2D, texture); 213 | 214 | // 设置纹理参数 215 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 216 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); 217 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 218 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 219 | 220 | // 配置纹理图像 221 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image); 222 | 223 | // 将0号单元纹理传递给着色器中的取样器变量 224 | var u_Sampler = gl.getUniformLocation(gl.program, 'u_Sampler'); 225 | if (!u_Sampler) { 226 | console.log('Failed to get the storage location of u_Sampler'); 227 | return false; 228 | } 229 | gl.uniform1i(u_Sampler, 0); 230 | 231 | return true; 232 | } 233 | 234 | //注册鼠标事件 235 | function initEventHandlers(canvas) { 236 | var dragging = false; // Dragging or not 237 | var lastX = -1, 238 | lastY = -1; // Last position of the mouse 239 | 240 | //鼠标按下 241 | canvas.onmousedown = function (ev) { 242 | var x = ev.clientX; 243 | var y = ev.clientY; 244 | // Start dragging if a moue is in 245 | var rect = ev.target.getBoundingClientRect(); 246 | if (rect.left <= x && x < rect.right && rect.top <= y && y < rect.bottom) { 247 | lastX = x; 248 | lastY = y; 249 | dragging = true; 250 | } 251 | }; 252 | 253 | //鼠标离开时 254 | canvas.onmouseleave = function (ev) { 255 | dragging = false; 256 | }; 257 | 258 | //鼠标释放 259 | canvas.onmouseup = function (ev) { 260 | dragging = false; 261 | }; 262 | 263 | //鼠标移动 264 | canvas.onmousemove = function (ev) { 265 | var x = ev.clientX; 266 | var y = ev.clientY; 267 | if (dragging) { 268 | var factor = 100 / canvas.height; // The rotation ratio 269 | var dx = factor * (x - lastX); 270 | var dy = factor * (y - lastY); 271 | currentAngle[0] = currentAngle[0] + dy; 272 | currentAngle[1] = currentAngle[1] + dx; 273 | } 274 | lastX = x, lastY = y; 275 | }; 276 | 277 | //鼠标缩放 278 | canvas.onmousewheel = function (event) { 279 | if (event.wheelDelta > 0) { 280 | curScale = curScale * 1.1; 281 | } else { 282 | curScale = curScale * 0.9; 283 | } 284 | }; 285 | } 286 | 287 | //设置MVP矩阵 288 | function setMVPMatrix(gl, canvas, cuboid) { 289 | // Get the storage location of u_MvpMatrix 290 | var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix'); 291 | if (!u_MvpMatrix) { 292 | console.log('Failed to get the storage location of u_MvpMatrix'); 293 | return; 294 | } 295 | 296 | //模型矩阵 297 | var modelMatrix = new Matrix4(); 298 | modelMatrix.scale(curScale, curScale, curScale); 299 | modelMatrix.rotate(currentAngle[0], 1.0, 0.0, 0.0); // Rotation around x-axis 300 | modelMatrix.rotate(currentAngle[1], 0.0, 1.0, 0.0); // Rotation around y-axis 301 | modelMatrix.translate(-cuboid.CenterX(), -cuboid.CenterY(), -cuboid.CenterZ()); 302 | 303 | //投影矩阵 304 | var fovy = 60; 305 | var near = 1; 306 | var projMatrix = new Matrix4(); 307 | projMatrix.setPerspective(fovy, canvas.width / canvas.height, 1, 10000); 308 | 309 | //计算lookAt()函数初始视点的高度 310 | var angle = fovy / 2 * Math.PI / 180.0; 311 | var eyeHight = (cuboid.LengthY() * 1.2) / 2.0 / angle; 312 | 313 | //视图矩阵 314 | var viewMatrix = new Matrix4(); // View matrix 315 | viewMatrix.lookAt(0, 0, eyeHight, 0, 0, 0, 0, 1, 0); 316 | 317 | //MVP矩阵 318 | var mvpMatrix = new Matrix4(); 319 | mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatrix); 320 | 321 | //将MVP矩阵传输到着色器的uniform变量u_MvpMatrix 322 | gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements); 323 | } 324 | 325 | // 326 | function initVertexBuffers(gl, gltfObj, binBuf) { 327 | //获取顶点数据位置信息 328 | var positionAccessorId = gltfObj.meshes[0].primitives[0].attributes.POSITION; 329 | if (gltfObj.accessors[positionAccessorId].componentType != 5126) { 330 | return 0; 331 | } 332 | 333 | var positionBufferViewId = gltfObj.accessors[positionAccessorId].bufferView; 334 | var verticesColors = new Float32Array(binBuf, gltfObj.bufferViews[positionBufferViewId].byteOffset, gltfObj.bufferViews[positionBufferViewId].byteLength / Float32Array.BYTES_PER_ELEMENT); 335 | 336 | gltfObj.cuboid = new Cuboid(gltfObj.accessors[positionAccessorId].min[0], gltfObj.accessors[positionAccessorId].max[0], gltfObj.accessors[positionAccessorId].min[1], gltfObj.accessors[positionAccessorId].max[1], gltfObj.accessors[positionAccessorId].min[2], gltfObj.accessors[positionAccessorId].max[2]); 337 | 338 | // 创建缓冲区对象 339 | var vertexColorBuffer = gl.createBuffer(); 340 | var indexBuffer = gl.createBuffer(); 341 | if (!vertexColorBuffer || !indexBuffer) { 342 | console.log('Failed to create the buffer object'); 343 | return -1; 344 | } 345 | 346 | // 将缓冲区对象绑定到目标 347 | gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer); 348 | // 向缓冲区对象写入数据 349 | gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW); 350 | 351 | //获取着色器中attribute变量a_Position的地址 352 | var a_Position = gl.getAttribLocation(gl.program, 'a_Position'); 353 | if (a_Position < 0) { 354 | console.log('Failed to get the storage location of a_Position'); 355 | return -1; 356 | } 357 | 358 | // 将缓冲区对象分配给a_Position变量 359 | gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, gltfObj.bufferViews[positionBufferViewId].byteStride, gltfObj.accessors[positionAccessorId].byteOffset); 360 | 361 | // 连接a_Position变量与分配给它的缓冲区对象 362 | gl.enableVertexAttribArray(a_Position); 363 | 364 | //获取顶点数据纹理信息 365 | var txtCoordAccessorId = gltfObj.meshes[0].primitives[0].attributes.TEXCOORD_0; 366 | if (gltfObj.accessors[txtCoordAccessorId].componentType != 5126) { 367 | return 0; 368 | } 369 | var txtCoordBufferViewId = gltfObj.accessors[txtCoordAccessorId].bufferView; 370 | 371 | //获取着色器中attribute变量a_TxtCoord的地址 372 | var a_TexCoord = gl.getAttribLocation(gl.program, 'a_TexCoord'); 373 | if (a_TexCoord < 0) { 374 | console.log('Failed to get the storage location of a_TexCoord'); 375 | return -1; 376 | } 377 | // 将缓冲区对象分配给a_Color变量 378 | gl.vertexAttribPointer(a_TexCoord, 2, gl.FLOAT, false, gltfObj.bufferViews[txtCoordBufferViewId].byteStride, gltfObj.accessors[txtCoordAccessorId].byteOffset); 379 | // 连接a_Color变量与分配给它的缓冲区对象 380 | gl.enableVertexAttribArray(a_TexCoord); 381 | 382 | //获取顶点数据索引信息 383 | var indicesAccessorId = gltfObj.meshes[0].primitives[0].indices; 384 | var indicesBufferViewId = gltfObj.accessors[indicesAccessorId].bufferView; 385 | var indices = new Uint16Array(binBuf, gltfObj.bufferViews[indicesBufferViewId].byteOffset, gltfObj.bufferViews[indicesBufferViewId].byteLength / Uint16Array.BYTES_PER_ELEMENT); 386 | 387 | // 将顶点索引写入到缓冲区对象 388 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); 389 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); 390 | 391 | return indices.length; 392 | } -------------------------------------------------------------------------------- /2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fafa1899/WebGLTutorial/4431734a5550fd5a33b19d91fedda478f7454804/2.gif -------------------------------------------------------------------------------- /2/HelloPoint1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Draw a point (1) 6 | 7 | 8 | 9 | 10 | Please use a browser that supports "canvas" 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /2/HelloPoint1.js: -------------------------------------------------------------------------------- 1 | // 顶点着色器程序 2 | var VSHADER_SOURCE = 3 | 'attribute vec4 a_Position;\n' + // attribute variable 4 | 'void main() {\n' + 5 | ' gl_Position = a_Position;\n' + // Set the vertex coordinates of the point 6 | ' gl_PointSize = 10.0;\n' + // Set the point size 7 | '}\n'; 8 | 9 | // 片元着色器程序 10 | var FSHADER_SOURCE = 11 | 'precision mediump float;\n' + 12 | 'uniform vec4 u_FragColor;\n' + // uniform変数 13 | 'void main() {\n' + 14 | ' gl_FragColor = u_FragColor;\n' + // Set the point color 15 | '}\n'; 16 | 17 | function main() { 18 | // 获取 元素 19 | var canvas = document.getElementById('webgl'); 20 | 21 | // 获取WebGL渲染上下文 22 | var gl = getWebGLContext(canvas); 23 | if (!gl) { 24 | console.log('Failed to get the rendering context for WebGL'); 25 | return; 26 | } 27 | 28 | // 初始化着色器 29 | if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) { 30 | console.log('Failed to intialize shaders.'); 31 | return; 32 | } 33 | 34 | // 获取attribute变量的存储位置 35 | var a_Position = gl.getAttribLocation(gl.program, 'a_Position'); 36 | if (a_Position < 0) { 37 | console.log('Failed to get the storage location of a_Position'); 38 | return; 39 | } 40 | 41 | // 将顶点位置传输给attribute变量 42 | gl.vertexAttrib3f(a_Position, 0.5, 0.5, 0.0); 43 | 44 | //获取u_FragColor变量的存储地址 45 | var u_FragColor = gl.getUniformLocation(gl.program, 'u_FragColor'); 46 | if (!u_FragColor) { 47 | console.log('Failed to get the storage location of u_FragColor'); 48 | return; 49 | } 50 | 51 | //将点的颜色传入到u_FragColor变量中 52 | gl.uniform4f(u_FragColor, 0.0, 0.8, 0.0, 1.0); 53 | 54 | // 指定清空的颜色 55 | gl.clearColor(0.0, 0.0, 0.0, 1.0); 56 | 57 | // 清空 58 | gl.clear(gl.COLOR_BUFFER_BIT); 59 | 60 | // 绘制一个点 61 | gl.drawArrays(gl.POINTS, 0, 1); 62 | } 63 | -------------------------------------------------------------------------------- /3/HelloTriangle.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello Triangle 6 | 7 | 8 | 9 | 10 | Please use a browser that supports "canvas" 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /3/HelloTriangle.js: -------------------------------------------------------------------------------- 1 | // 顶点着色器程序 2 | var VSHADER_SOURCE = 3 | 'attribute vec4 a_Position;\n' + // attribute variable 4 | 'void main() {\n' + 5 | ' gl_Position = a_Position;\n' + // Set the vertex coordinates of the point 6 | '}\n'; 7 | 8 | // 片元着色器程序 9 | var FSHADER_SOURCE = 10 | 'precision mediump float;\n' + 11 | 'uniform vec4 u_FragColor;\n' + // uniform変数 12 | 'void main() {\n' + 13 | ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' + 14 | '}\n'; 15 | 16 | function main() { 17 | // 获取 元素 18 | var canvas = document.getElementById('webgl'); 19 | 20 | // 获取WebGL渲染上下文 21 | var gl = getWebGLContext(canvas); 22 | if (!gl) { 23 | console.log('Failed to get the rendering context for WebGL'); 24 | return; 25 | } 26 | 27 | // 初始化着色器 28 | if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) { 29 | console.log('Failed to intialize shaders.'); 30 | return; 31 | } 32 | 33 | // 设置顶点位置 34 | var n = initVertexBuffers(gl); 35 | if (n < 0) { 36 | console.log('Failed to set the positions of the vertices'); 37 | return; 38 | } 39 | 40 | // 指定清空的颜色 41 | gl.clearColor(0.0, 0.0, 0.0, 1.0); 42 | 43 | // 清空 44 | gl.clear(gl.COLOR_BUFFER_BIT); 45 | 46 | // 绘制三角形 47 | gl.drawArrays(gl.TRIANGLES, 0, 3); 48 | } 49 | 50 | function initVertexBuffers(gl) { 51 | var vertices = new Float32Array([ 52 | 0, 0.5, -0.5, -0.5, 0.5, -0.5 53 | ]); 54 | var n = 3; // 点的个数 55 | 56 | // 创建缓冲区对象 57 | var vertexBuffer = gl.createBuffer(); 58 | if (!vertexBuffer) { 59 | console.log('Failed to create the buffer object'); 60 | return -1; 61 | } 62 | 63 | // 将缓冲区对象绑定到目标 64 | gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); 65 | // 向缓冲区对象写入数据 66 | gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); 67 | 68 | var a_Position = gl.getAttribLocation(gl.program, 'a_Position'); 69 | if (a_Position < 0) { 70 | console.log('Failed to get the storage location of a_Position'); 71 | return -1; 72 | } 73 | // 将缓冲区对象分配给a_Position变量 74 | gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0); 75 | 76 | // 连接a_Position变量与分配给它的缓冲区对象 77 | gl.enableVertexAttribArray(a_Position); 78 | 79 | return n; 80 | } -------------------------------------------------------------------------------- /4/ColoredTriangle.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello Triangle 6 | 7 | 8 | 9 | 10 | Please use a browser that supports "canvas" 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /4/ColoredTriangle.js: -------------------------------------------------------------------------------- 1 | // 顶点着色器程序 2 | var VSHADER_SOURCE = 3 | 'attribute vec4 a_Position;\n' + // attribute variable 4 | 'attribute vec4 a_Color;\n' + 5 | 'varying vec4 v_Color;\n' + 6 | 'void main() {\n' + 7 | ' gl_Position = a_Position;\n' + // Set the vertex coordinates of the point 8 | ' v_Color = a_Color;\n' + 9 | '}\n'; 10 | 11 | // 片元着色器程序 12 | var FSHADER_SOURCE = 13 | 'precision mediump float;\n' + 14 | 'varying vec4 v_Color;\n' + 15 | 'void main() {\n' + 16 | ' gl_FragColor = v_Color;\n' + 17 | '}\n'; 18 | 19 | function main() { 20 | // 获取 元素 21 | var canvas = document.getElementById('webgl'); 22 | 23 | // 获取WebGL渲染上下文 24 | var gl = getWebGLContext(canvas); 25 | if (!gl) { 26 | console.log('Failed to get the rendering context for WebGL'); 27 | return; 28 | } 29 | 30 | // 初始化着色器 31 | if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) { 32 | console.log('Failed to intialize shaders.'); 33 | return; 34 | } 35 | 36 | // 设置顶点位置 37 | var n = initVertexBuffers(gl); 38 | if (n < 0) { 39 | console.log('Failed to set the positions of the vertices'); 40 | return; 41 | } 42 | 43 | // 指定清空的颜色 44 | gl.clearColor(0.0, 0.0, 0.0, 1.0); 45 | 46 | // 清空 47 | gl.clear(gl.COLOR_BUFFER_BIT); 48 | 49 | // 绘制三角形 50 | gl.drawArrays(gl.TRIANGLES, 0, n); 51 | } 52 | 53 | function initVertexBuffers(gl) { 54 | // 顶点坐标和颜色 55 | var verticesColors = new Float32Array([ 56 | 0.0, 0.5, 1.0, 0.0, 0.0, 57 | -0.5, -0.5, 0.0, 1.0, 0.0, 58 | 0.5, -0.5, 0.0, 0.0, 1.0, 59 | ]); 60 | 61 | // 62 | var n = 3; // 点的个数 63 | var FSIZE = verticesColors.BYTES_PER_ELEMENT; //数组中每个元素的字节数 64 | 65 | // 创建缓冲区对象 66 | var vertexBuffer = gl.createBuffer(); 67 | if (!vertexBuffer) { 68 | console.log('Failed to create the buffer object'); 69 | return -1; 70 | } 71 | 72 | // 将缓冲区对象绑定到目标 73 | gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); 74 | // 向缓冲区对象写入数据 75 | gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW); 76 | 77 | //获取着色器中attribute变量a_Position的地址 78 | var a_Position = gl.getAttribLocation(gl.program, 'a_Position'); 79 | if (a_Position < 0) { 80 | console.log('Failed to get the storage location of a_Position'); 81 | return -1; 82 | } 83 | // 将缓冲区对象分配给a_Position变量 84 | gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 5*FSIZE, 0); 85 | 86 | // 连接a_Position变量与分配给它的缓冲区对象 87 | gl.enableVertexAttribArray(a_Position); 88 | 89 | //获取着色器中attribute变量a_Color的地址 90 | var a_Color = gl.getAttribLocation(gl.program, 'a_Color'); 91 | if(a_Color < 0) { 92 | console.log('Failed to get the storage location of a_Color'); 93 | return -1; 94 | } 95 | // 将缓冲区对象分配给a_Color变量 96 | gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 5, FSIZE * 2); 97 | // 连接a_Color变量与分配给它的缓冲区对象 98 | gl.enableVertexAttribArray(a_Color); 99 | 100 | // 解除绑定 101 | gl.bindBuffer(gl.ARRAY_BUFFER, null); 102 | 103 | return n; 104 | } -------------------------------------------------------------------------------- /6/Triangle_MVPMatrix.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello Triangle 6 | 7 | 8 | 9 | 10 | Please use a browser that supports "canvas" 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /6/Triangle_MVPMatrix.js: -------------------------------------------------------------------------------- 1 | // 顶点着色器程序 2 | var VSHADER_SOURCE = 3 | 'attribute vec4 a_Position;\n' + // attribute variable 4 | 'attribute vec4 a_Color;\n' + 5 | 'uniform mat4 u_MvpMatrix;\n' + 6 | 'varying vec4 v_Color;\n' + 7 | 'void main() {\n' + 8 | ' gl_Position = u_MvpMatrix * a_Position;\n' + // Set the vertex coordinates of the point 9 | ' v_Color = a_Color;\n' + 10 | '}\n'; 11 | 12 | // 片元着色器程序 13 | var FSHADER_SOURCE = 14 | 'precision mediump float;\n' + 15 | 'varying vec4 v_Color;\n' + 16 | 'void main() {\n' + 17 | ' gl_FragColor = v_Color;\n' + 18 | '}\n'; 19 | 20 | function main() { 21 | // 获取 元素 22 | var canvas = document.getElementById('webgl'); 23 | 24 | // 获取WebGL渲染上下文 25 | var gl = getWebGLContext(canvas); 26 | if (!gl) { 27 | console.log('Failed to get the rendering context for WebGL'); 28 | return; 29 | } 30 | 31 | // 初始化着色器 32 | if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) { 33 | console.log('Failed to intialize shaders.'); 34 | return; 35 | } 36 | 37 | // 设置顶点位置 38 | var n = initVertexBuffers(gl); 39 | if (n < 0) { 40 | console.log('Failed to set the positions of the vertices'); 41 | return; 42 | } 43 | 44 | //设置MVP矩阵 45 | setMVPMatrix(gl,canvas); 46 | 47 | // 指定清空的颜色 48 | gl.clearColor(0.0, 0.0, 0.0, 1.0); 49 | 50 | // 开启深度测试 51 | gl.enable(gl.DEPTH_TEST); 52 | 53 | // 清空颜色和深度缓冲区 54 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 55 | 56 | // 绘制三角形 57 | gl.drawArrays(gl.TRIANGLES, 0, n); 58 | } 59 | 60 | //设置MVP矩阵 61 | function setMVPMatrix(gl,canvas) { 62 | // Get the storage location of u_MvpMatrix 63 | var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix'); 64 | if (!u_MvpMatrix) { 65 | console.log('Failed to get the storage location of u_MvpMatrix'); 66 | return; 67 | } 68 | 69 | //模型矩阵 70 | var modelMatrix = new Matrix4(); 71 | modelMatrix.setTranslate(0.75, 0, 0); 72 | 73 | //视图矩阵 74 | var viewMatrix = new Matrix4(); // View matrix 75 | viewMatrix.setLookAt(0, 0, 5, 0, 0, -100, 0, 1, 0); 76 | 77 | //投影矩阵 78 | var projMatrix = new Matrix4(); // Projection matrix 79 | projMatrix.setPerspective(30, canvas.width / canvas.height, 1, 100); 80 | 81 | //MVP矩阵 82 | var mvpMatrix = new Matrix4(); 83 | mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatrix); 84 | 85 | //将MVP矩阵传输到着色器的uniform变量u_MvpMatrix 86 | gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements); 87 | } 88 | 89 | // 90 | function initVertexBuffers(gl) { 91 | // 顶点坐标和颜色 92 | var verticesColors = new Float32Array([ 93 | 0.0, 1.0, -4.0, 0.4, 1.0, 0.4, //绿色在后 94 | -0.5, -1.0, -4.0, 0.4, 1.0, 0.4, 95 | 0.5, -1.0, -4.0, 1.0, 0.4, 0.4, 96 | 97 | 0.0, 1.0, -2.0, 1.0, 1.0, 0.4, //黄色在中 98 | -0.5, -1.0, -2.0, 1.0, 1.0, 0.4, 99 | 0.5, -1.0, -2.0, 1.0, 0.4, 0.4, 100 | 101 | 0.0, 1.0, 0.0, 0.4, 0.4, 1.0, //蓝色在前 102 | -0.5, -1.0, 0.0, 0.4, 0.4, 1.0, 103 | 0.5, -1.0, 0.0, 1.0, 0.4, 0.4, 104 | ]); 105 | 106 | // 107 | var n = 9; // 点的个数 108 | var FSIZE = verticesColors.BYTES_PER_ELEMENT; //数组中每个元素的字节数 109 | 110 | // 创建缓冲区对象 111 | var vertexBuffer = gl.createBuffer(); 112 | if (!vertexBuffer) { 113 | console.log('Failed to create the buffer object'); 114 | return -1; 115 | } 116 | 117 | // 将缓冲区对象绑定到目标 118 | gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); 119 | // 向缓冲区对象写入数据 120 | gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW); 121 | 122 | //获取着色器中attribute变量a_Position的地址 123 | var a_Position = gl.getAttribLocation(gl.program, 'a_Position'); 124 | if (a_Position < 0) { 125 | console.log('Failed to get the storage location of a_Position'); 126 | return -1; 127 | } 128 | // 将缓冲区对象分配给a_Position变量 129 | gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE * 6, 0); 130 | 131 | // 连接a_Position变量与分配给它的缓冲区对象 132 | gl.enableVertexAttribArray(a_Position); 133 | 134 | //获取着色器中attribute变量a_Color的地址 135 | var a_Color = gl.getAttribLocation(gl.program, 'a_Color'); 136 | if (a_Color < 0) { 137 | console.log('Failed to get the storage location of a_Color'); 138 | return -1; 139 | } 140 | // 将缓冲区对象分配给a_Color变量 141 | gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 6, FSIZE * 3); 142 | // 连接a_Color变量与分配给它的缓冲区对象 143 | gl.enableVertexAttribArray(a_Color); 144 | 145 | // 解除绑定 146 | gl.bindBuffer(gl.ARRAY_BUFFER, null); 147 | 148 | return n; 149 | } -------------------------------------------------------------------------------- /7/Cuboid.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello Triangle 6 | 7 | 8 | 9 | 10 | Please use a browser that supports "canvas" 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /7/Cuboid.js: -------------------------------------------------------------------------------- 1 | // 顶点着色器程序 2 | var VSHADER_SOURCE = 3 | 'attribute vec4 a_Position;\n' + // attribute variable 4 | 'attribute vec4 a_Color;\n' + 5 | 'uniform mat4 u_MvpMatrix;\n' + 6 | 'varying vec4 v_Color;\n' + 7 | 'void main() {\n' + 8 | ' gl_Position = u_MvpMatrix * a_Position;\n' + // Set the vertex coordinates of the point 9 | ' v_Color = a_Color;\n' + 10 | '}\n'; 11 | 12 | // 片元着色器程序 13 | var FSHADER_SOURCE = 14 | 'precision mediump float;\n' + 15 | 'varying vec4 v_Color;\n' + 16 | 'void main() {\n' + 17 | ' gl_FragColor = v_Color;\n' + 18 | '}\n'; 19 | 20 | //定义一个矩形体:混合构造函数原型模式 21 | function Cuboid(minX, maxX, minY, maxY, minZ, maxZ) { 22 | this.minX = minX; 23 | this.maxX = maxX; 24 | this.minY = minY; 25 | this.maxY = maxY; 26 | this.minZ = minZ; 27 | this.maxZ = maxZ; 28 | } 29 | 30 | Cuboid.prototype = { 31 | constructor: Cuboid, 32 | CenterX: function () { 33 | return (this.minX + this.maxX) / 2.0; 34 | }, 35 | CenterY: function () { 36 | return (this.minY + this.maxY) / 2.0; 37 | }, 38 | CenterZ: function () { 39 | return (this.minZ + this.maxZ) / 2.0; 40 | }, 41 | LengthX: function () { 42 | return (this.maxX - this.minX); 43 | }, 44 | LengthY: function () { 45 | return (this.maxY - this.minY); 46 | } 47 | } 48 | 49 | var currentAngle = [35.0, 30.0]; // 绕X轴Y轴的旋转角度 ([x-axis, y-axis]) 50 | var curScale = 1.0; //当前的缩放比例 51 | 52 | function main() { 53 | // 获取 元素 54 | var canvas = document.getElementById('webgl'); 55 | 56 | // 获取WebGL渲染上下文 57 | var gl = getWebGLContext(canvas); 58 | if (!gl) { 59 | console.log('Failed to get the rendering context for WebGL'); 60 | return; 61 | } 62 | 63 | // 初始化着色器 64 | if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) { 65 | console.log('Failed to intialize shaders.'); 66 | return; 67 | } 68 | 69 | // 设置顶点位置 70 | var cuboid = new Cuboid(399589.072, 400469.072, 3995118.062, 3997558.062, 732, 1268); 71 | var n = initVertexBuffers(gl, cuboid); 72 | if (n < 0) { 73 | console.log('Failed to set the positions of the vertices'); 74 | return; 75 | } 76 | 77 | // 指定清空的颜色 78 | gl.clearColor(0.0, 0.0, 0.0, 1.0); 79 | 80 | // 开启深度测试 81 | gl.enable(gl.DEPTH_TEST); 82 | 83 | //绘制函数 84 | var tick = function () { 85 | //设置MVP矩阵 86 | setMVPMatrix(gl, canvas, cuboid); 87 | 88 | //清空颜色和深度缓冲区 89 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 90 | 91 | //绘制矩形体 92 | gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0); 93 | 94 | //请求浏览器调用tick 95 | requestAnimationFrame(tick); 96 | }; 97 | 98 | //开始绘制 99 | tick(); 100 | 101 | // 绘制矩形体 102 | gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0); 103 | } 104 | 105 | //设置MVP矩阵 106 | function setMVPMatrix(gl, canvas, cuboid) { 107 | // Get the storage location of u_MvpMatrix 108 | var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix'); 109 | if (!u_MvpMatrix) { 110 | console.log('Failed to get the storage location of u_MvpMatrix'); 111 | return; 112 | } 113 | 114 | //模型矩阵 115 | var modelMatrix = new Matrix4(); 116 | modelMatrix.scale(curScale, curScale, curScale); 117 | modelMatrix.rotate(currentAngle[0], 1.0, 0.0, 0.0); // Rotation around x-axis 118 | modelMatrix.rotate(currentAngle[1], 0.0, 1.0, 0.0); // Rotation around y-axis 119 | modelMatrix.translate(-cuboid.CenterX(), -cuboid.CenterY(), -cuboid.CenterZ()); 120 | 121 | //投影矩阵 122 | var fovy = 60; 123 | var near = 1; 124 | var projMatrix = new Matrix4(); 125 | projMatrix.setPerspective(fovy, canvas.width / canvas.height, 1, 10000); 126 | 127 | //计算lookAt()函数初始视点的高度 128 | var angle = fovy / 2 * Math.PI / 180.0; 129 | var eyeHight = (cuboid.LengthY() * 1.2) / 2.0 / angle; 130 | 131 | //视图矩阵 132 | var viewMatrix = new Matrix4(); // View matrix 133 | viewMatrix.lookAt(0, 0, eyeHight, 0, 0, 0, 0, 1, 0); 134 | 135 | //MVP矩阵 136 | var mvpMatrix = new Matrix4(); 137 | mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatrix); 138 | 139 | //将MVP矩阵传输到着色器的uniform变量u_MvpMatrix 140 | gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements); 141 | } 142 | 143 | // 144 | function initVertexBuffers(gl, cuboid) { 145 | // Create a cube 146 | // v6----- v5 147 | // /| /| 148 | // v1------v0| 149 | // | | | | 150 | // | |v7---|-|v4 151 | // |/ |/ 152 | // v2------v3 153 | // 顶点坐标和颜色 154 | var verticesColors = new Float32Array([ 155 | cuboid.maxX, cuboid.maxY, cuboid.maxZ, 1.0, 1.0, 1.0, // v0 White 156 | cuboid.minX, cuboid.maxY, cuboid.maxZ, 1.0, 0.0, 1.0, // v1 Magenta 157 | cuboid.minX, cuboid.minY, cuboid.maxZ, 1.0, 0.0, 0.0, // v2 Red 158 | cuboid.maxX, cuboid.minY, cuboid.maxZ, 1.0, 1.0, 0.0, // v3 Yellow 159 | cuboid.maxX, cuboid.minY, cuboid.minZ, 0.0, 1.0, 0.0, // v4 Green 160 | cuboid.maxX, cuboid.maxY, cuboid.minZ, 0.0, 1.0, 1.0, // v5 Cyan 161 | cuboid.minX, cuboid.maxY, cuboid.minZ, 0.0, 0.0, 1.0, // v6 Blue 162 | cuboid.minX, cuboid.minY, cuboid.minZ, 1.0, 0.0, 1.0 // v7 Black 163 | ]); 164 | 165 | //顶点索引 166 | var indices = new Uint8Array([ 167 | 0, 1, 2, 0, 2, 3, // 前 168 | 0, 3, 4, 0, 4, 5, // 右 169 | 0, 5, 6, 0, 6, 1, // 上 170 | 1, 6, 7, 1, 7, 2, // 左 171 | 7, 4, 3, 7, 3, 2, // 下 172 | 4, 7, 6, 4, 6, 5 // 后 173 | ]); 174 | 175 | // 176 | var FSIZE = verticesColors.BYTES_PER_ELEMENT; //数组中每个元素的字节数 177 | 178 | // 创建缓冲区对象 179 | var vertexColorBuffer = gl.createBuffer(); 180 | var indexBuffer = gl.createBuffer(); 181 | if (!vertexColorBuffer || !indexBuffer) { 182 | console.log('Failed to create the buffer object'); 183 | return -1; 184 | } 185 | 186 | // 将缓冲区对象绑定到目标 187 | gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer); 188 | // 向缓冲区对象写入数据 189 | gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW); 190 | 191 | //获取着色器中attribute变量a_Position的地址 192 | var a_Position = gl.getAttribLocation(gl.program, 'a_Position'); 193 | if (a_Position < 0) { 194 | console.log('Failed to get the storage location of a_Position'); 195 | return -1; 196 | } 197 | // 将缓冲区对象分配给a_Position变量 198 | gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE * 6, 0); 199 | 200 | // 连接a_Position变量与分配给它的缓冲区对象 201 | gl.enableVertexAttribArray(a_Position); 202 | 203 | //获取着色器中attribute变量a_Color的地址 204 | var a_Color = gl.getAttribLocation(gl.program, 'a_Color'); 205 | if (a_Color < 0) { 206 | console.log('Failed to get the storage location of a_Color'); 207 | return -1; 208 | } 209 | // 将缓冲区对象分配给a_Color变量 210 | gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 6, FSIZE * 3); 211 | // 连接a_Color变量与分配给它的缓冲区对象 212 | gl.enableVertexAttribArray(a_Color); 213 | 214 | // 将顶点索引写入到缓冲区对象 215 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); 216 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); 217 | 218 | return indices.length; 219 | } -------------------------------------------------------------------------------- /8/Cuboid_UI.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello Triangle 6 | 7 | 8 | 9 | 10 | Please use a browser that supports "canvas" 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /8/Cuboid_UI.js: -------------------------------------------------------------------------------- 1 | // 顶点着色器程序 2 | var VSHADER_SOURCE = 3 | 'attribute vec4 a_Position;\n' + // attribute variable 4 | 'attribute vec4 a_Color;\n' + 5 | 'uniform mat4 u_MvpMatrix;\n' + 6 | 'varying vec4 v_Color;\n' + 7 | 'void main() {\n' + 8 | ' gl_Position = u_MvpMatrix * a_Position;\n' + // Set the vertex coordinates of the point 9 | ' v_Color = a_Color;\n' + 10 | '}\n'; 11 | 12 | // 片元着色器程序 13 | var FSHADER_SOURCE = 14 | 'precision mediump float;\n' + 15 | 'varying vec4 v_Color;\n' + 16 | 'void main() {\n' + 17 | ' gl_FragColor = v_Color;\n' + 18 | '}\n'; 19 | 20 | //定义一个矩形体:混合构造函数原型模式 21 | function Cuboid(minX, maxX, minY, maxY, minZ, maxZ) { 22 | this.minX = minX; 23 | this.maxX = maxX; 24 | this.minY = minY; 25 | this.maxY = maxY; 26 | this.minZ = minZ; 27 | this.maxZ = maxZ; 28 | } 29 | 30 | Cuboid.prototype = { 31 | constructor: Cuboid, 32 | CenterX: function () { 33 | return (this.minX + this.maxX) / 2.0; 34 | }, 35 | CenterY: function () { 36 | return (this.minY + this.maxY) / 2.0; 37 | }, 38 | CenterZ: function () { 39 | return (this.minZ + this.maxZ) / 2.0; 40 | }, 41 | LengthX: function () { 42 | return (this.maxX - this.minX); 43 | }, 44 | LengthY: function () { 45 | return (this.maxY - this.minY); 46 | } 47 | } 48 | 49 | var currentAngle = [0.0, 0.0]; // 绕X轴Y轴的旋转角度 ([x-axis, y-axis]) 50 | var curScale = 1.0; //当前的缩放比例 51 | 52 | function main() { 53 | // 获取 元素 54 | var canvas = document.getElementById('webgl'); 55 | 56 | // 获取WebGL渲染上下文 57 | var gl = getWebGLContext(canvas); 58 | if (!gl) { 59 | console.log('Failed to get the rendering context for WebGL'); 60 | return; 61 | } 62 | 63 | // 初始化着色器 64 | if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) { 65 | console.log('Failed to intialize shaders.'); 66 | return; 67 | } 68 | 69 | // 设置顶点位置 70 | var cuboid = new Cuboid(399589.072, 400469.072, 3995118.062, 3997558.062, 732, 1268); 71 | var n = initVertexBuffers(gl, cuboid); 72 | if (n < 0) { 73 | console.log('Failed to set the positions of the vertices'); 74 | return; 75 | } 76 | 77 | //注册鼠标事件 78 | initEventHandlers(canvas); 79 | 80 | // 指定清空的颜色 81 | gl.clearColor(0.0, 0.0, 0.0, 1.0); 82 | 83 | // 开启深度测试 84 | gl.enable(gl.DEPTH_TEST); 85 | 86 | //绘制函数 87 | var tick = function () { 88 | //设置MVP矩阵 89 | setMVPMatrix(gl, canvas, cuboid); 90 | 91 | //清空颜色和深度缓冲区 92 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 93 | 94 | //绘制矩形体 95 | gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0); 96 | 97 | //请求浏览器调用tick 98 | requestAnimationFrame(tick); 99 | }; 100 | 101 | //开始绘制 102 | tick(); 103 | 104 | // 绘制矩形体 105 | gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0); 106 | } 107 | 108 | //注册鼠标事件 109 | function initEventHandlers(canvas) { 110 | var dragging = false; // Dragging or not 111 | var lastX = -1, lastY = -1; // Last position of the mouse 112 | 113 | //鼠标按下 114 | canvas.onmousedown = function (ev) { 115 | var x = ev.clientX; 116 | var y = ev.clientY; 117 | // Start dragging if a moue is in 118 | var rect = ev.target.getBoundingClientRect(); 119 | if (rect.left <= x && x < rect.right && rect.top <= y && y < rect.bottom) { 120 | lastX = x; 121 | lastY = y; 122 | dragging = true; 123 | } 124 | }; 125 | 126 | //鼠标离开时 127 | canvas.onmouseleave = function (ev) { 128 | dragging = false; 129 | }; 130 | 131 | //鼠标释放 132 | canvas.onmouseup = function (ev) { 133 | dragging = false; 134 | }; 135 | 136 | //鼠标移动 137 | canvas.onmousemove = function (ev) { 138 | var x = ev.clientX; 139 | var y = ev.clientY; 140 | if (dragging) { 141 | var factor = 100 / canvas.height; // The rotation ratio 142 | var dx = factor * (x - lastX); 143 | var dy = factor * (y - lastY); 144 | currentAngle[0] = currentAngle[0] + dy; 145 | currentAngle[1] = currentAngle[1] + dx; 146 | } 147 | lastX = x, lastY = y; 148 | }; 149 | 150 | //鼠标缩放 151 | canvas.onmousewheel = function (event) { 152 | if (event.wheelDelta > 0) { 153 | curScale = curScale * 1.1; 154 | } else { 155 | curScale = curScale * 0.9; 156 | } 157 | }; 158 | } 159 | 160 | //设置MVP矩阵 161 | function setMVPMatrix(gl, canvas, cuboid) { 162 | // Get the storage location of u_MvpMatrix 163 | var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix'); 164 | if (!u_MvpMatrix) { 165 | console.log('Failed to get the storage location of u_MvpMatrix'); 166 | return; 167 | } 168 | 169 | //模型矩阵 170 | var modelMatrix = new Matrix4(); 171 | modelMatrix.scale(curScale, curScale, curScale); 172 | modelMatrix.rotate(currentAngle[0], 1.0, 0.0, 0.0); // Rotation around x-axis 173 | modelMatrix.rotate(currentAngle[1], 0.0, 1.0, 0.0); // Rotation around y-axis 174 | modelMatrix.translate(-cuboid.CenterX(), -cuboid.CenterY(), -cuboid.CenterZ()); 175 | 176 | //投影矩阵 177 | var fovy = 60; 178 | var near = 1; 179 | var projMatrix = new Matrix4(); 180 | projMatrix.setPerspective(fovy, canvas.width / canvas.height, 1, 10000); 181 | 182 | //计算lookAt()函数初始视点的高度 183 | var angle = fovy / 2 * Math.PI / 180.0; 184 | var eyeHight = (cuboid.LengthY() * 1.2) / 2.0 / angle; 185 | 186 | //视图矩阵 187 | var viewMatrix = new Matrix4(); // View matrix 188 | viewMatrix.lookAt(0, 0, eyeHight, 0, 0, 0, 0, 1, 0); 189 | 190 | //MVP矩阵 191 | var mvpMatrix = new Matrix4(); 192 | mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatrix); 193 | 194 | //将MVP矩阵传输到着色器的uniform变量u_MvpMatrix 195 | gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements); 196 | } 197 | 198 | // 199 | function initVertexBuffers(gl, cuboid) { 200 | // Create a cube 201 | // v6----- v5 202 | // /| /| 203 | // v1------v0| 204 | // | | | | 205 | // | |v7---|-|v4 206 | // |/ |/ 207 | // v2------v3 208 | // 顶点坐标和颜色 209 | var verticesColors = new Float32Array([ 210 | cuboid.maxX, cuboid.maxY, cuboid.maxZ, 1.0, 1.0, 1.0, // v0 White 211 | cuboid.minX, cuboid.maxY, cuboid.maxZ, 1.0, 0.0, 1.0, // v1 Magenta 212 | cuboid.minX, cuboid.minY, cuboid.maxZ, 1.0, 0.0, 0.0, // v2 Red 213 | cuboid.maxX, cuboid.minY, cuboid.maxZ, 1.0, 1.0, 0.0, // v3 Yellow 214 | cuboid.maxX, cuboid.minY, cuboid.minZ, 0.0, 1.0, 0.0, // v4 Green 215 | cuboid.maxX, cuboid.maxY, cuboid.minZ, 0.0, 1.0, 1.0, // v5 Cyan 216 | cuboid.minX, cuboid.maxY, cuboid.minZ, 0.0, 0.0, 1.0, // v6 Blue 217 | cuboid.minX, cuboid.minY, cuboid.minZ, 1.0, 0.0, 1.0 // v7 Black 218 | ]); 219 | 220 | //顶点索引 221 | var indices = new Uint8Array([ 222 | 0, 1, 2, 0, 2, 3, // 前 223 | 0, 3, 4, 0, 4, 5, // 右 224 | 0, 5, 6, 0, 6, 1, // 上 225 | 1, 6, 7, 1, 7, 2, // 左 226 | 7, 4, 3, 7, 3, 2, // 下 227 | 4, 7, 6, 4, 6, 5 // 后 228 | ]); 229 | 230 | // 231 | var FSIZE = verticesColors.BYTES_PER_ELEMENT; //数组中每个元素的字节数 232 | 233 | // 创建缓冲区对象 234 | var vertexColorBuffer = gl.createBuffer(); 235 | var indexBuffer = gl.createBuffer(); 236 | if (!vertexColorBuffer || !indexBuffer) { 237 | console.log('Failed to create the buffer object'); 238 | return -1; 239 | } 240 | 241 | // 将缓冲区对象绑定到目标 242 | gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer); 243 | // 向缓冲区对象写入数据 244 | gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW); 245 | 246 | //获取着色器中attribute变量a_Position的地址 247 | var a_Position = gl.getAttribLocation(gl.program, 'a_Position'); 248 | if (a_Position < 0) { 249 | console.log('Failed to get the storage location of a_Position'); 250 | return -1; 251 | } 252 | // 将缓冲区对象分配给a_Position变量 253 | gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE * 6, 0); 254 | 255 | // 连接a_Position变量与分配给它的缓冲区对象 256 | gl.enableVertexAttribArray(a_Position); 257 | 258 | //获取着色器中attribute变量a_Color的地址 259 | var a_Color = gl.getAttribLocation(gl.program, 'a_Color'); 260 | if (a_Color < 0) { 261 | console.log('Failed to get the storage location of a_Color'); 262 | return -1; 263 | } 264 | // 将缓冲区对象分配给a_Color变量 265 | gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 6, FSIZE * 3); 266 | // 连接a_Color变量与分配给它的缓冲区对象 267 | gl.enableVertexAttribArray(a_Color); 268 | 269 | // 将顶点索引写入到缓冲区对象 270 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); 271 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); 272 | 273 | return indices.length; 274 | } -------------------------------------------------------------------------------- /9/TerrainViewer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 显示地形 7 | Hello Triangle 8 | 9 | 10 | 11 |
12 |
13 | 14 | 请使用支持WebGL的浏览器 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /9/TerrainViewer.js: -------------------------------------------------------------------------------- 1 | // 顶点着色器程序 2 | var VSHADER_SOURCE = 3 | 'attribute vec4 a_Position;\n' + //位置 4 | 'attribute vec4 a_Color;\n' + //颜色 5 | 'uniform mat4 u_MvpMatrix;\n' + 6 | 'varying vec4 v_Color;\n' + 7 | 'void main() {\n' + 8 | ' gl_Position = u_MvpMatrix * a_Position;\n' + // 设置顶点坐标 9 | ' v_Color = a_Color;\n' + 10 | '}\n'; 11 | 12 | // 片元着色器程序 13 | var FSHADER_SOURCE = 14 | 'precision mediump float;\n' + 15 | 'varying vec4 v_Color;\n' + 16 | 'void main() {\n' + 17 | ' gl_FragColor = v_Color;\n' + 18 | '}\n'; 19 | 20 | //定义一个矩形体:混合构造函数原型模式 21 | function Cuboid(minX, maxX, minY, maxY, minZ, maxZ) { 22 | this.minX = minX; 23 | this.maxX = maxX; 24 | this.minY = minY; 25 | this.maxY = maxY; 26 | this.minZ = minZ; 27 | this.maxZ = maxZ; 28 | } 29 | 30 | Cuboid.prototype = { 31 | constructor: Cuboid, 32 | CenterX: function () { 33 | return (this.minX + this.maxX) / 2.0; 34 | }, 35 | CenterY: function () { 36 | return (this.minY + this.maxY) / 2.0; 37 | }, 38 | CenterZ: function () { 39 | return (this.minZ + this.maxZ) / 2.0; 40 | }, 41 | LengthX: function () { 42 | return (this.maxX - this.minX); 43 | }, 44 | LengthY: function () { 45 | return (this.maxY - this.minY); 46 | } 47 | } 48 | 49 | //定义DEM 50 | function Terrain() { 51 | } 52 | Terrain.prototype = { 53 | constructor: Terrain, 54 | setWH: function (col, row) { 55 | this.col = col; 56 | this.row = row; 57 | } 58 | } 59 | 60 | var currentAngle = [0.0, 0.0]; // 绕X轴Y轴的旋转角度 ([x-axis, y-axis]) 61 | var curScale = 1.0; //当前的缩放比例 62 | 63 | function main() { 64 | var demFile = document.getElementById('demFile'); 65 | if (!demFile) { 66 | console.log("Failed to get demFile element!"); 67 | return; 68 | } 69 | 70 | //加载文件后的事件 71 | demFile.addEventListener("change", function (event) { 72 | //判断浏览器是否支持FileReader接口 73 | if (typeof FileReader == 'undefined') { 74 | console.log("你的浏览器不支持FileReader接口!"); 75 | return; 76 | } 77 | 78 | //读取文件后的事件 79 | var reader = new FileReader(); 80 | reader.onload = function () { 81 | if (reader.result) { 82 | var terrain = new Terrain(); 83 | if (!readDEMFile(reader.result, terrain)) { 84 | console.log("文件格式有误,不能读取该文件!"); 85 | } 86 | 87 | //绘制函数 88 | onDraw(gl, canvas, terrain); 89 | } 90 | } 91 | 92 | var input = event.target; 93 | reader.readAsText(input.files[0]); 94 | }); 95 | 96 | // 获取 元素 97 | var canvas = document.getElementById('webgl'); 98 | 99 | // 获取WebGL渲染上下文 100 | var gl = getWebGLContext(canvas); 101 | if (!gl) { 102 | console.log('Failed to get the rendering context for WebGL'); 103 | return; 104 | } 105 | 106 | // 初始化着色器 107 | if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) { 108 | console.log('Failed to intialize shaders.'); 109 | return; 110 | } 111 | 112 | // 指定清空的颜色 113 | gl.clearColor(0.0, 0.0, 0.0, 1.0); 114 | 115 | // 开启深度测试 116 | gl.enable(gl.DEPTH_TEST); 117 | 118 | //清空颜色和深度缓冲区 119 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 120 | } 121 | 122 | //绘制函数 123 | function onDraw(gl, canvas, terrain) { 124 | // 设置顶点位置 125 | //var cuboid = new Cuboid(399589.072, 400469.072, 3995118.062, 3997558.062, 732, 1268); 126 | var n = initVertexBuffers(gl, terrain); 127 | if (n < 0) { 128 | console.log('Failed to set the positions of the vertices'); 129 | return; 130 | } 131 | 132 | //注册鼠标事件 133 | initEventHandlers(canvas); 134 | 135 | //绘制函数 136 | var tick = function () { 137 | //设置MVP矩阵 138 | setMVPMatrix(gl, canvas, terrain.cuboid); 139 | 140 | //清空颜色和深度缓冲区 141 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 142 | 143 | //绘制矩形体 144 | gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_SHORT, 0); 145 | //gl.drawArrays(gl.Points, 0, n); 146 | 147 | //请求浏览器调用tick 148 | requestAnimationFrame(tick); 149 | }; 150 | 151 | //开始绘制 152 | tick(); 153 | } 154 | 155 | //读取DEM函数 156 | function readDEMFile(result, terrain) { 157 | var stringlines = result.split("\n"); 158 | if (!stringlines || stringlines.length <= 0) { 159 | return false; 160 | } 161 | 162 | //读取头信息 163 | var subline = stringlines[0].split("\t"); 164 | if (subline.length != 6) { 165 | return false; 166 | } 167 | var col = parseInt(subline[4]); //DEM宽 168 | var row = parseInt(subline[5]); //DEM高 169 | var verticeNum = col * row; 170 | if (verticeNum + 1 > stringlines.length) { 171 | return false; 172 | } 173 | terrain.setWH(col, row); 174 | 175 | //读取点信息 176 | var ci = 0; 177 | terrain.verticesColors = new Float32Array(verticeNum * 6); 178 | for (var i = 1; i < stringlines.length; i++) { 179 | if (!stringlines[i]) { 180 | continue; 181 | } 182 | 183 | var subline = stringlines[i].split(','); 184 | if (subline.length != 9) { 185 | continue; 186 | } 187 | 188 | for (var j = 0; j < 6; j++) { 189 | terrain.verticesColors[ci] = parseFloat(subline[j]); 190 | ci++; 191 | } 192 | } 193 | 194 | if (ci !== verticeNum * 6) { 195 | return false; 196 | } 197 | 198 | //包围盒 199 | var minX = terrain.verticesColors[0]; 200 | var maxX = terrain.verticesColors[0]; 201 | var minY = terrain.verticesColors[1]; 202 | var maxY = terrain.verticesColors[1]; 203 | var minZ = terrain.verticesColors[2]; 204 | var maxZ = terrain.verticesColors[2]; 205 | for (var i = 0; i < verticeNum; i++) { 206 | minX = Math.min(minX, terrain.verticesColors[i * 6]); 207 | maxX = Math.max(maxX, terrain.verticesColors[i * 6]); 208 | minY = Math.min(minY, terrain.verticesColors[i * 6 + 1]); 209 | maxY = Math.max(maxY, terrain.verticesColors[i * 6 + 1]); 210 | minZ = Math.min(minZ, terrain.verticesColors[i * 6 + 2]); 211 | maxZ = Math.max(maxZ, terrain.verticesColors[i * 6 + 2]); 212 | } 213 | 214 | terrain.cuboid = new Cuboid(minX, maxX, minY, maxY, minZ, maxZ); 215 | 216 | return true; 217 | } 218 | 219 | 220 | //注册鼠标事件 221 | function initEventHandlers(canvas) { 222 | var dragging = false; // Dragging or not 223 | var lastX = -1, lastY = -1; // Last position of the mouse 224 | 225 | //鼠标按下 226 | canvas.onmousedown = function (ev) { 227 | var x = ev.clientX; 228 | var y = ev.clientY; 229 | // Start dragging if a moue is in 230 | var rect = ev.target.getBoundingClientRect(); 231 | if (rect.left <= x && x < rect.right && rect.top <= y && y < rect.bottom) { 232 | lastX = x; 233 | lastY = y; 234 | dragging = true; 235 | } 236 | }; 237 | 238 | //鼠标离开时 239 | canvas.onmouseleave = function (ev) { 240 | dragging = false; 241 | }; 242 | 243 | //鼠标释放 244 | canvas.onmouseup = function (ev) { 245 | dragging = false; 246 | }; 247 | 248 | //鼠标移动 249 | canvas.onmousemove = function (ev) { 250 | var x = ev.clientX; 251 | var y = ev.clientY; 252 | if (dragging) { 253 | var factor = 100 / canvas.height; // The rotation ratio 254 | var dx = factor * (x - lastX); 255 | var dy = factor * (y - lastY); 256 | currentAngle[0] = currentAngle[0] + dy; 257 | currentAngle[1] = currentAngle[1] + dx; 258 | } 259 | lastX = x, lastY = y; 260 | }; 261 | 262 | //鼠标缩放 263 | canvas.onmousewheel = function (event) { 264 | if (event.wheelDelta > 0) { 265 | curScale = curScale * 1.1; 266 | } else { 267 | curScale = curScale * 0.9; 268 | } 269 | }; 270 | } 271 | 272 | //设置MVP矩阵 273 | function setMVPMatrix(gl, canvas, cuboid) { 274 | // Get the storage location of u_MvpMatrix 275 | var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix'); 276 | if (!u_MvpMatrix) { 277 | console.log('Failed to get the storage location of u_MvpMatrix'); 278 | return; 279 | } 280 | 281 | //模型矩阵 282 | var modelMatrix = new Matrix4(); 283 | modelMatrix.scale(curScale, curScale, curScale); 284 | modelMatrix.rotate(currentAngle[0], 1.0, 0.0, 0.0); // Rotation around x-axis 285 | modelMatrix.rotate(currentAngle[1], 0.0, 1.0, 0.0); // Rotation around y-axis 286 | modelMatrix.translate(-cuboid.CenterX(), -cuboid.CenterY(), -cuboid.CenterZ()); 287 | 288 | //投影矩阵 289 | var fovy = 60; 290 | var near = 1; 291 | var projMatrix = new Matrix4(); 292 | projMatrix.setPerspective(fovy, canvas.width / canvas.height, 1, 10000); 293 | 294 | //计算lookAt()函数初始视点的高度 295 | var angle = fovy / 2 * Math.PI / 180.0; 296 | var eyeHight = (cuboid.LengthY() * 1.2) / 2.0 / angle; 297 | 298 | //视图矩阵 299 | var viewMatrix = new Matrix4(); // View matrix 300 | viewMatrix.lookAt(0, 0, eyeHight, 0, 0, 0, 0, 1, 0); 301 | 302 | //MVP矩阵 303 | var mvpMatrix = new Matrix4(); 304 | mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatrix); 305 | 306 | //将MVP矩阵传输到着色器的uniform变量u_MvpMatrix 307 | gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements); 308 | } 309 | 310 | // 311 | function initVertexBuffers(gl, terrain) { 312 | //DEM的一个网格是由两个三角形组成的 313 | // 0------1 1 314 | // | | 315 | // | | 316 | // col col------col+1 317 | var col = terrain.col; 318 | var row = terrain.row; 319 | 320 | var indices = new Uint16Array((row - 1) * (col - 1) * 6); 321 | var ci = 0; 322 | for (var yi = 0; yi < row - 1; yi++) { 323 | //for (var yi = 0; yi < 10; yi++) { 324 | for (var xi = 0; xi < col - 1; xi++) { 325 | indices[ci * 6] = yi * col + xi; 326 | indices[ci * 6 + 1] = (yi + 1) * col + xi; 327 | indices[ci * 6 + 2] = yi * col + xi + 1; 328 | indices[ci * 6 + 3] = (yi + 1) * col + xi; 329 | indices[ci * 6 + 4] = (yi + 1) * col + xi + 1; 330 | indices[ci * 6 + 5] = yi * col + xi + 1; 331 | ci++; 332 | } 333 | } 334 | 335 | // 336 | var verticesColors = terrain.verticesColors; 337 | var FSIZE = verticesColors.BYTES_PER_ELEMENT; //数组中每个元素的字节数 338 | 339 | // 创建缓冲区对象 340 | var vertexColorBuffer = gl.createBuffer(); 341 | var indexBuffer = gl.createBuffer(); 342 | if (!vertexColorBuffer || !indexBuffer) { 343 | console.log('Failed to create the buffer object'); 344 | return -1; 345 | } 346 | 347 | // 将缓冲区对象绑定到目标 348 | gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer); 349 | // 向缓冲区对象写入数据 350 | gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW); 351 | 352 | //获取着色器中attribute变量a_Position的地址 353 | var a_Position = gl.getAttribLocation(gl.program, 'a_Position'); 354 | if (a_Position < 0) { 355 | console.log('Failed to get the storage location of a_Position'); 356 | return -1; 357 | } 358 | // 将缓冲区对象分配给a_Position变量 359 | gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE * 6, 0); 360 | 361 | // 连接a_Position变量与分配给它的缓冲区对象 362 | gl.enableVertexAttribArray(a_Position); 363 | 364 | //获取着色器中attribute变量a_Color的地址 365 | var a_Color = gl.getAttribLocation(gl.program, 'a_Color'); 366 | if (a_Color < 0) { 367 | console.log('Failed to get the storage location of a_Color'); 368 | return -1; 369 | } 370 | // 将缓冲区对象分配给a_Color变量 371 | gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 6, FSIZE * 3); 372 | // 连接a_Color变量与分配给它的缓冲区对象 373 | gl.enableVertexAttribArray(a_Color); 374 | 375 | // 将顶点索引写入到缓冲区对象 376 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); 377 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); 378 | 379 | return indices.length; 380 | } -------------------------------------------------------------------------------- /Data/DEM.prj: -------------------------------------------------------------------------------- 1 | PROJCS["UTM_Zone_12_Northern_Hemisphere",GEOGCS["GCS_Geographic Coordinate System",DATUM["D_WGS84",SPHEROID["WGS84",6378137,298.257223560493]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-111],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["Meter",1]] 2 | -------------------------------------------------------------------------------- /Data/DEM.tfw: -------------------------------------------------------------------------------- 1 | 10 2 | 0 3 | 0 4 | -10 5 | 399650 6 | 3997530 7 | -------------------------------------------------------------------------------- /Data/DEM.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fafa1899/WebGLTutorial/4431734a5550fd5a33b19d91fedda478f7454804/Data/DEM.tif -------------------------------------------------------------------------------- /Data/DOM.prj: -------------------------------------------------------------------------------- 1 | PROJCS["UTM_Zone_12_Northern_Hemisphere",GEOGCS["GCS_Geographic Coordinate System",DATUM["D_WGS84",SPHEROID["WGS84",6378137,298.257223560493]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-111],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["Meter",1]] 2 | -------------------------------------------------------------------------------- /Data/DOM.tfw: -------------------------------------------------------------------------------- 1 | 0.25 2 | 0 3 | 0 4 | -0.25 5 | 399650.125 6 | 3997529.875 7 | -------------------------------------------------------------------------------- /Data/DOM.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fafa1899/WebGLTutorial/4431734a5550fd5a33b19d91fedda478f7454804/Data/DOM.tif -------------------------------------------------------------------------------- /Data/DOM.tif.ovr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fafa1899/WebGLTutorial/4431734a5550fd5a33b19d91fedda478f7454804/Data/DOM.tif.ovr -------------------------------------------------------------------------------- /Data/new.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fafa1899/WebGLTutorial/4431734a5550fd5a33b19d91fedda478f7454804/Data/new.bin -------------------------------------------------------------------------------- /Data/new.gltf: -------------------------------------------------------------------------------- 1 | { 2 | "asset": { 3 | "generator": "CL", 4 | "version": "2.0" 5 | }, 6 | "scene": 0, 7 | "scenes": [ 8 | { 9 | "nodes": [ 10 | 0 11 | ] 12 | } 13 | ], 14 | "nodes": [ 15 | { 16 | "mesh": 0 17 | } 18 | ], 19 | "meshes": [ 20 | { 21 | "primitives": [ 22 | { 23 | "attributes": { 24 | "POSITION": 1, 25 | "TEXCOORD_0": 2 26 | }, 27 | "indices": 0, 28 | "material": 0 29 | } 30 | ] 31 | } 32 | ], 33 | "materials": [ 34 | { 35 | "pbrMetallicRoughness": { 36 | "baseColorTexture": { 37 | "index": 0 38 | } 39 | } 40 | } 41 | ], 42 | "textures": [ 43 | { 44 | "sampler": 0, 45 | "source": 0 46 | } 47 | ], 48 | "images": [ 49 | { 50 | "uri": "tex.jpg" 51 | } 52 | ], 53 | "samplers": [ 54 | { 55 | "magFilter": 9729, 56 | "minFilter": 9987, 57 | "wrapS": 33648, 58 | "wrapT": 33648 59 | } 60 | ], 61 | "buffers": [ 62 | { 63 | "uri": "new.bin", 64 | "byteLength": 595236 65 | } 66 | ], 67 | "bufferViews": [ 68 | { 69 | "buffer": 0, 70 | "byteOffset": 374400, 71 | "byteLength": 220836, 72 | "target": 34963 73 | }, 74 | { 75 | "buffer": 0, 76 | "byteStride": 20, 77 | "byteOffset": 0, 78 | "byteLength": 374400, 79 | "target": 34962 80 | } 81 | ], 82 | "accessors": [ 83 | { 84 | "bufferView": 0, 85 | "byteOffset": 0, 86 | "componentType": 5123, 87 | "count": 110418, 88 | "type": "SCALAR", 89 | "max": [ 90 | 18719 91 | ], 92 | "min": [ 93 | 0 94 | ] 95 | }, 96 | { 97 | "bufferView": 1, 98 | "byteOffset": 0, 99 | "componentType": 5126, 100 | "count": 18720, 101 | "type": "VEC3", 102 | "max": [ 103 | 770, 104 | 0.0, 105 | 1261.151611328125 106 | ], 107 | "min": [ 108 | 0.0, 109 | -2390, 110 | 733.5555419921875 111 | ] 112 | }, 113 | { 114 | "bufferView": 1, 115 | "byteOffset": 12, 116 | "componentType": 5126, 117 | "count": 18720, 118 | "type": "VEC2", 119 | "max": [ 120 | 1, 121 | 1 122 | ], 123 | "min": [ 124 | 0, 125 | 0 126 | ] 127 | } 128 | ] 129 | } 130 | -------------------------------------------------------------------------------- /Data/sky_cloud.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fafa1899/WebGLTutorial/4431734a5550fd5a33b19d91fedda478f7454804/Data/sky_cloud.jpg -------------------------------------------------------------------------------- /Data/tex.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fafa1899/WebGLTutorial/4431734a5550fd5a33b19d91fedda478f7454804/Data/tex.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 1. 绪论 2 | 最近研究WebGL,看了《WebGL编程指南》这本书,结合自己的专业知识写的一系列教程。之前在看OpenGL/WebGL的时候总是感觉OpenGL/WebGL看的时候懂,实际用起来却挺难,感觉中间总是隔着很多东西。现在一路边学边写,才明白这中间缺少的其实就是总结,是实践;把这个过程写出来,既是帮助他人,也是帮助自己。 3 | 4 | 现在这一系列文章也写了不少了,就写个目录汇总一下,方便查阅,以后增添了新的文章也会随之更新。这一系列教程由浅入深,知识也是循序渐进的,前后关联。实例也逐渐复杂,最终完成一个地形渲染的实例:
![image][imglink1]
图1:地形渲染(纹理)

![image][imglink2]
图2:地形渲染(颜色)
5 | 6 | # 2. 目录 7 | 8 | 1.[WebGL简易教程(一):第一个简单示例][netlink1] 9 | 10 | 概述了这篇教程的目的,编写了WebGL的第一个示例。 11 | 12 | 2.[WebGL简易教程(二):向着色器传输数据][netlink2] 13 | 14 | 改进了绘制一个点的实例,讲述了WebGL中向着色器(shader)传输数据的问题。 15 | 16 | 3.[WebGL简易教程(三):绘制一个三角形(缓冲区对象)][netlink3] 17 | 18 | 通过一个绘制三角形的具体实例,详解了WebGL中缓冲区对象(buffer object)的使用。 19 | 20 | 4.[WebGL简易教程(四):颜色][netlink4] 21 | 22 | 通过绘制彩色三角形的示例,介绍了varying变量,顶点着色器与片元着色器之间数据传输的过程:顶点装配与光栅化。 23 | 24 | 5.[WebGL简易教程(五):图形变换(模型、视图、投影变换)][netlink5] 25 | 26 | 详细讲解了OpenGL\WebGL关于绘制场景的图形变换过程,并推导了其图形变换矩阵。主要包括模型变换、视图变换以及投影变换。 27 | 28 | 6.[WebGL简易教程(六):第一个三维示例(使用模型视图投影变换)][netlink6] 29 | 30 | 通过使用模型视图投影变换,完成第一个真正三维场景的示例:显示一组由远及近的三角形。 31 | 32 | 7.[WebGL简易教程(七):绘制一个矩形体][netlink7] 33 | 34 | 通过一个绘制矩形包围盒的实例,进一步理解了模型视图投影变换。 35 | 36 | 8.[WebGL简易教程(八):三维场景交互][netlink8] 37 | 38 | 基于之前教程的知识,实现了一个三维场景的浏览实例:通过鼠标实现场景的旋转和缩放。 39 | 40 | 9.[WebGL简易教程(九):综合实例:地形的绘制][netlink9] 41 | 42 | 综合WebGL的知识,实现了绘制一张地形图的实例。 43 | 44 | 10.[WebGL简易教程(十):光照][netlink10] 45 | 46 | 讲述了WebGL光照生成的原理,并作出了实际案例。 47 | 48 | 11.[WebGL简易教程(十一):纹理][netlink11] 49 | 50 | WebGL中使用纹理的实例:给地形贴上一张真实的纹理。 51 | 52 | 12.[WebGL简易教程(十二):包围球与投影][netlink12] 53 | 54 | 通过包围球来设置模型视图投影变换,显示合适的渲染位置。 55 | 56 | 13.[WebGL简易教程(十三):帧缓存对象(离屏渲染)][netlink13] 57 | 58 | 详细论述了WebGL中帧缓冲区技术的实现。 59 | 60 | 14.[WebGL简易教程(十四):阴影][netlink14] 61 | 62 | 详述了WebGL中生成阴影的ShadowMap算法。 63 | 64 | 15.[WebGL简易教程(十五):加载gltf模型][netlink15] 65 | 66 | 详述了通过WebGL读取、解析并显示glTF格式数据的过程。 67 | 68 | # 3. 资源 69 | 其代码已经上传到GitHub:[地址](https://github.com/fafa1899/WebGLTutorial)。个人见解难免有所疏漏,欢迎大家来互相交流。 70 | 71 | [imglink1]:https://github.com/fafa1899/WebGLTutorial/blob/master/1.gif 72 | [imglink2]:https://github.com/fafa1899/WebGLTutorial/blob/master/2.gif 73 | 74 | [netlink1]:https://blog.csdn.net/charlee44/article/details/98474589 75 | [netlink2]:https://blog.csdn.net/charlee44/article/details/99174844 76 | [netlink3]:https://blog.csdn.net/charlee44/article/details/100534038 77 | [netlink4]:https://blog.csdn.net/charlee44/article/details/100830450 78 | [netlink5]:https://blog.csdn.net/charlee44/article/details/102063461 79 | [netlink6]:https://blog.csdn.net/charlee44/article/details/102173662 80 | [netlink7]:https://blog.csdn.net/charlee44/article/details/102251822 81 | [netlink8]:https://blog.csdn.net/charlee44/article/details/102258440 82 | [netlink9]:https://blog.csdn.net/charlee44/article/details/102403912 83 | [netlink10]:https://blog.csdn.net/charlee44/article/details/102536585 84 | [netlink11]:https://blog.csdn.net/charlee44/article/details/102583215 85 | [netlink12]:https://blog.csdn.net/charlee44/article/details/102992440 86 | [netlink13]:https://blog.csdn.net/charlee44/article/details/103333252 87 | [netlink14]:https://blog.csdn.net/charlee44/article/details/103435039 88 | [netlink15]:https://blog.csdn.net/charlee44/article/details/104051809 89 | 90 | -------------------------------------------------------------------------------- /lib/cuon-matrix.js: -------------------------------------------------------------------------------- 1 | // cuon-matrix.js (c) 2012 kanda and matsuda 2 | /** 3 | * This is a class treating 4x4 matrix. 4 | * This class contains the function that is equivalent to OpenGL matrix stack. 5 | * The matrix after conversion is calculated by multiplying a conversion matrix from the right. 6 | * The matrix is replaced by the calculated result. 7 | */ 8 | 9 | /** 10 | * Constructor of Matrix4 11 | * If opt_src is specified, new matrix is initialized by opt_src. 12 | * Otherwise, new matrix is initialized by identity matrix. 13 | * @param opt_src source matrix(option) 14 | */ 15 | var Matrix4 = function(opt_src) { 16 | var i, s, d; 17 | if (opt_src && typeof opt_src === 'object' && opt_src.hasOwnProperty('elements')) { 18 | s = opt_src.elements; 19 | d = new Float32Array(16); 20 | for (i = 0; i < 16; ++i) { 21 | d[i] = s[i]; 22 | } 23 | this.elements = d; 24 | } else { 25 | this.elements = new Float32Array([1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]); 26 | } 27 | }; 28 | 29 | /** 30 | * Set the identity matrix. 31 | * @return this 32 | */ 33 | Matrix4.prototype.setIdentity = function() { 34 | var e = this.elements; 35 | e[0] = 1; e[4] = 0; e[8] = 0; e[12] = 0; 36 | e[1] = 0; e[5] = 1; e[9] = 0; e[13] = 0; 37 | e[2] = 0; e[6] = 0; e[10] = 1; e[14] = 0; 38 | e[3] = 0; e[7] = 0; e[11] = 0; e[15] = 1; 39 | return this; 40 | }; 41 | 42 | /** 43 | * Copy matrix. 44 | * @param src source matrix 45 | * @return this 46 | */ 47 | Matrix4.prototype.set = function(src) { 48 | var i, s, d; 49 | 50 | s = src.elements; 51 | d = this.elements; 52 | 53 | if (s === d) { 54 | return; 55 | } 56 | 57 | for (i = 0; i < 16; ++i) { 58 | d[i] = s[i]; 59 | } 60 | 61 | return this; 62 | }; 63 | 64 | /** 65 | * Multiply the matrix from the right. 66 | * @param other The multiply matrix 67 | * @return this 68 | */ 69 | Matrix4.prototype.concat = function(other) { 70 | var i, e, a, b, ai0, ai1, ai2, ai3; 71 | 72 | // Calculate e = a * b 73 | e = this.elements; 74 | a = this.elements; 75 | b = other.elements; 76 | 77 | // If e equals b, copy b to temporary matrix. 78 | if (e === b) { 79 | b = new Float32Array(16); 80 | for (i = 0; i < 16; ++i) { 81 | b[i] = e[i]; 82 | } 83 | } 84 | 85 | for (i = 0; i < 4; i++) { 86 | ai0=a[i]; ai1=a[i+4]; ai2=a[i+8]; ai3=a[i+12]; 87 | e[i] = ai0 * b[0] + ai1 * b[1] + ai2 * b[2] + ai3 * b[3]; 88 | e[i+4] = ai0 * b[4] + ai1 * b[5] + ai2 * b[6] + ai3 * b[7]; 89 | e[i+8] = ai0 * b[8] + ai1 * b[9] + ai2 * b[10] + ai3 * b[11]; 90 | e[i+12] = ai0 * b[12] + ai1 * b[13] + ai2 * b[14] + ai3 * b[15]; 91 | } 92 | 93 | return this; 94 | }; 95 | Matrix4.prototype.multiply = Matrix4.prototype.concat; 96 | 97 | /** 98 | * Multiply the three-dimensional vector. 99 | * @param pos The multiply vector 100 | * @return The result of multiplication(Float32Array) 101 | */ 102 | Matrix4.prototype.multiplyVector3 = function(pos) { 103 | var e = this.elements; 104 | var p = pos.elements; 105 | var v = new Vector3(); 106 | var result = v.elements; 107 | 108 | result[0] = p[0] * e[0] + p[1] * e[4] + p[2] * e[ 8] + e[11]; 109 | result[1] = p[0] * e[1] + p[1] * e[5] + p[2] * e[ 9] + e[12]; 110 | result[2] = p[0] * e[2] + p[1] * e[6] + p[2] * e[10] + e[13]; 111 | 112 | return v; 113 | }; 114 | 115 | /** 116 | * Multiply the four-dimensional vector. 117 | * @param pos The multiply vector 118 | * @return The result of multiplication(Float32Array) 119 | */ 120 | Matrix4.prototype.multiplyVector4 = function(pos) { 121 | var e = this.elements; 122 | var p = pos.elements; 123 | var v = new Vector4(); 124 | var result = v.elements; 125 | 126 | result[0] = p[0] * e[0] + p[1] * e[4] + p[2] * e[ 8] + p[3] * e[12]; 127 | result[1] = p[0] * e[1] + p[1] * e[5] + p[2] * e[ 9] + p[3] * e[13]; 128 | result[2] = p[0] * e[2] + p[1] * e[6] + p[2] * e[10] + p[3] * e[14]; 129 | result[3] = p[0] * e[3] + p[1] * e[7] + p[2] * e[11] + p[3] * e[15]; 130 | 131 | return v; 132 | }; 133 | 134 | /** 135 | * Transpose the matrix. 136 | * @return this 137 | */ 138 | Matrix4.prototype.transpose = function() { 139 | var e, t; 140 | 141 | e = this.elements; 142 | 143 | t = e[ 1]; e[ 1] = e[ 4]; e[ 4] = t; 144 | t = e[ 2]; e[ 2] = e[ 8]; e[ 8] = t; 145 | t = e[ 3]; e[ 3] = e[12]; e[12] = t; 146 | t = e[ 6]; e[ 6] = e[ 9]; e[ 9] = t; 147 | t = e[ 7]; e[ 7] = e[13]; e[13] = t; 148 | t = e[11]; e[11] = e[14]; e[14] = t; 149 | 150 | return this; 151 | }; 152 | 153 | /** 154 | * Calculate the inverse matrix of specified matrix, and set to this. 155 | * @param other The source matrix 156 | * @return this 157 | */ 158 | Matrix4.prototype.setInverseOf = function(other) { 159 | var i, s, d, inv, det; 160 | 161 | s = other.elements; 162 | d = this.elements; 163 | inv = new Float32Array(16); 164 | 165 | inv[0] = s[5]*s[10]*s[15] - s[5] *s[11]*s[14] - s[9] *s[6]*s[15] 166 | + s[9]*s[7] *s[14] + s[13]*s[6] *s[11] - s[13]*s[7]*s[10]; 167 | inv[4] = - s[4]*s[10]*s[15] + s[4] *s[11]*s[14] + s[8] *s[6]*s[15] 168 | - s[8]*s[7] *s[14] - s[12]*s[6] *s[11] + s[12]*s[7]*s[10]; 169 | inv[8] = s[4]*s[9] *s[15] - s[4] *s[11]*s[13] - s[8] *s[5]*s[15] 170 | + s[8]*s[7] *s[13] + s[12]*s[5] *s[11] - s[12]*s[7]*s[9]; 171 | inv[12] = - s[4]*s[9] *s[14] + s[4] *s[10]*s[13] + s[8] *s[5]*s[14] 172 | - s[8]*s[6] *s[13] - s[12]*s[5] *s[10] + s[12]*s[6]*s[9]; 173 | 174 | inv[1] = - s[1]*s[10]*s[15] + s[1] *s[11]*s[14] + s[9] *s[2]*s[15] 175 | - s[9]*s[3] *s[14] - s[13]*s[2] *s[11] + s[13]*s[3]*s[10]; 176 | inv[5] = s[0]*s[10]*s[15] - s[0] *s[11]*s[14] - s[8] *s[2]*s[15] 177 | + s[8]*s[3] *s[14] + s[12]*s[2] *s[11] - s[12]*s[3]*s[10]; 178 | inv[9] = - s[0]*s[9] *s[15] + s[0] *s[11]*s[13] + s[8] *s[1]*s[15] 179 | - s[8]*s[3] *s[13] - s[12]*s[1] *s[11] + s[12]*s[3]*s[9]; 180 | inv[13] = s[0]*s[9] *s[14] - s[0] *s[10]*s[13] - s[8] *s[1]*s[14] 181 | + s[8]*s[2] *s[13] + s[12]*s[1] *s[10] - s[12]*s[2]*s[9]; 182 | 183 | inv[2] = s[1]*s[6]*s[15] - s[1] *s[7]*s[14] - s[5] *s[2]*s[15] 184 | + s[5]*s[3]*s[14] + s[13]*s[2]*s[7] - s[13]*s[3]*s[6]; 185 | inv[6] = - s[0]*s[6]*s[15] + s[0] *s[7]*s[14] + s[4] *s[2]*s[15] 186 | - s[4]*s[3]*s[14] - s[12]*s[2]*s[7] + s[12]*s[3]*s[6]; 187 | inv[10] = s[0]*s[5]*s[15] - s[0] *s[7]*s[13] - s[4] *s[1]*s[15] 188 | + s[4]*s[3]*s[13] + s[12]*s[1]*s[7] - s[12]*s[3]*s[5]; 189 | inv[14] = - s[0]*s[5]*s[14] + s[0] *s[6]*s[13] + s[4] *s[1]*s[14] 190 | - s[4]*s[2]*s[13] - s[12]*s[1]*s[6] + s[12]*s[2]*s[5]; 191 | 192 | inv[3] = - s[1]*s[6]*s[11] + s[1]*s[7]*s[10] + s[5]*s[2]*s[11] 193 | - s[5]*s[3]*s[10] - s[9]*s[2]*s[7] + s[9]*s[3]*s[6]; 194 | inv[7] = s[0]*s[6]*s[11] - s[0]*s[7]*s[10] - s[4]*s[2]*s[11] 195 | + s[4]*s[3]*s[10] + s[8]*s[2]*s[7] - s[8]*s[3]*s[6]; 196 | inv[11] = - s[0]*s[5]*s[11] + s[0]*s[7]*s[9] + s[4]*s[1]*s[11] 197 | - s[4]*s[3]*s[9] - s[8]*s[1]*s[7] + s[8]*s[3]*s[5]; 198 | inv[15] = s[0]*s[5]*s[10] - s[0]*s[6]*s[9] - s[4]*s[1]*s[10] 199 | + s[4]*s[2]*s[9] + s[8]*s[1]*s[6] - s[8]*s[2]*s[5]; 200 | 201 | det = s[0]*inv[0] + s[1]*inv[4] + s[2]*inv[8] + s[3]*inv[12]; 202 | if (det === 0) { 203 | return this; 204 | } 205 | 206 | det = 1 / det; 207 | for (i = 0; i < 16; i++) { 208 | d[i] = inv[i] * det; 209 | } 210 | 211 | return this; 212 | }; 213 | 214 | /** 215 | * Calculate the inverse matrix of this, and set to this. 216 | * @return this 217 | */ 218 | Matrix4.prototype.invert = function() { 219 | return this.setInverseOf(this); 220 | }; 221 | 222 | /** 223 | * Set the orthographic projection matrix. 224 | * @param left The coordinate of the left of clipping plane. 225 | * @param right The coordinate of the right of clipping plane. 226 | * @param bottom The coordinate of the bottom of clipping plane. 227 | * @param top The coordinate of the top top clipping plane. 228 | * @param near The distances to the nearer depth clipping plane. This value is minus if the plane is to be behind the viewer. 229 | * @param far The distances to the farther depth clipping plane. This value is minus if the plane is to be behind the viewer. 230 | * @return this 231 | */ 232 | Matrix4.prototype.setOrtho = function(left, right, bottom, top, near, far) { 233 | var e, rw, rh, rd; 234 | 235 | if (left === right || bottom === top || near === far) { 236 | throw 'null frustum'; 237 | } 238 | 239 | rw = 1 / (right - left); 240 | rh = 1 / (top - bottom); 241 | rd = 1 / (far - near); 242 | 243 | e = this.elements; 244 | 245 | e[0] = 2 * rw; 246 | e[1] = 0; 247 | e[2] = 0; 248 | e[3] = 0; 249 | 250 | e[4] = 0; 251 | e[5] = 2 * rh; 252 | e[6] = 0; 253 | e[7] = 0; 254 | 255 | e[8] = 0; 256 | e[9] = 0; 257 | e[10] = -2 * rd; 258 | e[11] = 0; 259 | 260 | e[12] = -(right + left) * rw; 261 | e[13] = -(top + bottom) * rh; 262 | e[14] = -(far + near) * rd; 263 | e[15] = 1; 264 | 265 | return this; 266 | }; 267 | 268 | /** 269 | * Multiply the orthographic projection matrix from the right. 270 | * @param left The coordinate of the left of clipping plane. 271 | * @param right The coordinate of the right of clipping plane. 272 | * @param bottom The coordinate of the bottom of clipping plane. 273 | * @param top The coordinate of the top top clipping plane. 274 | * @param near The distances to the nearer depth clipping plane. This value is minus if the plane is to be behind the viewer. 275 | * @param far The distances to the farther depth clipping plane. This value is minus if the plane is to be behind the viewer. 276 | * @return this 277 | */ 278 | Matrix4.prototype.ortho = function(left, right, bottom, top, near, far) { 279 | return this.concat(new Matrix4().setOrtho(left, right, bottom, top, near, far)); 280 | }; 281 | 282 | /** 283 | * Set the perspective projection matrix. 284 | * @param left The coordinate of the left of clipping plane. 285 | * @param right The coordinate of the right of clipping plane. 286 | * @param bottom The coordinate of the bottom of clipping plane. 287 | * @param top The coordinate of the top top clipping plane. 288 | * @param near The distances to the nearer depth clipping plane. This value must be plus value. 289 | * @param far The distances to the farther depth clipping plane. This value must be plus value. 290 | * @return this 291 | */ 292 | Matrix4.prototype.setFrustum = function(left, right, bottom, top, near, far) { 293 | var e, rw, rh, rd; 294 | 295 | if (left === right || top === bottom || near === far) { 296 | throw 'null frustum'; 297 | } 298 | if (near <= 0) { 299 | throw 'near <= 0'; 300 | } 301 | if (far <= 0) { 302 | throw 'far <= 0'; 303 | } 304 | 305 | rw = 1 / (right - left); 306 | rh = 1 / (top - bottom); 307 | rd = 1 / (far - near); 308 | 309 | e = this.elements; 310 | 311 | e[ 0] = 2 * near * rw; 312 | e[ 1] = 0; 313 | e[ 2] = 0; 314 | e[ 3] = 0; 315 | 316 | e[ 4] = 0; 317 | e[ 5] = 2 * near * rh; 318 | e[ 6] = 0; 319 | e[ 7] = 0; 320 | 321 | e[ 8] = (right + left) * rw; 322 | e[ 9] = (top + bottom) * rh; 323 | e[10] = -(far + near) * rd; 324 | e[11] = -1; 325 | 326 | e[12] = 0; 327 | e[13] = 0; 328 | e[14] = -2 * near * far * rd; 329 | e[15] = 0; 330 | 331 | return this; 332 | }; 333 | 334 | /** 335 | * Multiply the perspective projection matrix from the right. 336 | * @param left The coordinate of the left of clipping plane. 337 | * @param right The coordinate of the right of clipping plane. 338 | * @param bottom The coordinate of the bottom of clipping plane. 339 | * @param top The coordinate of the top top clipping plane. 340 | * @param near The distances to the nearer depth clipping plane. This value must be plus value. 341 | * @param far The distances to the farther depth clipping plane. This value must be plus value. 342 | * @return this 343 | */ 344 | Matrix4.prototype.frustum = function(left, right, bottom, top, near, far) { 345 | return this.concat(new Matrix4().setFrustum(left, right, bottom, top, near, far)); 346 | }; 347 | 348 | /** 349 | * Set the perspective projection matrix by fovy and aspect. 350 | * @param fovy The angle between the upper and lower sides of the frustum. 351 | * @param aspect The aspect ratio of the frustum. (width/height) 352 | * @param near The distances to the nearer depth clipping plane. This value must be plus value. 353 | * @param far The distances to the farther depth clipping plane. This value must be plus value. 354 | * @return this 355 | */ 356 | Matrix4.prototype.setPerspective = function(fovy, aspect, near, far) { 357 | var e, rd, s, ct; 358 | 359 | if (near === far || aspect === 0) { 360 | throw 'null frustum'; 361 | } 362 | if (near <= 0) { 363 | throw 'near <= 0'; 364 | } 365 | if (far <= 0) { 366 | throw 'far <= 0'; 367 | } 368 | 369 | fovy = Math.PI * fovy / 180 / 2; 370 | s = Math.sin(fovy); 371 | if (s === 0) { 372 | throw 'null frustum'; 373 | } 374 | 375 | rd = 1 / (far - near); 376 | ct = Math.cos(fovy) / s; 377 | 378 | e = this.elements; 379 | 380 | e[0] = ct / aspect; 381 | e[1] = 0; 382 | e[2] = 0; 383 | e[3] = 0; 384 | 385 | e[4] = 0; 386 | e[5] = ct; 387 | e[6] = 0; 388 | e[7] = 0; 389 | 390 | e[8] = 0; 391 | e[9] = 0; 392 | e[10] = -(far + near) * rd; 393 | e[11] = -1; 394 | 395 | e[12] = 0; 396 | e[13] = 0; 397 | e[14] = -2 * near * far * rd; 398 | e[15] = 0; 399 | 400 | return this; 401 | }; 402 | 403 | /** 404 | * Multiply the perspective projection matrix from the right. 405 | * @param fovy The angle between the upper and lower sides of the frustum. 406 | * @param aspect The aspect ratio of the frustum. (width/height) 407 | * @param near The distances to the nearer depth clipping plane. This value must be plus value. 408 | * @param far The distances to the farther depth clipping plane. This value must be plus value. 409 | * @return this 410 | */ 411 | Matrix4.prototype.perspective = function(fovy, aspect, near, far) { 412 | return this.concat(new Matrix4().setPerspective(fovy, aspect, near, far)); 413 | }; 414 | 415 | /** 416 | * Set the matrix for scaling. 417 | * @param x The scale factor along the X axis 418 | * @param y The scale factor along the Y axis 419 | * @param z The scale factor along the Z axis 420 | * @return this 421 | */ 422 | Matrix4.prototype.setScale = function(x, y, z) { 423 | var e = this.elements; 424 | e[0] = x; e[4] = 0; e[8] = 0; e[12] = 0; 425 | e[1] = 0; e[5] = y; e[9] = 0; e[13] = 0; 426 | e[2] = 0; e[6] = 0; e[10] = z; e[14] = 0; 427 | e[3] = 0; e[7] = 0; e[11] = 0; e[15] = 1; 428 | return this; 429 | }; 430 | 431 | /** 432 | * Multiply the matrix for scaling from the right. 433 | * @param x The scale factor along the X axis 434 | * @param y The scale factor along the Y axis 435 | * @param z The scale factor along the Z axis 436 | * @return this 437 | */ 438 | Matrix4.prototype.scale = function(x, y, z) { 439 | var e = this.elements; 440 | e[0] *= x; e[4] *= y; e[8] *= z; 441 | e[1] *= x; e[5] *= y; e[9] *= z; 442 | e[2] *= x; e[6] *= y; e[10] *= z; 443 | e[3] *= x; e[7] *= y; e[11] *= z; 444 | return this; 445 | }; 446 | 447 | /** 448 | * Set the matrix for translation. 449 | * @param x The X value of a translation. 450 | * @param y The Y value of a translation. 451 | * @param z The Z value of a translation. 452 | * @return this 453 | */ 454 | Matrix4.prototype.setTranslate = function(x, y, z) { 455 | var e = this.elements; 456 | e[0] = 1; e[4] = 0; e[8] = 0; e[12] = x; 457 | e[1] = 0; e[5] = 1; e[9] = 0; e[13] = y; 458 | e[2] = 0; e[6] = 0; e[10] = 1; e[14] = z; 459 | e[3] = 0; e[7] = 0; e[11] = 0; e[15] = 1; 460 | return this; 461 | }; 462 | 463 | /** 464 | * Multiply the matrix for translation from the right. 465 | * @param x The X value of a translation. 466 | * @param y The Y value of a translation. 467 | * @param z The Z value of a translation. 468 | * @return this 469 | */ 470 | Matrix4.prototype.translate = function(x, y, z) { 471 | var e = this.elements; 472 | e[12] += e[0] * x + e[4] * y + e[8] * z; 473 | e[13] += e[1] * x + e[5] * y + e[9] * z; 474 | e[14] += e[2] * x + e[6] * y + e[10] * z; 475 | e[15] += e[3] * x + e[7] * y + e[11] * z; 476 | return this; 477 | }; 478 | 479 | /** 480 | * Set the matrix for rotation. 481 | * The vector of rotation axis may not be normalized. 482 | * @param angle The angle of rotation (degrees) 483 | * @param x The X coordinate of vector of rotation axis. 484 | * @param y The Y coordinate of vector of rotation axis. 485 | * @param z The Z coordinate of vector of rotation axis. 486 | * @return this 487 | */ 488 | Matrix4.prototype.setRotate = function(angle, x, y, z) { 489 | var e, s, c, len, rlen, nc, xy, yz, zx, xs, ys, zs; 490 | 491 | angle = Math.PI * angle / 180; 492 | e = this.elements; 493 | 494 | s = Math.sin(angle); 495 | c = Math.cos(angle); 496 | 497 | if (0 !== x && 0 === y && 0 === z) { 498 | // Rotation around X axis 499 | if (x < 0) { 500 | s = -s; 501 | } 502 | e[0] = 1; e[4] = 0; e[ 8] = 0; e[12] = 0; 503 | e[1] = 0; e[5] = c; e[ 9] =-s; e[13] = 0; 504 | e[2] = 0; e[6] = s; e[10] = c; e[14] = 0; 505 | e[3] = 0; e[7] = 0; e[11] = 0; e[15] = 1; 506 | } else if (0 === x && 0 !== y && 0 === z) { 507 | // Rotation around Y axis 508 | if (y < 0) { 509 | s = -s; 510 | } 511 | e[0] = c; e[4] = 0; e[ 8] = s; e[12] = 0; 512 | e[1] = 0; e[5] = 1; e[ 9] = 0; e[13] = 0; 513 | e[2] =-s; e[6] = 0; e[10] = c; e[14] = 0; 514 | e[3] = 0; e[7] = 0; e[11] = 0; e[15] = 1; 515 | } else if (0 === x && 0 === y && 0 !== z) { 516 | // Rotation around Z axis 517 | if (z < 0) { 518 | s = -s; 519 | } 520 | e[0] = c; e[4] =-s; e[ 8] = 0; e[12] = 0; 521 | e[1] = s; e[5] = c; e[ 9] = 0; e[13] = 0; 522 | e[2] = 0; e[6] = 0; e[10] = 1; e[14] = 0; 523 | e[3] = 0; e[7] = 0; e[11] = 0; e[15] = 1; 524 | } else { 525 | // Rotation around another axis 526 | len = Math.sqrt(x*x + y*y + z*z); 527 | if (len !== 1) { 528 | rlen = 1 / len; 529 | x *= rlen; 530 | y *= rlen; 531 | z *= rlen; 532 | } 533 | nc = 1 - c; 534 | xy = x * y; 535 | yz = y * z; 536 | zx = z * x; 537 | xs = x * s; 538 | ys = y * s; 539 | zs = z * s; 540 | 541 | e[ 0] = x*x*nc + c; 542 | e[ 1] = xy *nc + zs; 543 | e[ 2] = zx *nc - ys; 544 | e[ 3] = 0; 545 | 546 | e[ 4] = xy *nc - zs; 547 | e[ 5] = y*y*nc + c; 548 | e[ 6] = yz *nc + xs; 549 | e[ 7] = 0; 550 | 551 | e[ 8] = zx *nc + ys; 552 | e[ 9] = yz *nc - xs; 553 | e[10] = z*z*nc + c; 554 | e[11] = 0; 555 | 556 | e[12] = 0; 557 | e[13] = 0; 558 | e[14] = 0; 559 | e[15] = 1; 560 | } 561 | 562 | return this; 563 | }; 564 | 565 | /** 566 | * Multiply the matrix for rotation from the right. 567 | * The vector of rotation axis may not be normalized. 568 | * @param angle The angle of rotation (degrees) 569 | * @param x The X coordinate of vector of rotation axis. 570 | * @param y The Y coordinate of vector of rotation axis. 571 | * @param z The Z coordinate of vector of rotation axis. 572 | * @return this 573 | */ 574 | Matrix4.prototype.rotate = function(angle, x, y, z) { 575 | return this.concat(new Matrix4().setRotate(angle, x, y, z)); 576 | }; 577 | 578 | /** 579 | * Set the viewing matrix. 580 | * @param eyeX, eyeY, eyeZ The position of the eye point. 581 | * @param centerX, centerY, centerZ The position of the reference point. 582 | * @param upX, upY, upZ The direction of the up vector. 583 | * @return this 584 | */ 585 | Matrix4.prototype.setLookAt = function(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ) { 586 | var e, fx, fy, fz, rlf, sx, sy, sz, rls, ux, uy, uz; 587 | 588 | fx = centerX - eyeX; 589 | fy = centerY - eyeY; 590 | fz = centerZ - eyeZ; 591 | 592 | // Normalize f. 593 | rlf = 1 / Math.sqrt(fx*fx + fy*fy + fz*fz); 594 | fx *= rlf; 595 | fy *= rlf; 596 | fz *= rlf; 597 | 598 | // Calculate cross product of f and up. 599 | sx = fy * upZ - fz * upY; 600 | sy = fz * upX - fx * upZ; 601 | sz = fx * upY - fy * upX; 602 | 603 | // Normalize s. 604 | rls = 1 / Math.sqrt(sx*sx + sy*sy + sz*sz); 605 | sx *= rls; 606 | sy *= rls; 607 | sz *= rls; 608 | 609 | // Calculate cross product of s and f. 610 | ux = sy * fz - sz * fy; 611 | uy = sz * fx - sx * fz; 612 | uz = sx * fy - sy * fx; 613 | 614 | // Set to this. 615 | e = this.elements; 616 | e[0] = sx; 617 | e[1] = ux; 618 | e[2] = -fx; 619 | e[3] = 0; 620 | 621 | e[4] = sy; 622 | e[5] = uy; 623 | e[6] = -fy; 624 | e[7] = 0; 625 | 626 | e[8] = sz; 627 | e[9] = uz; 628 | e[10] = -fz; 629 | e[11] = 0; 630 | 631 | e[12] = 0; 632 | e[13] = 0; 633 | e[14] = 0; 634 | e[15] = 1; 635 | 636 | // Translate. 637 | return this.translate(-eyeX, -eyeY, -eyeZ); 638 | }; 639 | 640 | /** 641 | * Multiply the viewing matrix from the right. 642 | * @param eyeX, eyeY, eyeZ The position of the eye point. 643 | * @param centerX, centerY, centerZ The position of the reference point. 644 | * @param upX, upY, upZ The direction of the up vector. 645 | * @return this 646 | */ 647 | Matrix4.prototype.lookAt = function(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ) { 648 | return this.concat(new Matrix4().setLookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ)); 649 | }; 650 | 651 | /** 652 | * Multiply the matrix for project vertex to plane from the right. 653 | * @param plane The array[A, B, C, D] of the equation of plane "Ax + By + Cz + D = 0". 654 | * @param light The array which stored coordinates of the light. if light[3]=0, treated as parallel light. 655 | * @return this 656 | */ 657 | Matrix4.prototype.dropShadow = function(plane, light) { 658 | var mat = new Matrix4(); 659 | var e = mat.elements; 660 | 661 | var dot = plane[0] * light[0] + plane[1] * light[1] + plane[2] * light[2] + plane[3] * light[3]; 662 | 663 | e[ 0] = dot - light[0] * plane[0]; 664 | e[ 1] = - light[1] * plane[0]; 665 | e[ 2] = - light[2] * plane[0]; 666 | e[ 3] = - light[3] * plane[0]; 667 | 668 | e[ 4] = - light[0] * plane[1]; 669 | e[ 5] = dot - light[1] * plane[1]; 670 | e[ 6] = - light[2] * plane[1]; 671 | e[ 7] = - light[3] * plane[1]; 672 | 673 | e[ 8] = - light[0] * plane[2]; 674 | e[ 9] = - light[1] * plane[2]; 675 | e[10] = dot - light[2] * plane[2]; 676 | e[11] = - light[3] * plane[2]; 677 | 678 | e[12] = - light[0] * plane[3]; 679 | e[13] = - light[1] * plane[3]; 680 | e[14] = - light[2] * plane[3]; 681 | e[15] = dot - light[3] * plane[3]; 682 | 683 | return this.concat(mat); 684 | } 685 | 686 | /** 687 | * Multiply the matrix for project vertex to plane from the right.(Projected by parallel light.) 688 | * @param normX, normY, normZ The normal vector of the plane.(Not necessary to be normalized.) 689 | * @param planeX, planeY, planeZ The coordinate of arbitrary points on a plane. 690 | * @param lightX, lightY, lightZ The vector of the direction of light.(Not necessary to be normalized.) 691 | * @return this 692 | */ 693 | Matrix4.prototype.dropShadowDirectionally = function(normX, normY, normZ, planeX, planeY, planeZ, lightX, lightY, lightZ) { 694 | var a = planeX * normX + planeY * normY + planeZ * normZ; 695 | return this.dropShadow([normX, normY, normZ, -a], [lightX, lightY, lightZ, 0]); 696 | }; 697 | 698 | /** 699 | * Constructor of Vector3 700 | * If opt_src is specified, new vector is initialized by opt_src. 701 | * @param opt_src source vector(option) 702 | */ 703 | var Vector3 = function(opt_src) { 704 | var v = new Float32Array(3); 705 | if (opt_src && typeof opt_src === 'object') { 706 | v[0] = opt_src[0]; v[1] = opt_src[1]; v[2] = opt_src[2]; 707 | } 708 | this.elements = v; 709 | } 710 | 711 | /** 712 | * Normalize. 713 | * @return this 714 | */ 715 | Vector3.prototype.normalize = function() { 716 | var v = this.elements; 717 | var c = v[0], d = v[1], e = v[2], g = Math.sqrt(c*c+d*d+e*e); 718 | if(g){ 719 | if(g == 1) 720 | return this; 721 | } else { 722 | v[0] = 0; v[1] = 0; v[2] = 0; 723 | return this; 724 | } 725 | g = 1/g; 726 | v[0] = c*g; v[1] = d*g; v[2] = e*g; 727 | return this; 728 | }; 729 | 730 | /** 731 | * Constructor of Vector4 732 | * If opt_src is specified, new vector is initialized by opt_src. 733 | * @param opt_src source vector(option) 734 | */ 735 | var Vector4 = function(opt_src) { 736 | var v = new Float32Array(4); 737 | if (opt_src && typeof opt_src === 'object') { 738 | v[0] = opt_src[0]; v[1] = opt_src[1]; v[2] = opt_src[2]; v[3] = opt_src[3]; 739 | } 740 | this.elements = v; 741 | } 742 | -------------------------------------------------------------------------------- /lib/cuon-utils.js: -------------------------------------------------------------------------------- 1 | // cuon-utils.js (c) 2012 kanda and matsuda 2 | /** 3 | * Create a program object and make current 4 | * @param gl GL context 5 | * @param vshader a vertex shader program (string) 6 | * @param fshader a fragment shader program (string) 7 | * @return true, if the program object was created and successfully made current 8 | */ 9 | function initShaders(gl, vshader, fshader) { 10 | var program = createProgram(gl, vshader, fshader); 11 | if (!program) { 12 | console.log('Failed to create program'); 13 | return false; 14 | } 15 | 16 | gl.useProgram(program); 17 | gl.program = program; 18 | 19 | return true; 20 | } 21 | 22 | /** 23 | * Create the linked program object 24 | * @param gl GL context 25 | * @param vshader a vertex shader program (string) 26 | * @param fshader a fragment shader program (string) 27 | * @return created program object, or null if the creation has failed 28 | */ 29 | function createProgram(gl, vshader, fshader) { 30 | // Create shader object 31 | var vertexShader = loadShader(gl, gl.VERTEX_SHADER, vshader); 32 | var fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fshader); 33 | if (!vertexShader || !fragmentShader) { 34 | return null; 35 | } 36 | 37 | // Create a program object 38 | var program = gl.createProgram(); 39 | if (!program) { 40 | return null; 41 | } 42 | 43 | // Attach the shader objects 44 | gl.attachShader(program, vertexShader); 45 | gl.attachShader(program, fragmentShader); 46 | 47 | // Link the program object 48 | gl.linkProgram(program); 49 | 50 | // Check the result of linking 51 | var linked = gl.getProgramParameter(program, gl.LINK_STATUS); 52 | if (!linked) { 53 | var error = gl.getProgramInfoLog(program); 54 | console.log('Failed to link program: ' + error); 55 | gl.deleteProgram(program); 56 | gl.deleteShader(fragmentShader); 57 | gl.deleteShader(vertexShader); 58 | return null; 59 | } 60 | return program; 61 | } 62 | 63 | /** 64 | * Create a shader object 65 | * @param gl GL context 66 | * @param type the type of the shader object to be created 67 | * @param source shader program (string) 68 | * @return created shader object, or null if the creation has failed. 69 | */ 70 | function loadShader(gl, type, source) { 71 | // Create shader object 72 | var shader = gl.createShader(type); 73 | if (shader == null) { 74 | console.log('unable to create shader'); 75 | return null; 76 | } 77 | 78 | // Set the shader program 79 | gl.shaderSource(shader, source); 80 | 81 | // Compile the shader 82 | gl.compileShader(shader); 83 | 84 | // Check the result of compilation 85 | var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS); 86 | if (!compiled) { 87 | var error = gl.getShaderInfoLog(shader); 88 | console.log('Failed to compile shader: ' + error); 89 | gl.deleteShader(shader); 90 | return null; 91 | } 92 | 93 | return shader; 94 | } 95 | 96 | /** 97 | * Initialize and get the rendering for WebGL 98 | * @param canvas element 99 | * @param opt_debug flag to initialize the context for debugging 100 | * @return the rendering context for WebGL 101 | */ 102 | function getWebGLContext(canvas, opt_debug) { 103 | // Get the rendering context for WebGL 104 | var gl = WebGLUtils.setupWebGL(canvas); 105 | if (!gl) return null; 106 | 107 | // if opt_debug is explicitly false, create the context for debugging 108 | if (arguments.length < 2 || opt_debug) { 109 | gl = WebGLDebugUtils.makeDebugContext(gl); 110 | } 111 | 112 | return gl; 113 | } 114 | -------------------------------------------------------------------------------- /lib/webgl-debug.js: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2009 The Chromium Authors. All rights reserved. 2 | //Use of this source code is governed by a BSD-style license that can be 3 | //found in the LICENSE file. 4 | 5 | // Various functions for helping debug WebGL apps. 6 | 7 | WebGLDebugUtils = function() { 8 | 9 | /** 10 | * Wrapped logging function. 11 | * @param {string} msg Message to log. 12 | */ 13 | var log = function(msg) { 14 | if (window.console && window.console.log) { 15 | window.console.log(msg); 16 | } 17 | }; 18 | 19 | /** 20 | * Which arguements are enums. 21 | * @type {!Object.} 22 | */ 23 | var glValidEnumContexts = { 24 | 25 | // Generic setters and getters 26 | 27 | 'enable': { 0:true }, 28 | 'disable': { 0:true }, 29 | 'getParameter': { 0:true }, 30 | 31 | // Rendering 32 | 33 | 'drawArrays': { 0:true }, 34 | 'drawElements': { 0:true, 2:true }, 35 | 36 | // Shaders 37 | 38 | 'createShader': { 0:true }, 39 | 'getShaderParameter': { 1:true }, 40 | 'getProgramParameter': { 1:true }, 41 | 42 | // Vertex attributes 43 | 44 | 'getVertexAttrib': { 1:true }, 45 | 'vertexAttribPointer': { 2:true }, 46 | 47 | // Textures 48 | 49 | 'bindTexture': { 0:true }, 50 | 'activeTexture': { 0:true }, 51 | 'getTexParameter': { 0:true, 1:true }, 52 | 'texParameterf': { 0:true, 1:true }, 53 | 'texParameteri': { 0:true, 1:true, 2:true }, 54 | 'texImage2D': { 0:true, 2:true, 6:true, 7:true }, 55 | 'texSubImage2D': { 0:true, 6:true, 7:true }, 56 | 'copyTexImage2D': { 0:true, 2:true }, 57 | 'copyTexSubImage2D': { 0:true }, 58 | 'generateMipmap': { 0:true }, 59 | 60 | // Buffer objects 61 | 62 | 'bindBuffer': { 0:true }, 63 | 'bufferData': { 0:true, 2:true }, 64 | 'bufferSubData': { 0:true }, 65 | 'getBufferParameter': { 0:true, 1:true }, 66 | 67 | // Renderbuffers and framebuffers 68 | 69 | 'pixelStorei': { 0:true, 1:true }, 70 | 'readPixels': { 4:true, 5:true }, 71 | 'bindRenderbuffer': { 0:true }, 72 | 'bindFramebuffer': { 0:true }, 73 | 'checkFramebufferStatus': { 0:true }, 74 | 'framebufferRenderbuffer': { 0:true, 1:true, 2:true }, 75 | 'framebufferTexture2D': { 0:true, 1:true, 2:true }, 76 | 'getFramebufferAttachmentParameter': { 0:true, 1:true, 2:true }, 77 | 'getRenderbufferParameter': { 0:true, 1:true }, 78 | 'renderbufferStorage': { 0:true, 1:true }, 79 | 80 | // Frame buffer operations (clear, blend, depth test, stencil) 81 | 82 | 'clear': { 0:true }, 83 | 'depthFunc': { 0:true }, 84 | 'blendFunc': { 0:true, 1:true }, 85 | 'blendFuncSeparate': { 0:true, 1:true, 2:true, 3:true }, 86 | 'blendEquation': { 0:true }, 87 | 'blendEquationSeparate': { 0:true, 1:true }, 88 | 'stencilFunc': { 0:true }, 89 | 'stencilFuncSeparate': { 0:true, 1:true }, 90 | 'stencilMaskSeparate': { 0:true }, 91 | 'stencilOp': { 0:true, 1:true, 2:true }, 92 | 'stencilOpSeparate': { 0:true, 1:true, 2:true, 3:true }, 93 | 94 | // Culling 95 | 96 | 'cullFace': { 0:true }, 97 | 'frontFace': { 0:true }, 98 | }; 99 | 100 | /** 101 | * Map of numbers to names. 102 | * @type {Object} 103 | */ 104 | var glEnums = null; 105 | 106 | /** 107 | * Initializes this module. Safe to call more than once. 108 | * @param {!WebGLRenderingContext} ctx A WebGL context. If 109 | * you have more than one context it doesn't matter which one 110 | * you pass in, it is only used to pull out constants. 111 | */ 112 | function init(ctx) { 113 | if (glEnums == null) { 114 | glEnums = { }; 115 | for (var propertyName in ctx) { 116 | if (typeof ctx[propertyName] == 'number') { 117 | glEnums[ctx[propertyName]] = propertyName; 118 | } 119 | } 120 | } 121 | } 122 | 123 | /** 124 | * Checks the utils have been initialized. 125 | */ 126 | function checkInit() { 127 | if (glEnums == null) { 128 | throw 'WebGLDebugUtils.init(ctx) not called'; 129 | } 130 | } 131 | 132 | /** 133 | * Returns true or false if value matches any WebGL enum 134 | * @param {*} value Value to check if it might be an enum. 135 | * @return {boolean} True if value matches one of the WebGL defined enums 136 | */ 137 | function mightBeEnum(value) { 138 | checkInit(); 139 | return (glEnums[value] !== undefined); 140 | } 141 | 142 | /** 143 | * Gets an string version of an WebGL enum. 144 | * 145 | * Example: 146 | * var str = WebGLDebugUtil.glEnumToString(ctx.getError()); 147 | * 148 | * @param {number} value Value to return an enum for 149 | * @return {string} The string version of the enum. 150 | */ 151 | function glEnumToString(value) { 152 | checkInit(); 153 | var name = glEnums[value]; 154 | return (name !== undefined) ? name : 155 | ("*UNKNOWN WebGL ENUM (0x" + value.toString(16) + ")"); 156 | } 157 | 158 | /** 159 | * Returns the string version of a WebGL argument. 160 | * Attempts to convert enum arguments to strings. 161 | * @param {string} functionName the name of the WebGL function. 162 | * @param {number} argumentIndx the index of the argument. 163 | * @param {*} value The value of the argument. 164 | * @return {string} The value as a string. 165 | */ 166 | function glFunctionArgToString(functionName, argumentIndex, value) { 167 | var funcInfo = glValidEnumContexts[functionName]; 168 | if (funcInfo !== undefined) { 169 | if (funcInfo[argumentIndex]) { 170 | return glEnumToString(value); 171 | } 172 | } 173 | return value.toString(); 174 | } 175 | 176 | /** 177 | * Given a WebGL context returns a wrapped context that calls 178 | * gl.getError after every command and calls a function if the 179 | * result is not gl.NO_ERROR. 180 | * 181 | * @param {!WebGLRenderingContext} ctx The webgl context to 182 | * wrap. 183 | * @param {!function(err, funcName, args): void} opt_onErrorFunc 184 | * The function to call when gl.getError returns an 185 | * error. If not specified the default function calls 186 | * console.log with a message. 187 | */ 188 | function makeDebugContext(ctx, opt_onErrorFunc) { 189 | init(ctx); 190 | opt_onErrorFunc = opt_onErrorFunc || function(err, functionName, args) { 191 | // apparently we can't do args.join(","); 192 | var argStr = ""; 193 | for (var ii = 0; ii < args.length; ++ii) { 194 | argStr += ((ii == 0) ? '' : ', ') + 195 | glFunctionArgToString(functionName, ii, args[ii]); 196 | } 197 | log("WebGL error "+ glEnumToString(err) + " in "+ functionName + 198 | "(" + argStr + ")"); 199 | }; 200 | 201 | // Holds booleans for each GL error so after we get the error ourselves 202 | // we can still return it to the client app. 203 | var glErrorShadow = { }; 204 | 205 | // Makes a function that calls a WebGL function and then calls getError. 206 | function makeErrorWrapper(ctx, functionName) { 207 | return function() { 208 | var result = ctx[functionName].apply(ctx, arguments); 209 | var err = ctx.getError(); 210 | if (err != 0) { 211 | glErrorShadow[err] = true; 212 | opt_onErrorFunc(err, functionName, arguments); 213 | } 214 | return result; 215 | }; 216 | } 217 | 218 | // Make a an object that has a copy of every property of the WebGL context 219 | // but wraps all functions. 220 | var wrapper = {}; 221 | for (var propertyName in ctx) { 222 | if (typeof ctx[propertyName] == 'function') { 223 | wrapper[propertyName] = makeErrorWrapper(ctx, propertyName); 224 | } else { 225 | wrapper[propertyName] = ctx[propertyName]; 226 | } 227 | } 228 | 229 | // Override the getError function with one that returns our saved results. 230 | wrapper.getError = function() { 231 | for (var err in glErrorShadow) { 232 | if (glErrorShadow[err]) { 233 | glErrorShadow[err] = false; 234 | return err; 235 | } 236 | } 237 | return ctx.NO_ERROR; 238 | }; 239 | 240 | return wrapper; 241 | } 242 | 243 | function resetToInitialState(ctx) { 244 | var numAttribs = ctx.getParameter(ctx.MAX_VERTEX_ATTRIBS); 245 | var tmp = ctx.createBuffer(); 246 | ctx.bindBuffer(ctx.ARRAY_BUFFER, tmp); 247 | for (var ii = 0; ii < numAttribs; ++ii) { 248 | ctx.disableVertexAttribArray(ii); 249 | ctx.vertexAttribPointer(ii, 4, ctx.FLOAT, false, 0, 0); 250 | ctx.vertexAttrib1f(ii, 0); 251 | } 252 | ctx.deleteBuffer(tmp); 253 | 254 | var numTextureUnits = ctx.getParameter(ctx.MAX_TEXTURE_IMAGE_UNITS); 255 | for (var ii = 0; ii < numTextureUnits; ++ii) { 256 | ctx.activeTexture(ctx.TEXTURE0 + ii); 257 | ctx.bindTexture(ctx.TEXTURE_CUBE_MAP, null); 258 | ctx.bindTexture(ctx.TEXTURE_2D, null); 259 | } 260 | 261 | ctx.activeTexture(ctx.TEXTURE0); 262 | ctx.useProgram(null); 263 | ctx.bindBuffer(ctx.ARRAY_BUFFER, null); 264 | ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, null); 265 | ctx.bindFramebuffer(ctx.FRAMEBUFFER, null); 266 | ctx.bindRenderbuffer(ctx.RENDERBUFFER, null); 267 | ctx.disable(ctx.BLEND); 268 | ctx.disable(ctx.CULL_FACE); 269 | ctx.disable(ctx.DEPTH_TEST); 270 | ctx.disable(ctx.DITHER); 271 | ctx.disable(ctx.SCISSOR_TEST); 272 | ctx.blendColor(0, 0, 0, 0); 273 | ctx.blendEquation(ctx.FUNC_ADD); 274 | ctx.blendFunc(ctx.ONE, ctx.ZERO); 275 | ctx.clearColor(0, 0, 0, 0); 276 | ctx.clearDepth(1); 277 | ctx.clearStencil(-1); 278 | ctx.colorMask(true, true, true, true); 279 | ctx.cullFace(ctx.BACK); 280 | ctx.depthFunc(ctx.LESS); 281 | ctx.depthMask(true); 282 | ctx.depthRange(0, 1); 283 | ctx.frontFace(ctx.CCW); 284 | ctx.hint(ctx.GENERATE_MIPMAP_HINT, ctx.DONT_CARE); 285 | ctx.lineWidth(1); 286 | ctx.pixelStorei(ctx.PACK_ALIGNMENT, 4); 287 | ctx.pixelStorei(ctx.UNPACK_ALIGNMENT, 4); 288 | ctx.pixelStorei(ctx.UNPACK_FLIP_Y_WEBGL, false); 289 | ctx.pixelStorei(ctx.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); 290 | // TODO: Delete this IF. 291 | if (ctx.UNPACK_COLORSPACE_CONVERSION_WEBGL) { 292 | ctx.pixelStorei(ctx.UNPACK_COLORSPACE_CONVERSION_WEBGL, ctx.BROWSER_DEFAULT_WEBGL); 293 | } 294 | ctx.polygonOffset(0, 0); 295 | ctx.sampleCoverage(1, false); 296 | ctx.scissor(0, 0, ctx.canvas.width, ctx.canvas.height); 297 | ctx.stencilFunc(ctx.ALWAYS, 0, 0xFFFFFFFF); 298 | ctx.stencilMask(0xFFFFFFFF); 299 | ctx.stencilOp(ctx.KEEP, ctx.KEEP, ctx.KEEP); 300 | ctx.viewport(0, 0, ctx.canvas.clientWidth, ctx.canvas.clientHeight); 301 | ctx.clear(ctx.COLOR_BUFFER_BIT | ctx.DEPTH_BUFFER_BIT | ctx.STENCIL_BUFFER_BIT); 302 | 303 | // TODO: This should NOT be needed but Firefox fails with 'hint' 304 | while(ctx.getError()); 305 | } 306 | 307 | function makeLostContextSimulatingContext(ctx) { 308 | var wrapper_ = {}; 309 | var contextId_ = 1; 310 | var contextLost_ = false; 311 | var resourceId_ = 0; 312 | var resourceDb_ = []; 313 | var onLost_ = undefined; 314 | var onRestored_ = undefined; 315 | var nextOnRestored_ = undefined; 316 | 317 | // Holds booleans for each GL error so can simulate errors. 318 | var glErrorShadow_ = { }; 319 | 320 | function isWebGLObject(obj) { 321 | //return false; 322 | return (obj instanceof WebGLBuffer || 323 | obj instanceof WebGLFramebuffer || 324 | obj instanceof WebGLProgram || 325 | obj instanceof WebGLRenderbuffer || 326 | obj instanceof WebGLShader || 327 | obj instanceof WebGLTexture); 328 | } 329 | 330 | function checkResources(args) { 331 | for (var ii = 0; ii < args.length; ++ii) { 332 | var arg = args[ii]; 333 | if (isWebGLObject(arg)) { 334 | return arg.__webglDebugContextLostId__ == contextId_; 335 | } 336 | } 337 | return true; 338 | } 339 | 340 | function clearErrors() { 341 | var k = Object.keys(glErrorShadow_); 342 | for (var ii = 0; ii < k.length; ++ii) { 343 | delete glErrorShdow_[k]; 344 | } 345 | } 346 | 347 | // Makes a function that simulates WebGL when out of context. 348 | function makeLostContextWrapper(ctx, functionName) { 349 | var f = ctx[functionName]; 350 | return function() { 351 | // Only call the functions if the context is not lost. 352 | if (!contextLost_) { 353 | if (!checkResources(arguments)) { 354 | glErrorShadow_[ctx.INVALID_OPERATION] = true; 355 | return; 356 | } 357 | var result = f.apply(ctx, arguments); 358 | return result; 359 | } 360 | }; 361 | } 362 | 363 | for (var propertyName in ctx) { 364 | if (typeof ctx[propertyName] == 'function') { 365 | wrapper_[propertyName] = makeLostContextWrapper(ctx, propertyName); 366 | } else { 367 | wrapper_[propertyName] = ctx[propertyName]; 368 | } 369 | } 370 | 371 | function makeWebGLContextEvent(statusMessage) { 372 | return {statusMessage: statusMessage}; 373 | } 374 | 375 | function freeResources() { 376 | for (var ii = 0; ii < resourceDb_.length; ++ii) { 377 | var resource = resourceDb_[ii]; 378 | if (resource instanceof WebGLBuffer) { 379 | ctx.deleteBuffer(resource); 380 | } else if (resource instanceof WebctxFramebuffer) { 381 | ctx.deleteFramebuffer(resource); 382 | } else if (resource instanceof WebctxProgram) { 383 | ctx.deleteProgram(resource); 384 | } else if (resource instanceof WebctxRenderbuffer) { 385 | ctx.deleteRenderbuffer(resource); 386 | } else if (resource instanceof WebctxShader) { 387 | ctx.deleteShader(resource); 388 | } else if (resource instanceof WebctxTexture) { 389 | ctx.deleteTexture(resource); 390 | } 391 | } 392 | } 393 | 394 | wrapper_.loseContext = function() { 395 | if (!contextLost_) { 396 | contextLost_ = true; 397 | ++contextId_; 398 | while (ctx.getError()); 399 | clearErrors(); 400 | glErrorShadow_[ctx.CONTEXT_LOST_WEBGL] = true; 401 | setTimeout(function() { 402 | if (onLost_) { 403 | onLost_(makeWebGLContextEvent("context lost")); 404 | } 405 | }, 0); 406 | } 407 | }; 408 | 409 | wrapper_.restoreContext = function() { 410 | if (contextLost_) { 411 | if (onRestored_) { 412 | setTimeout(function() { 413 | freeResources(); 414 | resetToInitialState(ctx); 415 | contextLost_ = false; 416 | if (onRestored_) { 417 | var callback = onRestored_; 418 | onRestored_ = nextOnRestored_; 419 | nextOnRestored_ = undefined; 420 | callback(makeWebGLContextEvent("context restored")); 421 | } 422 | }, 0); 423 | } else { 424 | throw "You can not restore the context without a listener" 425 | } 426 | } 427 | }; 428 | 429 | // Wrap a few functions specially. 430 | wrapper_.getError = function() { 431 | if (!contextLost_) { 432 | var err; 433 | while (err = ctx.getError()) { 434 | glErrorShadow_[err] = true; 435 | } 436 | } 437 | for (var err in glErrorShadow_) { 438 | if (glErrorShadow_[err]) { 439 | delete glErrorShadow_[err]; 440 | return err; 441 | } 442 | } 443 | return ctx.NO_ERROR; 444 | }; 445 | 446 | var creationFunctions = [ 447 | "createBuffer", 448 | "createFramebuffer", 449 | "createProgram", 450 | "createRenderbuffer", 451 | "createShader", 452 | "createTexture" 453 | ]; 454 | for (var ii = 0; ii < creationFunctions.length; ++ii) { 455 | var functionName = creationFunctions[ii]; 456 | wrapper_[functionName] = function(f) { 457 | return function() { 458 | if (contextLost_) { 459 | return null; 460 | } 461 | var obj = f.apply(ctx, arguments); 462 | obj.__webglDebugContextLostId__ = contextId_; 463 | resourceDb_.push(obj); 464 | return obj; 465 | }; 466 | }(ctx[functionName]); 467 | } 468 | 469 | var functionsThatShouldReturnNull = [ 470 | "getActiveAttrib", 471 | "getActiveUniform", 472 | "getBufferParameter", 473 | "getContextAttributes", 474 | "getAttachedShaders", 475 | "getFramebufferAttachmentParameter", 476 | "getParameter", 477 | "getProgramParameter", 478 | "getProgramInfoLog", 479 | "getRenderbufferParameter", 480 | "getShaderParameter", 481 | "getShaderInfoLog", 482 | "getShaderSource", 483 | "getTexParameter", 484 | "getUniform", 485 | "getUniformLocation", 486 | "getVertexAttrib" 487 | ]; 488 | for (var ii = 0; ii < functionsThatShouldReturnNull.length; ++ii) { 489 | var functionName = functionsThatShouldReturnNull[ii]; 490 | wrapper_[functionName] = function(f) { 491 | return function() { 492 | if (contextLost_) { 493 | return null; 494 | } 495 | return f.apply(ctx, arguments); 496 | } 497 | }(wrapper_[functionName]); 498 | } 499 | 500 | var isFunctions = [ 501 | "isBuffer", 502 | "isEnabled", 503 | "isFramebuffer", 504 | "isProgram", 505 | "isRenderbuffer", 506 | "isShader", 507 | "isTexture" 508 | ]; 509 | for (var ii = 0; ii < isFunctions.length; ++ii) { 510 | var functionName = isFunctions[ii]; 511 | wrapper_[functionName] = function(f) { 512 | return function() { 513 | if (contextLost_) { 514 | return false; 515 | } 516 | return f.apply(ctx, arguments); 517 | } 518 | }(wrapper_[functionName]); 519 | } 520 | 521 | wrapper_.checkFramebufferStatus = function(f) { 522 | return function() { 523 | if (contextLost_) { 524 | return ctx.FRAMEBUFFER_UNSUPPORTED; 525 | } 526 | return f.apply(ctx, arguments); 527 | }; 528 | }(wrapper_.checkFramebufferStatus); 529 | 530 | wrapper_.getAttribLocation = function(f) { 531 | return function() { 532 | if (contextLost_) { 533 | return -1; 534 | } 535 | return f.apply(ctx, arguments); 536 | }; 537 | }(wrapper_.getAttribLocation); 538 | 539 | wrapper_.getVertexAttribOffset = function(f) { 540 | return function() { 541 | if (contextLost_) { 542 | return 0; 543 | } 544 | return f.apply(ctx, arguments); 545 | }; 546 | }(wrapper_.getVertexAttribOffset); 547 | 548 | wrapper_.isContextLost = function() { 549 | return contextLost_; 550 | }; 551 | 552 | function wrapEvent(listener) { 553 | if (typeof(listener) == "function") { 554 | return listener; 555 | } else { 556 | return function(info) { 557 | listener.handleEvent(info); 558 | } 559 | } 560 | } 561 | 562 | wrapper_.registerOnContextLostListener = function(listener) { 563 | onLost_ = wrapEvent(listener); 564 | }; 565 | 566 | wrapper_.registerOnContextRestoredListener = function(listener) { 567 | if (contextLost_) { 568 | nextOnRestored_ = wrapEvent(listener); 569 | } else { 570 | onRestored_ = wrapEvent(listener); 571 | } 572 | } 573 | 574 | return wrapper_; 575 | } 576 | 577 | return { 578 | /** 579 | * Initializes this module. Safe to call more than once. 580 | * @param {!WebGLRenderingContext} ctx A WebGL context. If 581 | * you have more than one context it doesn't matter which one 582 | * you pass in, it is only used to pull out constants. 583 | */ 584 | 'init': init, 585 | 586 | /** 587 | * Returns true or false if value matches any WebGL enum 588 | * @param {*} value Value to check if it might be an enum. 589 | * @return {boolean} True if value matches one of the WebGL defined enums 590 | */ 591 | 'mightBeEnum': mightBeEnum, 592 | 593 | /** 594 | * Gets an string version of an WebGL enum. 595 | * 596 | * Example: 597 | * WebGLDebugUtil.init(ctx); 598 | * var str = WebGLDebugUtil.glEnumToString(ctx.getError()); 599 | * 600 | * @param {number} value Value to return an enum for 601 | * @return {string} The string version of the enum. 602 | */ 603 | 'glEnumToString': glEnumToString, 604 | 605 | /** 606 | * Converts the argument of a WebGL function to a string. 607 | * Attempts to convert enum arguments to strings. 608 | * 609 | * Example: 610 | * WebGLDebugUtil.init(ctx); 611 | * var str = WebGLDebugUtil.glFunctionArgToString('bindTexture', 0, gl.TEXTURE_2D); 612 | * 613 | * would return 'TEXTURE_2D' 614 | * 615 | * @param {string} functionName the name of the WebGL function. 616 | * @param {number} argumentIndx the index of the argument. 617 | * @param {*} value The value of the argument. 618 | * @return {string} The value as a string. 619 | */ 620 | 'glFunctionArgToString': glFunctionArgToString, 621 | 622 | /** 623 | * Given a WebGL context returns a wrapped context that calls 624 | * gl.getError after every command and calls a function if the 625 | * result is not NO_ERROR. 626 | * 627 | * You can supply your own function if you want. For example, if you'd like 628 | * an exception thrown on any GL error you could do this 629 | * 630 | * function throwOnGLError(err, funcName, args) { 631 | * throw WebGLDebugUtils.glEnumToString(err) + " was caused by call to" + 632 | * funcName; 633 | * }; 634 | * 635 | * ctx = WebGLDebugUtils.makeDebugContext( 636 | * canvas.getContext("webgl"), throwOnGLError); 637 | * 638 | * @param {!WebGLRenderingContext} ctx The webgl context to wrap. 639 | * @param {!function(err, funcName, args): void} opt_onErrorFunc The function 640 | * to call when gl.getError returns an error. If not specified the default 641 | * function calls console.log with a message. 642 | */ 643 | 'makeDebugContext': makeDebugContext, 644 | 645 | /** 646 | * Given a WebGL context returns a wrapped context that adds 4 647 | * functions. 648 | * 649 | * ctx.loseContext: 650 | * simulates a lost context event. 651 | * 652 | * ctx.restoreContext: 653 | * simulates the context being restored. 654 | * 655 | * ctx.registerOnContextLostListener(listener): 656 | * lets you register a listener for context lost. Use instead 657 | * of addEventListener('webglcontextlostevent', listener); 658 | * 659 | * ctx.registerOnContextRestoredListener(listener): 660 | * lets you register a listener for context restored. Use 661 | * instead of addEventListener('webglcontextrestored', 662 | * listener); 663 | * 664 | * @param {!WebGLRenderingContext} ctx The webgl context to wrap. 665 | */ 666 | 'makeLostContextSimulatingContext': makeLostContextSimulatingContext, 667 | 668 | /** 669 | * Resets a context to the initial state. 670 | * @param {!WebGLRenderingContext} ctx The webgl context to 671 | * reset. 672 | */ 673 | 'resetToInitialState': resetToInitialState 674 | }; 675 | 676 | }(); 677 | 678 | -------------------------------------------------------------------------------- /lib/webgl-utils.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010, Google Inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above 12 | * copyright notice, this list of conditions and the following disclaimer 13 | * in the documentation and/or other materials provided with the 14 | * distribution. 15 | * * Neither the name of Google Inc. nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | 33 | /** 34 | * @fileoverview This file contains functions every webgl program will need 35 | * a version of one way or another. 36 | * 37 | * Instead of setting up a context manually it is recommended to 38 | * use. This will check for success or failure. On failure it 39 | * will attempt to present an approriate message to the user. 40 | * 41 | * gl = WebGLUtils.setupWebGL(canvas); 42 | * 43 | * For animated WebGL apps use of setTimeout or setInterval are 44 | * discouraged. It is recommended you structure your rendering 45 | * loop like this. 46 | * 47 | * function render() { 48 | * window.requestAnimationFrame(render, canvas); 49 | * 50 | * // do rendering 51 | * ... 52 | * } 53 | * render(); 54 | * 55 | * This will call your rendering function up to the refresh rate 56 | * of your display but will stop rendering if your app is not 57 | * visible. 58 | */ 59 | 60 | WebGLUtils = function() { 61 | 62 | /** 63 | * Creates the HTLM for a failure message 64 | * @param {string} canvasContainerId id of container of th 65 | * canvas. 66 | * @return {string} The html. 67 | */ 68 | var makeFailHTML = function(msg) { 69 | return '' + 70 | '
' + msg + '
'; 71 | return '' + 72 | '' + 73 | '
' + 74 | '
' + 75 | '
' + msg + '
' + 76 | '
' + 77 | '
'; 78 | }; 79 | 80 | /** 81 | * Mesasge for getting a webgl browser 82 | * @type {string} 83 | */ 84 | var GET_A_WEBGL_BROWSER = '' + 85 | 'This page requires a browser that supports WebGL.
' + 86 | 'Click here to upgrade your browser.'; 87 | 88 | /** 89 | * Mesasge for need better hardware 90 | * @type {string} 91 | */ 92 | var OTHER_PROBLEM = '' + 93 | "It doesn't appear your computer can support WebGL.
" + 94 | 'Click here for more information.'; 95 | 96 | /** 97 | * Creates a webgl context. If creation fails it will 98 | * change the contents of the container of the 99 | * tag to an error message with the correct links for WebGL. 100 | * @param {Element} canvas. The canvas element to create a 101 | * context from. 102 | * @param {WebGLContextCreationAttirbutes} opt_attribs Any 103 | * creation attributes you want to pass in. 104 | * @param {function:(msg)} opt_onError An function to call 105 | * if there is an error during creation. 106 | * @return {WebGLRenderingContext} The created context. 107 | */ 108 | var setupWebGL = function(canvas, opt_attribs, opt_onError) { 109 | function handleCreationError(msg) { 110 | var container = document.getElementsByTagName("body")[0]; 111 | //var container = canvas.parentNode; 112 | if (container) { 113 | var str = window.WebGLRenderingContext ? 114 | OTHER_PROBLEM : 115 | GET_A_WEBGL_BROWSER; 116 | if (msg) { 117 | str += "

Status: " + msg; 118 | } 119 | container.innerHTML = makeFailHTML(str); 120 | } 121 | }; 122 | 123 | opt_onError = opt_onError || handleCreationError; 124 | 125 | if (canvas.addEventListener) { 126 | canvas.addEventListener("webglcontextcreationerror", function(event) { 127 | opt_onError(event.statusMessage); 128 | }, false); 129 | } 130 | var context = create3DContext(canvas, opt_attribs); 131 | if (!context) { 132 | if (!window.WebGLRenderingContext) { 133 | opt_onError(""); 134 | } else { 135 | opt_onError(""); 136 | } 137 | } 138 | 139 | return context; 140 | }; 141 | 142 | /** 143 | * Creates a webgl context. 144 | * @param {!Canvas} canvas The canvas tag to get context 145 | * from. If one is not passed in one will be created. 146 | * @return {!WebGLContext} The created context. 147 | */ 148 | var create3DContext = function(canvas, opt_attribs) { 149 | var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"]; 150 | var context = null; 151 | for (var ii = 0; ii < names.length; ++ii) { 152 | try { 153 | context = canvas.getContext(names[ii], opt_attribs); 154 | } catch(e) {} 155 | if (context) { 156 | break; 157 | } 158 | } 159 | return context; 160 | } 161 | 162 | return { 163 | create3DContext: create3DContext, 164 | setupWebGL: setupWebGL 165 | }; 166 | }(); 167 | 168 | /** 169 | * Provides requestAnimationFrame in a cross browser 170 | * way. 171 | */ 172 | if (!window.requestAnimationFrame) { 173 | window.requestAnimationFrame = (function() { 174 | return window.requestAnimationFrame || 175 | window.webkitRequestAnimationFrame || 176 | window.mozRequestAnimationFrame || 177 | window.oRequestAnimationFrame || 178 | window.msRequestAnimationFrame || 179 | function(/* function FrameRequestCallback */ callback, /* DOMElement Element */ element) { 180 | window.setTimeout(callback, 1000/60); 181 | }; 182 | })(); 183 | } 184 | 185 | /** * ERRATA: 'cancelRequestAnimationFrame' renamed to 'cancelAnimationFrame' to reflect an update to the W3C Animation-Timing Spec. 186 | * 187 | * Cancels an animation frame request. 188 | * Checks for cross-browser support, falls back to clearTimeout. 189 | * @param {number} Animation frame request. */ 190 | if (!window.cancelAnimationFrame) { 191 | window.cancelAnimationFrame = (window.cancelRequestAnimationFrame || 192 | window.webkitCancelAnimationFrame || window.webkitCancelRequestAnimationFrame || 193 | window.mozCancelAnimationFrame || window.mozCancelRequestAnimationFrame || 194 | window.msCancelAnimationFrame || window.msCancelRequestAnimationFrame || 195 | window.oCancelAnimationFrame || window.oCancelRequestAnimationFrame || 196 | window.clearTimeout); 197 | } --------------------------------------------------------------------------------