PageRenderTime 55ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/lesson10/index.html

https://github.com/k1mo/webgl-lessons
HTML | 427 lines | 315 code | 112 blank | 0 comment | 0 complexity | ba63d16ecabc9708ebd74c0d7bb58241 MD5 | raw file
  1. <html>
  2. <head>
  3. <title>Learning WebGL &mdash; lesson 10</title>
  4. <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
  5. <script type="text/javascript" src="sylvester.js"></script>
  6. <script type="text/javascript" src="glUtils.js"></script>
  7. <script id="shader-fs" type="x-shader/x-fragment">
  8. #ifdef GL_ES
  9. precision highp float;
  10. #endif
  11. varying vec2 vTextureCoord;
  12. uniform sampler2D uSampler;
  13. void main(void) {
  14. gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
  15. }
  16. </script>
  17. <script id="shader-vs" type="x-shader/x-vertex">
  18. attribute vec3 aVertexPosition;
  19. attribute vec2 aTextureCoord;
  20. uniform mat4 uMVMatrix;
  21. uniform mat4 uPMatrix;
  22. varying vec2 vTextureCoord;
  23. void main(void) {
  24. gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
  25. vTextureCoord = aTextureCoord;
  26. }
  27. </script>
  28. <script type="text/javascript">
  29. var gl;
  30. function initGL(canvas) {
  31. try {
  32. gl = canvas.getContext("experimental-webgl");
  33. gl.viewportWidth = canvas.width;
  34. gl.viewportHeight = canvas.height;
  35. } catch(e) {
  36. }
  37. if (!gl) {
  38. alert("Could not initialise WebGL, sorry :-(");
  39. }
  40. }
  41. function getShader(gl, id) {
  42. var shaderScript = document.getElementById(id);
  43. if (!shaderScript) {
  44. return null;
  45. }
  46. var str = "";
  47. var k = shaderScript.firstChild;
  48. while (k) {
  49. if (k.nodeType == 3) {
  50. str += k.textContent;
  51. }
  52. k = k.nextSibling;
  53. }
  54. var shader;
  55. if (shaderScript.type == "x-shader/x-fragment") {
  56. shader = gl.createShader(gl.FRAGMENT_SHADER);
  57. } else if (shaderScript.type == "x-shader/x-vertex") {
  58. shader = gl.createShader(gl.VERTEX_SHADER);
  59. } else {
  60. return null;
  61. }
  62. gl.shaderSource(shader, str);
  63. gl.compileShader(shader);
  64. if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
  65. alert(gl.getShaderInfoLog(shader));
  66. return null;
  67. }
  68. return shader;
  69. }
  70. var shaderProgram;
  71. function initShaders() {
  72. var fragmentShader = getShader(gl, "shader-fs");
  73. var vertexShader = getShader(gl, "shader-vs");
  74. shaderProgram = gl.createProgram();
  75. gl.attachShader(shaderProgram, vertexShader);
  76. gl.attachShader(shaderProgram, fragmentShader);
  77. gl.linkProgram(shaderProgram);
  78. if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
  79. alert("Could not initialise shaders");
  80. }
  81. gl.useProgram(shaderProgram);
  82. shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
  83. gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
  84. shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord");
  85. gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute);
  86. shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
  87. shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
  88. shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler");
  89. }
  90. function handleLoadedTexture(texture) {
  91. gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
  92. gl.bindTexture(gl.TEXTURE_2D, texture);
  93. gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image);
  94. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
  95. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
  96. gl.bindTexture(gl.TEXTURE_2D, null);
  97. }
  98. var mudTexture;
  99. function initTexture() {
  100. mudTexture = gl.createTexture();
  101. mudTexture.image = new Image();
  102. mudTexture.image.onload = function() {
  103. handleLoadedTexture(mudTexture)
  104. }
  105. mudTexture.image.src = "mud.gif";
  106. }
  107. var mvMatrix;
  108. var mvMatrixStack = [];
  109. function mvPushMatrix(m) {
  110. if (m) {
  111. mvMatrixStack.push(m.dup());
  112. mvMatrix = m.dup();
  113. } else {
  114. mvMatrixStack.push(mvMatrix.dup());
  115. }
  116. }
  117. function mvPopMatrix() {
  118. if (mvMatrixStack.length == 0)
  119. {
  120. throw "Invalid popMatrix!";
  121. }
  122. mvMatrix = mvMatrixStack.pop();
  123. return mvMatrix;
  124. }
  125. function loadIdentity() {
  126. mvMatrix = Matrix.I(4);
  127. }
  128. function multMatrix(m) {
  129. mvMatrix = mvMatrix.x(m);
  130. }
  131. function mvTranslate(v) {
  132. var m = Matrix.Translation($V([v[0], v[1], v[2]])).ensure4x4();
  133. multMatrix(m);
  134. }
  135. function mvRotate(ang, v) {
  136. var arad = ang * Math.PI / 180.0;
  137. var m = Matrix.Rotation(arad, $V([v[0], v[1], v[2]])).ensure4x4();
  138. multMatrix(m);
  139. }
  140. var pMatrix;
  141. function perspective(fovy, aspect, znear, zfar) {
  142. pMatrix = makePerspective(fovy, aspect, znear, zfar);
  143. }
  144. function setMatrixUniforms() {
  145. gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, new Float32Array(pMatrix.flatten()));
  146. gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, new Float32Array(mvMatrix.flatten()));
  147. }
  148. var currentlyPressedKeys = Object();
  149. function handleKeyDown(event) {
  150. currentlyPressedKeys[event.keyCode] = true;
  151. }
  152. function handleKeyUp(event) {
  153. currentlyPressedKeys[event.keyCode] = false;
  154. }
  155. var pitch = 0;
  156. var pitchRate = 0;
  157. var yaw = 0;
  158. var yawRate = 0;
  159. var xPos = 0;
  160. var yPos = 0.4;
  161. var zPos = 0;
  162. var speed = 0;
  163. function handleKeys() {
  164. if (currentlyPressedKeys[33]) {
  165. // Page Up
  166. pitchRate = 0.1;
  167. } else if (currentlyPressedKeys[34]) {
  168. // Page Down
  169. pitchRate = -0.1;
  170. } else {
  171. pitchRate = 0;
  172. }
  173. if (currentlyPressedKeys[37] || currentlyPressedKeys[65]) {
  174. // Left cursor key or A
  175. yawRate = 0.1;
  176. } else if (currentlyPressedKeys[39] || currentlyPressedKeys[68]) {
  177. // Right cursor key or D
  178. yawRate = -0.1;
  179. } else {
  180. yawRate = 0;
  181. }
  182. if (currentlyPressedKeys[38] || currentlyPressedKeys[87]) {
  183. // Up cursor key or W
  184. speed = 0.003;
  185. } else if (currentlyPressedKeys[40] || currentlyPressedKeys[83]) {
  186. // Down cursor key
  187. speed = -0.003;
  188. } else {
  189. speed = 0;
  190. }
  191. }
  192. var worldVertexPositionBuffer = null;
  193. var worldVertexTextureCoordBuffer = null;
  194. function handleLoadedWorld(data) {
  195. var lines = data.split("\n");
  196. var vertexCount = 0;
  197. var vertexPositions = [];
  198. var vertexTextureCoords = [];
  199. for (var i in lines) {
  200. var vals = lines[i].replace(/^\s+/, "").split(/\s+/);
  201. if (vals.length == 5 && vals[0] != "//") {
  202. // It is a line describing a vertex; get X, Y and Z first
  203. vertexPositions.push(parseFloat(vals[0]));
  204. vertexPositions.push(parseFloat(vals[1]));
  205. vertexPositions.push(parseFloat(vals[2]));
  206. // And then the texture coords
  207. vertexTextureCoords.push(parseFloat(vals[3]));
  208. vertexTextureCoords.push(parseFloat(vals[4]));
  209. vertexCount += 1;
  210. }
  211. }
  212. worldVertexPositionBuffer = gl.createBuffer();
  213. gl.bindBuffer(gl.ARRAY_BUFFER, worldVertexPositionBuffer);
  214. gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexPositions), gl.STATIC_DRAW);
  215. worldVertexPositionBuffer.itemSize = 3;
  216. worldVertexPositionBuffer.numItems = vertexCount;
  217. worldVertexTextureCoordBuffer = gl.createBuffer();
  218. gl.bindBuffer(gl.ARRAY_BUFFER, worldVertexTextureCoordBuffer);
  219. gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexTextureCoords), gl.STATIC_DRAW);
  220. worldVertexTextureCoordBuffer.itemSize = 2;
  221. worldVertexTextureCoordBuffer.numItems = vertexCount;
  222. document.getElementById("loadingtext").textContent = "";
  223. }
  224. function loadWorld() {
  225. var request = new XMLHttpRequest();
  226. request.open("GET", "world.txt");
  227. request.onreadystatechange = function() {
  228. if (request.readyState == 4) {
  229. handleLoadedWorld(request.responseText);
  230. }
  231. }
  232. request.send();
  233. }
  234. function drawScene() {
  235. gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
  236. gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  237. if (worldVertexTextureCoordBuffer == null || worldVertexPositionBuffer == null) {
  238. return;
  239. }
  240. perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0);
  241. loadIdentity();
  242. mvRotate(-pitch, [1, 0, 0]);
  243. mvRotate(-yaw, [0, 1, 0]);
  244. mvTranslate([-xPos, -yPos, -zPos]);
  245. gl.activeTexture(gl.TEXTURE0);
  246. gl.bindTexture(gl.TEXTURE_2D, mudTexture);
  247. gl.uniform1i(shaderProgram.samplerUniform, 0);
  248. gl.bindBuffer(gl.ARRAY_BUFFER, worldVertexTextureCoordBuffer);
  249. gl.vertexAttribPointer(shaderProgram.textureCoordAttribute, worldVertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);
  250. gl.bindBuffer(gl.ARRAY_BUFFER, worldVertexPositionBuffer);
  251. gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, worldVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
  252. setMatrixUniforms();
  253. gl.drawArrays(gl.TRIANGLES, 0, worldVertexPositionBuffer.numItems);
  254. }
  255. var lastTime = 0;
  256. var piOver180 = Math.PI / 180;
  257. // Used to make us "jog" up and down as we move forward.
  258. var joggingAngle = 0;
  259. function animate() {
  260. var timeNow = new Date().getTime();
  261. if (lastTime != 0) {
  262. var elapsed = timeNow - lastTime;
  263. if (speed != 0) {
  264. xPos -= Math.sin(yaw * piOver180) * speed * elapsed;
  265. zPos -= Math.cos(yaw * piOver180) * speed * elapsed;
  266. joggingAngle += elapsed * 0.6; // 0.6 "fiddle factor" - makes it feel more realistic :-)
  267. yPos = Math.sin(joggingAngle * piOver180) / 20 + 0.4
  268. }
  269. yaw += yawRate * elapsed;
  270. pitch += pitchRate * elapsed;
  271. }
  272. lastTime = timeNow;
  273. }
  274. function tick() {
  275. handleKeys();
  276. drawScene();
  277. animate();
  278. }
  279. function webGLStart() {
  280. var canvas = document.getElementById("lesson10-canvas");
  281. initGL(canvas);
  282. initShaders();
  283. initTexture();
  284. loadWorld();
  285. gl.clearColor(0.0, 0.0, 0.0, 1.0);
  286. gl.clearDepth(1.0);
  287. gl.enable(gl.DEPTH_TEST);
  288. gl.depthFunc(gl.LEQUAL);
  289. document.onkeydown = handleKeyDown;
  290. document.onkeyup = handleKeyUp;
  291. setInterval(tick, 15);
  292. }
  293. </script>
  294. <style type="text/css">
  295. #loadingtext {
  296. position:absolute;
  297. top:250px;
  298. left:150px;
  299. font-size:2em;
  300. color: white;
  301. }
  302. </style>
  303. </head>
  304. <body onload="webGLStart();">
  305. <a href="http://learningwebgl.com/blog/?p=1067">&lt;&lt; Back to Lesson 10</a><br />
  306. <canvas id="lesson10-canvas" style="border: none;" width="500" height="500"></canvas>
  307. <div id="loadingtext">Loading world...</div>
  308. <br/>
  309. Use the cursor keys or WASD to run around, and <code>Page Up</code>/<code>Page Down</code> to
  310. look up and down.
  311. <br/>
  312. <br/>
  313. <a href="http://learningwebgl.com/blog/?p=1067">&lt;&lt; Back to Lesson 10</a>
  314. </body>
  315. </html>