├── .gitattributes ├── .gitignore ├── 404.html ├── CNAME ├── Lesson1 └── index.html ├── Lesson10 └── index.html ├── Lesson2 ├── demo_lesson1.html ├── demo_lesson1.txt ├── fsh_lesson2.txt ├── functions_lesson_2.js ├── index.html └── vsh_lesson2.txt ├── Lesson3 ├── demo.html ├── functions_lesson_3.js ├── index.html └── vocaloid.jpg ├── Lesson4 ├── Network.jpg ├── Sources.jpg ├── checkElement.jpg ├── console.jpg ├── debug.jpg ├── demo-lesson4.html ├── devConsole.jpg ├── error.jpg ├── functions_lesson_4.js ├── index.html ├── vocaloid.jpg └── webgl_null.jpg ├── Lesson5 ├── demo-lesson5.html ├── functions_lesson_5.js ├── index.html ├── vocaloid.jpg └── vocaloid2.jpg ├── Lesson6 ├── demo-lesson6.js ├── demo-requestAnimationFrame.html ├── demo-requestAnimationFrame.js ├── demo_lesson6.html ├── index.html └── webgl_matrix.js ├── Lesson7 ├── 1.jpg ├── 2.jpg ├── demo_net.js ├── demo_net_deprecated.js ├── demo_net_html5.html ├── demo_net_html5.js ├── demo_net_mouse.js ├── demo_net_webgl.html ├── index.html └── webgl_lesson7.js ├── Lesson8 ├── 0.jpg ├── 1.jpg ├── 2.jpg ├── 3.jpg ├── 4.jpg ├── demo_scene_roaming.html ├── demo_scene_roaming.js ├── index.html └── wge │ ├── cube.js │ ├── wgeAlgorithm.js │ ├── wgeCore.js │ ├── wgeGUI.js │ ├── wgeScene.js │ ├── wgeSprite3d.js │ └── wgeWebGL.js ├── Lesson9 └── index.html ├── README.html ├── README.md ├── WGE ├── index.html ├── webgl │ ├── index.html │ ├── wgeFilters.js │ ├── wgeScene.js │ ├── wgeSprite2d.fsh.txt │ ├── wgeSprite2d.js │ ├── wgeSprite2d.vsh.txt │ ├── wgeSprite2dExt.fsh.txt │ ├── wgeSprite2dExt.vsh.txt │ ├── wgeSprite3d.fsh.txt │ ├── wgeSprite3d.js │ ├── wgeSprite3d.vsh.txt │ └── wgeWebGL.js ├── wgeAlgorithm.js ├── wgeAnimation.js ├── wgeColor.js ├── wgeCore.js └── wgeGUI.js ├── WebGL-Lessons.sln ├── _config.yml └── index.html /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | 46 | [Dd]ebug/ 47 | [Rr]elease/ 48 | x64/ 49 | build/ 50 | [Bb]in/ 51 | [Oo]bj/ 52 | 53 | # MSTest test Results 54 | [Tt]est[Rr]esult*/ 55 | [Bb]uild[Ll]og.* 56 | 57 | *_i.c 58 | *_p.c 59 | *.ilk 60 | *.meta 61 | *.obj 62 | *.pch 63 | *.pdb 64 | *.pgc 65 | *.pgd 66 | *.rsp 67 | *.sbr 68 | *.tlb 69 | *.tli 70 | *.tlh 71 | *.tmp 72 | *.tmp_proj 73 | *.log 74 | *.vspscc 75 | *.vssscc 76 | .builds 77 | *.pidb 78 | *.log 79 | *.scc 80 | 81 | # Visual C++ cache files 82 | ipch/ 83 | *.aps 84 | *.ncb 85 | *.opensdf 86 | *.sdf 87 | *.cachefile 88 | 89 | # Visual Studio profiler 90 | *.psess 91 | *.vsp 92 | *.vspx 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | 101 | # TeamCity is a build add-in 102 | _TeamCity* 103 | 104 | # DotCover is a Code Coverage Tool 105 | *.dotCover 106 | 107 | # NCrunch 108 | *.ncrunch* 109 | .*crunch*.local.xml 110 | 111 | # Installshield output folder 112 | [Ee]xpress/ 113 | 114 | # DocProject is a documentation generator add-in 115 | DocProject/buildhelp/ 116 | DocProject/Help/*.HxT 117 | DocProject/Help/*.HxC 118 | DocProject/Help/*.hhc 119 | DocProject/Help/*.hhk 120 | DocProject/Help/*.hhp 121 | DocProject/Help/Html2 122 | DocProject/Help/html 123 | 124 | # Click-Once directory 125 | publish/ 126 | 127 | # Publish Web Output 128 | *.Publish.xml 129 | *.pubxml 130 | 131 | # NuGet Packages Directory 132 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 133 | #packages/ 134 | 135 | # Windows Azure Build Output 136 | csx 137 | *.build.csdef 138 | 139 | # Windows Store app package directory 140 | AppPackages/ 141 | 142 | # Others 143 | sql/ 144 | *.Cache 145 | ClientBin/ 146 | [Ss]tyle[Cc]op.* 147 | ~$* 148 | *~ 149 | *.dbmdl 150 | *.[Pp]ublish.xml 151 | *.pfx 152 | *.publishsettings 153 | 154 | # RIA/Silverlight projects 155 | Generated_Code/ 156 | 157 | # Backup & report files from converting an old project file to a newer 158 | # Visual Studio version. Backup files are not needed, because we have git ;-) 159 | _UpgradeReport_Files/ 160 | Backup*/ 161 | UpgradeLog*.XML 162 | UpgradeLog*.htm 163 | 164 | # SQL Server files 165 | App_Data/*.mdf 166 | App_Data/*.ldf 167 | 168 | ############# 169 | ## Windows detritus 170 | ############# 171 | 172 | # Windows image file caches 173 | Thumbs.db 174 | ehthumbs.db 175 | 176 | # Folder config file 177 | Desktop.ini 178 | 179 | # Recycle Bin used on file shares 180 | $RECYCLE.BIN/ 181 | 182 | # Mac crap 183 | .DS_Store 184 | 185 | 186 | ############# 187 | ## Python 188 | ############# 189 | 190 | *.py[co] 191 | 192 | # Packages 193 | *.egg 194 | *.egg-info 195 | dist/ 196 | build/ 197 | eggs/ 198 | parts/ 199 | var/ 200 | sdist/ 201 | develop-eggs/ 202 | .installed.cfg 203 | 204 | # Installer logs 205 | pip-log.txt 206 | 207 | # Unit test / coverage reports 208 | .coverage 209 | .tox 210 | 211 | #Translations 212 | *.mo 213 | 214 | #Mr Developer 215 | .mr.developer.cfg 216 | -------------------------------------------------------------------------------- /404.html: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | webgl-lesson.wysaid.org 2 | -------------------------------------------------------------------------------- /Lesson10/index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Lesson 10 7 | 8 | 9 |

WebGL simple lesson 10 (blog.wysaid.org)

10 | 11 | 12 | -------------------------------------------------------------------------------- /Lesson2/demo_lesson1.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 7 | 8 |

9 |


10 | 11 | 12 | 13 | 14 | 15 |

16 | 17 | 30 | 31 | 46 | 47 | 63 | 64 | 82 | 83 | -------------------------------------------------------------------------------- /Lesson2/demo_lesson1.txt: -------------------------------------------------------------------------------- 1 |  2 | 3 | 7 | 8 |

9 |


10 | 11 | 12 | 13 | 14 | 15 |

16 | 17 | 30 | 31 | 46 | 47 | 63 | 64 | 82 | 83 | -------------------------------------------------------------------------------- /Lesson2/fsh_lesson2.txt: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | varying vec2 textureCoordinate; 3 | void main() 4 | { 5 | gl_FragColor = vec4(textureCoordinate, 0.0, 1.0); 6 | } -------------------------------------------------------------------------------- /Lesson2/functions_lesson_2.js: -------------------------------------------------------------------------------- 1 | var webgl = null; 2 | var vertexShaderObject = null; 3 | var fragmentShaderObject = null; 4 | var programObject = null; 5 | var v4PositionIndex = null; 6 | 7 | function webglInit() { 8 | var myCanvasObject = document.getElementById("webglView"); //此处的webglView为你的canvas的id 9 | var myDivObject = document.getElementById("containerView"); //此处的containerView为你的canvas的父div元素id 10 | webgl = myCanvasObject.getContext("experimental-webgl"); 11 | if(webgl == null) 12 | alert("你的浏览器不支持webgl"); 13 | myCanvasObject.width = myDivObject.clientWidth; //千万注意,参见下面说明。 14 | myCanvasObject.height = myDivObject.clientHeight; //同上 15 | webgl.viewport(0, 0, myDivObject.clientWidth, myDivObject.clientHeight);//同上 16 | } 17 | 18 | function shaderInitWithVertexAndFragmentShader(vsh, fsh) { 19 | vertexShaderObject = webgl.createShader(webgl.VERTEX_SHADER); 20 | fragmentShaderObject = webgl.createShader(webgl.FRAGMENT_SHADER); 21 | webgl.shaderSource(vertexShaderObject, vsh); 22 | webgl.shaderSource(fragmentShaderObject, fsh); 23 | webgl.compileShader(vertexShaderObject); 24 | webgl.compileShader(fragmentShaderObject); 25 | if (!webgl.getShaderParameter(vertexShaderObject, webgl.COMPILE_STATUS)) { alert(webgl.getShaderInfoLog(vertexShaderObject) + "in vertex shader"); return; } 26 | if (!webgl.getShaderParameter(fragmentShaderObject, webgl.COMPILE_STATUS)) { alert(webgl.getShaderInfoLog(fragmentShaderObject) + "in fragment shader"); return; } 27 | } 28 | 29 | function initShaderProgram(positionName) { 30 | programObject = webgl.createProgram(); 31 | webgl.attachShader(programObject, vertexShaderObject); 32 | webgl.attachShader(programObject, fragmentShaderObject); 33 | webgl.bindAttribLocation(programObject, v4PositionIndex, positionName); 34 | webgl.linkProgram(programObject); 35 | if (!webgl.getProgramParameter(programObject, webgl.LINK_STATUS)) { 36 | alert(webgl.getProgramInfoLog(programObject)); 37 | return; 38 | } 39 | webgl.useProgram(programObject); 40 | } 41 | 42 | function renderWebGL(vertices, vSize, vLen, vsh, fsh, positionName){ 43 | webglInit(); 44 | shaderInitWithVertexAndFragmentShader(vsh, fsh); 45 | initShaderProgram(positionName); 46 | 47 | var buffer = webgl.createBuffer(); 48 | webgl.bindBuffer(webgl.ARRAY_BUFFER, buffer); 49 | webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(vertices), webgl.STATIC_DRAW); 50 | 51 | webgl.enableVertexAttribArray(v4PositionIndex); 52 | webgl.vertexAttribPointer(v4PositionIndex, vSize, webgl.FLOAT, false, 0, 0); 53 | 54 | webgl.clearColor(0.0, 0.0, 0.0, 1.0); 55 | webgl.clear(webgl.COLOR_BUFFER_BIT); 56 | webgl.drawArrays(webgl.TRIANGLE_STRIP, 0, vLen); 57 | } 58 | 59 | function getScriptTextByID(scriptID){ 60 | var shaderScript = document.getElementById(scriptID); 61 | if (shaderScript == null) return ""; 62 | 63 | if (shaderScript.textContent != null && shaderScript.textContent != "") { 64 | return shaderScript.textContent; 65 | } 66 | if (shaderScript.text != null && shaderScript.text != "") { 67 | return shaderScript.text; 68 | } 69 | var sourceCode = ""; 70 | var child = shaderScript.firstChild; 71 | while (child) { 72 | if (child.nodeType == child.TEXT_NODE) sourceCode += child.textContent; 73 | child = child.nextSibling; 74 | } 75 | return sourceCode; 76 | } 77 | 78 | function requestURLPlainText(url) { 79 | var xmlHttp = new XMLHttpRequest(); 80 | xmlHttp.open("GET", url, false); 81 | xmlHttp.send(); 82 | return xmlHttp.responseText; 83 | } 84 | 85 | function renderTriangle() 86 | { 87 | var vertices = 88 | [ 89 | 0.0, 0.5, 90 | -0.5, -0.5, 91 | 0.5, -0.5, 92 | ]; 93 | var vsh = getScriptTextByID("vsh_lesson2"); 94 | var fsh = getScriptTextByID("fsh_lesson2"); 95 | renderWebGL(vertices, 2, 3, vsh, fsh, "position"); 96 | } -------------------------------------------------------------------------------- /Lesson2/index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Lesson 2 5 | 9 | 10 | 11 |

第二话,动态加载shader (blog.wysaid.org)

12 |

上一张封装了基本的WebGL功能,包括初始化、创建着色器对象、创建&编译着色器程序然后应用着色器渲染的简单范例。这里强调一下本教程定位:在观看本教程时,作者会假设你对于OpenGL Shader Language有一定理解,所以不会去解释简单的shader代码的。如果您对GLSL一无所知,建议你先去了解。

13 |

首先,回顾一下上期内容。在lesson 1,我们集成了WebGL的一些基本功能,然后用它制作了一个小demo。 由于讲得比较仓促,而且后期有些修改校正,完整代码请点击下面的按钮:

14 | 15 |
16 | 17 | 18 |
19 | 20 |

那么,开始第二话吧!

21 |
第一小节
22 |

上次我们封装出一个叫做"renderWebGL(verticesArray, vertexSize, VertexLength, vsh, fsh, vertexIndex)"的函数,参数有六个,
第一个为所需绘制的图形的顶点数组。
第二个参数分别为每个顶点的数据大小,如果为float,那么就是1,如果是vec2,那么就是2。(不要问我传整数怎么办,我拒绝回答无聊的问题@_@)。
第三个为顶点个数。
第四个第五个分别是顶点着色器代码与片元着色器代码。
第六个是顶点着色器里面待使用的顶点的变量名。

23 |

然后最后实现了三个如renderTriangle()这样的函数实现一步调用并绘制出三角形/矩形/圆

24 |

但是,显然每次写这样的函数都是很恶心的,为什么就不能直接把shader写到一起,而要使用javascript代码来拼凑呢?下面介绍两种常见加载shader的方法:

25 |

1. html标签方式。这种方式适用于直接嵌入shader代码到网页中,优点是写起来方便

26 |
用法 27 |

Vertex Shader通常写在一个类型为"x-shader/x-vertex"的script标签内,像这样:

28 |
<script id="Your Vertex Shader ID" type="x-shader/x-vertex">Vertex Shader Code</script>
29 |

Fragment Shader通常写在一个类型为"x-shader/x-fragment"的script标签内,像这样:

30 |
<script id="Your Fragment Shader ID" type="x-shader/x-fragment">Fragment Shader Code</script>
31 |

此外,我们还需要一个Javascript函数来获取这些标签内容,记住,把有用的模块划分出来是非常有助于后续的学习的哦

32 |
 33 | //虽然看起来直接获取innerHTML就可以了,但毕竟我们写的不是HTML。
 34 | //如果你觉得没必要这么麻烦,那么你完全可以改为直接返回innerHTML
 35 | function getScriptTextByID(scriptID){
 36 | 	var shaderScript = document.getElementById(scriptID);
 37 |     if (shaderScript == null) return "";
 38 | 
 39 |     if (shaderScript.textContent != null && shaderScript.textContent != "") {
 40 |         return shaderScript.textContent;
 41 |     }
 42 |     if (shaderScript.text != null && shaderScript.text != "") {
 43 |         return shaderScript.text;
 44 |     }
 45 |     var sourceCode = "";
 46 |     var child = shaderScript.firstChild;
 47 |     while (child) {
 48 |         if (child.nodeType == child.TEXT_NODE) sourceCode += child.textContent;
 49 |         child = child.nextSibling;
 50 |     }
 51 |     return sourceCode;
 52 | }
 53 | 
54 | 55 |
56 |

但是,你应该知道,其实对于两种shader来说,这个script的type是无关紧要的,因为我们用到的只是它的内容。

57 |

所以,如果你嫌script标签写起来麻烦,你也可以写到一个div标签或者其他自定义标签里面,只要设定它的style="display:none",不让它显示出来,然后我们通过它的id获取它里面的文本就行了。当然,我并不推荐这样做,养成良好的习惯是我们学习过程中应有的态度。

58 | 59 |

2. js动态加载方式。这种方式适用于shader较多的情况,优点是可以把shader写到单独的文本文件里面,在需要的时候才加载。

60 |
方法: 61 |
 62 | function requestURLPlainText(url) {
 63 |     var xmlHttp = new XMLHttpRequest();
 64 |     xmlHttp.open("GET", url, false);
 65 |     xmlHttp.send();
 66 |     return xmlHttp.responseText;
 67 | }
 68 | 
69 | 70 |

为了获取shader文本,我们封装出requestURLPlainText函数,这个函数接收的参数为shader文件的url,返回shader文件的内容。

71 |

简单描述一下吧:首先要知道Javascript里面的XMLHttpRequest对象。为了不引入不必要的麻烦,我不会引用任何第三方的库,如果你会jQuery什么的话,就用你自己的方法吧。XMLHttpRequest为我们提供了动态网页请求的功能。

72 |
73 | 74 |

这里不得不先提一下的是使用这种方式的话,就不能在本地直接用浏览器打开html文件运行WebGL程序了(简单来讲,你的浏览器地址栏里面出现类似于file:///这样的话,是运行不出结果的), 一般情况下,你必须把你的程序置身于web环境下,通过IP或者域名访问(例如你访问本教程这样)。而本教程将介绍这两种方法。所以如果后面的demo如果使用了这种方式编写,请在Web环境下打开该demo。
(Tips: 没有在本机配置apache之类的读者如果安装有vs2010以上的IDE,可以通过vs打开demo,然后右键选择从浏览器打开,vs会根据demo是否需要web环境自动创建IIS连接。)

75 |
第二小节
76 |

上一小结介绍了一些相关知识,那么我们来开始应用吧。

77 |

首先是第一种方式,在上面给出的demo code的最前面加上如下代码(也就是我们的shader代码):

78 |
79 |
 80 | <script id="vsh_lesson2" type="x-shader/x-vertex">
 81 | precision mediump float;
 82 | attribute vec4 position;
 83 | varying vec2 textureCoordinate;
 84 | void main()
 85 | {
 86 | 	gl_Position = position;
 87 | 	textureCoordinate = position.xy + 0.5;
 88 | }
 89 | </script>
 90 | 
 91 | <script id="fsh_lesson2" type="x-shader/x-fragment">
 92 | precision mediump float;
 93 | varying vec2 textureCoordinate;
 94 | void main()
 95 | {
 96 | 	gl_FragColor = vec4(textureCoordinate, 0.0, 1.0);
 97 | }
 98 | </script>
 99 | 
100 | 
101 |

然后,修改renderTriangle函数,如下

102 |
103 | //这下是不是清爽多了?而且shader代码就是纯粹的shader代码,没有夹杂任何javascript
104 | function renderTriangle()
105 | {
106 | 	var vertices = 
107 | 	[
108 | 		0.0, 0.5,
109 | 		-0.5, -0.5,
110 | 		0.5, -0.5,
111 | 	];
112 | 	var vsh = getScriptTextByID("vsh_lesson2");
113 | 	var fsh = getScriptTextByID("fsh_lesson2");	
114 | 	renderWebGL(vertices, 2, 3, vsh, fsh, "position");
115 | }
116 | 
117 |

然后是第二种方式,我们需要把前面提到的Vertex Shader代码和Fragment Shader代码保存下来。在本小节中,我们把Vertex Shader保存为vsh_lesson2.js, 把Fragment Shader保存为fsh_lesson2.js,然后把它们放在demo相同的目录下。

118 |

然后通过如下代码方式访问:

119 |
120 | //这下是不是跟上面的方法差不多?只不过封装形式不一样罢了。
121 | function renderTriangle(){
122 | 	var vertices = 
123 | 	[
124 | 		0.0, 0.5,
125 | 		-0.5, -0.5,
126 | 		0.5, -0.5,
127 | 	];
128 | 	var vsh = requestURLPlainText("vsh_lesson2.txt");
129 | 	var fsh = requestURLPlainText("fsh_lesson2.txt");	
130 | 	renderWebGL(vertices, 2, 3, vsh, fsh, "position");
131 | }
132 | 
133 | 134 |

嗯,本话要讲的差不多就这些,下面让我们运行一下本小节成果吧!

135 | 139 | 140 |
141 |

192 |


193 | 194 | 195 | 196 |

197 |

为了让读者看出两者都运行成功了,我特地把第二个三角形反了个方向。lesson2只实现了三角形,如果你是初学者,希望你能按照本章封装的方法,自己把矩形跟圆形都改成这种方式实现。本期做的是功能性讲解,所以demo的变化在内而不在外。

198 |

好的,第二期教程到此为止,请关注lesson 3。系列教程地址:webgl-lesson.wysaid.org

199 | 201 | 211 | 212 | 220 | 221 | -------------------------------------------------------------------------------- /Lesson2/vsh_lesson2.txt: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | attribute vec4 position; 3 | varying vec2 textureCoordinate; 4 | void main() 5 | { 6 | gl_Position = position; 7 | textureCoordinate = position.xy + 0.5; 8 | } -------------------------------------------------------------------------------- /Lesson3/demo.html: -------------------------------------------------------------------------------- 1 |  2 | demo 3 | 4 | 5 | 9 | 10 |

11 |


12 | 13 | 14 | 15 | 16 | 17 | 18 |

19 | 32 | 33 | 42 | 51 | 67 | 84 | 100 | 101 |
102 | 103 | -------------------------------------------------------------------------------- /Lesson3/vocaloid.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wysaid/WebGL-Lessons/5020e8a9a78491c1181d0b2397d4135b13c14497/Lesson3/vocaloid.jpg -------------------------------------------------------------------------------- /Lesson4/Network.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wysaid/WebGL-Lessons/5020e8a9a78491c1181d0b2397d4135b13c14497/Lesson4/Network.jpg -------------------------------------------------------------------------------- /Lesson4/Sources.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wysaid/WebGL-Lessons/5020e8a9a78491c1181d0b2397d4135b13c14497/Lesson4/Sources.jpg -------------------------------------------------------------------------------- /Lesson4/checkElement.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wysaid/WebGL-Lessons/5020e8a9a78491c1181d0b2397d4135b13c14497/Lesson4/checkElement.jpg -------------------------------------------------------------------------------- /Lesson4/console.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wysaid/WebGL-Lessons/5020e8a9a78491c1181d0b2397d4135b13c14497/Lesson4/console.jpg -------------------------------------------------------------------------------- /Lesson4/debug.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wysaid/WebGL-Lessons/5020e8a9a78491c1181d0b2397d4135b13c14497/Lesson4/debug.jpg -------------------------------------------------------------------------------- /Lesson4/demo-lesson4.html: -------------------------------------------------------------------------------- 1 |  2 | demo - lesson 4 3 | 4 | 5 | 9 | 10 |
11 |

12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
21 | 22 |
23 |
24 | 25 | 38 | 39 | 48 | 57 | 73 | 90 | 106 | 107 | 122 |
123 | 124 | -------------------------------------------------------------------------------- /Lesson4/devConsole.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wysaid/WebGL-Lessons/5020e8a9a78491c1181d0b2397d4135b13c14497/Lesson4/devConsole.jpg -------------------------------------------------------------------------------- /Lesson4/error.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wysaid/WebGL-Lessons/5020e8a9a78491c1181d0b2397d4135b13c14497/Lesson4/error.jpg -------------------------------------------------------------------------------- /Lesson4/index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Lesson 4 5 | 9 | 22 | 23 | 32 | 41 | 57 | 74 | 90 | 91 | 92 | 93 |

第四话,学会使用浏览器调试以及给WebGL代码增加日志(blog.wysaid.org)

94 |

上一期讲到了添加纹理进行一些简单的图像处理,后面的内容会越来越复杂。所以也很容易出现各种问题,所以我认为,在进入到下一阶段之前,必须要做的事情有二:

95 |

首先,我们必须学会使用浏览器调试我们的代码,否则我们只能在入门的边缘徘徊,很难写出一个像样的东西。

96 |

然后,我们要整改我们的demo表现形式了,给它增加一个log-box,实时打印出我们代码中所有可能遇到的问题,而不是像以前那样弹出一个对话框来显示。

97 |

那么,先讲讲查看错误和调试吧。

98 |

上一期的demo是这样的:

99 | 100 |

101 |


102 | 103 | 104 | 105 | 106 | 107 |

108 |

这里需要提示一下的是,有读者问我为什么他比对着教程做出来的练习代码无法看到任何东西, 这里我比对了一下几个浏览器,发现Chrome的核心会对直接打开的html文件报一下错误:

109 | 110 |

而该读者因为不知道如何查看错误而浪费了很多时间(这也是我为什么要马上讲一下调试和查看错误的原因)

111 |

首先,要想好好学WebGL的话,支持WebGL的浏览器最好多准备几个: IE11, Chrome, Firefox以及Opera (新版opera的核心跟chrome一模一样,如果有chrome的话,就可以不管opera了。国产的浏览器像搜狗啊,360什么的,如果要支持WebGL就切换到高速模式吧,切换以后跟chrome核心应该也是差不多的), 这样的话在代码无法运行的时候可以多换几个浏览器试试看,不一定是自己的WebGL代码的问题

112 |

比如上面Chrome报的错是告诉你,那是一个"SecurityError",Chrome不允许WebGL在直接打开文档的情况下访问img标签的内容。

113 |

而这样的情况在使用IE11和Firefox时就不会出现,依然正常运行。

114 |

那么,在哪里查看错误呢?

115 |

用浏览器打开你的代码文件或者相应的uri,右键选择“审查元素”(IE为“检查元素”)

116 | 117 |

然后你会看到下面的界面(为了节省篇幅,这里以chrome为例,其他浏览器请自行辨别,相信自己的智商^_^)

118 | 119 |

我用红线勾出了三处对于我们比较有用的功能,简要描述一下

120 |

Network

121 | 122 |

对于这个功能,会用的人肯定爱不释手了,因为在这个对话框下,会显示我们打开一个网站页面时加载到的所有东西,比如一段音乐,一个视频。有的人为了下载某个网站上的某个小音源或者视频而苦恼不已,找来找去找不到工具,殊不知最好的工具就是他自己的浏览器。只要打开Network页面,你可以直接右键另存下你所加载的这些东西(比如你在某贴吧或者论坛听到的一段语音,这种一般不提供下载链接什么的,抑或者你在bilibili.tv上看到的任何视频,都可以直接获取到,一般是hlv格式),比你去缓存页面寻找方便不知道多少倍。甚至……它还会显示你的一些特殊举动,比如你在使用网银或者支付宝,跟银行或者支付宝网站之间的所有请求包括get和post及其所有参数都会显示到里面。也许你可以利用这个功能去做一个简单的对付某投票页面的刷票工具,假如你是一个大学生,学校教务网站做得不够完善的话,分析一下请求的规则是可以成功的。

123 |

好了,有点跑题了。之所以我们会用到这个页面,是因为我们第二章介绍了使用js代码读取shader脚本和图片。如果我们的程序未能运行出结果,请查看此页面,看看你是否请求了正确的资源,以及资源是否请求到位。如果请求失败,一般它的status会被标记为404。

124 |

125 |

Sources

126 | 127 |

这个页面就是我们调试代码的页面了,单击该页面左上角会显示出我们用到js的文件,html代码将被隐藏。这里我们打开上一话的js文件,可以看到chrome已经把我们的文件内容显示出来了。

128 |

如果你会调试C/C++/java等等的代码,那么接下来你的操作就跟它们很相似了:

129 | 130 |

首先,我们随便选一行在左侧显示行号的地方点击一下,就算是为我们的程序打上了一个断点。

131 |

这时候如果你的程序正在运行WebGL代码,且要经过此行的话,整个Web页面会立即停下。然后屏幕上方会显示"paused in debugger"字样。如果没有出现,那么说明此时并没有代码正在执行或者经过该处,比如本教程提供的demo都是需要点击之后执行的,那么你就需要点击一下运行的按钮了,比如本文一开头引用到的demo下方提供的按钮。

132 |

当整个页面停住以后,右侧被我画上红圈的地方会变为可用状态,懂得调试的话就会知道依次是resume(继续运行直到下一个断点), step over(仅执行完当前所在行), step in(如果当前行有函数调用,则进入该函数并停在函数开头,否则跟step over一样), step out(跳出当前函数,如果无法跳出,比如包含循环,那么跟resume一样) 【黑字部分如果描述不准确还望邮件或者留言告知】,后面则是禁用/启用断点以及在异常处中断

133 |

页面处于调试状态时,你就可以像使用如VS之类的IDE编写C程序那样把鼠标移到变量之上查看变量信息了。右侧同样有Watch和Call Stack等。总之,chrome的调试功能用起来是相当友好的,个人非常喜欢。如果你用不习惯的话,也可以用Fire debug之类的东西, 也许操作不同,但最终肯定都能达到想要的效果。

134 |

Watch就是添加监视变量,为了不显得啰嗦,不讲了吧。

135 |

Call Stack顾名思义就是调用栈,可以看到当前执行到的代码所处的函数层次等信息,为了不显得啰嗦,也不讲了吧,你肯定明白的。

136 |

以及还有些杂七杂八的东西不见得你会用到,自己去摸索吧。

137 |

138 |

Console

139 | 140 |

控制台, 听名字就知道是很重要的东西。你可以在任何时候使用它。

141 |

比如我以前写过的一段点赞的小代码:如何利用js点赞

142 |

当然,本次不是介绍这类用法的。当你的web页面处于调试状态下时,你可以用这个控制台查看一些变量,比如本教程的demo将WebGL的上下文保存为webgl全局变量,那么你这时候就可以输入webgl查看它的信息。

143 |

如果只是查看变量信息的话,用sources窗口就足够了,用控制台还可以修改当前可用的变量:

144 | 145 |

如上图,只要输入 webgl = null,就可以把全局的webgl变量设置为null,这时候只要继续往下执行就会出现无法处理的fatal error导致程序停止执行了。(是不是想到利用这个在某些只利用js过滤关键词的网站上发表自由言论了呢^_^)

146 |

对于你编程有什么用呢?当然有用, 如果你在对某个固定参数进行调整的话,为了不重复修改代码,直接在这上面修改,成功之后再修改代码,是再合适不过的。不过由于WebGL的程序并不需要经过编译器编译链接的漫长等待,所以优势不怎么明显……总之,觉得有用并且好用的话,就用吧。

147 |

控制台的功能需要你自己去不断摸索的,一些命令也需要有所了解,在此篇幅有限不再讨论。

148 |
149 |

然后是,修改demo代码,给我们的demo加上一个log窗口

150 |

log窗口有很多选择,只要是一个可以显示文字的标签就够了,所以万能的div当然是可以的。不过既然提到了文字,那么还是用专门实现文字的textarea显得更科学。

151 |

不罗嗦,直接上代码吧:

152 |

153 | <!-- 创建一个id为logBox的texarea标签,并设置一下它的显示方式,
154 | 加上一句启动后默认的话,比如WebGL 正在初始化之类的。 -->
155 | <div style="border:groove;background-color:#ddd">
156 | <textarea id="logBox" style="border:groove;margin:auto;width:100%;height:200px;left:1px">
157 | WebGL is initializing...
158 | </textarea>
159 | <input type="button" value = "Appen somthing" title="添加一句话"
160 | onclick="appendLog('This is lesson-4. By wysaid')">
161 | <input type="button" value = "Clear Log" title="清除日志" onclick="clearLogbox()">
162 | <input type="button" value = "Maximize Logbox" title="放大日志窗口" onclick="maximizeLogbox()">
163 | <input type="button" value = "Restore Logbox" title="恢复日志窗口" onclick="restoreLogbox()">
164 | </div>
165 | 
166 | <script type="text/javascript">
167 | //写几个控制log窗口的辅助函数。
168 | //如果你喜欢,也可以把logBox变量写为全局的,不过这里不是很有必要。
169 | function appendLog(logString) {
170 | 	var logBox = document.getElementById("logBox");
171 | 	logBox.value += logString + "\n";
172 | 	//让log文字在文字超过可显示区域时自动滚动到最底部。
173 | 	logBox.scrollTop = logBox.scrollHeight;
174 | }
175 | 
176 | function clearLogbox() {
177 | 	var logBox = document.getElementById("logBox");
178 | 	logBox.value = "";
179 | }
180 | 
181 | function maximizeLogbox() {
182 | 	var logBox = document.getElementById("logBox");
183 | 	logBox.style.height = logBox.scrollHeight + 20 + "px";
184 | }
185 | 
186 | function restoreLogbox() {
187 | 	var logBox = document.getElementById("logBox");
188 | 	//后面的200px取你自己最初设定的值,其实按百分比设定是最好的,
189 | 	//不过写百分比的话,放在本教程里面页面就乱了,你可以自己尝试。
190 | 	logBox.style.height = "200px";
191 | }
192 | 
193 |
195 | 196 |
197 | 198 |

好的,本期差不多就这么多了,附上一个实现好的demo吧,点击下面的按钮打开:

199 | 200 |

好的,第四期教程到此为止,请关注lesson 5。系列教程地址:webgl-lesson.wysaid.org

201 | 202 | -------------------------------------------------------------------------------- /Lesson4/vocaloid.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wysaid/WebGL-Lessons/5020e8a9a78491c1181d0b2397d4135b13c14497/Lesson4/vocaloid.jpg -------------------------------------------------------------------------------- /Lesson4/webgl_null.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wysaid/WebGL-Lessons/5020e8a9a78491c1181d0b2397d4135b13c14497/Lesson4/webgl_null.jpg -------------------------------------------------------------------------------- /Lesson5/demo-lesson5.html: -------------------------------------------------------------------------------- 1 |  2 | demo - lesson 5 By wysaid 3 | 4 | 5 | 6 | 7 | 15 | 16 |
17 |
18 | 19 |
20 |
21 |
22 |
23 |
24 | 45 |
46 |

48 | 49 |
50 |
51 | 67 | 68 | 79 | 80 | 94 |
95 | 96 |
97 | 98 |
99 | 100 | -------------------------------------------------------------------------------- /Lesson5/index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Lesson 5 5 | 14 | 15 | 16 | 17 |

第五话, 使用前面已有的知识做一些有用的小作品。

18 |

上一期讲到了使用浏览器调试代码以及添加log窗口。相信你已经懂得如何在开发过程中打印log

19 |

如果你去逛过关于编程的贴吧或者论坛,比如百度C语言/C++贴吧,那么你肯定可以看到很多初学者只学了控制台,甚至可以说对于输出的API只知道输出到控制台的printf或者cout.

20 |

但是他们却可以利用仅有的那一点知识,发挥自己的创造力、激情以及勇气做出一个又一个的小作品,比如控制台小黑框版本的贪吃蛇、俄罗斯方块甚至文字RPG

21 |

如果说看到这里你还是云里雾里的话,不妨停下来思考一下,发挥一下自己的创造力,不要以为现在还什么都不能做,我们现在学的其实已经可以做很多东西。

22 |

预告一下,下期将进入WebGL的3D部分。 本期将带领大家使用前面几节的知识做出一个看起来还可以的东西。

23 |

如果你认为本教程进度过快,那么,请务必放慢速度,好好地自己实现一下前面几话的demo

24 |

经过本人校对,发现前面几话的代码存在着不足以及拼写错误等(人力有限),如果你是一个细节控,相信你已经发现了,如果有什么疑问请到上方标题处我的博客留言。

25 | 26 |

好的,开始吧,这一节我们讲一讲使用WebGL进行图像处理

27 |

如果你会使用Photoshop,那么你一定知道,Photoshop提供了丰富多彩的图像调整功能,包括亮度对比度饱和度色阶曝光色相色彩平衡模糊锐化以及曲线调整等等

28 |

作为一个即将/正在学习WebGL的程序员或者说爱好者,你们避免不了与各种算法打交道,所以我挑几个简单的描述一下。

29 | 30 |

亮度调整,最简单粗暴的方法就是直接给一个颜色的RGB值同时+N然后对越界的颜色比如某通道小于0(大于255)那么设置为0(设置为255)。最终结果为 (color+N)当然也会有看起来更好的方法,比如将RGB转为HSL,然后增加L(luminance)值再转回RGB

31 |
32 | 33 |

对比度调整,最简单粗暴的方法当然莫过于将RGB值同时减去它们值域的一半(设为N,如果单通道的范围是[0, 1]那么N就是0.5),得到值(color-N),然后乘以对比度值contrast之后再加回来,结果就是:(color-N)*contrast + N (即color * contrast + (1 - contrast) * N,相当于GLSL里面的mix(N, color, contrast))。其中contrast > 0。不难看出,当contrast=1时是原来的颜色,当contrast小于1时对比度减小,大于1时对比度增大。

34 | 35 |

饱和度调整,最简单粗暴的方法跟对比度类似,只不过用到的N不再是颜色值域的一半,而是RGB值的灰度(加权平均)或者说平均值(N = (R+G+B)/3),最终结果为:mix(N, color, saturation)

36 | 37 |

模糊锐化调整,当然是卷积了。最简单粗暴的方法莫过于对四/八领域采样然后加权平均了,请参考高斯模糊。前面几话里面的Emboss和Edge的demo里面已经有采样的例子,你可以参考一下。以直接取平均值的模糊为例,假如某个点p的RGB值为pColor,它的上下左右的点分别为pColor1,pColor2,pColor3,pColor4,那么这里用到的N = (pColor + pColor1 + pColor2 + pColor3 + pColor4) / 5。最终结果为:mix(N, color, intensity)。也许你会有疑问,不是模糊和锐化吗,这就跟intensity参数有关了,原理其实还是跟对比度饱和度调整类似,当intensity取值[0, 1)时为模糊,且为0时取得模糊的最大值。当intensity为1时是原来的颜色。当intensity>1 时为锐化(想知道更多为什么的话请自行搜索"反锐化遮罩/掩模"

38 | 39 |

曲线调整,这个就是这一次的demo了。也是列举的这几个里面最麻烦的一个。希望你能根据此demo代码,自己实现出以上四种调整

40 |

描述一下它的原理吧: 对于任何图片上的所有像素,现在以RGB888为例,三个通道取值都是[0, 255]。我们根据这个定义域创建一个函数 f(x)。当 f(x) = x 时,很容易知道,函数 f(x) 的定义域和值域都是[0, 255]。

41 |

我们有图像A,对于图像A上的所有点Pn执行Pn = f(Pn),最终得到新的图像A',那么A'就是A经过曲线f(x)调整过后的图像。举个简单的例子,当g(x)=x*x/255 时易知x/255的范围为[0, 1],所以g(x)的值域依然是[0, 255],此时A经过g(x)生成出的图像A'就要整体比原来的图像偏暗了

42 |

而在Photoshop中,曲线调整并不是一个固定的函数表达式,而是给出f(x)=x的函数图像让用户去拖动,然后根据用户给出的所有点计算出一条新的过所有点的平滑的曲线F(x) (F(x)为分段函数,如果曲线某一段超出边界,则取边界值,比如某一段计算出来小于0,那么最终那一段直接取0)。这也就是看似神奇的曲线调整的基本含义。

43 |

作为附加知识,讲这么多差不多够了。需要注意的一点就是由于定义域和值域都是一定的,所以我们是可以将多次曲线调整合并并映射为一次的。比如我们要先进行f(x)然后进行g(x),最终也就是g(f(x)),可在定义域内取一定的精度然后将定义域内的所有值全部映射出来形成新的曲线函数h(x)

44 |
45 |
46 |
47 | 48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | 75 |

77 | 78 |
79 |
80 | 96 | 97 | 109 | 110 | 124 |
125 |

好的,本期差不多就这么多了,完整的demo点击下面的按钮打开。请自行右键另存代码并查看。记得实现前面四种效果哦~

126 | 127 |

好的,第五期教程到此为止,请关注lesson 6。系列教程地址:webgl-lesson.wysaid.org

128 | 129 | -------------------------------------------------------------------------------- /Lesson5/vocaloid.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wysaid/WebGL-Lessons/5020e8a9a78491c1181d0b2397d4135b13c14497/Lesson5/vocaloid.jpg -------------------------------------------------------------------------------- /Lesson5/vocaloid2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wysaid/WebGL-Lessons/5020e8a9a78491c1181d0b2397d4135b13c14497/Lesson5/vocaloid2.jpg -------------------------------------------------------------------------------- /Lesson6/demo-lesson6.js: -------------------------------------------------------------------------------- 1 | var isMouseButtonDown = false; 2 | var lastX = 0; 3 | var lastY = 0; 4 | var bigModel = null; 5 | var smallModel = null; 6 | var modelXMax = 250.0; 7 | var modelYMax = 250.0; 8 | var modelZMax = 250.0; 9 | var smallModeSize = 30.0; 10 | var viewport = new WYVec4(0.0, 0.0, modelXMax, modelYMax); 11 | 12 | var perspectiveMatrix = WYMat4.makeIdentity(); 13 | var orthoMatrix = WYMat4.makeIdentity(); 14 | 15 | var projMatrix = orthoMatrix; 16 | var modelViewMatrix = WYMat4.makeIdentity(); 17 | var smallModelViewMatrix = WYMat4.makeIdentity(); 18 | 19 | var usePerspective = true; 20 | var autoRotate = true; 21 | 22 | var cx = 0; 23 | var cy = 0; 24 | var dx = Math.random() + 1; 25 | var dy = Math.random() + 1; 26 | 27 | function mouseDown(mouseEvent) 28 | { 29 | isMouseButtonDown = true; 30 | lastX = mouseEvent.offsetX == null ? mouseEvent.layerX : mouseEvent.offsetX; 31 | lastY = mouseEvent.offsetY == null ? mouseEvent.layerY : mouseEvent.offsetY; 32 | } 33 | 34 | function mouseUp() 35 | { 36 | isMouseButtonDown = false; 37 | } 38 | 39 | function rotate(mouseEvent) 40 | { 41 | if(!isMouseButtonDown) return; 42 | var x = mouseEvent.offsetX == null ? mouseEvent.layerX : mouseEvent.offsetX; 43 | var y = mouseEvent.offsetY == null ? mouseEvent.layerY : mouseEvent.offsetY; 44 | 45 | modelViewMatrix = WYMat4.mat4Mul(modelViewMatrix, WYMat4.makeXRotation((y - lastY) / 100.0)); 46 | modelViewMatrix = WYMat4.mat4Mul(modelViewMatrix, WYMat4.makeYRotation((x - lastX) / 100.0)); 47 | 48 | lastX = x; 49 | lastY = y; 50 | autoRotate = false; 51 | } 52 | 53 | function genBigModel(width, height, depth) 54 | { 55 | bigModel = new Array(30); 56 | for(var i = 0; i != 30; ++i) 57 | { 58 | bigModel[i] = new WYVec4(Math.random() * width * 2 - width, 59 | Math.random() * height * 2 - height, 60 | Math.random() * depth * 2 - depth, 61 | 1.0); 62 | } 63 | } 64 | 65 | function genSmallModel(size) 66 | { 67 | smallModel = new Array( 68 | new WYVec4(-size, -size, size, 1.0), 69 | new WYVec4(size, -size, size, 1.0), 70 | new WYVec4(size, size, size, 1.0), 71 | new WYVec4(-size, size, size, 1.0), 72 | new WYVec4(-size, -size, -size, 1.0), 73 | new WYVec4(size, -size, -size, 1.0), 74 | new WYVec4(size, size, -size, 1.0), 75 | new WYVec4(-size, size, -size, 1.0)); 76 | } 77 | 78 | genBigModel(modelXMax, modelYMax, modelZMax); 79 | genSmallModel(smallModeSize); 80 | 81 | function resizeCanvas(w, h) 82 | { 83 | viewport.data[2] = w; 84 | viewport.data[3] = h; 85 | var len = (w < h ? w : h); 86 | 87 | orthoMatrix = WYMat4.makeOrtho(-w / 2.0, w / 2.0, -h / 2.0, h / 2.0, -1.0, 1.0); 88 | perspectiveMatrix = WYMat4.makePerspective(45.0, w / h, -1.0, 1.0); 89 | modelViewMatrix = WYMat4.makeLookAt(0.0, 0.0, len, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); 90 | smallModelViewMatrix = WYMat4.makeTranslation(-w / 2.0, -h / 2.0, 0.0); 91 | if(usePerspective) 92 | { 93 | projMatrix = perspectiveMatrix; 94 | } 95 | else 96 | { 97 | projMatrix = orthoMatrix; 98 | } 99 | cx = 0; 100 | cy = 0; 101 | } 102 | 103 | function setOrtho() 104 | { 105 | usePerspective = false; 106 | projMatrix = orthoMatrix; 107 | } 108 | 109 | function setPerspective() 110 | { 111 | usePerspective = true; 112 | projMatrix = perspectiveMatrix; 113 | } 114 | 115 | function resetModel() 116 | { 117 | var len = (viewport.data[2] > viewport.data[3] ? viewport.data[3] : viewport.data[2]) / 2.0; 118 | genBigModel(len, len, len); 119 | genSmallModel(len / 20.0 + Math.random() * (len / 20.0)); 120 | } 121 | 122 | function drawBigModel(ctx) 123 | { 124 | var modelPoint = new Array(bigModel.length); 125 | 126 | for(var i = 0; i < bigModel.length; ++i) 127 | { 128 | modelPoint[i] = new WYVec3(); 129 | WYMat4.projectM4(bigModel[i], modelViewMatrix, projMatrix, viewport, modelPoint[i]); 130 | } 131 | 132 | for(var i = 0; i < bigModel.length; ++i) 133 | { 134 | for(var j = i+1; j < bigModel.length; ++j) 135 | { 136 | ctx.moveTo(modelPoint[i].data[0], modelPoint[i].data[1]); 137 | ctx.lineTo(modelPoint[j].data[0], modelPoint[j].data[1]); 138 | } 139 | } 140 | if(autoRotate) 141 | { 142 | modelViewMatrix = WYMat4.mat4Mul(modelViewMatrix, WYMat4.makeXRotation(0.01)); 143 | modelViewMatrix = WYMat4.mat4Mul(modelViewMatrix, WYMat4.makeYRotation(0.01)); 144 | modelViewMatrix = WYMat4.mat4Mul(modelViewMatrix, WYMat4.makeZRotation(Math.random() / 100.0)); 145 | } 146 | } 147 | 148 | function drawSmallModel(ctx) 149 | { 150 | var modelPoint = new Array(smallModel.length); 151 | for(var i = 0; i < smallModel.length; ++i) 152 | { 153 | modelPoint[i] = new WYVec3(); 154 | WYMat4.projectM4(smallModel[i], smallModelViewMatrix, orthoMatrix, viewport, modelPoint[i]); 155 | } 156 | 157 | for(var i = 0; i < smallModel.length; ++i) 158 | { 159 | for(var j = i+1; j < smallModel.length; ++j) 160 | { 161 | ctx.moveTo(modelPoint[i].data[0] + cx, modelPoint[i].data[1] + cy); 162 | ctx.lineTo(modelPoint[j].data[0] + cx, modelPoint[j].data[1] + cy); 163 | } 164 | } 165 | ctx.fillText("这不是WebGL哟", modelPoint[0].data[0] + cx, modelPoint[0].data[1] + cy); 166 | cx += dx; 167 | cy += dy; 168 | if(cx < 0 || cx > viewport.data[2]) 169 | dx = -dx; 170 | if(cy < 0 || cy > viewport.data[3]) 171 | dy = -dy; 172 | smallModelViewMatrix = WYMat4.mat4Mul(smallModelViewMatrix, WYMat4.makeXRotation(Math.random() / 10.0)); 173 | smallModelViewMatrix = WYMat4.mat4Mul(smallModelViewMatrix, WYMat4.makeYRotation(Math.random() / 20.0)); 174 | smallModelViewMatrix = WYMat4.mat4Mul(smallModelViewMatrix, WYMat4.makeZRotation(Math.random() / 30.0)); 175 | } 176 | 177 | function drawCanvas(cvsName) 178 | { 179 | var cvsObj = document.getElementById(cvsName); 180 | var cvsContext = cvsObj.getContext("2d"); 181 | cvsContext.fillStyle="#000"; 182 | cvsContext.clearRect(0, 0, cvsObj.width, cvsObj.height); 183 | cvsContext.fillText("鼠标点击红色区域可以旋转大的模型", 20, 20); 184 | cvsContext.lineWidth = 2; 185 | cvsContext.beginPath(); 186 | cvsContext.strokeStyle = "#ff0"; 187 | drawBigModel(cvsContext); 188 | cvsContext.stroke(); 189 | cvsContext.closePath(); 190 | cvsContext.beginPath(); 191 | cvsContext.strokeStyle = "#00f"; 192 | drawSmallModel(cvsContext); 193 | cvsContext.stroke(); 194 | cvsContext.closePath(); 195 | } -------------------------------------------------------------------------------- /Lesson6/demo-requestAnimationFrame.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 |
6 |
7 |
8 | 9 | 22 | -------------------------------------------------------------------------------- /Lesson6/demo-requestAnimationFrame.js: -------------------------------------------------------------------------------- 1 | var isMouseButtonDown = false; 2 | var lastX = 0; 3 | var lastY = 0; 4 | var bigModel = null; 5 | var smallModel = null; 6 | var modelXMax = 250.0; 7 | var modelYMax = 250.0; 8 | var modelZMax = 250.0; 9 | var smallModeSize = 30.0; 10 | var viewport = new WYVec4(0.0, 0.0, modelXMax, modelYMax); 11 | 12 | var perspectiveMatrix = WYMat4.makeIdentity(); 13 | var orthoMatrix = WYMat4.makeIdentity(); 14 | 15 | var projMatrix = orthoMatrix; 16 | var modelViewMatrix = WYMat4.makeIdentity(); 17 | var smallModelViewMatrix = WYMat4.makeIdentity(); 18 | 19 | var usePerspective = true; 20 | var autoRotate = true; 21 | 22 | var cx = 0; 23 | var cy = 0; 24 | var dx = Math.random() + 1; 25 | var dy = Math.random() + 1; 26 | 27 | (function() { 28 | var lastTime = 0; 29 | var vendors = ['ms', 'moz', 'webkit', 'o']; 30 | for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { 31 | window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; 32 | window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] 33 | || window[vendors[x]+'CancelRequestAnimationFrame']; 34 | } 35 | 36 | if (!window.requestAnimationFrame) 37 | window.requestAnimationFrame = function(callback, element) { 38 | var currTime = new Date().getTime(); 39 | var timeToCall = Math.max(0, 16 - (currTime - lastTime)); 40 | var id = window.setTimeout(function() { callback(currTime + timeToCall); }, 41 | timeToCall); 42 | lastTime = currTime + timeToCall; 43 | return id; 44 | }; 45 | 46 | if (!window.cancelAnimationFrame) 47 | window.cancelAnimationFrame = function(id) { 48 | clearTimeout(id); 49 | }; 50 | }()); 51 | 52 | function mouseDown(mouseEvent) 53 | { 54 | isMouseButtonDown = true; 55 | lastX = mouseEvent.offsetX == null ? mouseEvent.layerX : mouseEvent.offsetX; 56 | lastY = mouseEvent.offsetY == null ? mouseEvent.layerY : mouseEvent.offsetY; 57 | } 58 | 59 | function mouseUp() 60 | { 61 | isMouseButtonDown = false; 62 | } 63 | 64 | function rotate(mouseEvent) 65 | { 66 | if(!isMouseButtonDown) return; 67 | var x = mouseEvent.offsetX == null ? mouseEvent.layerX : mouseEvent.offsetX; 68 | var y = mouseEvent.offsetY == null ? mouseEvent.layerY : mouseEvent.offsetY; 69 | 70 | modelViewMatrix = WYMat4.mat4Mul(modelViewMatrix, WYMat4.makeXRotation((y - lastY) / 100.0)); 71 | modelViewMatrix = WYMat4.mat4Mul(modelViewMatrix, WYMat4.makeYRotation((x - lastX) / 100.0)); 72 | 73 | lastX = x; 74 | lastY = y; 75 | autoRotate = false; 76 | } 77 | 78 | function genBigModel(width, height, depth) 79 | { 80 | bigModel = new Array(30); 81 | for(var i = 0; i != 30; ++i) 82 | { 83 | bigModel[i] = new WYVec4(Math.random() * width * 2 - width, 84 | Math.random() * height * 2 - height, 85 | Math.random() * depth * 2 - depth, 86 | 1.0); 87 | } 88 | } 89 | 90 | function genSmallModel(size) 91 | { 92 | smallModel = new Array( 93 | new WYVec4(-size, -size, size, 1.0), 94 | new WYVec4(size, -size, size, 1.0), 95 | new WYVec4(size, size, size, 1.0), 96 | new WYVec4(-size, size, size, 1.0), 97 | new WYVec4(-size, -size, -size, 1.0), 98 | new WYVec4(size, -size, -size, 1.0), 99 | new WYVec4(size, size, -size, 1.0), 100 | new WYVec4(-size, size, -size, 1.0)); 101 | } 102 | 103 | genBigModel(modelXMax, modelYMax, modelZMax); 104 | genSmallModel(smallModeSize); 105 | 106 | function resizeCanvas(w, h) 107 | { 108 | viewport.data[2] = w; 109 | viewport.data[3] = h; 110 | var len = (w < h ? w : h); 111 | 112 | orthoMatrix = WYMat4.makeOrtho(-w / 2.0, w / 2.0, -h / 2.0, h / 2.0, -1.0, 1.0); 113 | perspectiveMatrix = WYMat4.makePerspective(45.0, w / h, -1.0, 1.0); 114 | modelViewMatrix = WYMat4.makeLookAt(0.0, 0.0, len, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); 115 | smallModelViewMatrix = WYMat4.makeTranslation(-w / 2.0, -h / 2.0, 0.0); 116 | if(usePerspective) 117 | { 118 | projMatrix = perspectiveMatrix; 119 | } 120 | else 121 | { 122 | projMatrix = orthoMatrix; 123 | } 124 | cx = 0; 125 | cy = 0; 126 | } 127 | 128 | function setOrtho() 129 | { 130 | usePerspective = false; 131 | projMatrix = orthoMatrix; 132 | } 133 | 134 | function setPerspective() 135 | { 136 | usePerspective = true; 137 | projMatrix = perspectiveMatrix; 138 | } 139 | 140 | function resetModel() 141 | { 142 | var len = (viewport.data[2] > viewport.data[3] ? viewport.data[3] : viewport.data[2]) / 2.0; 143 | genBigModel(len, len, len); 144 | genSmallModel(len / 20.0 + Math.random() * (len / 20.0)); 145 | } 146 | 147 | function drawBigModel(ctx) 148 | { 149 | var modelPoint = new Array(bigModel.length); 150 | 151 | for(var i = 0; i < bigModel.length; ++i) 152 | { 153 | modelPoint[i] = new WYVec3(); 154 | WYMat4.projectM4(bigModel[i], modelViewMatrix, projMatrix, viewport, modelPoint[i]); 155 | } 156 | 157 | for(var i = 0; i < bigModel.length; ++i) 158 | { 159 | for(var j = i+1; j < bigModel.length; ++j) 160 | { 161 | ctx.moveTo(modelPoint[i].data[0], modelPoint[i].data[1]); 162 | ctx.lineTo(modelPoint[j].data[0], modelPoint[j].data[1]); 163 | } 164 | } 165 | if(autoRotate) 166 | { 167 | modelViewMatrix = WYMat4.mat4Mul(modelViewMatrix, WYMat4.makeXRotation(0.01)); 168 | modelViewMatrix = WYMat4.mat4Mul(modelViewMatrix, WYMat4.makeYRotation(0.01)); 169 | modelViewMatrix = WYMat4.mat4Mul(modelViewMatrix, WYMat4.makeZRotation(Math.random() / 100.0)); 170 | } 171 | } 172 | 173 | function drawSmallModel(ctx) 174 | { 175 | var modelPoint = new Array(smallModel.length); 176 | for(var i = 0; i < smallModel.length; ++i) 177 | { 178 | modelPoint[i] = new WYVec3(); 179 | WYMat4.projectM4(smallModel[i], smallModelViewMatrix, orthoMatrix, viewport, modelPoint[i]); 180 | } 181 | 182 | for(var i = 0; i < smallModel.length; ++i) 183 | { 184 | for(var j = i+1; j < smallModel.length; ++j) 185 | { 186 | ctx.moveTo(modelPoint[i].data[0] + cx, modelPoint[i].data[1] + cy); 187 | ctx.lineTo(modelPoint[j].data[0] + cx, modelPoint[j].data[1] + cy); 188 | } 189 | } 190 | ctx.fillText("这不是WebGL哟", modelPoint[0].data[0] + cx, modelPoint[0].data[1] + cy); 191 | cx += dx; 192 | cy += dy; 193 | if(cx < 0 || cx > viewport.data[2]) 194 | dx = -dx; 195 | if(cy < 0 || cy > viewport.data[3]) 196 | dy = -dy; 197 | smallModelViewMatrix = WYMat4.mat4Mul(smallModelViewMatrix, WYMat4.makeXRotation(Math.random() / 10.0)); 198 | smallModelViewMatrix = WYMat4.mat4Mul(smallModelViewMatrix, WYMat4.makeYRotation(Math.random() / 20.0)); 199 | smallModelViewMatrix = WYMat4.mat4Mul(smallModelViewMatrix, WYMat4.makeZRotation(Math.random() / 30.0)); 200 | } 201 | 202 | function drawCanvas(cvsName) 203 | { 204 | requestAnimationFrame(drawCanvas); 205 | var cvsObj = document.getElementById('webgl-lesson6'); 206 | var cvsContext = cvsObj.getContext("2d"); 207 | cvsContext.fillStyle="#000"; 208 | cvsContext.clearRect(0, 0, cvsObj.width, cvsObj.height); 209 | cvsContext.fillText("鼠标点击红色区域可以旋转大的模型", 20, 20); 210 | cvsContext.lineWidth = 2; 211 | cvsContext.beginPath(); 212 | cvsContext.strokeStyle = "#ff0"; 213 | drawBigModel(cvsContext); 214 | cvsContext.stroke(); 215 | cvsContext.closePath(); 216 | cvsContext.beginPath(); 217 | cvsContext.strokeStyle = "#00f"; 218 | drawSmallModel(cvsContext); 219 | cvsContext.stroke(); 220 | cvsContext.closePath(); 221 | } -------------------------------------------------------------------------------- /Lesson6/demo_lesson6.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 |
7 |
8 | 9 | 22 | -------------------------------------------------------------------------------- /Lesson7/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wysaid/WebGL-Lessons/5020e8a9a78491c1181d0b2397d4135b13c14497/Lesson7/1.jpg -------------------------------------------------------------------------------- /Lesson7/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wysaid/WebGL-Lessons/5020e8a9a78491c1181d0b2397d4135b13c14497/Lesson7/2.jpg -------------------------------------------------------------------------------- /Lesson7/demo_net.js: -------------------------------------------------------------------------------- 1 | /* 2 | Author: wysaid 3 | Blog: blog.wysaid.org 4 | Mail: wysaid@gmail.com OR admin@wysaid.org 5 | */ 6 | 7 | //为了便于区分函数名和类名,本文件所有类均以wy开头(轻吐槽@_@b 8 | //本文件仅包含网格处理部分,不包含渲染代码。 9 | 10 | var g_meshSize = [80, 60]; 11 | 12 | function WYPointVector() 13 | { 14 | this.dx = 0.0; 15 | this.dy = 0.0; 16 | } 17 | 18 | function WYMesh() 19 | { 20 | this.mesh = new Array(2); 21 | this.meshAcc = null; 22 | this.layer = 0; 23 | this.width = 0; 24 | this.height = 0; 25 | this.intensity = 0.2; 26 | this.lastPoint = -1; 27 | 28 | this.initMesh = function(w, h) 29 | { 30 | if(w < 2 || h < 2) 31 | return false; 32 | 33 | this.width = w; 34 | this.height = h; 35 | this.mesh[0] = new Array(w * h * 2); 36 | this.mesh[1] = new Array(w * h * 2); 37 | this.meshAcc = new Array(w * h); 38 | 39 | var widthStep = 1.0 / (w - 1.0); 40 | var heightStep = 1.0 / (h - 1.0); 41 | 42 | for(var i = 0; i < h; ++i) 43 | { 44 | var heightI = i * heightStep; 45 | var index = w * i; 46 | for(var j = 0; j < w; ++j) 47 | { 48 | var widthJ = j * widthStep; 49 | this.mesh[0][index * 2] = widthJ; 50 | this.mesh[0][index * 2 + 1] = heightI; 51 | 52 | this.mesh[1][index * 2] = widthJ; 53 | this.mesh[1][index * 2 + 1] = heightI; 54 | 55 | this.meshAcc[index] = new WYPointVector(); 56 | ++index; 57 | } 58 | } 59 | return true; 60 | } 61 | 62 | this.update = function() 63 | { 64 | var widthStep = 1.0 / (this.width - 1.0); 65 | var heightStep = 1.0 / (this.height - 1.0); 66 | var layer = this.layer; 67 | var layerAfter = (layer + 1) % 2; 68 | var width = this.width; 69 | var height = this.height; 70 | var widthM1 = width - 1; 71 | var heightM1 = height - 1; 72 | var thismesh = this.mesh[layer]; 73 | var nextmesh = this.mesh[layerAfter]; 74 | 75 | for(var i = 1; i < heightM1; ++i) 76 | { 77 | var k = width * i; 78 | for(var j = 1; j < widthM1; ++j) 79 | { 80 | var h = k + j; 81 | var h2 = h * 2; 82 | var dx, dy; 83 | var pntUp, pntDown, pntLeft, pntRight; 84 | pntDown = (h + width) * 2; 85 | pntUp = (h - width) * 2; 86 | pntLeft = (h - 1) * 2; 87 | pntRight = (h + 1) * 2; 88 | 89 | dx = thismesh[pntLeft] + thismesh[pntRight] - thismesh[h2] * 2.0; 90 | dy = thismesh[pntLeft + 1] + thismesh[pntRight + 1] - thismesh[h2 + 1] * 2.0; 91 | 92 | dx += thismesh[pntUp] + thismesh[pntDown] - thismesh[h2] * 2.0; 93 | dy += thismesh[pntUp + 1] + thismesh[pntDown + 1] - thismesh[h2 + 1] * 2.0; 94 | 95 | //模拟能量损失, 当加速度方向与速度方向相反时加快减速。 96 | if(dx * this.meshAcc[h].dx < 0.0) 97 | dx *= 1.0 + this.intensity; 98 | if(dy * this.meshAcc[h].dy < 0.0) 99 | dy *= 1.0 + this.intensity; 100 | 101 | this.meshAcc[h].dx += dx * this.intensity; 102 | this.meshAcc[h].dy += dy * this.intensity; 103 | 104 | nextmesh[h2] = thismesh[h2] + this.meshAcc[h].dx; 105 | nextmesh[h2 + 1] = thismesh[h2 + 1] + this.meshAcc[h].dy; 106 | 107 | } 108 | } 109 | this.layer = layerAfter; 110 | } 111 | 112 | this.catchPoint = function(x, y) 113 | { 114 | var pointIndex; 115 | var heightM1 = this.height - 1; 116 | var widthM1 = this.width - 1; 117 | var thismesh = this.mesh[this.layer]; 118 | 119 | if(this.lastPoint < 0) 120 | { 121 | var mdis = 1e9; 122 | for(var i = 1; i < heightM1; ++i) 123 | { 124 | var k = this.width * i; 125 | for(var j = 1; j < widthM1; ++j) 126 | { 127 | var h = (k + j) * 2; 128 | var dis = Math.abs(x - thismesh[h]) + Math.abs(y - thismesh[h + 1]); 129 | if(dis < mdis) 130 | { 131 | pointIndex = h; 132 | mdis = dis; 133 | } 134 | } 135 | } 136 | this.lastPoint = parseInt(pointIndex); 137 | } 138 | else 139 | pointIndex = this.lastPoint; 140 | 141 | var mesh0 = this.mesh[0]; 142 | var mesh1 = this.mesh[1]; 143 | 144 | mesh0[pointIndex] = x; 145 | mesh0[pointIndex + 1] = y; 146 | mesh1[pointIndex] = x; 147 | mesh1[pointIndex + 1] = y; 148 | 149 | this.meshAcc[pointIndex / 2].dx = 0.0; 150 | this.meshAcc[pointIndex / 2].dy = 0.0; 151 | } 152 | 153 | this.releasePoint = function() 154 | { 155 | this.lastPoint = -1; 156 | } 157 | 158 | this.intensityInc = function(value) 159 | { 160 | this.intensity += value; 161 | if(this.intensity > 0.3) 162 | this.intensity = 0.3; 163 | } 164 | 165 | this.intensityDec = function(value) 166 | { 167 | this.intensity -= value; 168 | if(this.intensity < 0.001) 169 | this.intensity = 0.001; 170 | } 171 | 172 | } 173 | 174 | var g_mesh = new WYMesh(); 175 | 176 | g_mesh.initMesh(g_meshSize[0], g_meshSize[1]); 177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /Lesson7/demo_net_deprecated.js: -------------------------------------------------------------------------------- 1 | /* 2 | Author: wysaid 3 | Blog: blog.wysaid.org 4 | Mail: wysaid@gmail.com OR admin@wysaid.org 5 | */ 6 | 7 | //为了便于区分函数名和类名,本文件所有类均以wy开头(轻吐槽@_@b 8 | //本文件仅包含网格处理部分,不包含渲染代码。 9 | //本章代码并未用到此文件,因为这个版本的mesh数据不适合在webgl中使用 10 | //但是这个文件的mesh处理代码较容易看懂,所以保留之,供君参考 11 | 12 | var g_meshSize = [80, 60]; 13 | 14 | function WYPoint(vx, vy) 15 | { 16 | this.x = vx; 17 | this.y = vy; 18 | this.dx = 0.0; 19 | this.dy = 0.0; 20 | } 21 | 22 | function WYMesh() 23 | { 24 | this.mesh = new Array(2); 25 | this.layer = 0; 26 | this.width = 0; 27 | this.height = 0; 28 | this.intensity = 0.2; 29 | this.lastPoint = -1; 30 | 31 | this.initMesh = function(w, h) 32 | { 33 | if(w < 2 || h < 2) 34 | return false; 35 | 36 | this.width = w; 37 | this.height = h; 38 | this.mesh[0] = new Array(w * h); 39 | this.mesh[1] = new Array(w * h); 40 | var widthStep = 1.0 / (w - 1.0); 41 | var heightStep = 1.0 / (h - 1.0); 42 | 43 | for(var i = 0; i < h; ++i) 44 | { 45 | var heightI = i * heightStep; 46 | var index = w * i; 47 | for(var j = 0; j < w; ++j) 48 | { 49 | var widthJ = j * widthStep; 50 | this.mesh[0][index] = new WYPoint(widthJ, heightI); 51 | this.mesh[1][index] = new WYPoint(widthJ, heightI); 52 | ++index; 53 | } 54 | } 55 | return true; 56 | } 57 | 58 | this.update = function() 59 | { 60 | var widthStep = 1.0 / (this.width - 1.0); 61 | var heightStep = 1.0 / (this.height - 1.0); 62 | var layer = this.layer; 63 | var layerAfter = (layer + 1) % 2; 64 | var width = this.width; 65 | var height = this.height; 66 | var widthM1 = width - 1; 67 | var heightM1 = height - 1; 68 | var thismesh = this.mesh[layer]; 69 | var nextmesh = this.mesh[layerAfter]; 70 | 71 | for(var i = 1; i < heightM1; ++i) 72 | { 73 | var k = width * i; 74 | for(var j = 1; j < widthM1; ++j) 75 | { 76 | var h = k + j; 77 | var dx, dy; 78 | var pnt, pntUp, pntDown, pntLeft, pntRight; 79 | pnt = thismesh[h]; 80 | pntDown = thismesh[h + width]; 81 | pntUp = thismesh[h - width]; 82 | pntLeft = thismesh[h - 1]; 83 | pntRight = thismesh[h + 1]; 84 | 85 | dx = pntLeft.x + pntRight.x - pnt.x * 2.0; 86 | dy = pntLeft.y + pntRight.y - pnt.y * 2.0; 87 | 88 | dx += pntUp.x + pntDown.x - pnt.x * 2.0; 89 | dy += pntUp.y + pntDown.y - pnt.y * 2.0; 90 | 91 | //模拟能量损失, 当加速度方向与速度方向相反时加快减速。 92 | if(dx * pnt.dx < 0.0) 93 | dx *= 1.0 + this.intensity; 94 | if(dy * pnt.dy < 0.0) 95 | dy *= 1.0 + this.intensity; 96 | 97 | pnt.dx += dx * this.intensity; 98 | pnt.dy += dy * this.intensity; 99 | nextmesh[h].dx = pnt.dx; 100 | nextmesh[h].dy = pnt.dy; 101 | 102 | nextmesh[h].x = pnt.x + pnt.dx; 103 | nextmesh[h].y = pnt.y + pnt.dy; 104 | 105 | } 106 | } 107 | this.layer = layerAfter; 108 | } 109 | 110 | this.catchPoint = function(x, y) 111 | { 112 | var pointIndex; 113 | var heightM1 = this.height - 1; 114 | var widthM1 = this.width - 1; 115 | var thismesh = this.mesh[this.layer]; 116 | 117 | if(this.lastPoint < 0) 118 | { 119 | var mdis = 1e9; 120 | for(var i = 1; i < heightM1; ++i) 121 | { 122 | var k = this.width * i; 123 | for(var j = 1; j < widthM1; ++j) 124 | { 125 | var h = k + j; 126 | var dis = Math.abs(x - thismesh[h].x) + Math.abs(y - thismesh[h].y); 127 | if(dis < mdis) 128 | { 129 | pointIndex = h; 130 | mdis = dis; 131 | } 132 | } 133 | } 134 | this.lastPoint = pointIndex; 135 | } 136 | else 137 | pointIndex = this.lastPoint; 138 | 139 | var mesh0 = this.mesh[0]; 140 | var mesh1 = this.mesh[1]; 141 | 142 | mesh0[pointIndex].x = x; 143 | mesh0[pointIndex].y = y; 144 | mesh1[pointIndex].x = x; 145 | mesh1[pointIndex].y = y; 146 | 147 | mesh0[pointIndex].dx = 0.0; 148 | mesh0[pointIndex].dy = 0.0; 149 | mesh1[pointIndex].dx = 0.0; 150 | mesh1[pointIndex].dy = 0.0; 151 | } 152 | 153 | this.releasePoint = function() 154 | { 155 | this.lastPoint = -1; 156 | } 157 | 158 | this.intensityInc = function(value) 159 | { 160 | this.intensity += value; 161 | if(this.intensity > 0.3) 162 | this.intensity = 0.3; 163 | } 164 | 165 | this.intensityDec = function(value) 166 | { 167 | this.intensity -= value; 168 | if(this.intensity < 0.001) 169 | this.intensity = 0.001; 170 | } 171 | 172 | } 173 | 174 | var g_mesh = new WYMesh(); 175 | 176 | g_mesh.initMesh(g_meshSize[0], g_meshSize[1]); 177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /Lesson7/demo_net_html5.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Elasticity Mesh 4 | 5 | 8 | 9 | 10 | 11 | 12 | 13 |

如果您的浏览器无法流畅运行,请选择PC版: https://github.com/wysaid/EGE_Net

14 |
15 |
16 |
17 | 18 | 22 | 23 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Lesson7/demo_net_html5.js: -------------------------------------------------------------------------------- 1 | 2 | function drawMesh_html5(ctx, meshObj) 3 | { 4 | var mesh = meshObj.mesh[meshObj.layer]; 5 | var meshWidth = meshObj.width; 6 | var meshHeight = meshObj.height; 7 | for(var i = 0; i < meshHeight; ++i) 8 | { 9 | var k = i * meshWidth; 10 | for(var j = 1; j < meshWidth; ++j) 11 | { 12 | var h2 = (k + j) * 2; 13 | var h1 = h2 - 2; 14 | ctx.moveTo(mesh[h1] * cvsWidth, mesh[h1 + 1] * cvsHeight); 15 | ctx.lineTo(mesh[h2] * cvsWidth, mesh[h2 + 1] * cvsHeight); 16 | } 17 | } 18 | 19 | for(var i = 0; i < meshWidth; ++i) 20 | { 21 | for(var j = 1; j < meshHeight; ++j) 22 | { 23 | var h2 = (j * meshWidth + i) * 2; 24 | var h1 = ((j - 1) * meshWidth + i) * 2; 25 | ctx.moveTo(mesh[h1] * cvsWidth, mesh[h1 + 1] * cvsHeight); 26 | ctx.lineTo(mesh[h2] * cvsWidth, mesh[h2 + 1] * cvsHeight); 27 | } 28 | } 29 | 30 | } 31 | 32 | function drawCanvas_html5(objID) 33 | { 34 | if(isMouseDown) 35 | { 36 | g_mesh.catchPoint(lastX / cvsWidth, lastY / cvsHeight); 37 | } 38 | 39 | var cvsObj = document.getElementById(objID); 40 | var cvsContext = cvsObj.getContext("2d"); 41 | cvsContext.fillStyle = "#fff"; 42 | cvsContext.clearRect(0, 0, cvsWidth, cvsHeight); 43 | 44 | cvsContext.beginPath(); 45 | cvsContext.strokeStyle = "#0f0"; 46 | drawMesh_html5(cvsContext, g_mesh); 47 | cvsContext.fillText("当前网格强度: " + g_mesh.intensity + ". Author URL: http://blog.wysaid.org", 20, 20); 48 | cvsContext.stroke(); 49 | cvsContext.closePath(); 50 | g_mesh.update(); 51 | } 52 | 53 | function bodyResize_html5() 54 | { 55 | var divObj = document.getElementById("canvas_father_html5"); 56 | var cvsObj = document.getElementById("webgl_lesson7_html5"); 57 | cvsObj.width = divObj.clientWidth; 58 | cvsObj.height = divObj.clientHeight; 59 | cvsWidth = cvsObj.width; 60 | cvsHeight = cvsObj.height; 61 | drawCanvas_html5('webgl_lesson7_html5') 62 | } -------------------------------------------------------------------------------- /Lesson7/demo_net_mouse.js: -------------------------------------------------------------------------------- 1 | var isMouseDown = false; 2 | var cvsWidth; 3 | var cvsHeight; 4 | var lastX, lastY; 5 | 6 | function netIntensityInc() 7 | { 8 | g_mesh.intensityInc(0.005); 9 | } 10 | 11 | function netIntensityDec() 12 | { 13 | g_mesh.intensityDec(0.005); 14 | } 15 | 16 | function mouseUp(evt) 17 | { 18 | isMouseDown = false; 19 | g_mesh.releasePoint(); 20 | } 21 | 22 | function mouseDown(evt) 23 | { 24 | isMouseDown = true; 25 | lastX = evt.offsetX == null ? evt.layerX : evt.offsetX; 26 | lastY = evt.offsetY == null ? evt.layerY : evt.offsetY; 27 | g_mesh.catchPoint(lastX / cvsWidth, lastY / cvsHeight); 28 | } 29 | 30 | function mouseMove(evt) 31 | { 32 | if(!isMouseDown) 33 | return ; 34 | lastX = evt.offsetX == null ? evt.layerX : evt.offsetX; 35 | lastY = evt.offsetY == null ? evt.layerY : evt.offsetY; 36 | } -------------------------------------------------------------------------------- /Lesson7/demo_net_webgl.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Elasticity Mesh 4 | 5 | 7 | 8 | 19 | 20 | 29 | 30 | 40 | 41 | 42 | 43 | 44 | 45 | 46 |

如果您的浏览器无法运行WebGL,还有HTML5简单版: http://webgl-lesson.wysaid.org/Lesson7/demo_net_html5.html

47 |
48 |
49 |
50 | 51 |

53 | 54 |
55 | 56 | 57 | 58 | 59 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /Lesson7/webgl_lesson7.js: -------------------------------------------------------------------------------- 1 | /* 2 | Author: wysaid 3 | Blog: blog.wysaid.org 4 | Mail: wysaid@gmail.com OR admin@wysaid.org 5 | */ 6 | 7 | //如果你喜欢,也可以把logBox变量写为全局的,不过不是很有必要。 8 | function appendLog(logString) { 9 | var logBox = document.getElementById("logBox"); 10 | logBox.value += logString + "\n"; 11 | //让log文字在文字超过可显示区域时自动滚动到最底部。 12 | logBox.scrollTop = logBox.scrollHeight; 13 | } 14 | 15 | function clearLogbox() { 16 | var logBox = document.getElementById("logBox"); 17 | logBox.value = ""; 18 | } 19 | 20 | function maximizeLogbox() { 21 | var logBox = document.getElementById("logBox"); 22 | logBox.style.height = logBox.scrollHeight + 20 + "px"; 23 | } 24 | 25 | function restoreLogbox() { 26 | var logBox = document.getElementById("logBox"); 27 | //后面的200px取你自己最初设定的值,其实按百分比设定是最好的, 28 | //不过写百分比的话,放在本教程里面页面就乱了,你可以自己尝试。 29 | logBox.style.height = "200px"; 30 | } 31 | 32 | var webgl = null; 33 | 34 | var deformProgram = null; 35 | var deformProgramPositionIndex = 0; 36 | var deformProgramTextureIndex = 1; 37 | var meshProgram = null; 38 | var meshProgramPositionIndex = 0; 39 | var meshProgramTextureIndex = 1; 40 | 41 | var meshVBO = null; 42 | var meshIndexVBO = null; 43 | var textureVBO = null; 44 | var meshTexture = null; 45 | var meshIndexSize = 0; 46 | var bShowMesh = true; 47 | 48 | var programPositionName = "vPosition"; 49 | var programTextureName = "vTexture"; 50 | 51 | function webglInit() { 52 | var myCanvasObject = document.getElementById("webgl-lesson7"); //此处的webglView为你的canvas的id 53 | var myDivObject = document.getElementById("canvas_father"); //此处的containerView为你的canvas的父div元素id 54 | webgl = myCanvasObject.getContext("experimental-webgl"); 55 | if(webgl == null) 56 | { 57 | appendLog("你的浏览器不支持webgl,请使用Chrome等支持WebGL的浏览器"); 58 | return false; 59 | } 60 | 61 | myCanvasObject.width = myDivObject.clientWidth; //千万注意,参见下面说明。 62 | myCanvasObject.height = myDivObject.clientHeight; //同上 63 | cvsWidth = myCanvasObject.width; 64 | cvsHeight = myCanvasObject.height; 65 | webgl.viewport(0, 0, cvsWidth, cvsHeight);//同上 66 | return true; 67 | } 68 | 69 | 70 | //修正初始化shader的函数,支持更多的定制. (以前是单例模式) 71 | //shaderType: webgl.VERTEX_SHADER 或者 webgl.FRAGMENT_SHADER 72 | function createShaderWithString(shaderCode, shaderType) 73 | { 74 | var shaderObject = webgl.createShader(shaderType); 75 | webgl.shaderSource(shaderObject, shaderCode); 76 | webgl.compileShader(shaderObject); 77 | if(!webgl.getShaderParameter(shaderObject, webgl.COMPILE_STATUS)) 78 | { 79 | appendLog(webgl.getShaderInfoLog(shaderObject)); 80 | return null; 81 | } 82 | return shaderObject; 83 | } 84 | 85 | function getScriptTextByID(scriptID){ 86 | var shaderScript = document.getElementById(scriptID); 87 | if (shaderScript == null) return ""; 88 | 89 | if (shaderScript.textContent != null && shaderScript.textContent != "") { 90 | return shaderScript.textContent; 91 | } 92 | if (shaderScript.text != null && shaderScript.text != "") { 93 | return shaderScript.text; 94 | } 95 | var sourceCode = ""; 96 | var child = shaderScript.firstChild; 97 | while (child) { 98 | if (child.nodeType == child.TEXT_NODE) sourceCode += child.textContent; 99 | child = child.nextSibling; 100 | } 101 | return sourceCode; 102 | } 103 | 104 | function requestURLPlainText(url) { 105 | var xmlHttp = new XMLHttpRequest(); 106 | xmlHttp.open("GET", url, false); 107 | xmlHttp.send(); 108 | return xmlHttp.responseText; 109 | } 110 | 111 | function createTextureByImgObject(imgObj){ 112 | //webgl 对象为前面教程里面函数创建的全局WebGL上下文对象。 113 | //下面这一句可有可无,但为了养成良好习惯,还是写上吧。 114 | //把临时纹理绑定设定在0号。 115 | webgl.activeTexture(webgl.TEXTURE0); 116 | 117 | //创建纹理对象,并设置其属性。需要注意的是, 118 | //在GLES里面,TEXTURE_WRAP_S/T 只能设置为GL_CLAMP_TO_EDGE,所以也是可以不写的哟。 119 | var textureObject = webgl.createTexture(); 120 | webgl.bindTexture(webgl.TEXTURE_2D, textureObject); 121 | webgl.texImage2D(webgl.TEXTURE_2D, 0, webgl.RGBA, webgl.RGBA, webgl.UNSIGNED_BYTE, imgObj); 122 | webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_MIN_FILTER, webgl.NEAREST); 123 | webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_MAG_FILTER, webgl.NEAREST); 124 | webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_S, webgl.CLAMP_TO_EDGE); 125 | webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_T, webgl.CLAMP_TO_EDGE); 126 | 127 | return textureObject; 128 | } 129 | 130 | //在此,我们封装出一个通过img标签的id创建一个纹理的函数: 131 | function createTextureByImgID(imgID){ 132 | var imgObj = document.getElementById(imgID); 133 | if(imgObj == null) { 134 | return null; //这里应该写一些容错代码,但是本阶段属于初级阶段,暂不考虑。 135 | } 136 | return createTextureByImgObject(imgObj); 137 | } 138 | 139 | ///////////////////////// 140 | //Lesson7 新增方法 141 | ///////////////////////// 142 | 143 | function globalInitialize() 144 | { 145 | webglInit(); 146 | 147 | //初始化变形显示program 148 | var vshDeformCode = getScriptTextByID("vshDeform"); 149 | var fshDeformCode = getScriptTextByID("fshDeform"); 150 | var vshDeformObj = createShaderWithString(vshDeformCode, webgl.VERTEX_SHADER); 151 | var fshDeformObj = createShaderWithString(fshDeformCode, webgl.FRAGMENT_SHADER); 152 | deformProgram = webgl.createProgram(); 153 | webgl.attachShader(deformProgram, vshDeformObj); 154 | webgl.attachShader(deformProgram, fshDeformObj); 155 | 156 | //初始化网格显示program 157 | var vshMeshCode = getScriptTextByID("vshDeform"); 158 | var fshMeshCode = getScriptTextByID("fshMesh"); 159 | var vshMeshObj = createShaderWithString(vshMeshCode, webgl.VERTEX_SHADER); 160 | var fshMeshObj = createShaderWithString(fshMeshCode, webgl.FRAGMENT_SHADER); 161 | meshProgram = webgl.createProgram(); 162 | webgl.attachShader(meshProgram, vshMeshObj); 163 | webgl.attachShader(meshProgram, fshMeshObj); 164 | 165 | //绑定顶点属性: 166 | webgl.bindAttribLocation(deformProgram, deformProgramPositionIndex, programPositionName); 167 | webgl.bindAttribLocation(meshProgram, meshProgramPositionIndex, programPositionName); 168 | 169 | webgl.bindAttribLocation(deformProgram, deformProgramTextureIndex, programTextureName); 170 | webgl.bindAttribLocation(meshProgram, meshProgramTextureIndex, programTextureName); 171 | 172 | webgl.linkProgram(deformProgram); 173 | webgl.linkProgram(meshProgram); 174 | 175 | if (!webgl.getProgramParameter(deformProgram, webgl.LINK_STATUS)) 176 | { 177 | appendLog(webgl.getProgramInfoLog(deformProgram)); 178 | return; 179 | } 180 | 181 | if (!webgl.getProgramParameter(meshProgram, webgl.LINK_STATUS)) 182 | { 183 | appendLog(webgl.getProgramInfoLog(meshProgram)); 184 | return; 185 | } 186 | 187 | //创建纹理 188 | meshTexture = createTextureByImgID("meshTexture"); 189 | 190 | //绑定纹理 191 | webgl.activeTexture(webgl.TEXTURE0); 192 | webgl.bindTexture(webgl.TEXTURE_2D, meshTexture); 193 | 194 | webgl.useProgram(meshProgram); 195 | var texUniform = webgl.getUniformLocation(meshProgram, "inputImageTexture"); 196 | webgl.uniform1i(texUniform, 0); 197 | 198 | webgl.useProgram(deformProgram); 199 | texUniform = webgl.getUniformLocation(deformProgram, "inputImageTexture"); 200 | webgl.uniform1i(texUniform, 0); 201 | 202 | meshVBO = webgl.createBuffer(); 203 | meshIndexVBO = webgl.createBuffer(); 204 | textureVBO = webgl.createBuffer(); 205 | 206 | webgl.bindBuffer(webgl.ARRAY_BUFFER, meshVBO); 207 | webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(g_mesh.mesh[g_mesh.layer]), webgl.STREAM_DRAW); 208 | 209 | webgl.bindBuffer(webgl.ARRAY_BUFFER, textureVBO); 210 | webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(g_mesh.mesh[g_mesh.layer]), webgl.STATIC_DRAW); 211 | 212 | webgl.bindBuffer(webgl.ELEMENT_ARRAY_BUFFER, meshIndexVBO); 213 | 214 | meshIndexSize = (g_meshSize[0] - 1) * (g_meshSize[1] - 1) * 2 * 3; 215 | var indexBufferData = new Array(meshIndexSize); 216 | 217 | var index = 0; 218 | 219 | for(var i = 0; i < g_meshSize[1] - 1; ++i) 220 | { 221 | var pos1 = i * g_meshSize[0]; 222 | var pos2 = (i + 1) * g_meshSize[0]; 223 | if(i % 2) 224 | { 225 | for(var j = 0; j < g_meshSize[0] - 1; ++j) 226 | { 227 | var k = index * 3; 228 | indexBufferData[k] = pos1 + j; 229 | indexBufferData[k + 1] = pos1 + j + 1; 230 | indexBufferData[k + 2] = pos2 + j; 231 | indexBufferData[k + 3] = pos2 + j; 232 | indexBufferData[k + 4] = pos1 + j + 1; 233 | indexBufferData[k + 5] = pos2 + j + 1; 234 | index += 2; 235 | } 236 | } 237 | else 238 | { 239 | for(var j = g_meshSize[0] - 2; j >= 0; --j) 240 | { 241 | var k = index * 3; 242 | indexBufferData[k] = pos1 + j + 1; 243 | indexBufferData[k + 1] = pos2 + j + 1; 244 | indexBufferData[k + 2] = pos2 + j; 245 | indexBufferData[k + 3] = pos1 + j; 246 | indexBufferData[k + 4] = pos1 + j + 1; 247 | indexBufferData[k + 5] = pos2 + j; 248 | index += 2; 249 | } 250 | } 251 | } 252 | webgl.bufferData(webgl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indexBufferData), webgl.STATIC_DRAW); 253 | 254 | appendLog("当前网格强度:" + g_mesh.intensity); 255 | } 256 | 257 | function updateBuffer() 258 | { 259 | if(meshVBO == null || meshVBO == 0) 260 | return ; 261 | webgl.bindBuffer(webgl.ARRAY_BUFFER, meshVBO); 262 | webgl.bufferData(webgl.ARRAY_BUFFER, (g_mesh.mesh[g_mesh.layer]), webgl.DYNAMIC_DRAW); 263 | } 264 | 265 | function drawScene() 266 | { 267 | if(meshVBO == null || meshVBO == 0) 268 | return ; 269 | 270 | webgl.activeTexture(webgl.TEXTURE0); 271 | webgl.bindTexture(webgl.TEXTURE_2D, meshTexture); 272 | 273 | webgl.useProgram(deformProgram); 274 | 275 | 276 | webgl.bindBuffer(webgl.ARRAY_BUFFER, meshVBO); 277 | webgl.bufferData(webgl.ARRAY_BUFFER, new Float32Array(g_mesh.mesh[g_mesh.layer]), webgl.DYNAMIC_DRAW); 278 | webgl.enableVertexAttribArray(deformProgramPositionIndex); 279 | webgl.vertexAttribPointer(deformProgramPositionIndex, 2, webgl.FLOAT, false, 0, 0); 280 | 281 | webgl.bindBuffer(webgl.ARRAY_BUFFER, textureVBO); 282 | webgl.enableVertexAttribArray(deformProgramTextureIndex); 283 | webgl.vertexAttribPointer(deformProgramTextureIndex, 2, webgl.FLOAT, false, 0, 0); 284 | 285 | webgl.bindBuffer(webgl.ELEMENT_ARRAY_BUFFER, meshIndexVBO); 286 | 287 | webgl.drawElements(webgl.TRIANGLES, meshIndexSize, webgl.UNSIGNED_SHORT, 0); 288 | 289 | if(bShowMesh) 290 | { 291 | webgl.useProgram(meshProgram); 292 | webgl.drawElements(webgl.LINE_STRIP, meshIndexSize, webgl.UNSIGNED_SHORT, 0); 293 | } 294 | 295 | webgl.finish(); 296 | } 297 | 298 | //下面是一些本章专用辅助函数 299 | 300 | function drawCanvas() 301 | { 302 | if(isMouseDown) 303 | { 304 | g_mesh.catchPoint(lastX / cvsWidth, lastY / cvsHeight); 305 | } 306 | 307 | drawScene(); 308 | 309 | g_mesh.update(); 310 | } 311 | 312 | function bodyResize() 313 | { 314 | var divObj = document.getElementById("canvas_father"); 315 | var cvsObj = document.getElementById("webgl-lesson7"); 316 | cvsObj.width = divObj.clientWidth; 317 | cvsObj.height = divObj.clientHeight; 318 | cvsWidth = cvsObj.width; 319 | cvsHeight = cvsObj.height; 320 | webgl.viewport(0, 0, cvsObj.width, cvsObj.height); 321 | drawCanvas(); 322 | } 323 | 324 | function imageOnload() { 325 | webglInit(); 326 | if(webgl == null) 327 | { 328 | appendLog("See more: http://get.webgl.org/"); 329 | return; 330 | } 331 | appendLog("WebGL is supported! That's cool!\nHere are some information for your device:"); 332 | appendLog("The max renderbuffer size your browser support: " + webgl.getParameter(webgl.MAX_RENDERBUFFER_SIZE)); 333 | appendLog("The max texture image units your browser support: " + webgl.getParameter(webgl.MAX_TEXTURE_IMAGE_UNITS)); 334 | appendLog("The max texture size your browser support: " + webgl.getParameter(webgl.MAX_TEXTURE_SIZE)); 335 | appendLog("The max cube map texture size your browser support: " + webgl.getParameter(webgl.MAX_CUBE_MAP_TEXTURE_SIZE)); 336 | appendLog("The max viewport dims your browser support: " + webgl.getParameter(webgl.MAX_VIEWPORT_DIMS)[0] + " x " + webgl.getParameter(webgl.MAX_VIEWPORT_DIMS)[1]); 337 | } -------------------------------------------------------------------------------- /Lesson8/0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wysaid/WebGL-Lessons/5020e8a9a78491c1181d0b2397d4135b13c14497/Lesson8/0.jpg -------------------------------------------------------------------------------- /Lesson8/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wysaid/WebGL-Lessons/5020e8a9a78491c1181d0b2397d4135b13c14497/Lesson8/1.jpg -------------------------------------------------------------------------------- /Lesson8/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wysaid/WebGL-Lessons/5020e8a9a78491c1181d0b2397d4135b13c14497/Lesson8/2.jpg -------------------------------------------------------------------------------- /Lesson8/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wysaid/WebGL-Lessons/5020e8a9a78491c1181d0b2397d4135b13c14497/Lesson8/3.jpg -------------------------------------------------------------------------------- /Lesson8/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wysaid/WebGL-Lessons/5020e8a9a78491c1181d0b2397d4135b13c14497/Lesson8/4.jpg -------------------------------------------------------------------------------- /Lesson8/demo_scene_roaming.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | simpleDemo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 45 | 46 |
47 |
48 | Initializing... 49 |
50 |
51 | WGE demo操作说明:
52 | 使用鼠标可拖动视角, 53 | 鼠标滚轮可以调节视野角度。 54 | 使用键盘w,a,s,d控制场景漫游, 55 | 空格键为跳跃。 56 |
57 | 58 |
59 |
60 | 61 |
62 |
63 |
64 | 65 |
66 | 67 | 68 | 69 | 70 | 71 | 72 |
73 | 74 | 75 | -------------------------------------------------------------------------------- /Lesson8/demo_scene_roaming.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var maxCacheSpriteNum = 1000; 4 | var maxSpriteNum = 10; 5 | var runningSpriteNum = 3; 6 | 7 | WGE.Sprite3d.ShaderDir = "wge/"; 8 | 9 | 10 | var MyBoundingBox = WGE.Class(WGE.Sprite3d, 11 | { 12 | vshBoundingBox : "varying vec2 vTextureCoord;attribute vec4 aPosition;attribute vec2 aTexCoord;uniform mat4 mvp;void main(){gl_Position = mvp * aPosition;vTextureCoord = aTexCoord;}", 13 | 14 | fshBoundingBox : "precision mediump float;varying vec2 vTextureCoord;uniform sampler2D sTexture;uniform float alpha;void main(){gl_FragColor = texture2D(sTexture,vTextureCoord) * alpha;}", 15 | 16 | alphaName : "alpha", 17 | alpha : 1.0, 18 | 19 | initialize : function(canvas, ctx) 20 | { 21 | WGE.Sprite3d.initialize.call(this, canvas, ctx, this.vshBoundingBox, this.fshBoundingBox); 22 | this.setAlpha(this.alpha); 23 | }, 24 | 25 | setAlpha : function(alpha) 26 | { 27 | var program = this._program; 28 | program.bind(); 29 | program.sendUniform1f(this.alphaName, alpha); 30 | this.alpha = alpha; 31 | } 32 | }); 33 | 34 | var MySprite = WGE.Class(WGE.Sprite3d, 35 | { 36 | x : 0, 37 | y : 0, 38 | z : 0, 39 | dx : 0, 40 | dy : 0, 41 | dz : 0, 42 | 43 | drx : 1, 44 | drz : 1, 45 | dry : 1, 46 | rot : 0, 47 | dRot : 0, 48 | 49 | scaling : 1, 50 | 51 | randomize : function() 52 | { 53 | this.x = Math.random() * 2.0 - 1.0; 54 | this.y = Math.random() * 2.0 - 1.0; 55 | this.z = Math.random(); 56 | this.dx = ((Math.random() - 0.5) * 0.5 + 0.001); 57 | this.dy = (Math.random() - 0.5) * 0.5 + 0.001; 58 | this.dz = Math.random() * 0.5 + 0.001; 59 | 60 | this.drx = (Math.random() - 0.5) * 10 + 0.1; 61 | this.dry = (Math.random() - 0.5) * 10 + 0.1; 62 | this.drz = (Math.random() - 0.5) * 10 + 0.1; 63 | this.dRot = Math.PI * Math.random() * 0.01 + 0.01; 64 | 65 | this.scaling = Math.random() * 10 + 3; 66 | }, 67 | 68 | update : function(dt, boundingX, boundingY, boundingZ) 69 | { 70 | dt *= 60 / 1000; 71 | this.x += this.dx * dt; 72 | this.y += this.dy * dt; 73 | this.z += this.dz * dt; 74 | 75 | if(this.x < -boundingX) 76 | { 77 | this.dx = -this.dx; 78 | this.x = -boundingX; 79 | } 80 | else if(this.x > boundingX) 81 | { 82 | this.dx = -this.dx; 83 | this.x = boundingX; 84 | } 85 | 86 | if(this.y < -boundingY) 87 | { 88 | this.dy = -this.dy; 89 | this.y = -boundingY; 90 | } 91 | else if(this.y > boundingY) 92 | { 93 | this.dy = -this.dy; 94 | this.y = boundingY; 95 | } 96 | 97 | if(this.z < 0) 98 | { 99 | this.dz = -this.dz; 100 | this.z = 0; 101 | } 102 | else if(this.z > boundingZ * 2) 103 | { 104 | this.dz = -this.dz; 105 | this.z = boundingZ * 2; 106 | } 107 | this.rot += this.dRot * dt; 108 | 109 | this._modelMatrix = WGE.mat4Translation(this.x, this.y, this.z); 110 | this.scale(this.scaling, this.scaling, this.scaling); 111 | this.rotate(this.rot, this.drx, this.dry, this.drz); 112 | } 113 | 114 | }); 115 | 116 | var MyGUI = WGE.Class(WGE.GUIInterface, WGE.SceneInterface, 117 | { 118 | context : undefined, 119 | isMouseDown : false, 120 | boundingBox : null, 121 | deltaTime : 10, 122 | 123 | boxSizeX : 100, 124 | boxSizeY : 100, 125 | boxSizeZ : 60, 126 | 127 | zHeight : 10, 128 | dz : 0, //z方向速度 129 | ddz : -0.05, //重力加速度 130 | 131 | isForwarding : false, 132 | isBacking : false, 133 | isGoingLeft : false, 134 | isGoingRight : false, 135 | 136 | sprites : null, 137 | textures : null, 138 | 139 | initialize : function() 140 | { 141 | WGE.GUIInterface.initialize.apply(this, arguments); 142 | WGE.SceneInterface.initialize.call(this, this.canvas.width, this.canvas.height); 143 | 144 | var gl = this.context; 145 | gl.clearColor(0.0, 0.0, 0.0, 1.0); 146 | gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); 147 | this.useBlend(false); 148 | this.eye.data[2] = this.zHeight; 149 | this.updateView(); 150 | }, 151 | 152 | bindFather : function(fatherObj) 153 | { 154 | if(WGE.GUIInterface.bindFather.call(this, fatherObj)); 155 | { 156 | var context = this.canvas.getContext('experimental-webgl'); 157 | if(!context) 158 | { 159 | alert('你的浏览器不支持webgl啊,坟蛋!换chrome或者firefox好吗?'); 160 | } 161 | this.context = context; 162 | return !!this.context; 163 | } 164 | return false; 165 | }, 166 | 167 | initSprites : function() 168 | { 169 | var boundingBox = new MyBoundingBox(this.canvas, this.context); 170 | boundingBox.initSprite(WGE.CubeModel.mesh, 3, WGE.CubeModel.texMesh, 2, WGE.CubeModel.meshIndex, WGE.ID('test3'), {TEXTURE_MIN_FILTER:'LINEAR',TEXTURE_MAG_FILTER:'LINEAR'}); 171 | boundingBox.translateZ(this.boxSizeZ); 172 | boundingBox.scale(this.boxSizeX, this.boxSizeY, this.boxSizeZ); 173 | this.boundingBox = boundingBox; 174 | 175 | var sprites = []; 176 | var textures = []; 177 | var context = this.context; 178 | this.sprites = sprites; 179 | this.textures = textures; 180 | for(var i = 0; i != 5; ++i) 181 | { 182 | var img = WGE.ID('test' + i); 183 | var tex = new WGE.Texture2D(context); 184 | tex.initWithImg(img); 185 | textures.push(tex); 186 | } 187 | 188 | for(var i = 0; i != maxSpriteNum; ++i) 189 | { 190 | var sprite = new MySprite(this.canvas, this.context); 191 | sprite.initSprite(WGE.CubeModel.mesh, 3, WGE.CubeModel.texMesh, 2, WGE.CubeModel.meshIndex, textures[parseInt(Math.random() * textures.length)]); 192 | 193 | sprite.randomize(); 194 | sprites.push(sprite); 195 | } 196 | }, 197 | 198 | addSprite : function(n) 199 | { 200 | var num = n ? n : 50; 201 | 202 | if(runningSpriteNum + num < maxSpriteNum) 203 | { 204 | runningSpriteNum += num; 205 | return ; 206 | } 207 | 208 | if(runningSpriteNum >= maxCacheSpriteNum) 209 | return; 210 | 211 | var newMaxNum = Math.min(maxSpriteNum + num, maxCacheSpriteNum); 212 | 213 | var textures = this.textures; 214 | for(var i = maxSpriteNum; i < newMaxNum; ++i) 215 | { 216 | var sprite = new MySprite(this.canvas, this.context); 217 | sprite.initSprite(WGE.CubeModel.mesh, 3, WGE.CubeModel.texMesh, 2, WGE.CubeModel.meshIndex, textures[parseInt(Math.random() * textures.length)]); 218 | 219 | sprite.randomize(); 220 | this.sprites.push(sprite); 221 | } 222 | runningSpriteNum = maxSpriteNum = newMaxNum; 223 | }, 224 | 225 | decSprite : function(n) 226 | { 227 | runningSpriteNum -= (n ? n : 10); 228 | if(runningSpriteNum < 1) 229 | runningSpriteNum = 1; 230 | }, 231 | 232 | boundingBoxAlphaPlus : function(value) 233 | { 234 | var b = this.boundingBox; 235 | var alpha = Math.max(Math.min(b.alpha + value, 1.0), 0.001); 236 | b.setAlpha(alpha); 237 | }, 238 | 239 | update : function(dt) 240 | { 241 | this.deltaTime = dt; 242 | 243 | var motion = 1; 244 | var needUpdateView = false; 245 | 246 | if(this.isForwarding || this.isBacking || this.isGoingLeft || this.isGoingRight) 247 | { 248 | if(this.isForwarding) 249 | this.goForward(motion); 250 | else if(this.isBacking) 251 | this.goBack(motion); 252 | 253 | if(this.isGoingRight) 254 | this.goRight(motion); 255 | else if(this.isGoingLeft) 256 | this.goLeft(motion); 257 | 258 | this.boundingMyPosition(); 259 | needUpdateView = true; 260 | } 261 | 262 | var eye = this.eye.data; 263 | 264 | if(this.dz || eye[2] > this.zHeight) 265 | { 266 | if(eye[2] < this.zHeight) 267 | { 268 | eye[2] = this.zHeight; 269 | this.dz = 0; 270 | } 271 | else if(eye[2] > this.boxSizeZ * 1.9) 272 | { 273 | eye[2] = this.boxSizeZ * 1.9; 274 | this.dz = 0; 275 | } 276 | else 277 | { 278 | this.dz += this.ddz; 279 | eye[2] += this.dz; 280 | needUpdateView = true; 281 | } 282 | } 283 | 284 | if(needUpdateView) 285 | { 286 | this.updateView(); 287 | } 288 | 289 | var sprites = this.sprites; 290 | for(var i = 0; i < runningSpriteNum; ++i) 291 | { 292 | sprites[i].update(dt, this.boxSizeX, this.boxSizeY, this.boxSizeZ); 293 | } 294 | }, 295 | 296 | render : function() 297 | { 298 | var mvp = WGE.mat4Mul(this.projectionMatrix, this.modelViewMatrix); 299 | var gl = this.context; 300 | gl.clear(this.context.COLOR_BUFFER_BIT); 301 | 302 | this.boundingBox.render(mvp); 303 | 304 | var sprites = this.sprites; 305 | for(var i = 0; i < runningSpriteNum; ++i) 306 | { 307 | sprites[i].render(mvp); 308 | } 309 | }, 310 | 311 | useBlend : function(blend) 312 | { 313 | var gl = this.context; 314 | 315 | if(blend) 316 | { 317 | gl.disable(gl.DEPTH_TEST); 318 | gl.enable(gl.BLEND); 319 | } 320 | else 321 | { 322 | gl.enable(gl.DEPTH_TEST); 323 | gl.disable(gl.BLEND); 324 | } 325 | }, 326 | 327 | mouseDownEvent : function(e, x, y) 328 | { 329 | this.isMouseDown = true; 330 | this.x = x; 331 | this.y = y; 332 | }, 333 | 334 | mouseUpEvent : function(e, x, y) 335 | { 336 | this.isMouseDown = false; 337 | }, 338 | 339 | boundingMyPosition : function() 340 | { 341 | var eye = this.eye.data; 342 | var xBound = this.boxSizeX * 0.95; 343 | var xLim = this.boxSizeX * 0.95 344 | 345 | if(eye[0] < -xBound) 346 | eye[0] = -xLim; 347 | else if(eye[0] > xBound) 348 | eye[0] = xLim; 349 | 350 | var yBound = this.boxSizeY * 0.95; 351 | var yLim = this.boxSizeY * 0.95 352 | 353 | if(eye[1] < -yBound) 354 | eye[1] = -yLim; 355 | else if(eye[1] > yBound) 356 | eye[1] = yLim; 357 | 358 | }, 359 | 360 | mouseMoveEvent : function(e, x, y) 361 | { 362 | if(this.isMouseDown) 363 | { 364 | var lookDir = this.lookDir.data; 365 | var radX = (x - this.x) / 150.0; 366 | var radY = (y - this.y) / 150.0; 367 | 368 | this.turnRight(radX); 369 | this.lookUp(radY); 370 | this.updateView(); 371 | 372 | this.x = x; 373 | this.y = y; 374 | } 375 | }, 376 | 377 | keyDownEvent : function(e) 378 | { 379 | var charCode = e.keyCode || e.which; 380 | if(charCode < 97) 381 | charCode += 32; //大写转小写 382 | 383 | switch(charCode) 384 | { 385 | case 119: //'w' 386 | this.isForwarding = true; 387 | this.isBacking = false; 388 | break; 389 | case 97: //'a' 390 | this.isGoingLeft = true; 391 | this.isGoingRight = false; 392 | break; 393 | case 115: //'s' 394 | this.isBacking = true; 395 | this.isForwarding = false; 396 | break; 397 | case 100: //'d' 398 | this.isGoingRight = true; 399 | this.isGoingLeft = false; 400 | break; 401 | case 32: case 64://space 402 | this.dz = 2.5; 403 | e.stopPropagation(); 404 | e.preventDefault(); 405 | default: 406 | ; 407 | } 408 | }, 409 | 410 | keyUpEvent : function(e) 411 | { 412 | var charCode = e.which || e.keyCode; 413 | if(charCode < 97) 414 | charCode += 32; //大写转小写 415 | switch(charCode) 416 | { 417 | case 119: //'w' 418 | this.isForwarding = false; 419 | break; 420 | case 97: //'a' 421 | this.isGoingLeft = false; 422 | break; 423 | case 115: //'s' 424 | this.isBacking = false; 425 | break; 426 | case 100: //'d' 427 | this.isGoingRight = false; 428 | break; 429 | default: 430 | } 431 | }, 432 | 433 | wheelEvent : function(e, delta) 434 | { 435 | this.lookIn(delta > 0 ? 0.1 : -0.1); 436 | this.resize(this.canvas.width, this.canvas.height); 437 | e.stopPropagation(); 438 | e.preventDefault(); 439 | }, 440 | 441 | resizeEvent : function() 442 | { 443 | if(this.context) 444 | { 445 | var w = this.canvas.width, h = this.canvas.height; 446 | this.projectionMatrix = WGE.makePerspective(Math.PI / 3, w / h, 1, 10000.0); 447 | 448 | this.context.viewport(0, 0, this.canvas.width, this.canvas.height); 449 | } 450 | } 451 | }); -------------------------------------------------------------------------------- /Lesson8/index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | WebGL Lesson8 - wysaid 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |

第八话, 手动实现类似于一般3D游戏的场景漫游, 创建场景以及简单的模型,以及对模型附加纹理

19 |

这一话跟上一话隔了数月之久, 是我偷懒啦。 好不容易到了2015, 继续一边学习一边写这个吧。 希望对你有所帮助。

20 |

废话不多说,进入正题

21 | 22 |

先上个demo瞧瞧, 在demo里面点击鼠标左键可以拖动视野, 当demo窗口获得焦点之后, 使用键盘的w,a,s,d可以控制“自己”游览整个区域

23 |

使用鼠标滚轮还可以有远近视野效果哦。 不过页面也会随之滚动, 你可以在最下方完整的demo里面体验~

24 |
25 | 26 | 27 | 39 |
40 |
41 | 42 | 43 | 44 | 45 | 46 |
47 | 48 |
49 |

怎么样, 有意思吧!

50 |

如果你不曾接触过WebGL, 是否燃起了对WebGL的热情呢?那么真正的进入正题吧!

51 |

经过前面课程的了解, 也许你已经会使用WebGL做简单的图像处理, 绘制一些简单的图样, 或者用矩阵模拟简单的伪3d。 到了本章, 就要充分利用矩阵相关的知识了。

52 |

由于我本人偷懒, 这个demo直接用了之前封装过的WGE写的, 所以这里必须介绍一下WGE了

53 |

如果你到github下载本章教程你会发现lesson8目录下有一个WGE目录, 这个目录里面存放了七个文件, 代码都是比较工整的, 也比较简洁, 需要你去看一下哦, 因为我太懒了, 所以……就不一一解释了

54 |

本章用到的最重要的就是wgeScene.js这个文件了, 所以我只讲一下这个文件

55 | 该文件内容如下: 56 | 57 |

文件分割线 开始

58 | 59 |
 60 | "use strict";
 61 | /*
 62 |  * wgeScene.js
 63 |  *
 64 |  *  Created on: 2014-8-13
 65 |  *      Author: Wang Yang
 66 |  *        blog: http://blog.wysaid.org
 67 |  */
 68 | 
 69 |  /*
 70 |      简介: wgeScene提供WebGL场景漫游。
 71 |  */
 72 | 
 73 | //WGE.SceneInterface 并不提供多余的渲染方式等,仅维护模型视点矩阵以及投影矩阵
 74 | //所有的Sprite或者其他东西只要在WGE.Scene提供的世界观基础上变换,
 75 | //可保证整个世界的一致性。
 76 | 
 77 | //默认使用透视投影,如果需要平行投影请自己
 78 | //更多动作如跳跃等请继承此类实现。
 79 | 
 80 | //特别注意! wgeScene 使用 xOy 平面作为漫游地面!z正方向为向上方向
 81 | WGE.SceneInterface = WGE.Class(
 82 | {
 83 | 	modelViewMatrix : null, //4x4 模型视点矩阵
 84 | 	projectionMatrix : null, //4x4 投影矩阵
 85 | 
 86 | 	eye : null,    // 当前视点位置(Vec3)。 默认(0, 0, 0)
 87 | 
 88 | 	// lookDir 为观测方向,默认(0, 1, 0),x,y两个分量的模为1。 注: 不是观测位置。
 89 | 	// 如果需要手动修改lookDir 请保证dirLen大小与dir的x,y两个分量的模相等, 否则将影响移动速度。
 90 | 	lookDir : null,
 91 | 	dirLen : 1000,
 92 | 
 93 | 	fovyRad : Math.PI/3,  //视景体的视野的角度(弧度)
 94 | 	zNear : 1.0,          //透视投影近裁剪面
 95 | 	zFar : 10000.0,       //透视投影远裁剪面
 96 | 
 97 | 	initialize : function(w, h)
 98 | 	{
 99 | 		this.eye = new WGE.Vec3(0, 0, 0);
100 | 		this.lookDir = new WGE.Vec3(0, 1000, 0);
101 | 		this.updateView();
102 | 		if(!(w && h))
103 | 		{
104 | 			w = 800;
105 | 			h = 600;
106 | 		}
107 | 		this.resize(w, h);
108 | 	},
109 | 
110 | 	//如果有任何移动旋转或者调整视野操作,
111 | 	//之后必须调用updateView 函数 更新模型视点矩阵
112 | 	updateView : function()
113 | 	{
114 | 		var eye = this.eye.data, lookDir = this.lookDir.data;
115 | 		var centerX = eye[0] + lookDir[0], centerY = eye[1] + lookDir[1];
116 | 		var len = Math.sqrt(centerX*centerX + centerY*centerY);
117 | 		var tmp = -lookDir[2] / len;
118 | 		var dirBackX = centerX * tmp, dirBackY = centerY * tmp;
119 | 		this.modelViewMatrix = 
120 | 		    WGE.makeLookAt(eye[0], eye[1], eye[2], centerX, centerY, lookDir[2], dirBackX, dirBackY, len);
121 | 	},
122 | 
123 | 	//向右转(弧度), 负值将向左转.
124 | 	turnRight : function(rad)
125 | 	{
126 | 		var d = this.lookDir.data;
127 | 		var v = WGE.mat2MulVec2(WGE.mat2Rotation(rad), this.lookDir).data;
128 | 		d[0] = v[0];
129 | 		d[1] = v[1];
130 | 	},
131 | 
132 | 	//向右旋转到(弧度), 以Y轴正方向为起始方向。
133 | 	turnRightTo : function(rad)
134 | 	{
135 | 		var d = this.lookDir.data;
136 | 		var v = WGE.mat2MulVec2(WGE.mat2Rotation(rad), new WGE.Vec3(0, 1, 0)).data;
137 | 		d[0] = v[0];
138 | 		d[1] = v[1];
139 | 	},
140 | 
141 | 	//向上观察, motion计算关系:
142 | 	//向上弧度计算公式为 arctan(tan("当前向上弧度") + motion) - "当前向上弧度"
143 | 	lookUp : function(motion)
144 | 	{
145 | 		this.lookDir.data[2] += motion * this.dirLen;
146 | 		var lookUpMax = this.dirLen * 3.732; //tan(PI / 75);
147 | 		if(this.lookDir.data[2] > lookUpMax) 
148 | 		this.lookDir.data[2] = lookUpMax;
149 | 		else if(this.lookDir.data[2] < -lookUpMax)
150 | 			this.lookDir.data[2] = -lookUpMax;
151 | 	},
152 | 
153 | 	//向上观察到(弧度),直接仰视到所看弧度
154 | 	//范围[-PI/2.4, PI/2.4] ,约正负75度角
155 | 	lookUpTo : function(rad)
156 | 	{
157 | 		if(rad > Math.PI / 2.4)
158 | 			rad = Math.PI / 2.4;
159 | 		else if(rad < -Math.PI / 2.4)
160 | 			rad = -Math.PI / 2.4;
161 | 		this.lookDir.data[2] = Math.tan(rad) * this.dirLen;
162 | 	},
163 | 
164 | 	//视野角度(弧度), 
165 | 	lookIn : function(rad)
166 | 	{
167 | 		this.fovyRad += rad;
168 | 		if(this.fovyRad < Math.PI / 10)
169 | 			this.fovyRad = Math.PI / 10;
170 | 		else if(this.fovyRad > Math.PI / 2.4)
171 | 			this.fovyRad = Math.PI / 2.4;
172 | 	},
173 | 
174 | 	//视野角度, 范围[PI/10, PI/2.4]
175 | 	lookInTo : function(rad)
176 | 	{
177 | 		if(rad < Math.PI / 10)
178 | 			rad = Math.PI / 10;
179 | 		else if(rad > Math.PI / 2.4)
180 | 			rad = Math.PI / 2.4;
181 | 		this.fovyRad = rad;
182 | 	},
183 | 
184 | 	//向前移动
185 | 	goForward : function(motion)
186 | 	{
187 | 		var m = motion / this.dirLen;
188 | 		this.eye.data[0] += this.lookDir.data[0] * m;
189 | 		this.eye.data[1] += this.lookDir.data[1] * m;
190 | 	},
191 | 
192 | 	//向后移动
193 | 	goBack : function(motion)
194 | 	{
195 | 		var m = motion / this.dirLen;
196 | 		this.eye.data[0] -= this.lookDir.data[0] * m;
197 | 		this.eye.data[1] -= this.lookDir.data[1] * m;
198 | 	},
199 | 
200 | 	//向左移动
201 | 	goLeft : function(motion)
202 | 	{
203 | 		var m = motion / this.dirLen;
204 | 		this.eye.data[0] -= this.lookDir.data[1] * m;
205 | 		this.eye.data[1] += this.lookDir.data[0] * m;
206 | 	},
207 | 
208 | 	//向右移动
209 | 	goRight : function(motion)
210 | 	{
211 | 		var m = motion / this.dirLen;
212 | 		this.eye.data[0] += this.lookDir.data[1] * m;
213 | 		this.eye.data[1] -= this.lookDir.data[0] * m;
214 | 	},
215 | 
216 | 	//默认透视投影
217 | 	resize : function(w, h)
218 | 	{
219 | 		this.projectionMatrix = WGE.makePerspective(this.fovyRad, w / h, this.zNear, this.zFar);
220 | 	}
221 | 
222 | });
223 | 
224 | 225 |

文件分割线 完

226 |

你可以看到, 本文件定义了一个场景漫游接口, 使用 xOy 平面作为漫游地面!z正方向为向上方向, 默认分辨率大小为800*600, 如果想改变这个分辨率需要自己传入分辨率大小, 当场景大小有变化的时候也需要手动调用resize修改场景大小。 如果不想使用透视投影, 则需要重写本类, 覆盖这个resize方法

227 |

wgeScene提供了简单的场景漫游矩阵运算, 上面注释都写的很清楚, 我就不罗嗦了(其实还是偷懒吧……大概)

228 |

下面的一些描述并不专业, 也不一定准确, 只是按个人理解口头描述一下, 觉得有什么地方写错了希望多多包涵, 给我发个邮件, 以便于我更正, thanks

229 |

场景漫游需要一个全局的模型视点矩阵, 和一个投影矩阵, 其他需要保存的还有:

230 | 231 |

视点位置: 就是场景漫游中, 你 “自己”所在的位置, 通常这个位置并不是你场景中“角色”所在的位置(这个后面有机会再说吧), 而是一个假想的屏幕前的你, 所在的位置(如果是FPS游戏或者启用角色视角之类的话, 那么角色所在的位置和“你”的位置就都是这个), 这个变量这里用一个vec3表示, 本次demo中使用w,a,s,d即可移动视点在xOy平面上的位置, 通过空格键即可控制跳跃(视点的z值)

232 | 233 |

观测方向: 在三维坐标平面, 你在某个位置, 你要看到东西, 你还需要有个观测方向, 比如你是向上看的, 还是向前后左右看的, 抑或是向下看的。这个方向也用一个vec3表示(这个一定表示的是假想的屏幕前的“你”的观测方向。 比如你玩某些3d网游的时候, 可以看大角色是朝向前方, 但是角色不用转身, 你一样可以调整视角观察到角色身后的东西)

234 | 235 |

观测向上方向: 这只是一个辅助值, 用以确定你观察的方向。 假如你在某点 (x0,y0,z0), 观察点为(x1,y1,z1), 假设这个点上的物体为一个电视, 你站着看到正确的图像, ok。 但是你还可以躺着看, 这时候你看到的电视是横着的, 你还可以倒立着看, 对吧。 那么你看到的电视就是倒着的了。 而你自己的位置和观测点的位置没有任何变化, 变化的只是你观测时选择的向上方向(为什么叫“向上方向”呢, 因为OpenGL默认使用向上方向来定下这个变化, 如果当初制定标准时选择左右或者下都是没问题的, 既然标准如此, 我们就选择向上吧!) 在这里, 我们选择的向上方向当然为xOy平面的上方, 当你平视前方的时候, 这个值为z轴的正方向。 当你抬头仰视或者俯视的时候, 这个值必须通过仰角计算出来。 具体如何做你可以看看 wgeScene.js里面的 lookup函数和updateView 函数的简单实现。 还需要你努力去理解 @_@

236 | 237 |

模型视点矩阵: 本demo使用仿 gluLookAt的方法实现, 将前面说到的信息汇总为一个模型视点矩阵, 具体参见 wgeScene.js 里面的updateView 函数。最重要的一步就是我们必须要通过视点位置与观测点位置形成的方向向量, 计算出两点之间基于xOy平面的仰角, 然后算出向上的仰望方向, 否则无法实现俯仰观测

238 | 239 |

透视投影: 基本不用考虑平行投影了, 除非你只是展示一个模型而不是以类似于游戏的形式漫游场景。 本次demo用的是仿gluPerspective的方法实现, 可以参见 wgeScene.js 的resize 函数, 其中远裁剪面使用了一个较大的值, 然后视野角度作为变量存在, 这个"视野角度"(我也不知道是不是该这么叫)的作用可以比喻为一个望远镜, 通过改变"视野角度", 可以让我们以远近不同的方式观察物体, 就像我们用望远镜观察远处的物体一样。 这个比喻比较扯淡,但是一般人比较好理解。实际理论应该类似于凸透镜成像, 不同度数(曲率)的凸透镜成清晰像的大小是不同的, 在可视区域看到的成像就有了大小或者说远近的感觉。 好吧, 就当我在扯淡

240 | 241 |

最后, 使用模型视点矩阵左乘投影矩阵, 便得到了我们的ModelViewProjection(mvp)矩阵了, 将mvp应用到整个场景的变化中, 我们就可以进行本次的场景漫游了!

242 | 243 | 244 | 245 |
246 |

好的, 本次讲解比较简单, 但是如果你是初学者的话, 需要理解的东西还是蛮多的, 如果你想学, 又觉得本次的代码比较坑, 就……在心里默默数神兽某马吧~呼呼

247 |

第八期教程到此为止,请关注lesson 9。系列教程地址: http://webgl-lesson.wysaid.org

248 | 249 | 250 | -------------------------------------------------------------------------------- /Lesson8/wge/cube.js: -------------------------------------------------------------------------------- 1 | /* 2 | * cube.js 3 | * 4 | * Created on: 2014-8-13 5 | * Author: Wang Yang 6 | * blog: http://blog.wysaid.org 7 | */ 8 | 9 | //好吧,这个模型是我(wy)手动创建的。 木有版权,哈哈哈 10 | 11 | WGE.CubeModel = {}; 12 | WGE.CubeModel.mesh = new Float32Array( 13 | [1, 1, 1, -1, 1, 1, -1,-1, 1, 1,-1, 1, 14 | 1, 1, 1, 1,-1, 1, 1,-1,-1, 1, 1,-1, 15 | 1, 1, 1, 1, 1,-1, -1, 1,-1, -1, 1, 1, 16 | -1, 1, 1, -1, 1,-1, -1,-1,-1, -1,-1, 1, 17 | -1,-1,-1, 1,-1,-1, 1,-1, 1, -1,-1, 1, 18 | 1,-1,-1, -1,-1,-1, -1, 1,-1, 1, 1,-1]); 19 | 20 | WGE.CubeModel.meshDataSize = 3; //每个点包含三个分量。 21 | 22 | WGE.CubeModel.meshIndex = new Uint16Array( 23 | [0, 1, 2, 0, 2, 3, 24 | 4, 5, 6, 4, 6, 7, 25 | 8, 9,10, 8,10,11, 26 | 12,13,14, 12,14,15, 27 | 16,17,18, 16,18,19, 28 | 20,21,22, 20,22,23]); 29 | 30 | WGE.CubeModel.texMesh = new Float32Array( 31 | [1, 1, 0, 1, 0, 0, 1, 0, 32 | 0, 1, 0, 0, 1, 0, 1, 1, 33 | 1, 0, 1, 1, 0, 1, 0, 0, 34 | 1, 1, 0, 1, 0, 0, 1, 0, 35 | 0, 0, 1, 0, 1, 1, 0, 1, 36 | 0, 0, 1, 0, 1, 1, 0, 1]); 37 | 38 | WGE.CubeModel.texMeshDataSize = 2; 39 | 40 | WGE.CubeModel.normals = new Float32Array( 41 | [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 42 | 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 43 | 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 44 | -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 45 | 0,-1, 0, 0,-1, 0, 0,-1, 0, 0,-1, 0, 46 | 0, 0,-1, 0, 0,-1, 0, 0,-1, 0, 0,-1]); 47 | 48 | WGE.CubeModel.normalsDataSize = 3; -------------------------------------------------------------------------------- /Lesson8/wge/wgeGUI.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | * wgeGUI.js 4 | * 5 | * Created on: 2014-6-23 6 | * Author: Wang Yang 7 | * blog: http://blog.wysaid.org 8 | */ 9 | 10 | /* 11 | 简介: 提供简单的界面接口. 12 | */ 13 | 14 | WGE.GUIInterface = WGE.Class( 15 | { 16 | boundingWidth : undefined, 17 | boundingHeight : undefined, 18 | canvas : undefined, 19 | father : undefined, 20 | fatherWidthName : ['width', 'clientWidth', 'offsetWidth'], 21 | fatherHeightName : ['height', 'clientHeight', 'offsetHeight'], 22 | resizeEvent : null, //Event由子类或者用户设置 23 | mouseMoveEvent : null, 24 | mouseDownEvent : null, 25 | mouseUpEvent : null, 26 | mouseClickEvent : null, 27 | mouseDBLClickEvent : null, 28 | mouseOverEvent : null, 29 | mouseOutEvent : null, 30 | wheelEvent : null, 31 | keyDownEvent : null, 32 | keyUpEvent : null, 33 | keypressEvent : null, 34 | 35 | _animationRequest : null, 36 | startTime : 0, 37 | lastTime : 0, 38 | nowTime : 0, 39 | 40 | _forceAutoResize : false, //强制resize,设置标记后将在每一帧检测是否需要resize 41 | 42 | //将在gui 重新绑定father或者release时解除对于原有father的绑定。 43 | _events : null, 44 | 45 | initialize : function(fatherObj) 46 | { 47 | this.setupEvents(); 48 | this.bindFather(fatherObj); 49 | }, 50 | 51 | setupEvents : function() 52 | { 53 | //Mark : onresize 添加至此无效。 54 | this._events = { 55 | 'mousemove' : this.onmousemove.bind(this), 56 | 'click' : this.onclick.bind(this), 57 | 'mousedown' : this.onmousedown.bind(this), 58 | 'mouseup' : this.onmouseup.bind(this), 59 | 'dblclick' : this.ondblclick.bind(this), 60 | 'mouseover' : this.onmouseover.bind(this), 61 | 'mouseout' : this.onmouseout.bind(this), 62 | 'keydown' : this.onkeydown.bind(this), 63 | 'keypress' : this.onkeypress.bind(this), 64 | 'keyup' : this.onkeyup.bind(this), 65 | //wheel 方法在firefox中不受支持。 66 | 'wheel' : this.onwheel.bind(this), 67 | }; 68 | 69 | if(document.body.onwheel === undefined) 70 | { 71 | this._events['mousewheel'] = this._events['wheel']; 72 | this._events['wheel'] = undefined; 73 | } 74 | }, 75 | 76 | release : function() 77 | { 78 | this.canvas = undefined; 79 | if(this.father && this.father.removeEventListener) 80 | { 81 | for(var i in _events) 82 | { 83 | this.father.removeEventListener(i, _events[i]); 84 | } 85 | } 86 | this.father = undefined; 87 | }, 88 | 89 | //设置在运行过程中,强制对界面长宽进行检测和刷新。 90 | //如果您已经手动将onresize 事件添加到 body的onresize属性中,则没必要启用。 91 | forceAutoResize : function(flag) 92 | { 93 | this._forceAutoResize = flag; 94 | }, 95 | 96 | isStarted : function() 97 | { 98 | return !!this._animationRequest; 99 | }, 100 | 101 | start : function() 102 | { 103 | if(this._animationRequest) 104 | { 105 | console.warn("wgeGUI is already started!"); 106 | return; 107 | } 108 | // this.onresize(); 109 | this.startTime = Date.now(); 110 | this.lastTime = this.startTime; 111 | this._animationRequest = requestAnimationFrame(this._run.bind(this)); 112 | }, 113 | 114 | stop : function() 115 | { 116 | if(this._animationRequest) 117 | { 118 | cancelAnimationFrame(this._animationRequest); 119 | this._animationRequest = null; 120 | } 121 | }, 122 | 123 | _run : function() 124 | { 125 | if(this._forceAutoResize) 126 | { 127 | this.onresize(); 128 | } 129 | 130 | this.nowTime = Date.now(); 131 | var deltaTime = this.nowTime - this.lastTime; 132 | 133 | this.update(deltaTime); 134 | this.render(deltaTime); 135 | 136 | this.lastTime = this.nowTime; 137 | 138 | //如果在_run函数执行期间调用过stop,则不再继续请求frame. 139 | if(this._animationRequest) 140 | this._animationRequest = requestAnimationFrame(this._run.bind(this)); 141 | }, 142 | 143 | //update和render 由用户自定义, 144 | //类型为函数,包含一个参数表示两次调用之间的间隔时间(ms) 145 | update : function(deltaTime) 146 | { 147 | 148 | }, 149 | 150 | render : function(deltaTime) 151 | { 152 | 153 | }, 154 | 155 | //由于canvas元素不支持部分事件(如根据stype属性的百分比宽高设置实际像素宽高), 156 | //需要将它绑定到一个支持此类事件的DOM上,如body, div等 157 | //画面将占满整个father元素,且根据father元素自适应 158 | bindFather : function(fatherObj, width, height) 159 | { 160 | if(typeof fatherObj != 'object') 161 | { 162 | return false; 163 | } 164 | 165 | this.release(); 166 | 167 | if(width && height) 168 | { 169 | this.boundingWidth = width; 170 | this.boundingHeight = height; 171 | } 172 | 173 | this.canvas = WGE.CE('canvas'); 174 | fatherObj.appendChild(this.canvas); 175 | this.father = fatherObj; 176 | 177 | for(var eventName in this._events) 178 | { 179 | fatherObj.addEventListener(eventName, this._events[eventName]); 180 | } 181 | 182 | var widthName = null, heightName = null; 183 | 184 | for(var i in this.fatherWidthName) 185 | { 186 | if(typeof fatherObj[this.fatherWidthName[i]] == 'number') 187 | { 188 | widthName = this.fatherWidthName[i]; 189 | break; 190 | } 191 | } 192 | 193 | this.fatherWidthName = widthName; 194 | 195 | for(var i in this.fatherHeightName) 196 | { 197 | if(typeof fatherObj[this.fatherHeightName[i]] == 'number') 198 | { 199 | heightName = this.fatherHeightName[i]; 200 | break; 201 | } 202 | } 203 | 204 | this.fatherHeightName = heightName; 205 | 206 | this.onresize(); 207 | return true; 208 | }, 209 | 210 | //经过测试,发现大部分元素不支持onresize, 建议手动添加至body中。 211 | onresize : function(e) 212 | { 213 | var cvs = this.canvas, father = this.father; 214 | 215 | var width = this.boundingWidth || father[this.fatherWidthName]; 216 | var height = this.boundingHeight || father[this.fatherHeightName]; 217 | 218 | //当 forceAutoResize 启用时,可以有效减少事件调用。 219 | if(cvs.width != width || cvs.height != height) 220 | { 221 | cvs.width = width; 222 | cvs.height = height; 223 | if(typeof this.resizeEvent == 'function') 224 | this.resizeEvent(e); 225 | } 226 | }, 227 | 228 | onmousemove : function(e) 229 | { 230 | if(this.mouseMoveEvent) 231 | { 232 | this.mouseMoveEvent(e, e.offsetX || e.layerX, e.offsetY || e.layerY); 233 | } 234 | }, 235 | 236 | onclick : function(e) 237 | { 238 | if(this.mouseClickEvent) 239 | { 240 | this.mouseClickEvent(e, e.offsetX || e.layerX, e.offsetY || e.layerY); 241 | } 242 | }, 243 | 244 | onmousedown : function(e) 245 | { 246 | if(this.mouseDownEvent) 247 | { 248 | this.mouseDownEvent(e, e.offsetX || e.layerX, e.offsetY || e.layerY); 249 | } 250 | }, 251 | 252 | onmouseup : function(e) 253 | { 254 | if(this.mouseUpEvent) 255 | { 256 | this.mouseUpEvent(e, e.offsetX || e.layerX, e.offsetY || e.layerY); 257 | } 258 | }, 259 | 260 | ondblclick : function(e) 261 | { 262 | if(this.mouseDBLClickEvent) 263 | { 264 | this.mouseDBLClickEvent(e, e.offsetX || e.layerX, e.offsetY || e.layerY); 265 | } 266 | }, 267 | 268 | onmouseover : function(e) 269 | { 270 | if(this.mouseOverEvent) 271 | { 272 | this.mouseOverEvent(e, e.offsetX || e.layerX, e.offsetY || e.layerY); 273 | } 274 | }, 275 | 276 | onmouseout : function(e) 277 | { 278 | if(this.mouseOutEvent) 279 | { 280 | this.mouseOutEvent(e, e.offsetX || e.layerX, e.offsetY || e.layerY); 281 | } 282 | }, 283 | 284 | onwheel : function(e) 285 | { 286 | if(this.wheelEvent) 287 | this.wheelEvent(e, e.deltaY || e.wheelDelta); 288 | }, 289 | 290 | //注: 如果div元素无法响应key事件,则很可能是因为div无法获得焦点,请设置tabindex 291 | onkeydown : function() 292 | { 293 | if(this.keyDownEvent) 294 | this.keyDownEvent.apply(this, arguments); 295 | }, 296 | 297 | onkeypress : function() 298 | { 299 | if(this.keypressEvent) 300 | this.keypressEvent.apply(this, arguments); 301 | }, 302 | 303 | onkeyup : function() 304 | { 305 | if(this.keyUpEvent) 306 | this.keyUpEvent.apply(this, arguments); 307 | } 308 | 309 | }); 310 | 311 | /* 312 | 313 | ##使用方式 314 | 315 | 例: 316 | 317 | //此GUI将占满整个屏幕,并随机在屏幕中绘制小红点 318 | //如果鼠标按下的话,小红点将绘制到鼠标点击的位置。 319 | 320 | var myGUI = WGE.Class(WGE.GUIInterface, 321 | { 322 | context : undefined, 323 | x : 0, 324 | y : 0, 325 | isMouseDown : false, 326 | 327 | bindFather : function(fatherObj) 328 | { 329 | if(WGE.GUIInterface.bindFather.call(this, fatherObj)); 330 | { 331 | this.context = this.canvas.getContext('2d'); 332 | return !!this.context; 333 | } 334 | return false; 335 | }, 336 | 337 | update : function() 338 | { 339 | if(!this.isMouseDown) 340 | { 341 | this.x = Math.random() * this.canvas.width; 342 | this.y = Math.random() * this.canvas.height; 343 | } 344 | }, 345 | 346 | render : function() 347 | { 348 | var ctx = this.context; 349 | var cvs = this.canvas; 350 | ctx.clearRect(0, 0, cvs.width, cvs.height); 351 | this.context.fillStyle = "#f00"; 352 | ctx.fillRect(this.x, this.y, 100, 100); 353 | ctx.fillText("click me!", 10, 10); 354 | }, 355 | 356 | mouseDownEvent : function(e) 357 | { 358 | this.isMouseDown = true; 359 | this.x = e.x || e.offsetX; 360 | this.y = e.y || e.offsetY; 361 | }, 362 | 363 | mouseUpEvent : function(e) 364 | { 365 | this.isMouseDown = false; 366 | }, 367 | 368 | mouseMoveEvent : function(e) 369 | { 370 | if(this.isMouseDown) 371 | { 372 | this.x = e.offsetX || e.layerX; 373 | this.y = e.offsetY || e.layerY; 374 | } 375 | } 376 | }); 377 | 378 | //// 调用代码如下: 379 | 380 | var gui = new myGUI(document.body); 381 | 382 | //下面两句都是使整个ui大小跟随父元素变化,推荐前者。嫌麻烦或者跟已有代码有冲突(比如body的onresize有别的代码会随时更改)写成后者也没关系。 383 | document.body.setAttribute("onresize", "gui.onresize(event);"); //较好 384 | //gui.forceAutoResize(true); //这一句和上一句功能类似,这种方法可保证正确性 385 | 386 | gui.start(); 387 | 388 | //// 怎么样,简单吧?! 389 | 390 | */ -------------------------------------------------------------------------------- /Lesson8/wge/wgeScene.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | * wgeScene.js 4 | * 5 | * Created on: 2014-8-13 6 | * Author: Wang Yang 7 | * blog: http://blog.wysaid.org 8 | */ 9 | 10 | /* 11 | 简介: wgeScene提供WebGL场景漫游。 12 | */ 13 | 14 | //WGE.SceneInterface 并不提供多余的渲染方式等,仅维护模型视点矩阵以及投影矩阵 15 | //所有的Sprite或者其他东西只要在WGE.Scene提供的世界观基础上变换, 16 | //可保证整个世界的一致性。 17 | 18 | //默认使用透视投影,如果需要平行投影请自己 19 | //更多动作如跳跃等请继承此类实现。 20 | 21 | //特别注意! wgeScene 使用 xOy 平面作为漫游地面!z正方向为向上方向 22 | WGE.SceneInterface = WGE.Class( 23 | { 24 | modelViewMatrix : null, //4x4 模型视点矩阵 25 | projectionMatrix : null, //4x4 投影矩阵 26 | 27 | eye : null, // 当前视点位置(Vec3)。 默认(0, 0, 0) 28 | 29 | // lookDir 为观测方向,默认(0, 1, 0),x,y两个分量的模为1。 注: 不是观测位置。 30 | // 如果需要手动修改lookDir 请保证dirLen大小与dir的x,y两个分量的模相等, 否则将影响移动速度。 31 | lookDir : null, 32 | dirLen : 1000, 33 | 34 | fovyRad : Math.PI/3, //视景体的视野的角度(弧度) 35 | zNear : 1.0, //透视投影近裁剪面 36 | zFar : 10000.0, //透视投影远裁剪面 37 | 38 | initialize : function(w, h) 39 | { 40 | this.eye = new WGE.Vec3(0, 0, 0); 41 | this.lookDir = new WGE.Vec3(0, 1000, 0); 42 | this.updateView(); 43 | if(!(w && h)) 44 | { 45 | w = 800; 46 | h = 600; 47 | } 48 | this.resize(w, h); 49 | }, 50 | 51 | //如果有任何移动旋转或者调整视野操作, 52 | //之后必须调用updateView 函数 更新模型视点矩阵 53 | updateView : function() 54 | { 55 | var eye = this.eye.data, lookDir = this.lookDir.data; 56 | var centerX = eye[0] + lookDir[0], centerY = eye[1] + lookDir[1]; 57 | var len = Math.sqrt(centerX*centerX + centerY*centerY); 58 | var tmp = -lookDir[2] / len; 59 | var dirBackX = centerX * tmp, dirBackY = centerY * tmp; 60 | this.modelViewMatrix = WGE.makeLookAt(eye[0], eye[1], eye[2], centerX, centerY, lookDir[2], dirBackX, dirBackY, len); 61 | }, 62 | 63 | //向右转(弧度), 负值将向左转. 64 | turnRight : function(rad) 65 | { 66 | var d = this.lookDir.data; 67 | var v = WGE.mat2MulVec2(WGE.mat2Rotation(rad), this.lookDir).data; 68 | d[0] = v[0]; 69 | d[1] = v[1]; 70 | }, 71 | 72 | //向右旋转到(弧度), 以Y轴正方向为起始方向。 73 | turnRightTo : function(rad) 74 | { 75 | var d = this.lookDir.data; 76 | var v = WGE.mat2MulVec2(WGE.mat2Rotation(rad), new WGE.Vec3(0, 1, 0)).data; 77 | d[0] = v[0]; 78 | d[1] = v[1]; 79 | }, 80 | 81 | //向上观察, motion计算关系: 82 | //向上弧度计算公式为 arctan(tan("当前向上弧度") + motion) - "当前向上弧度" 83 | lookUp : function(motion) 84 | { 85 | this.lookDir.data[2] += motion * this.dirLen; 86 | var lookUpMax = this.dirLen * 3.732; //tan(PI / 75); 87 | if(this.lookDir.data[2] > lookUpMax) 88 | this.lookDir.data[2] = lookUpMax; 89 | else if(this.lookDir.data[2] < -lookUpMax) 90 | this.lookDir.data[2] = -lookUpMax; 91 | }, 92 | 93 | //向上观察到(弧度),直接仰视到所看弧度 94 | //范围[-PI/2.4, PI/2.4] ,约正负75度角 95 | lookUpTo : function(rad) 96 | { 97 | if(rad > Math.PI / 2.4) 98 | rad = Math.PI / 2.4; 99 | else if(rad < -Math.PI / 2.4) 100 | rad = -Math.PI / 2.4; 101 | this.lookDir.data[2] = Math.tan(rad) * this.dirLen; 102 | }, 103 | 104 | //视野角度(弧度), 105 | lookIn : function(rad) 106 | { 107 | this.fovyRad += rad; 108 | if(this.fovyRad < Math.PI / 10) 109 | this.fovyRad = Math.PI / 10; 110 | else if(this.fovyRad > Math.PI / 2.4) 111 | this.fovyRad = Math.PI / 2.4; 112 | }, 113 | 114 | //视野角度, 范围[PI/10, PI/2.4] 115 | lookInTo : function(rad) 116 | { 117 | if(rad < Math.PI / 10) 118 | rad = Math.PI / 10; 119 | else if(rad > Math.PI / 2.4) 120 | rad = Math.PI / 2.4; 121 | this.fovyRad = rad; 122 | }, 123 | 124 | //向前移动 125 | goForward : function(motion) 126 | { 127 | var m = motion / this.dirLen; 128 | this.eye.data[0] += this.lookDir.data[0] * m; 129 | this.eye.data[1] += this.lookDir.data[1] * m; 130 | }, 131 | 132 | //向后移动 133 | goBack : function(motion) 134 | { 135 | var m = motion / this.dirLen; 136 | this.eye.data[0] -= this.lookDir.data[0] * m; 137 | this.eye.data[1] -= this.lookDir.data[1] * m; 138 | }, 139 | 140 | //向左移动 141 | goLeft : function(motion) 142 | { 143 | var m = motion / this.dirLen; 144 | this.eye.data[0] -= this.lookDir.data[1] * m; 145 | this.eye.data[1] += this.lookDir.data[0] * m; 146 | }, 147 | 148 | //向右移动 149 | goRight : function(motion) 150 | { 151 | var m = motion / this.dirLen; 152 | this.eye.data[0] += this.lookDir.data[1] * m; 153 | this.eye.data[1] -= this.lookDir.data[0] * m; 154 | }, 155 | 156 | //默认透视投影 157 | resize : function(w, h) 158 | { 159 | this.projectionMatrix = WGE.makePerspective(this.fovyRad, w / h, this.zNear, this.zFar); 160 | } 161 | 162 | }); -------------------------------------------------------------------------------- /Lesson8/wge/wgeSprite3d.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | * wgeSprite3d.js for webgl 4 | * 5 | * Created on: 2014-8-6 6 | * Author: Wang Yang 7 | * Blog: http://blog.wysaid.org 8 | */ 9 | 10 | /* 11 | 本类只提供基础功能,需要更强大的功能需要自己继承定制。 12 | */ 13 | 14 | //提示: zIndex 在Sprite3d中无意义,故不提供。开启OpenGL的深度测试即可。 15 | // 不直接提供 pos等参数,如果需要,则继承此类,并记录这些参数以使用。 16 | //若要按远近先后混合,请在渲染前按远近顺序排序。 17 | 18 | //同名参数含义请参考Sprite2d,为了减少篇幅,这里不再赘述 19 | //内部包含set方法的参数,请使用set方法,不要直接修改参数值。 20 | 21 | //与Sprite2d 的默认坐标系不同! 22 | 23 | WGE.Sprite3d = WGE.Class( 24 | { 25 | canvas : null, 26 | texture : null, 27 | 28 | renderMethod : null, //渲染方式,默认为 gl.TRIANGLES. 29 | 30 | _modelMatrix : null, //4x4模型矩阵,内含sprite3d所包含模型所进行的所有矩阵转换操作。 31 | 32 | _hotspot: null, 33 | 34 | _program : null, 35 | _context : null, 36 | _textureRelease : true, 37 | 38 | _meshVBO : null, 39 | _meshIndexVBO : null, 40 | _textureVBO : null, 41 | _vboNoRelease : false, 42 | _meshIndexSize : null, 43 | _vertexDataType : null, //顶点数据类型,默认为 gl.FLOAT 44 | _meshIndexDataType : null, //索引数据类型,默认为 gl.UNSIGNED_SHORT 45 | _meshDataSize : null, //每个顶点所包含的分量,可选值为1,2,3,4,默认4 46 | _texDataSize : null, //每个纹理坐标所包含的分量,同上。 47 | //定义attrib location 48 | _vertAttribLoc : 0, 49 | _texAttribLoc : 1, 50 | 51 | //缓存一些可能用到的location 52 | _mvpLoc : null, 53 | _textureLoc : null, 54 | 55 | //vsh和fsh表示自写的shader代码,可以自行定制。 56 | initialize : function(canvas, ctx, vsh, fsh) 57 | { 58 | // this.pos = new WGE.Vec3(0, 0, 0); 59 | // this.scaling = new WGE.Vec3(1, 1, 1); 60 | // this._hotspot = new WGE.Vec3(0, 0, 0); 61 | this._modelMatrix = WGE.mat4Identity(); 62 | 63 | this.canvas = canvas; 64 | if(!canvas) 65 | { 66 | console.error("Invalid Params while creating WGE.Sprite3d!"); 67 | } 68 | var gl = ctx || WGE.webgl || this.canvas.getContext('webgl') || this.canvas.getContext('experimental-webgl'); 69 | this._context = gl; 70 | 71 | if(!this.renderMethod) 72 | { 73 | this.renderMethod = gl.TRIANGLES; 74 | } 75 | 76 | if(vsh && fsh) 77 | { 78 | this._initProgram(vsh, fsh); 79 | } 80 | else 81 | { 82 | if(!WGE.Sprite3d.VertexShader) 83 | WGE.Sprite3d.VertexShader = WGE.requestTextByURL(WGE.Sprite3d.ShaderDir + "wgeSprite3d.vsh.txt"); 84 | if(!WGE.Sprite3d.FragmentShader) 85 | WGE.Sprite3d.FragmentShader = WGE.requestTextByURL(WGE.Sprite3d.ShaderDir + "wgeSprite3d.fsh.txt"); 86 | this._initProgram(WGE.Sprite3d.VertexShader, WGE.Sprite3d.FragmentShader); 87 | } 88 | this._meshVBO = gl.createBuffer(); 89 | this._meshIndexVBO = gl.createBuffer(); 90 | this._textureVBO = gl.createBuffer(); 91 | this._vertexDataType = gl.FLOAT; 92 | this._meshIndexDataType = gl.UNSIGNED_SHORT; 93 | }, 94 | 95 | release : function() 96 | { 97 | var gl = this._context; 98 | if(this.texture && this.texture.release) 99 | this.texture.release(); 100 | this._program.release(); 101 | 102 | gl.deleteBuffer(this._meshVBO); 103 | gl.deleteBuffer(this._meshIndexVBO); 104 | gl.deleteBuffer(this._textureVBO); 105 | 106 | this.canvas = this.texture = this._program = this._context = null; 107 | }, 108 | 109 | //仅简单渲染模型外形,如需要光照等请自行继承操作,重写shader。 110 | //vertexArr, texArr, indexArr 分别代表模型顶点数据,纹理坐标,面索引。 111 | //顶点数据,纹理坐标必须为 Array或者 Float32Array 112 | //面索引必须为 Array 或者 Uint16Array. 如果需要其他类型请自行重写本方法。 113 | //vertexDataSize 表示每个顶点包含几个分量, 范围为1,2,3,4 114 | //texDataSize 表示每个纹理坐标包含几个分量,范围为1,2,3,4 115 | //末尾两个参数将直接传递给 initTexture 函数 116 | initSprite : function(vertexArr, vertexDataSize, texArr, texDataSize, indexArr, tex, noRelease) 117 | { 118 | var gl = this._context; 119 | var vertData = vertexArr instanceof Array ? new Float32Array(vertexArr) : vertexArr; 120 | gl.bindBuffer(gl.ARRAY_BUFFER, this._meshVBO); 121 | gl.bufferData(gl.ARRAY_BUFFER, vertData, gl.STATIC_DRAW); 122 | 123 | var texData = texArr instanceof Array ? new Float32Array(texArr) : texArr; 124 | gl.bindBuffer(gl.ARRAY_BUFFER, this._textureVBO); 125 | gl.bufferData(gl.ARRAY_BUFFER, texData, gl.STATIC_DRAW); 126 | 127 | var indexData = indexArr instanceof Array ? new Uint16Array(indexArr) : indexArr; 128 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._meshIndexVBO); 129 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexData, gl.STATIC_DRAW); 130 | 131 | this._meshIndexSize = indexArr.length; 132 | this._meshDataSize = vertexDataSize; 133 | this._texDataSize = texDataSize; 134 | 135 | if(tex) 136 | this.initTexture(tex, noRelease); 137 | WGE.checkGLErr("WGE.Sprite3d.initSprite", gl); 138 | }, 139 | 140 | //使用已创建好的buffer来初始化sprite,标注noRelease以后说明这些buffer是共享的,不允许此对象删除。 141 | initBuffer : function(vertBuffer, vertexDataSize, texBuffer, texDataSize, vertIndexBuffer, noRelease) 142 | { 143 | this._meshVBO = vertBuffer; 144 | this._textureVBO = texBuffer; 145 | this._meshIndexVBO = vertIndexBuffer; 146 | this._meshDataSize = vertexDataSize; 147 | this._texDataSize = texDataSize; 148 | this._vboNoRelease = !!noRelease; 149 | }, 150 | 151 | //首参数为 WGE.Texture2D 时, 第二个参数表示是否在销毁本类的同时销毁 WGE.Texture2D 152 | //首参数为 img 对象时,第二个参数表示新创建的 WGE.Texture2D 的配置参数。详情参见WGE.Texture2D 构造函数。 153 | initTexture : function(tex, noRelease) 154 | { 155 | if(!tex) 156 | return false; 157 | 158 | if(tex instanceof WGE.Texture2D) 159 | { 160 | this._textureRelease = !noRelease; 161 | this.texture = tex; 162 | } 163 | else 164 | { 165 | this._textureRelease = true; 166 | this.texture = new WGE.Texture2D(this._context, noRelease); 167 | this.texture.initWithImg(tex); 168 | } 169 | return true; 170 | }, 171 | 172 | //参数: 4x4矩阵,由整个世界给出,以确定当前sprite的局部。 173 | render : function(mvp) 174 | { 175 | var matrix = WGE.mat4Mul(mvp, this._modelMatrix); 176 | var gl = this._context; 177 | var program = this._program; 178 | program.bind(); 179 | gl.uniformMatrix4fv(this._mvpLoc, false, matrix.data); 180 | 181 | this.texture.bindToIndex(1); //index请随意~ 尽量不要大于7就好 182 | gl.uniform1i(this._textureLoc, 1); 183 | 184 | gl.bindBuffer(gl.ARRAY_BUFFER, this._meshVBO); 185 | gl.enableVertexAttribArray(this._vertAttribLoc); 186 | gl.vertexAttribPointer(this._vertAttribLoc, this._meshDataSize, this._vertexDataType, false, 0, 0) 187 | 188 | gl.bindBuffer(gl.ARRAY_BUFFER, this._textureVBO); 189 | gl.enableVertexAttribArray(this._texAttribLoc); 190 | gl.vertexAttribPointer(this._texAttribLoc, this._texDataSize, this._vertexDataType, false, 0, 0); 191 | 192 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._meshIndexVBO); 193 | gl.drawElements(this.renderMethod, this._meshIndexSize, this._meshIndexDataType, 0); 194 | }, 195 | 196 | translate : function(tx, ty, tz) 197 | { 198 | this.translateX(tx); 199 | this.translateY(ty); 200 | this.translateZ(tz); 201 | }, 202 | 203 | translateX : function(tx) 204 | { 205 | this._modelMatrix.translateX(tx); 206 | }, 207 | 208 | translateY : function(ty) 209 | { 210 | this._modelMatrix.translateY(ty); 211 | }, 212 | 213 | translateZ : function(tz) 214 | { 215 | this._modelMatrix.translateZ(tz); 216 | }, 217 | 218 | scale : function(sx, sy, sz) 219 | { 220 | this.scaleX(sx); 221 | this.scaleY(sy); 222 | this.scaleZ(sz); 223 | }, 224 | 225 | scaleX : function(sx) 226 | { 227 | this._modelMatrix.scaleX(sx); 228 | }, 229 | 230 | scaleY : function(sy) 231 | { 232 | this._modelMatrix.scaleY(sy); 233 | }, 234 | 235 | scaleZ : function(sz) 236 | { 237 | this._modelMatrix.scaleZ(sz); 238 | }, 239 | 240 | rotate : function(rad, x, y, z) 241 | { 242 | this._modelMatrix.rotate(rad, x, y, z); 243 | }, 244 | 245 | rotateX : function(rad) 246 | { 247 | this._modelMatrix.rotateX(rad); 248 | }, 249 | 250 | rotateY : function(rad) 251 | { 252 | this._modelMatrix.rotateY(rad); 253 | }, 254 | 255 | rotateZ : function(rad) 256 | { 257 | this._modelMatrix.rotateZ(rad); 258 | }, 259 | 260 | _initProgram : function(vsh, fsh) 261 | { 262 | var gl = this._context; 263 | var program = new WGE.Program(gl); 264 | this._program = program; 265 | 266 | program.initWithShaderCode(vsh, fsh); 267 | 268 | program.bindAttribLocation(WGE.Sprite3d.AttribVertexName, this._vertAttribLoc); 269 | program.bindAttribLocation(WGE.Sprite3d.AttribTextureName, this._texAttribLoc); 270 | 271 | if(!program.link()) 272 | { 273 | console.error("WGE.Sprite3d : Program link Failed!"); 274 | return false; 275 | } 276 | 277 | program.bind(); 278 | 279 | this._mvpLoc = program.uniformLocation(WGE.Sprite3d.MVPName); 280 | this._textureLoc = program.uniformLocation(WGE.Sprite3d.TextureName); 281 | if(!(this._mvpLoc && this._textureLoc)) 282 | { 283 | console.warn("WGE.Sprite3d : Not all uniform locations are correct!"); 284 | } 285 | 286 | WGE.checkGLErr("WGE.Sprite3d - init program", gl); 287 | return true; 288 | } 289 | 290 | 291 | 292 | }); 293 | 294 | WGE.Sprite3d.VertexShader = "varying vec2 vTextureCoord;attribute vec4 aPosition;attribute vec4 aTexCoord;uniform mat4 mvp;void main(){gl_Position = mvp * aPosition;vTextureCoord = aTexCoord.st;}"; 295 | 296 | WGE.Sprite3d.FragmentShader = "precision mediump float;varying vec2 vTextureCoord;uniform sampler2D sTexture;void main(){gl_FragColor = texture2D(sTexture,vTextureCoord);}"; 297 | 298 | WGE.Sprite3d.AttribVertexName = "aPosition"; 299 | WGE.Sprite3d.AttribTextureName = "aTexCoord"; 300 | WGE.Sprite3d.TextureName = "sTexture"; 301 | WGE.Sprite3d.MVPName = "mvp"; -------------------------------------------------------------------------------- /Lesson8/wge/wgeWebGL.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | * wgeWebGL.js 4 | * 5 | * Created on: 2014-6-23 6 | * Author: Wang Yang 7 | * blog: http://blog.wysaid.org 8 | */ 9 | 10 | /* 11 | 本文件提供了对webgl的简单封装。 12 | 13 | */ 14 | 15 | WGE.webgl = null; 16 | 17 | //绑定WGE当前的webgl 上下文。 18 | //绑定以后对于后面的大部分方法,如省略末尾的context参数则自动使用当前绑定这个。 19 | WGE.bindContext = function(ctx) 20 | { 21 | WGE.webgl = ctx; 22 | } 23 | 24 | //检查WebGL 是否发生错误,如有,则输出错误信息。 25 | WGE.checkGLErr = function(tag, context) 26 | { 27 | var ctx = context || WGE.webgl; 28 | for (var error = ctx.getError(); error; error = ctx.getError()) 29 | { 30 | var msg; 31 | switch (error) 32 | { 33 | case ctx.INVALID_ENUM: msg = "invalid enum"; break; 34 | case ctx.INVALID_FRAMEBUFFER_OPERATION: msg = "invalid framebuffer operation"; break; 35 | case ctx.INVALID_OPERATION: msg = "invalid operation"; break; 36 | case ctx.INVALID_VALUE: msg = "invalid value"; break; 37 | case ctx.OUT_OF_MEMORY: msg = "out of memory"; break; 38 | default: msg = "unknown error"; 39 | } 40 | console.error(tag, msg, error); 41 | } 42 | }; 43 | 44 | WGE.Texture2D = WGE.Class( 45 | { 46 | texture : null, 47 | width : 0, 48 | height : 0, 49 | _context : null, 50 | //一些纹理配置参数 51 | _conf : { 52 | TEXTURE_MIN_FILTER : 'NEAREST', 53 | TEXTURE_MAG_FILTER : 'NEAREST', 54 | TEXTURE_WRAP_S : 'CLAMP_TO_EDGE', 55 | TEXTURE_WRAP_T : 'CLAMP_TO_EDGE', 56 | }, 57 | 58 | _shouldRelease : true, 59 | 60 | initialize : function(ctx, config) 61 | { 62 | this._context = ctx || WGE.webgl; 63 | if(config) 64 | WGE.extend(this._conf, config); 65 | }, 66 | 67 | bindContext : function(ctx) 68 | { 69 | this._context = ctx || WGE.webgl; 70 | }, 71 | 72 | //使用已经创建好的 WebGL 纹理对象来创建, 73 | //noRelease 指定是否需要调用webgl方法释放纹理,如不填写则将释放纹理 74 | //ctx 指定该纹理对象所绑定的context,如不填写则默认绑定当前全局上下文对象。 75 | initWithTexture : function(texObj, w, h, noRelease, ctx) 76 | { 77 | this.texture = texObj; 78 | this.width = w || texObj.width; 79 | this.height = h || texObj.height; 80 | this._shouldRelease = !noRelease; 81 | this._context = ctx || WGE.webgl || this._context; 82 | }, 83 | 84 | initWithTag : function(tagID) 85 | { 86 | this.initWithImg(WGE.ID(tagID)); 87 | }, 88 | 89 | initWithImg : function(imageObj) 90 | { 91 | if(!imageObj) 92 | return; 93 | var webgl = this._context; 94 | this.width = imageObj.width; 95 | this.height = imageObj.height; 96 | this.texture = webgl.createTexture(); 97 | webgl.bindTexture(webgl.TEXTURE_2D, this.texture); 98 | webgl.texImage2D(webgl.TEXTURE_2D, 0, webgl.RGBA, webgl.RGBA, webgl.UNSIGNED_BYTE, imageObj); 99 | 100 | webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_MIN_FILTER, webgl[this._conf.TEXTURE_MIN_FILTER]); 101 | webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_MAG_FILTER, webgl[this._conf.TEXTURE_MAG_FILTER]); 102 | webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_S, webgl[this._conf.TEXTURE_WRAP_S]); 103 | webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_T, webgl[this._conf.TEXTURE_WRAP_T]); 104 | }, 105 | 106 | initWithURL : function(imgURL, callback) 107 | { 108 | var self = this; 109 | var image = new Image(); 110 | image.onload = function(){ 111 | self.initWithImg.call(self, image); 112 | callback(); 113 | }; 114 | image.src = imgURL; 115 | }, 116 | 117 | //textureIndex 从0开始, 对应 webgl.TEXTURE0 及其以后 118 | //如 textureIndex 填写 N, 则 对应 webgl.TEXTURE(N) 119 | //之后使用时,可以与传递的uniform值直接对应。 120 | bindToIndex : function(textureIndex) 121 | { 122 | var webgl = this._context; 123 | webgl.activeTexture(webgl.TEXTURE0 + textureIndex); 124 | webgl.bindTexture(webgl.TEXTURE_2D, this.texture); 125 | }, 126 | 127 | release : function() 128 | { 129 | var webgl = this._context || WGE.webgl; 130 | 131 | if(this.texture && webgl && this._shouldRelease) 132 | { 133 | webgl.deleteTexture(this.texture); 134 | } 135 | 136 | this.texture = null; 137 | this._context = null; 138 | } 139 | }); 140 | 141 | WGE.Framebuffer = WGE.Class( 142 | { 143 | framebuffer : null, 144 | _context : null, 145 | 146 | initialize : function(ctx) 147 | { 148 | this._context = ctx || WGE.webgl; 149 | this.framebuffer = this._context.createFramebuffer(); 150 | }, 151 | 152 | release : function() 153 | { 154 | this._context.deleteFramebuffer(this.framebuffer); 155 | this.framebuffer = null; 156 | this._context = null; 157 | }, 158 | 159 | bind : function() 160 | { 161 | this._context.bindFramebuffer(this._context.FRAMEBUFFER, this.framebuffer); 162 | }, 163 | 164 | bindTexture2D : function(texObj, attachment) 165 | { 166 | this.bind(); 167 | var webgl = this._context; 168 | var attach = isNaN(attachment) ? attachment : webgl[attachment]; 169 | webgl.framebufferTexture2D(webgl.FRAMEBUFFER, webgl[attach], webgl.TEXTURE_2D, texObj, 0); 170 | 171 | if(webgl.checkFramebufferStatus(webgl.FRAMEBUFFER) != webgl.FRAMEBUFFER_COMPLETE) 172 | { 173 | console.error("WGE.Framebuffer - bindTexture2D - Frame buffer is not completed."); 174 | } 175 | } 176 | }); 177 | 178 | WGE.Shader = WGE.Class( 179 | { 180 | shaderType : null, 181 | shader : null, 182 | _context : null, 183 | 184 | //shaderType 取值: 185 | // 1. (webgl对象).VERTEX_SHADER 或者 (webgl对象).FRAGMENT_SHADER 186 | // 2. 字符串 "VERTEX_SHADER" 或者 "FRAGMENT_SHADER" 187 | initialize : function(ctx, shaderType, shaderCode) 188 | { 189 | var webgl = ctx || WGE.webgl; 190 | this._context = webgl; 191 | this.shaderType = typeof shaderType == 'string' ? webgl[shaderType] : shaderType; 192 | 193 | this.shader = webgl.createShader(this.shaderType); 194 | 195 | if(shaderCode) 196 | { 197 | loadShaderCode(shaderCode); 198 | } 199 | }, 200 | 201 | release : function() 202 | { 203 | this._context.deleteShader(this.shader); 204 | this.shader = this.shaderType = null; 205 | }, 206 | 207 | loadShaderCode : function(shaderCode) 208 | { 209 | var webgl = this._context; 210 | webgl.shaderSource(this.shader, shaderCode); 211 | webgl.compileShader(this.shader); 212 | if (!webgl.getShaderParameter(this.shader, webgl.COMPILE_STATUS)) 213 | { 214 | console.error(webgl.getShaderInfoLog(this.shader), this.shaderType) 215 | return false; 216 | } 217 | return true; 218 | }, 219 | 220 | loadShaderFromTag : function(tagID) 221 | { 222 | return this.loadShaderCode(WGE.getContentByID(tagID)); 223 | }, 224 | 225 | //非异步加载,若网络较差则可能等待较长时间。 226 | loadShaderFromURL : function(url) 227 | { 228 | return this.loadShaderCode(WGE.requestTextByURL(url)); 229 | } 230 | 231 | }); 232 | 233 | WGE.Program = WGE.Class( 234 | { 235 | _context : null, 236 | vertShader : null, 237 | fragShader : null, 238 | program : null, 239 | 240 | initialize : function(ctx) 241 | { 242 | var webgl = ctx || WGE.webgl; 243 | this._context = webgl; 244 | this.program = webgl.createProgram(); 245 | this.vertShader = new WGE.Shader(webgl, webgl.VERTEX_SHADER); 246 | this.fragShader = new WGE.Shader(webgl, webgl.FRAGMENT_SHADER); 247 | }, 248 | 249 | release : function() 250 | { 251 | this._context.deleteProgram(this.program); 252 | this.vertShader.release(); 253 | this.fragShader.release(); 254 | this._context = this.vertShader = this.fragShader = this.program = null; 255 | }, 256 | 257 | //简便用法, 一步完成shader设定 258 | initWithShaderCode : function(vsh, fsh) 259 | { 260 | if(!(vsh && fsh)) 261 | return false; 262 | return this.vertShader.loadShaderCode(vsh) && 263 | this.fragShader.loadShaderCode(fsh); 264 | }, 265 | 266 | initWithShaderTag : function(vshTag, fshTag) 267 | { 268 | if(!(vsh && fsh)) 269 | return false; 270 | return this.vertShader.loadShaderFromTag(vshTag) && 271 | this.fragShader.loadShaderFromTag(fshTag); 272 | }, 273 | 274 | initWithShaderURL : function(vshURL, fshURL) 275 | { 276 | if(!vshURL && fshURL) 277 | return false; 278 | return this.vertShader.loadShaderFromURL(vshURL) && 279 | this.fragShader.loadShaderFromURL(fshURL); 280 | }, 281 | 282 | loadFragmentShaderCode : function(shaderCode) 283 | { 284 | return this.fragShader.loadShaderCode(shaderCode); 285 | }, 286 | 287 | loadVertexShaderCode : function(shaderCode) 288 | { 289 | return this.vertShader.loadShaderCode(shaderCode); 290 | }, 291 | 292 | loadFragmentShaderFromTag : function(shaderCode) 293 | { 294 | return this.fragShader.loadShaderFromTag(shaderCode); 295 | }, 296 | 297 | loadVertexShaderFromTag : function(shaderCode) 298 | { 299 | return this.vertShader.loadShaderFromTag(shaderCode); 300 | }, 301 | 302 | loadFragmentShaderFromURL : function(shaderCode) 303 | { 304 | return this.fragShader.loadShaderFromURL(shaderCode); 305 | }, 306 | 307 | loadVertexShaderFromURL : function(shaderCode) 308 | { 309 | return this.vertShader.loadShaderFromURL(shaderCode); 310 | }, 311 | 312 | //在两种shader均指定并且初始化成功后,再执行链接操作 313 | link : function() 314 | { 315 | var webgl = this._context; 316 | webgl.attachShader(this.program, this.vertShader.shader); 317 | webgl.attachShader(this.program, this.fragShader.shader); 318 | webgl.linkProgram(this.program); 319 | if (!webgl.getProgramParameter(this.program, webgl.LINK_STATUS)) 320 | { 321 | console.error(webgl.getProgramInfoLog(this.program)); 322 | return false; 323 | } 324 | return true; 325 | }, 326 | 327 | bind : function() 328 | { 329 | this._context.useProgram(this.program); 330 | }, 331 | 332 | uniformLocation : function(uniformName) 333 | { 334 | var loc = this._context.getUniformLocation(this.program, uniformName); 335 | if(!loc) 336 | { 337 | console.error("Uniform Name " + uniformName + " doesnot exist!"); 338 | } 339 | return loc; 340 | }, 341 | 342 | attribLocation : function(attribName) 343 | { 344 | return this._context.getAttribLocation(this.program, attribName); 345 | }, 346 | 347 | //Should be called before "bind()" 348 | bindAttribLocation : function(attribName, location) 349 | { 350 | this._context.bindAttribLocation(this.program, location, attribName); 351 | }, 352 | 353 | sendUniform1f : function(uniformName, v1) 354 | { 355 | var loc = this.uniformLocation(uniformName); 356 | this._context.uniform1f(loc, v1); 357 | }, 358 | 359 | sendUniform2f : function(uniformName, v1, v2) 360 | { 361 | var loc = this.uniformLocation(uniformName); 362 | this._context.uniform2f(loc, v1, v2); 363 | }, 364 | 365 | sendUniform3f : function(uniformName, v1, v2, v3) 366 | { 367 | var loc = this.uniformLocation(uniformName); 368 | this._context.uniform3f(loc, v1, v2, v3); 369 | }, 370 | 371 | sendUniform4f : function(uniformName, v1, v2, v3, v4) 372 | { 373 | var loc = this.uniformLocation(uniformName); 374 | this._context.uniform4f(loc, v1, v2, v3, v4); 375 | }, 376 | 377 | sendUniform1i : function(uniformName, v1) 378 | { 379 | var loc = this.uniformLocation(uniformName); 380 | this._context.uniform1i(loc, v1); 381 | }, 382 | 383 | sendUniform2i : function(uniformName, v1, v2) 384 | { 385 | var loc = this.uniformLocation(uniformName); 386 | this._context.uniform2i(loc, v1, v2); 387 | }, 388 | 389 | sendUniform3i : function(uniformName, v1, v2, v3) 390 | { 391 | var loc = this.uniformLocation(uniformName); 392 | this._context.uniform3i(loc, v1, v2, v3); 393 | }, 394 | 395 | sendUniform4i : function(uniformName, v1, v2, v3, v4) 396 | { 397 | var loc = this.uniformLocation(uniformName); 398 | this._context.uniform4i(loc, v1, v2, v3, v4); 399 | }, 400 | 401 | sendUniformMat2 : function(uniformName, transpose, matrix) 402 | { 403 | var loc = this.uniformLocation(uniformName); 404 | this._context.uniformMatrix2fv(loc, transpose, matrix); 405 | }, 406 | 407 | sendUniformMat3 : function(uniformName, transpose, matrix) 408 | { 409 | var loc = this.uniformLocation(uniformName); 410 | this._context.uniformMatrix3fv(loc, transpose, matrix); 411 | }, 412 | 413 | sendUniformMat4 : function(uniformName, transpose, matrix) 414 | { 415 | var loc = this.uniformLocation(uniformName); 416 | this._context.uniformMatrix4fv(loc, transpose, matrix); 417 | } 418 | 419 | }); -------------------------------------------------------------------------------- /Lesson9/index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Lesson 9 7 | 8 | 9 |

WebGL simple lesson 9 (blog.wysaid.org)

10 | 11 | 12 | -------------------------------------------------------------------------------- /README.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 关于本教程 5 | 6 | 7 |

关于本教程 (blog.wysaid.org)

8 |

本教程由wysaid编写。

9 |

闲来无事所写,希望对于对webgl感兴趣的你有一点点帮助或者说勾起你的兴趣。

10 |

当然,不会也没关系,你可以随便看看。本教程将附带可执行的demo供你取乐。

11 |

顺便指出一下写这个的目的:作者自己也在自学WebGL,算是学习笔记吧。

12 |

如果您你觉得我照抄了某人的教程博取眼球,希望附上地址并指出。不说改正什么的虚伪言辞,删掉此物还是可以做到的。

13 |
14 |

作者本人也仅仅出于爱好,并非专业人士,水平不高

15 |

如有纰漏还望指出. mail: admin@wysaid.org

16 |

敬上

17 | 18 |

全部教程地址: https://github.com/wysaid/WebGL-Lessons 可直接clone或者下载zip包.

19 | 20 |

21 | 重大更新!作者自己正在构建一个简单易用的小lib,简称WGE(你可以脑补为WebGL Game Engine 或者 wysaid go easy之类的)吧,封装了一些webgl操作,使得构建一个简单应用更加便捷,支持2D小游戏和3D小游戏制作,作者本人将会使用本lib做几个简单好看的demo出来,希望可以帮到读者。对于希望以此为工作的读者,也不失为一个小小参考或者说跳板,可以使用本lib小试牛刀,之后再去用一用更加完善和强大的three.js 22 | 参考地址:http://wge.wysaid.org 23 |

24 | 25 | 43 |

本教程目前更新到第八章,更新速度较慢,

44 | 45 | 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |  2 |

关于本教程 (blog.wysaid.org)

3 |

本教程由wysaid编写。

4 |

闲来无事所写,希望对于对webgl感兴趣的你有一点点帮助或者说勾起你的兴趣。

5 |

当然,不会也没关系,你可以随便看看。本教程将附带可执行的demo供你取乐。

6 |

顺便指出一下写这个的目的:作者自己也在自学WebGL,算是学习笔记吧。

7 |

如果您你觉得我照抄了某人的教程博取眼球,希望附上地址并指出。不说改正什么的虚伪言辞,删掉此物还是可以做到的。

8 |
9 |

作者本人也仅仅出于爱好,并非专业人士,水平不高

10 |

如有纰漏还望指出: mail: admin@wysaid.org

11 |

敬上

12 |

13 | 重大更新!作者自己正在构建一个简单易用的小lib,简称WGE(你可以脑补为WebGL Game Engine 或者 wysaid go easy之类的)吧,封装了一些webgl操作,使得构建一个简单应用更加便捷,支持2D小游戏和3D小游戏制作,作者本人将会使用本lib做几个简单好看的demo出来,希望可以帮到读者。对于希望以此为工作的读者,也不失为一个小小参考或者说跳板,可以使用本lib小试牛刀,之后再去用一用更加完善和强大的three.js 14 | 参考地址:http://wge.wysaid.org 15 |

16 | ## 在线访问本教程 ## 17 | 18 | http://webgl-lesson.wysaid.org/ -------------------------------------------------------------------------------- /WGE/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | WGE (Web Graphics Engine) 5 | 6 | 84 | 95 | 96 | 97 |
98 |
99 |
100 | 101 | 126 |
127 |
128 |
129 |

WGE (Web Graphics Engine) 是一个基于Web平台的图形引擎。主要使用js, canvas 2d, canvas webgl开发

130 |

WGE 目前主要定位为一个简单易用,快速上手,并能够加快web应用开发的框架库

131 |
132 |
133 |

2d版: 暂停github更新。

134 |

GUI测试: canvas-2d GUI测试页面

135 |

2d 版移入bitbucket,停止github上的更新。

136 |

^_^ 你可以看看WebGL版~

137 |
138 | 139 |
140 |

webgl版: 编写中

141 |

GUI测试: canvas-webgl GUI测试页面

142 |

慎用WGE

143 |

开发者以外的个人最好将WGE作为学习使用

144 | 145 |
146 |
147 |
148 |
149 |

WGE正在建设中!

150 |
151 |
152 |

当前版本: 0.0.1

153 |
154 |
155 |

联系我: 请点击

156 |
157 |
158 |
159 |
160 | 161 | 162 | -------------------------------------------------------------------------------- /WGE/webgl/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /WGE/webgl/wgeFilters.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | * wgeFilters.js 4 | * 5 | * Created on: 2014-8-5 6 | * Author: Wang Yang 7 | * blog: http://blog.wysaid.org 8 | */ 9 | 10 | WGE.TextureBlendMode = 11 | { 12 | BLEND_MIX : 0, // 0 13 | BLEND_OVERLAY : 1, // 1 14 | BLEND_HARDLIGHT : 2, // 2 15 | BLEND_SOFTLIGHT : 3, // 3 16 | BLEND_SCREEN : 4, // 4 17 | BLEND_LINEARLIGHT : 5, // 5 18 | BLEND_VIVIDLIGHT : 6, // 6 19 | BLEND_MULTIPLY : 7, // 7 20 | BLEND_EXCLUDE : 8, // 8 21 | BLEND_COLORBURN : 9, // 9 22 | BLEND_DARKEN : 10, // 10 23 | BLEND_LIGHTEN : 11, // 11 24 | BLEND_COLORDODGE : 12, // 12 25 | BLEND_COLORDODGEADOBE : 13,// 13 26 | BLEND_LINEARDODGE : 14, // 14 27 | BLEND_LINEARBURN : 15, // 15 28 | BLEND_PINLIGHT : 16, // 16 29 | BLEND_HARDMIX : 17, // 17 30 | BLEND_DIFFERENCE : 18, // 18 31 | BLEND_ADD : 19, // 19 32 | BLEND_COLOR : 20, // 20 33 | 34 | ///////////// Special blend mode below ////////////// 35 | 36 | BlEND_ADD_REVERSE : 21, // 21 37 | BLEND_COLOR_BW : 22, // 22 38 | 39 | ///////////// Special blend mode above ////////////// 40 | 41 | BLEND_MAX_NUM : 23 //Its value defines the max num of blend. 42 | }; 43 | 44 | WGE.TextureBlendModeName = 45 | [ 46 | "MIX", // 0 47 | "OVERLAY", // 1 48 | "HARDLIGHT", // 2 49 | "SOFTLIGHT", // 3 50 | "SCREEN", // 4 51 | "LINEARLIGHT", // 5 52 | "VIVIDLIGHT", // 6 53 | "MULTIPLY", // 7 54 | "EXCLUDE", // 8 55 | "COLORBURN", // 9 56 | "DARKEN", // 10 57 | "LIGHTEN", // 11 58 | "COLORDODGE", // 12 59 | "COLORDODGEADOBE",// 13 60 | "LINEARDODGE", // 14 61 | "LINEARBURN", // 15 62 | "PINLIGHT", // 16 63 | "HARDMIX", // 17 64 | "DIFFERENCE", // 18 65 | "ADD", // 19 66 | "COLOR", // 20 67 | 68 | ///////////// Special blend mode below ////////////// 69 | 70 | "ADD_REVERSE", // 21 71 | "COLOR_BW", // 22 72 | ]; 73 | 74 | WGE.getBlendNameByMode = function(blendMode) 75 | { 76 | if(blendMode < WGE.TextureBlendMode.BLEND_MIX || blendMode >= WGE.TextureBlendMode.BLEND_MAX_NUM) 77 | { 78 | return "Invalid Blend Mode"; 79 | } 80 | return WGE.TextureBlendModeName[blendMode]; 81 | }; -------------------------------------------------------------------------------- /WGE/webgl/wgeScene.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | * wgeScene.js 4 | * 5 | * Created on: 2014-8-13 6 | * Author: Wang Yang 7 | * blog: http://blog.wysaid.org 8 | */ 9 | 10 | /* 11 | 简介: wgeScene提供WebGL场景漫游。 12 | */ 13 | 14 | //WGE.SceneInterface 并不提供多余的渲染方式等,仅维护模型视点矩阵以及投影矩阵 15 | //所有的Sprite或者其他东西只要在WGE.Scene提供的世界观基础上变换, 16 | //可保证整个世界的一致性。 17 | 18 | //默认使用透视投影,如果需要平行投影请自己 19 | //更多动作如跳跃等请继承此类实现。 20 | 21 | //特别注意! wgeScene 使用 xOy 平面作为漫游地面!z正方向为向上方向 22 | WGE.SceneInterface = WGE.Class( 23 | { 24 | modelViewMatrix : null, //4x4 模型视点矩阵 25 | projectionMatrix : null, //4x4 投影矩阵 26 | 27 | eye : null, // 当前视点位置(Vec3)。 默认(0, 0, 0) 28 | 29 | // lookDir 为观测方向,默认(0, 1, 0),x,y两个分量的模为1。 注: 不是观测位置。 30 | // 如果需要手动修改lookDir 请保证dirLen大小与dir的x,y两个分量的模相等, 否则将影响移动速度。 31 | lookDir : null, 32 | dirLen : 1000, 33 | 34 | fovyRad : Math.PI/3, //视景体的视野的角度(弧度) 35 | zNear : 1.0, //透视投影近裁剪面 36 | zFar : 10000.0, //透视投影远裁剪面 37 | 38 | initialize : function(w, h) 39 | { 40 | this.eye = new WGE.Vec3(0, 0, 0); 41 | this.lookDir = new WGE.Vec3(0, 1000, 0); 42 | this.updateView(); 43 | if(!(w && h)) 44 | { 45 | w = 800; 46 | h = 600; 47 | } 48 | this.resize(w, h); 49 | }, 50 | 51 | //如果有任何移动旋转或者调整视野操作, 52 | //之后必须调用updateView 函数 更新模型视点矩阵 53 | updateView : function() 54 | { 55 | var eye = this.eye.data, lookDir = this.lookDir.data; 56 | var centerX = eye[0] + lookDir[0], centerY = eye[1] + lookDir[1]; 57 | var len = Math.sqrt(centerX*centerX + centerY*centerY); 58 | var tmp = -lookDir[2] / len; 59 | var dirBackX = centerX * tmp, dirBackY = centerY * tmp; 60 | this.modelViewMatrix = WGE.makeLookAt(eye[0], eye[1], eye[2], centerX, centerY, lookDir[2], dirBackX, dirBackY, len); 61 | }, 62 | 63 | //向右转(弧度), 负值将向左转. 64 | turnRight : function(rad) 65 | { 66 | var d = this.lookDir.data; 67 | var v = WGE.mat2MulVec2(WGE.mat2Rotation(rad), this.lookDir).data; 68 | d[0] = v[0]; 69 | d[1] = v[1]; 70 | }, 71 | 72 | //向右旋转到(弧度), 以Y轴正方向为起始方向。 73 | turnRightTo : function(rad) 74 | { 75 | var d = this.lookDir.data; 76 | var v = WGE.mat2MulVec2(WGE.mat2Rotation(rad), new WGE.Vec3(0, 1, 0)).data; 77 | d[0] = v[0]; 78 | d[1] = v[1]; 79 | }, 80 | 81 | //向上观察, motion计算关系: 82 | //向上弧度计算公式为 arctan(tan("当前向上弧度") + motion) - "当前向上弧度" 83 | lookUp : function(motion) 84 | { 85 | this.lookDir.data[2] += motion * this.dirLen; 86 | var lookUpMax = this.dirLen * 3.732; //tan(PI / 75); 87 | if(this.lookDir.data[2] > lookUpMax) 88 | this.lookDir.data[2] = lookUpMax; 89 | else if(this.lookDir.data[2] < -lookUpMax) 90 | this.lookDir.data[2] = -lookUpMax; 91 | }, 92 | 93 | //向上观察到(弧度),直接仰视到所看弧度 94 | //范围[-PI/2.4, PI/2.4] ,约正负75度角 95 | lookUpTo : function(rad) 96 | { 97 | if(rad > Math.PI / 2.4) 98 | rad = Math.PI / 2.4; 99 | else if(rad < -Math.PI / 2.4) 100 | rad = -Math.PI / 2.4; 101 | this.lookDir.data[2] = Math.tan(rad) * this.dirLen; 102 | }, 103 | 104 | //视野角度(弧度), 105 | lookIn : function(rad) 106 | { 107 | this.fovyRad += rad; 108 | if(this.fovyRad < Math.PI / 10) 109 | this.fovyRad = Math.PI / 10; 110 | else if(this.fovyRad > Math.PI / 2.4) 111 | this.fovyRad = Math.PI / 2.4; 112 | }, 113 | 114 | //视野角度, 范围[PI/10, PI/2.4] 115 | lookInTo : function(rad) 116 | { 117 | if(rad < Math.PI / 10) 118 | rad = Math.PI / 10; 119 | else if(rad > Math.PI / 2.4) 120 | rad = Math.PI / 2.4; 121 | this.fovyRad = rad; 122 | }, 123 | 124 | //向前移动 125 | goForward : function(motion) 126 | { 127 | var m = motion / this.dirLen; 128 | this.eye.data[0] += this.lookDir.data[0] * m; 129 | this.eye.data[1] += this.lookDir.data[1] * m; 130 | }, 131 | 132 | //向后移动 133 | goBack : function(motion) 134 | { 135 | var m = motion / this.dirLen; 136 | this.eye.data[0] -= this.lookDir.data[0] * m; 137 | this.eye.data[1] -= this.lookDir.data[1] * m; 138 | }, 139 | 140 | //向左移动 141 | goLeft : function(motion) 142 | { 143 | var m = motion / this.dirLen; 144 | this.eye.data[0] -= this.lookDir.data[1] * m; 145 | this.eye.data[1] += this.lookDir.data[0] * m; 146 | }, 147 | 148 | //向右移动 149 | goRight : function(motion) 150 | { 151 | var m = motion / this.dirLen; 152 | this.eye.data[0] += this.lookDir.data[1] * m; 153 | this.eye.data[1] -= this.lookDir.data[0] * m; 154 | }, 155 | 156 | //默认透视投影 157 | resize : function(w, h) 158 | { 159 | this.projectionMatrix = WGE.makePerspective(this.fovyRad, w / h, this.zNear, this.zFar); 160 | } 161 | 162 | }); -------------------------------------------------------------------------------- /WGE/webgl/wgeSprite2d.fsh.txt: -------------------------------------------------------------------------------- 1 | /* 2 | * wgeSprite2d.fsh 3 | * 4 | * Created on: 2014-8-6 5 | * Author: Wang Yang 6 | * Blog: http://blog.wysaid.org 7 | */ 8 | 9 | precision mediump float; 10 | varying vec2 vTextureCoord; 11 | uniform sampler2D sTexture; 12 | uniform float alpha; 13 | 14 | void main() 15 | { 16 | gl_FragColor = texture2D(sTexture, vTextureCoord); 17 | gl_FragColor.a *= alpha; 18 | gl_FragColor.rgb *= gl_FragColor.a; 19 | } -------------------------------------------------------------------------------- /WGE/webgl/wgeSprite2d.vsh.txt: -------------------------------------------------------------------------------- 1 | /* 2 | * wgeSprite2d.vsh 3 | * 4 | * Created on: 2014-8-6 5 | * Author: Wang Yang 6 | * Blog: http://blog.wysaid.org 7 | */ 8 | 9 | //Range: [-1, 1] 10 | attribute vec2 aPosition; 11 | varying vec2 vTextureCoord; 12 | uniform mat4 m4Projection; 13 | uniform vec2 v2HalfTexSize; 14 | 15 | uniform float rotation; 16 | uniform vec2 v2Scaling; 17 | uniform vec2 v2Translation; 18 | uniform vec2 v2Hotspot; 19 | uniform vec2 canvasflip; 20 | uniform vec2 spriteflip; 21 | uniform float zIndex; 22 | 23 | mat3 mat3ZRotation(float rad) 24 | { 25 | float cosRad = cos(rad); 26 | float sinRad = sin(rad); 27 | return mat3(cosRad, sinRad, 0.0, -sinRad, cosRad, 0.0, 0.0, 0.0, 1.0); 28 | } 29 | 30 | void main() 31 | { 32 | vTextureCoord = (aPosition.xy * spriteflip + 1.0) / 2.0; 33 | vec3 pos = mat3ZRotation(rotation) * vec3((aPosition - v2Hotspot) * v2HalfTexSize, zIndex); 34 | pos.xy = (pos.xy + v2Hotspot * v2HalfTexSize); 35 | pos.xy *= v2Scaling; 36 | pos.xy += v2Translation - v2Scaling * v2HalfTexSize * v2Hotspot; 37 | gl_Position = m4Projection * vec4(pos, 1.0); 38 | gl_Position.xy *= canvasflip; 39 | } -------------------------------------------------------------------------------- /WGE/webgl/wgeSprite2dExt.fsh.txt: -------------------------------------------------------------------------------- 1 | /* 2 | * wgeSprite2d.fsh 3 | * 4 | * Created on: 2014-8-6 5 | * Author: Wang Yang 6 | * Blog: http://blog.wysaid.org 7 | */ 8 | 9 | precision mediump float; 10 | varying vec2 vTextureCoord; 11 | uniform sampler2D sTexture; 12 | uniform float alpha; 13 | uniform vec3 blendColor; 14 | 15 | void main() 16 | { 17 | gl_FragColor = texture2D(sTexture, vTextureCoord); 18 | gl_FragColor.a *= alpha; 19 | gl_FragColor.rgb *= gl_FragColor.a * blendColor; 20 | } -------------------------------------------------------------------------------- /WGE/webgl/wgeSprite2dExt.vsh.txt: -------------------------------------------------------------------------------- 1 | /* 2 | * wgeSprite2dExt.vsh 3 | * 4 | * Created on: 2014-8-6 5 | * Author: Wang Yang 6 | * Blog: http://blog.wysaid.org 7 | */ 8 | 9 | //Range: [-1, 1] 10 | attribute vec2 aPosition; 11 | varying vec2 vTextureCoord; 12 | uniform mat4 m4Projection; 13 | uniform vec2 v2HalfTexSize; 14 | 15 | uniform mat3 rotation; 16 | uniform vec2 v2Scaling; 17 | uniform vec2 v2Translation; 18 | uniform vec2 v2Hotspot; 19 | uniform vec2 canvasflip; 20 | uniform vec2 spriteflip; 21 | uniform float zIndex; 22 | 23 | void main() 24 | { 25 | vTextureCoord = (aPosition.xy * spriteflip + 1.0) / 2.0; 26 | vec3 pos = rotation * vec3((aPosition - v2Hotspot) * v2HalfTexSize, zIndex); 27 | pos.xy = (pos.xy + v2Hotspot * v2HalfTexSize); 28 | pos.xy *= v2Scaling; 29 | pos.xy += v2Translation - v2Scaling * v2HalfTexSize * v2Hotspot; 30 | gl_Position = m4Projection * vec4(pos, 1.0); 31 | gl_Position.xy *= canvasflip; 32 | } -------------------------------------------------------------------------------- /WGE/webgl/wgeSprite3d.fsh.txt: -------------------------------------------------------------------------------- 1 | /* 2 | * wgeSprite.fsh 3 | * 4 | * Created on: 2014-8-8 5 | * Author: Wang Yang 6 | * Blog: http://blog.wysaid.org 7 | */ 8 | 9 | precision mediump float; 10 | varying vec2 vTextureCoord; 11 | uniform sampler2D sTexture; 12 | 13 | void main() 14 | { 15 | gl_FragColor = texture2D(sTexture, vTextureCoord); 16 | } -------------------------------------------------------------------------------- /WGE/webgl/wgeSprite3d.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | * wgeSprite3d.js for webgl 4 | * 5 | * Created on: 2014-8-6 6 | * Author: Wang Yang 7 | * Blog: http://blog.wysaid.org 8 | */ 9 | 10 | /* 11 | 本类只提供基础功能,需要更强大的功能需要自己继承定制。 12 | */ 13 | 14 | //提示: zIndex 在Sprite3d中无意义,故不提供。开启OpenGL的深度测试即可。 15 | // 不直接提供 pos等参数,如果需要,则继承此类,并记录这些参数以使用。 16 | //若要按远近先后混合,请在渲染前按远近顺序排序。 17 | 18 | //同名参数含义请参考Sprite2d,为了减少篇幅,这里不再赘述 19 | //内部包含set方法的参数,请使用set方法,不要直接修改参数值。 20 | 21 | //与Sprite2d 的默认坐标系不同! 22 | 23 | WGE.Sprite3d = WGE.Class( 24 | { 25 | canvas : null, 26 | texture : null, 27 | 28 | renderMethod : null, //渲染方式,默认为 gl.TRIANGLES. 29 | 30 | _modelMatrix : null, //4x4模型矩阵,内含sprite3d所包含模型所进行的所有矩阵转换操作。 31 | 32 | _hotspot: null, 33 | 34 | _program : null, 35 | _context : null, 36 | _textureRelease : true, 37 | 38 | _meshVBO : null, 39 | _meshIndexVBO : null, 40 | _textureVBO : null, 41 | _vboNoRelease : false, 42 | _meshIndexSize : null, 43 | _vertexDataType : null, //顶点数据类型,默认为 gl.FLOAT 44 | _meshIndexDataType : null, //索引数据类型,默认为 gl.UNSIGNED_SHORT 45 | _meshDataSize : null, //每个顶点所包含的分量,可选值为1,2,3,4,默认4 46 | _texDataSize : null, //每个纹理坐标所包含的分量,同上。 47 | //定义attrib location 48 | _vertAttribLoc : 0, 49 | _texAttribLoc : 1, 50 | 51 | //缓存一些可能用到的location 52 | _mvpLoc : null, 53 | _textureLoc : null, 54 | 55 | //vsh和fsh表示自写的shader代码,可以自行定制。 56 | initialize : function(canvas, ctx, vsh, fsh) 57 | { 58 | // this.pos = new WGE.Vec3(0, 0, 0); 59 | // this.scaling = new WGE.Vec3(1, 1, 1); 60 | // this._hotspot = new WGE.Vec3(0, 0, 0); 61 | this._modelMatrix = WGE.mat4Identity(); 62 | 63 | this.canvas = canvas; 64 | if(!canvas) 65 | { 66 | console.error("Invalid Params while creating WGE.Sprite3d!"); 67 | } 68 | var gl = ctx || WGE.webgl || this.canvas.getContext('webgl') || this.canvas.getContext('experimental-webgl'); 69 | this._context = gl; 70 | 71 | if(!this.renderMethod) 72 | { 73 | this.renderMethod = gl.TRIANGLES; 74 | } 75 | 76 | if(vsh && fsh) 77 | { 78 | this._initProgram(vsh, fsh); 79 | } 80 | else 81 | { 82 | if(!WGE.Sprite3d.VertexShader) 83 | WGE.Sprite3d.VertexShader = WGE.requestTextByURL(WGE.Sprite3d.ShaderDir + "wgeSprite3d.vsh.txt"); 84 | if(!WGE.Sprite3d.FragmentShader) 85 | WGE.Sprite3d.FragmentShader = WGE.requestTextByURL(WGE.Sprite3d.ShaderDir + "wgeSprite3d.fsh.txt"); 86 | this._initProgram(WGE.Sprite3d.VertexShader, WGE.Sprite3d.FragmentShader); 87 | } 88 | this._meshVBO = gl.createBuffer(); 89 | this._meshIndexVBO = gl.createBuffer(); 90 | this._textureVBO = gl.createBuffer(); 91 | this._vertexDataType = gl.FLOAT; 92 | this._meshIndexDataType = gl.UNSIGNED_SHORT; 93 | }, 94 | 95 | release : function() 96 | { 97 | var gl = this._context; 98 | if(this.texture && this.texture.release) 99 | this.texture.release(); 100 | this._program.release(); 101 | 102 | gl.deleteBuffer(this._meshVBO); 103 | gl.deleteBuffer(this._meshIndexVBO); 104 | gl.deleteBuffer(this._textureVBO); 105 | 106 | this.canvas = this.texture = this._program = this._context = null; 107 | }, 108 | 109 | //仅简单渲染模型外形,如需要光照等请自行继承操作,重写shader。 110 | //vertexArr, texArr, indexArr 分别代表模型顶点数据,纹理坐标,面索引。 111 | //顶点数据,纹理坐标必须为 Array或者 Float32Array 112 | //面索引必须为 Array 或者 Uint16Array. 如果需要其他类型请自行重写本方法。 113 | //vertexDataSize 表示每个顶点包含几个分量, 范围为1,2,3,4 114 | //texDataSize 表示每个纹理坐标包含几个分量,范围为1,2,3,4 115 | //末尾两个参数将直接传递给 initTexture 函数 116 | initSprite : function(vertexArr, vertexDataSize, texArr, texDataSize, indexArr, tex, noRelease) 117 | { 118 | var gl = this._context; 119 | var vertData = vertexArr instanceof Array ? new Float32Array(vertexArr) : vertexArr; 120 | gl.bindBuffer(gl.ARRAY_BUFFER, this._meshVBO); 121 | gl.bufferData(gl.ARRAY_BUFFER, vertData, gl.STATIC_DRAW); 122 | 123 | var texData = texArr instanceof Array ? new Float32Array(texArr) : texArr; 124 | gl.bindBuffer(gl.ARRAY_BUFFER, this._textureVBO); 125 | gl.bufferData(gl.ARRAY_BUFFER, texData, gl.STATIC_DRAW); 126 | 127 | var indexData = indexArr instanceof Array ? new Uint16Array(indexArr) : indexArr; 128 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._meshIndexVBO); 129 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexData, gl.STATIC_DRAW); 130 | 131 | this._meshIndexSize = indexArr.length; 132 | this._meshDataSize = vertexDataSize; 133 | this._texDataSize = texDataSize; 134 | 135 | if(tex) 136 | this.initTexture(tex, noRelease); 137 | WGE.checkGLErr("WGE.Sprite3d.initSprite", gl); 138 | }, 139 | 140 | //使用已创建好的buffer来初始化sprite,标注noRelease以后说明这些buffer是共享的,不允许此对象删除。 141 | initBuffer : function(vertBuffer, vertexDataSize, texBuffer, texDataSize, vertIndexBuffer, noRelease) 142 | { 143 | this._meshVBO = vertBuffer; 144 | this._textureVBO = texBuffer; 145 | this._meshIndexVBO = vertIndexBuffer; 146 | this._meshDataSize = vertexDataSize; 147 | this._texDataSize = texDataSize; 148 | this._vboNoRelease = !!noRelease; 149 | }, 150 | 151 | //首参数为 WGE.Texture2D 时, 第二个参数表示是否在销毁本类的同时销毁 WGE.Texture2D 152 | //首参数为 img 对象时,第二个参数表示新创建的 WGE.Texture2D 的配置参数。详情参见WGE.Texture2D 构造函数。 153 | initTexture : function(tex, noRelease) 154 | { 155 | if(!tex) 156 | return false; 157 | 158 | if(tex instanceof WGE.Texture2D) 159 | { 160 | this._textureRelease = !noRelease; 161 | this.texture = tex; 162 | } 163 | else 164 | { 165 | this._textureRelease = true; 166 | this.texture = new WGE.Texture2D(this._context, noRelease); 167 | this.texture.initWithImg(tex); 168 | } 169 | return true; 170 | }, 171 | 172 | //参数: 4x4矩阵,由整个世界给出,以确定当前sprite的局部。 173 | render : function(mvp) 174 | { 175 | var matrix = WGE.mat4Mul(mvp, this._modelMatrix); 176 | var gl = this._context; 177 | var program = this._program; 178 | program.bind(); 179 | gl.uniformMatrix4fv(this._mvpLoc, false, matrix.data); 180 | 181 | this.texture.bindToIndex(1); //index请随意~ 尽量不要大于7就好 182 | gl.uniform1i(this._textureLoc, 1); 183 | 184 | gl.bindBuffer(gl.ARRAY_BUFFER, this._meshVBO); 185 | gl.enableVertexAttribArray(this._vertAttribLoc); 186 | gl.vertexAttribPointer(this._vertAttribLoc, this._meshDataSize, this._vertexDataType, false, 0, 0) 187 | 188 | gl.bindBuffer(gl.ARRAY_BUFFER, this._textureVBO); 189 | gl.enableVertexAttribArray(this._texAttribLoc); 190 | gl.vertexAttribPointer(this._texAttribLoc, this._texDataSize, this._vertexDataType, false, 0, 0); 191 | 192 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._meshIndexVBO); 193 | gl.drawElements(this.renderMethod, this._meshIndexSize, this._meshIndexDataType, 0); 194 | }, 195 | 196 | translate : function(tx, ty, tz) 197 | { 198 | this.translateX(tx); 199 | this.translateY(ty); 200 | this.translateZ(tz); 201 | }, 202 | 203 | translateX : function(tx) 204 | { 205 | this._modelMatrix.translateX(tx); 206 | }, 207 | 208 | translateY : function(ty) 209 | { 210 | this._modelMatrix.translateY(ty); 211 | }, 212 | 213 | translateZ : function(tz) 214 | { 215 | this._modelMatrix.translateZ(tz); 216 | }, 217 | 218 | scale : function(sx, sy, sz) 219 | { 220 | this.scaleX(sx); 221 | this.scaleY(sy); 222 | this.scaleZ(sz); 223 | }, 224 | 225 | scaleX : function(sx) 226 | { 227 | this._modelMatrix.scaleX(sx); 228 | }, 229 | 230 | scaleY : function(sy) 231 | { 232 | this._modelMatrix.scaleY(sy); 233 | }, 234 | 235 | scaleZ : function(sz) 236 | { 237 | this._modelMatrix.scaleZ(sz); 238 | }, 239 | 240 | rotate : function(rad, x, y, z) 241 | { 242 | this._modelMatrix.rotate(rad, x, y, z); 243 | }, 244 | 245 | rotateX : function(rad) 246 | { 247 | this._modelMatrix.rotateX(rad); 248 | }, 249 | 250 | rotateY : function(rad) 251 | { 252 | this._modelMatrix.rotateY(rad); 253 | }, 254 | 255 | rotateZ : function(rad) 256 | { 257 | this._modelMatrix.rotateZ(rad); 258 | }, 259 | 260 | _initProgram : function(vsh, fsh) 261 | { 262 | var gl = this._context; 263 | var program = new WGE.Program(gl); 264 | this._program = program; 265 | 266 | program.initWithShaderCode(vsh, fsh); 267 | 268 | program.bindAttribLocation(WGE.Sprite3d.AttribVertexName, this._vertAttribLoc); 269 | program.bindAttribLocation(WGE.Sprite3d.AttribTextureName, this._texAttribLoc); 270 | 271 | if(!program.link()) 272 | { 273 | console.error("WGE.Sprite3d : Program link Failed!"); 274 | return false; 275 | } 276 | 277 | program.bind(); 278 | 279 | this._mvpLoc = program.uniformLocation(WGE.Sprite3d.MVPName); 280 | this._textureLoc = program.uniformLocation(WGE.Sprite3d.TextureName); 281 | if(!(this._mvpLoc && this._textureLoc)) 282 | { 283 | console.warn("WGE.Sprite3d : Not all uniform locations are correct!"); 284 | } 285 | 286 | WGE.checkGLErr("WGE.Sprite3d - init program", gl); 287 | return true; 288 | } 289 | 290 | 291 | 292 | }); 293 | 294 | WGE.Sprite3d.VertexShader = "varying vec2 vTextureCoord;attribute vec4 aPosition;attribute vec4 aTexCoord;uniform mat4 mvp;void main(){gl_Position = mvp * aPosition;vTextureCoord = aTexCoord.st;}"; 295 | 296 | WGE.Sprite3d.FragmentShader = "precision mediump float;varying vec2 vTextureCoord;uniform sampler2D sTexture;void main(){gl_FragColor = texture2D(sTexture,vTextureCoord);}"; 297 | 298 | WGE.Sprite3d.AttribVertexName = "aPosition"; 299 | WGE.Sprite3d.AttribTextureName = "aTexCoord"; 300 | WGE.Sprite3d.TextureName = "sTexture"; 301 | WGE.Sprite3d.MVPName = "mvp"; -------------------------------------------------------------------------------- /WGE/webgl/wgeSprite3d.vsh.txt: -------------------------------------------------------------------------------- 1 | /* 2 | * wgeSprite3d.vsh 3 | * 4 | * Created on: 2014-8-8 5 | * Author: Wang Yang 6 | * Blog: http://blog.wysaid.org 7 | */ 8 | 9 | varying vec2 vTextureCoord; 10 | attribute vec4 aPosition; 11 | attribute vec4 aTexCoord; 12 | 13 | uniform mat4 mvp; 14 | 15 | void main() 16 | { 17 | gl_Position = mvp * aPosition; 18 | vTextureCoord = aTexCoord.st; 19 | } -------------------------------------------------------------------------------- /WGE/webgl/wgeWebGL.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | * wgeWebGL.js 4 | * 5 | * Created on: 2014-6-23 6 | * Author: Wang Yang 7 | * blog: http://blog.wysaid.org 8 | */ 9 | 10 | /* 11 | 本文件提供了对webgl的简单封装。 12 | 13 | */ 14 | 15 | WGE.webgl = null; 16 | 17 | //绑定WGE当前的webgl 上下文。 18 | //绑定以后对于后面的大部分方法,如省略末尾的context参数则自动使用当前绑定这个。 19 | WGE.bindContext = function(ctx) 20 | { 21 | WGE.webgl = ctx; 22 | } 23 | 24 | //检查WebGL 是否发生错误,如有,则输出错误信息。 25 | WGE.checkGLErr = function(tag, context) 26 | { 27 | var ctx = context || WGE.webgl; 28 | for (var error = ctx.getError(); error; error = ctx.getError()) 29 | { 30 | var msg; 31 | switch (error) 32 | { 33 | case ctx.INVALID_ENUM: msg = "invalid enum"; break; 34 | case ctx.INVALID_FRAMEBUFFER_OPERATION: msg = "invalid framebuffer operation"; break; 35 | case ctx.INVALID_OPERATION: msg = "invalid operation"; break; 36 | case ctx.INVALID_VALUE: msg = "invalid value"; break; 37 | case ctx.OUT_OF_MEMORY: msg = "out of memory"; break; 38 | default: msg = "unknown error"; 39 | } 40 | console.error(tag, msg, error); 41 | } 42 | }; 43 | 44 | WGE.Texture2D = WGE.Class( 45 | { 46 | texture : null, 47 | width : 0, 48 | height : 0, 49 | _context : null, 50 | //一些纹理配置参数 51 | _conf : { 52 | TEXTURE_MIN_FILTER : 'NEAREST', 53 | TEXTURE_MAG_FILTER : 'NEAREST', 54 | TEXTURE_WRAP_S : 'CLAMP_TO_EDGE', 55 | TEXTURE_WRAP_T : 'CLAMP_TO_EDGE', 56 | }, 57 | 58 | _shouldRelease : true, 59 | 60 | initialize : function(ctx, config) 61 | { 62 | this._context = ctx || WGE.webgl; 63 | if(config) 64 | WGE.extend(this._conf, config); 65 | }, 66 | 67 | bindContext : function(ctx) 68 | { 69 | this._context = ctx || WGE.webgl; 70 | }, 71 | 72 | //使用已经创建好的 WebGL 纹理对象来创建, 73 | //noRelease 指定是否需要调用webgl方法释放纹理,如不填写则将释放纹理 74 | //ctx 指定该纹理对象所绑定的context,如不填写则默认绑定当前全局上下文对象。 75 | initWithTexture : function(texObj, w, h, noRelease, ctx) 76 | { 77 | this.texture = texObj; 78 | this.width = w || texObj.width; 79 | this.height = h || texObj.height; 80 | this._shouldRelease = !noRelease; 81 | this._context = ctx || WGE.webgl || this._context; 82 | }, 83 | 84 | initWithTag : function(tagID) 85 | { 86 | this.initWithImg(WGE.ID(tagID)); 87 | }, 88 | 89 | initWithImg : function(imageObj) 90 | { 91 | if(!imageObj) 92 | return; 93 | var webgl = this._context; 94 | this.width = imageObj.width; 95 | this.height = imageObj.height; 96 | this.texture = webgl.createTexture(); 97 | webgl.bindTexture(webgl.TEXTURE_2D, this.texture); 98 | webgl.texImage2D(webgl.TEXTURE_2D, 0, webgl.RGBA, webgl.RGBA, webgl.UNSIGNED_BYTE, imageObj); 99 | 100 | webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_MIN_FILTER, webgl[this._conf.TEXTURE_MIN_FILTER]); 101 | webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_MAG_FILTER, webgl[this._conf.TEXTURE_MAG_FILTER]); 102 | webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_S, webgl[this._conf.TEXTURE_WRAP_S]); 103 | webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_T, webgl[this._conf.TEXTURE_WRAP_T]); 104 | }, 105 | 106 | initWithURL : function(imgURL, callback) 107 | { 108 | var self = this; 109 | var image = new Image(); 110 | image.onload = function(){ 111 | self.initWithImg.call(self, image); 112 | callback(); 113 | }; 114 | image.src = imgURL; 115 | }, 116 | 117 | //textureIndex 从0开始, 对应 webgl.TEXTURE0 及其以后 118 | //如 textureIndex 填写 N, 则 对应 webgl.TEXTURE(N) 119 | //之后使用时,可以与传递的uniform值直接对应。 120 | bindToIndex : function(textureIndex) 121 | { 122 | var webgl = this._context; 123 | webgl.activeTexture(webgl.TEXTURE0 + textureIndex); 124 | webgl.bindTexture(webgl.TEXTURE_2D, this.texture); 125 | }, 126 | 127 | release : function() 128 | { 129 | var webgl = this._context || WGE.webgl; 130 | 131 | if(this.texture && webgl && this._shouldRelease) 132 | { 133 | webgl.deleteTexture(this.texture); 134 | } 135 | 136 | this.texture = null; 137 | this._context = null; 138 | } 139 | }); 140 | 141 | WGE.Framebuffer = WGE.Class( 142 | { 143 | framebuffer : null, 144 | _context : null, 145 | 146 | initialize : function(ctx) 147 | { 148 | this._context = ctx || WGE.webgl; 149 | this.framebuffer = this._context.createFramebuffer(); 150 | }, 151 | 152 | release : function() 153 | { 154 | this._context.deleteFramebuffer(this.framebuffer); 155 | this.framebuffer = null; 156 | this._context = null; 157 | }, 158 | 159 | bind : function() 160 | { 161 | this._context.bindFramebuffer(this._context.FRAMEBUFFER, this.framebuffer); 162 | }, 163 | 164 | bindTexture2D : function(texObj, attachment) 165 | { 166 | this.bind(); 167 | var webgl = this._context; 168 | var attach = isNaN(attachment) ? attachment : webgl[attachment]; 169 | webgl.framebufferTexture2D(webgl.FRAMEBUFFER, webgl[attach], webgl.TEXTURE_2D, texObj, 0); 170 | 171 | if(webgl.checkFramebufferStatus(webgl.FRAMEBUFFER) != webgl.FRAMEBUFFER_COMPLETE) 172 | { 173 | console.error("WGE.Framebuffer - bindTexture2D - Frame buffer is not completed."); 174 | } 175 | } 176 | }); 177 | 178 | WGE.Shader = WGE.Class( 179 | { 180 | shaderType : null, 181 | shader : null, 182 | _context : null, 183 | 184 | //shaderType 取值: 185 | // 1. (webgl对象).VERTEX_SHADER 或者 (webgl对象).FRAGMENT_SHADER 186 | // 2. 字符串 "VERTEX_SHADER" 或者 "FRAGMENT_SHADER" 187 | initialize : function(ctx, shaderType, shaderCode) 188 | { 189 | var webgl = ctx || WGE.webgl; 190 | this._context = webgl; 191 | this.shaderType = typeof shaderType == 'string' ? webgl[shaderType] : shaderType; 192 | 193 | this.shader = webgl.createShader(this.shaderType); 194 | 195 | if(shaderCode) 196 | { 197 | loadShaderCode(shaderCode); 198 | } 199 | }, 200 | 201 | release : function() 202 | { 203 | this._context.deleteShader(this.shader); 204 | this.shader = this.shaderType = null; 205 | }, 206 | 207 | loadShaderCode : function(shaderCode) 208 | { 209 | var webgl = this._context; 210 | webgl.shaderSource(this.shader, shaderCode); 211 | webgl.compileShader(this.shader); 212 | if (!webgl.getShaderParameter(this.shader, webgl.COMPILE_STATUS)) 213 | { 214 | console.error(webgl.getShaderInfoLog(this.shader), this.shaderType) 215 | return false; 216 | } 217 | return true; 218 | }, 219 | 220 | loadShaderFromTag : function(tagID) 221 | { 222 | return this.loadShaderCode(WGE.getContentByID(tagID)); 223 | }, 224 | 225 | //非异步加载,若网络较差则可能等待较长时间。 226 | loadShaderFromURL : function(url) 227 | { 228 | return this.loadShaderCode(WGE.requestTextByURL(url)); 229 | } 230 | 231 | }); 232 | 233 | WGE.Program = WGE.Class( 234 | { 235 | _context : null, 236 | vertShader : null, 237 | fragShader : null, 238 | program : null, 239 | 240 | initialize : function(ctx) 241 | { 242 | var webgl = ctx || WGE.webgl; 243 | this._context = webgl; 244 | this.program = webgl.createProgram(); 245 | this.vertShader = new WGE.Shader(webgl, webgl.VERTEX_SHADER); 246 | this.fragShader = new WGE.Shader(webgl, webgl.FRAGMENT_SHADER); 247 | }, 248 | 249 | release : function() 250 | { 251 | this._context.deleteProgram(this.program); 252 | this.vertShader.release(); 253 | this.fragShader.release(); 254 | this._context = this.vertShader = this.fragShader = this.program = null; 255 | }, 256 | 257 | //简便用法, 一步完成shader设定 258 | initWithShaderCode : function(vsh, fsh) 259 | { 260 | if(!(vsh && fsh)) 261 | return false; 262 | return this.vertShader.loadShaderCode(vsh) && 263 | this.fragShader.loadShaderCode(fsh); 264 | }, 265 | 266 | initWithShaderTag : function(vshTag, fshTag) 267 | { 268 | if(!(vsh && fsh)) 269 | return false; 270 | return this.vertShader.loadShaderFromTag(vshTag) && 271 | this.fragShader.loadShaderFromTag(fshTag); 272 | }, 273 | 274 | initWithShaderURL : function(vshURL, fshURL) 275 | { 276 | if(!vshURL && fshURL) 277 | return false; 278 | return this.vertShader.loadShaderFromURL(vshURL) && 279 | this.fragShader.loadShaderFromURL(fshURL); 280 | }, 281 | 282 | loadFragmentShaderCode : function(shaderCode) 283 | { 284 | return this.fragShader.loadShaderCode(shaderCode); 285 | }, 286 | 287 | loadVertexShaderCode : function(shaderCode) 288 | { 289 | return this.vertShader.loadShaderCode(shaderCode); 290 | }, 291 | 292 | loadFragmentShaderFromTag : function(shaderCode) 293 | { 294 | return this.fragShader.loadShaderFromTag(shaderCode); 295 | }, 296 | 297 | loadVertexShaderFromTag : function(shaderCode) 298 | { 299 | return this.vertShader.loadShaderFromTag(shaderCode); 300 | }, 301 | 302 | loadFragmentShaderFromURL : function(shaderCode) 303 | { 304 | return this.fragShader.loadShaderFromURL(shaderCode); 305 | }, 306 | 307 | loadVertexShaderFromURL : function(shaderCode) 308 | { 309 | return this.vertShader.loadShaderFromURL(shaderCode); 310 | }, 311 | 312 | //在两种shader均指定并且初始化成功后,再执行链接操作 313 | link : function() 314 | { 315 | var webgl = this._context; 316 | webgl.attachShader(this.program, this.vertShader.shader); 317 | webgl.attachShader(this.program, this.fragShader.shader); 318 | webgl.linkProgram(this.program); 319 | if (!webgl.getProgramParameter(this.program, webgl.LINK_STATUS)) 320 | { 321 | console.error(webgl.getProgramInfoLog(this.program)); 322 | return false; 323 | } 324 | return true; 325 | }, 326 | 327 | bind : function() 328 | { 329 | this._context.useProgram(this.program); 330 | }, 331 | 332 | uniformLocation : function(uniformName) 333 | { 334 | var loc = this._context.getUniformLocation(this.program, uniformName); 335 | if(!loc) 336 | { 337 | console.error("Uniform Name " + uniformName + " doesnot exist!"); 338 | } 339 | return loc; 340 | }, 341 | 342 | attribLocation : function(attribName) 343 | { 344 | return this._context.getAttribLocation(this.program, attribName); 345 | }, 346 | 347 | //Should be called before "bind()" 348 | bindAttribLocation : function(attribName, location) 349 | { 350 | this._context.bindAttribLocation(this.program, location, attribName); 351 | }, 352 | 353 | sendUniform1f : function(uniformName, v1) 354 | { 355 | var loc = this.uniformLocation(uniformName); 356 | this._context.uniform1f(loc, v1); 357 | }, 358 | 359 | sendUniform2f : function(uniformName, v1, v2) 360 | { 361 | var loc = this.uniformLocation(uniformName); 362 | this._context.uniform2f(loc, v1, v2); 363 | }, 364 | 365 | sendUniform3f : function(uniformName, v1, v2, v3) 366 | { 367 | var loc = this.uniformLocation(uniformName); 368 | this._context.uniform3f(loc, v1, v2, v3); 369 | }, 370 | 371 | sendUniform4f : function(uniformName, v1, v2, v3, v4) 372 | { 373 | var loc = this.uniformLocation(uniformName); 374 | this._context.uniform4f(loc, v1, v2, v3, v4); 375 | }, 376 | 377 | sendUniform1i : function(uniformName, v1) 378 | { 379 | var loc = this.uniformLocation(uniformName); 380 | this._context.uniform1i(loc, v1); 381 | }, 382 | 383 | sendUniform2i : function(uniformName, v1, v2) 384 | { 385 | var loc = this.uniformLocation(uniformName); 386 | this._context.uniform2i(loc, v1, v2); 387 | }, 388 | 389 | sendUniform3i : function(uniformName, v1, v2, v3) 390 | { 391 | var loc = this.uniformLocation(uniformName); 392 | this._context.uniform3i(loc, v1, v2, v3); 393 | }, 394 | 395 | sendUniform4i : function(uniformName, v1, v2, v3, v4) 396 | { 397 | var loc = this.uniformLocation(uniformName); 398 | this._context.uniform4i(loc, v1, v2, v3, v4); 399 | }, 400 | 401 | sendUniformMat2 : function(uniformName, transpose, matrix) 402 | { 403 | var loc = this.uniformLocation(uniformName); 404 | this._context.uniformMatrix2fv(loc, transpose, matrix); 405 | }, 406 | 407 | sendUniformMat3 : function(uniformName, transpose, matrix) 408 | { 409 | var loc = this.uniformLocation(uniformName); 410 | this._context.uniformMatrix3fv(loc, transpose, matrix); 411 | }, 412 | 413 | sendUniformMat4 : function(uniformName, transpose, matrix) 414 | { 415 | var loc = this.uniformLocation(uniformName); 416 | this._context.uniformMatrix4fv(loc, transpose, matrix); 417 | } 418 | 419 | }); -------------------------------------------------------------------------------- /WGE/wgeColor.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wysaid/WebGL-Lessons/5020e8a9a78491c1181d0b2397d4135b13c14497/WGE/wgeColor.js -------------------------------------------------------------------------------- /WGE/wgeGUI.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | * wgeGUI.js 4 | * 5 | * Created on: 2014-6-23 6 | * Author: Wang Yang 7 | * blog: http://blog.wysaid.org 8 | */ 9 | 10 | /* 11 | 简介: 提供简单的界面接口. 12 | */ 13 | 14 | WGE.GUIInterface = WGE.Class( 15 | { 16 | boundingWidth : undefined, 17 | boundingHeight : undefined, 18 | canvas : undefined, 19 | father : undefined, 20 | fatherWidthName : ['width', 'clientWidth', 'offsetWidth'], 21 | fatherHeightName : ['height', 'clientHeight', 'offsetHeight'], 22 | resizeEvent : null, //Event由子类或者用户设置 23 | mouseMoveEvent : null, 24 | mouseDownEvent : null, 25 | mouseUpEvent : null, 26 | mouseClickEvent : null, 27 | mouseDBLClickEvent : null, 28 | mouseOverEvent : null, 29 | mouseOutEvent : null, 30 | wheelEvent : null, 31 | keyDownEvent : null, 32 | keyUpEvent : null, 33 | keypressEvent : null, 34 | 35 | _animationRequest : null, 36 | startTime : 0, 37 | lastTime : 0, 38 | nowTime : 0, 39 | 40 | _forceAutoResize : false, //强制resize,设置标记后将在每一帧检测是否需要resize 41 | 42 | //将在gui 重新绑定father或者release时解除对于原有father的绑定。 43 | _events : null, 44 | 45 | initialize : function(fatherObj) 46 | { 47 | this.setupEvents(); 48 | this.bindFather(fatherObj); 49 | }, 50 | 51 | setupEvents : function() 52 | { 53 | //Mark : onresize 添加至此无效。 54 | this._events = { 55 | 'mousemove' : this.onmousemove.bind(this), 56 | 'click' : this.onclick.bind(this), 57 | 'mousedown' : this.onmousedown.bind(this), 58 | 'mouseup' : this.onmouseup.bind(this), 59 | 'dblclick' : this.ondblclick.bind(this), 60 | 'mouseover' : this.onmouseover.bind(this), 61 | 'mouseout' : this.onmouseout.bind(this), 62 | 'keydown' : this.onkeydown.bind(this), 63 | 'keypress' : this.onkeypress.bind(this), 64 | 'keyup' : this.onkeyup.bind(this), 65 | //wheel 方法在firefox中不受支持。 66 | 'wheel' : this.onwheel.bind(this), 67 | }; 68 | 69 | if(document.body.onwheel === undefined) 70 | { 71 | this._events['mousewheel'] = this._events['wheel']; 72 | this._events['wheel'] = undefined; 73 | } 74 | }, 75 | 76 | release : function() 77 | { 78 | this.canvas = undefined; 79 | if(this.father && this.father.removeEventListener) 80 | { 81 | for(var i in _events) 82 | { 83 | this.father.removeEventListener(i, _events[i]); 84 | } 85 | } 86 | this.father = undefined; 87 | }, 88 | 89 | //设置在运行过程中,强制对界面长宽进行检测和刷新。 90 | //如果您已经手动将onresize 事件添加到 body的onresize属性中,则没必要启用。 91 | forceAutoResize : function(flag) 92 | { 93 | this._forceAutoResize = flag; 94 | }, 95 | 96 | isStarted : function() 97 | { 98 | return !!this._animationRequest; 99 | }, 100 | 101 | start : function() 102 | { 103 | if(this._animationRequest) 104 | { 105 | console.warn("wgeGUI is already started!"); 106 | return; 107 | } 108 | // this.onresize(); 109 | this.startTime = Date.now(); 110 | this.lastTime = this.startTime; 111 | this._animationRequest = requestAnimationFrame(this._run.bind(this)); 112 | }, 113 | 114 | stop : function() 115 | { 116 | if(this._animationRequest) 117 | { 118 | cancelAnimationFrame(this._animationRequest); 119 | this._animationRequest = null; 120 | } 121 | }, 122 | 123 | _run : function() 124 | { 125 | if(this._forceAutoResize) 126 | { 127 | this.onresize(); 128 | } 129 | 130 | this.nowTime = Date.now(); 131 | var deltaTime = this.nowTime - this.lastTime; 132 | 133 | this.update(deltaTime); 134 | this.render(deltaTime); 135 | 136 | this.lastTime = this.nowTime; 137 | 138 | //如果在_run函数执行期间调用过stop,则不再继续请求frame. 139 | if(this._animationRequest) 140 | this._animationRequest = requestAnimationFrame(this._run.bind(this)); 141 | }, 142 | 143 | //update和render 由用户自定义, 144 | //类型为函数,包含一个参数表示两次调用之间的间隔时间(ms) 145 | update : function(deltaTime) 146 | { 147 | 148 | }, 149 | 150 | render : function(deltaTime) 151 | { 152 | 153 | }, 154 | 155 | //由于canvas元素不支持部分事件(如根据stype属性的百分比宽高设置实际像素宽高), 156 | //需要将它绑定到一个支持此类事件的DOM上,如body, div等 157 | //画面将占满整个father元素,且根据father元素自适应 158 | bindFather : function(fatherObj, width, height) 159 | { 160 | if(typeof fatherObj != 'object') 161 | { 162 | return false; 163 | } 164 | 165 | this.release(); 166 | 167 | if(width && height) 168 | { 169 | this.boundingWidth = width; 170 | this.boundingHeight = height; 171 | } 172 | 173 | this.canvas = WGE.CE('canvas'); 174 | fatherObj.appendChild(this.canvas); 175 | this.father = fatherObj; 176 | 177 | for(var eventName in this._events) 178 | { 179 | fatherObj.addEventListener(eventName, this._events[eventName]); 180 | } 181 | 182 | var widthName = null, heightName = null; 183 | 184 | for(var i in this.fatherWidthName) 185 | { 186 | if(typeof fatherObj[this.fatherWidthName[i]] == 'number') 187 | { 188 | widthName = this.fatherWidthName[i]; 189 | break; 190 | } 191 | } 192 | 193 | this.fatherWidthName = widthName; 194 | 195 | for(var i in this.fatherHeightName) 196 | { 197 | if(typeof fatherObj[this.fatherHeightName[i]] == 'number') 198 | { 199 | heightName = this.fatherHeightName[i]; 200 | break; 201 | } 202 | } 203 | 204 | this.fatherHeightName = heightName; 205 | 206 | this.onresize(); 207 | return true; 208 | }, 209 | 210 | //经过测试,发现大部分元素不支持onresize, 建议手动添加至body中。 211 | onresize : function(e) 212 | { 213 | var cvs = this.canvas, father = this.father; 214 | 215 | var width = this.boundingWidth || father[this.fatherWidthName]; 216 | var height = this.boundingHeight || father[this.fatherHeightName]; 217 | 218 | //当 forceAutoResize 启用时,可以有效减少事件调用。 219 | if(cvs.width != width || cvs.height != height) 220 | { 221 | cvs.width = width; 222 | cvs.height = height; 223 | if(typeof this.resizeEvent == 'function') 224 | this.resizeEvent(e); 225 | } 226 | }, 227 | 228 | onmousemove : function(e) 229 | { 230 | if(this.mouseMoveEvent) 231 | { 232 | this.mouseMoveEvent(e, e.offsetX || e.layerX, e.offsetY || e.layerY); 233 | } 234 | }, 235 | 236 | onclick : function(e) 237 | { 238 | if(this.mouseClickEvent) 239 | { 240 | this.mouseClickEvent(e, e.offsetX || e.layerX, e.offsetY || e.layerY); 241 | } 242 | }, 243 | 244 | onmousedown : function(e) 245 | { 246 | if(this.mouseDownEvent) 247 | { 248 | this.mouseDownEvent(e, e.offsetX || e.layerX, e.offsetY || e.layerY); 249 | } 250 | }, 251 | 252 | onmouseup : function(e) 253 | { 254 | if(this.mouseUpEvent) 255 | { 256 | this.mouseUpEvent(e, e.offsetX || e.layerX, e.offsetY || e.layerY); 257 | } 258 | }, 259 | 260 | ondblclick : function(e) 261 | { 262 | if(this.mouseDBLClickEvent) 263 | { 264 | this.mouseDBLClickEvent(e, e.offsetX || e.layerX, e.offsetY || e.layerY); 265 | } 266 | }, 267 | 268 | onmouseover : function(e) 269 | { 270 | if(this.mouseOverEvent) 271 | { 272 | this.mouseOverEvent(e, e.offsetX || e.layerX, e.offsetY || e.layerY); 273 | } 274 | }, 275 | 276 | onmouseout : function(e) 277 | { 278 | if(this.mouseOutEvent) 279 | { 280 | this.mouseOutEvent(e, e.offsetX || e.layerX, e.offsetY || e.layerY); 281 | } 282 | }, 283 | 284 | onwheel : function(e) 285 | { 286 | if(this.wheelEvent) 287 | this.wheelEvent(e, e.deltaY || e.wheelDelta); 288 | }, 289 | 290 | //注: 如果div元素无法响应key事件,则很可能是因为div无法获得焦点,请设置tabindex 291 | onkeydown : function() 292 | { 293 | if(this.keyDownEvent) 294 | this.keyDownEvent.apply(this, arguments); 295 | }, 296 | 297 | onkeypress : function() 298 | { 299 | if(this.keypressEvent) 300 | this.keypressEvent.apply(this, arguments); 301 | }, 302 | 303 | onkeyup : function() 304 | { 305 | if(this.keyUpEvent) 306 | this.keyUpEvent.apply(this, arguments); 307 | } 308 | 309 | }); 310 | 311 | /* 312 | 313 | ##使用方式 314 | 315 | 例: 316 | 317 | //此GUI将占满整个屏幕,并随机在屏幕中绘制小红点 318 | //如果鼠标按下的话,小红点将绘制到鼠标点击的位置。 319 | 320 | var myGUI = WGE.Class(WGE.GUIInterface, 321 | { 322 | context : undefined, 323 | x : 0, 324 | y : 0, 325 | isMouseDown : false, 326 | 327 | bindFather : function(fatherObj) 328 | { 329 | if(WGE.GUIInterface.bindFather.call(this, fatherObj)); 330 | { 331 | this.context = this.canvas.getContext('2d'); 332 | return !!this.context; 333 | } 334 | return false; 335 | }, 336 | 337 | update : function() 338 | { 339 | if(!this.isMouseDown) 340 | { 341 | this.x = Math.random() * this.canvas.width; 342 | this.y = Math.random() * this.canvas.height; 343 | } 344 | }, 345 | 346 | render : function() 347 | { 348 | var ctx = this.context; 349 | var cvs = this.canvas; 350 | ctx.clearRect(0, 0, cvs.width, cvs.height); 351 | this.context.fillStyle = "#f00"; 352 | ctx.fillRect(this.x, this.y, 100, 100); 353 | ctx.fillText("click me!", 10, 10); 354 | }, 355 | 356 | mouseDownEvent : function(e) 357 | { 358 | this.isMouseDown = true; 359 | this.x = e.x || e.offsetX; 360 | this.y = e.y || e.offsetY; 361 | }, 362 | 363 | mouseUpEvent : function(e) 364 | { 365 | this.isMouseDown = false; 366 | }, 367 | 368 | mouseMoveEvent : function(e) 369 | { 370 | if(this.isMouseDown) 371 | { 372 | this.x = e.offsetX || e.layerX; 373 | this.y = e.offsetY || e.layerY; 374 | } 375 | } 376 | }); 377 | 378 | //// 调用代码如下: 379 | 380 | var gui = new myGUI(document.body); 381 | 382 | //下面两句都是使整个ui大小跟随父元素变化,推荐前者。嫌麻烦或者跟已有代码有冲突(比如body的onresize有别的代码会随时更改)写成后者也没关系。 383 | document.body.setAttribute("onresize", "gui.onresize(event);"); //较好 384 | //gui.forceAutoResize(true); //这一句和上一句功能类似,这种方法可保证正确性 385 | 386 | gui.start(); 387 | 388 | //// 怎么样,简单吧?! 389 | 390 | */ -------------------------------------------------------------------------------- /WebGL-Lessons.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{BB467242-7E58-4A90-AE67-8DC2CBEB3E10}" 5 | ProjectSection(SolutionItems) = preProject 6 | index.html = index.html 7 | README.html = README.html 8 | EndProjectSection 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Lesson 01", "Lesson 01", "{E8B1EC7E-DEC6-412A-B032-C1241FDC1243}" 11 | ProjectSection(SolutionItems) = preProject 12 | Lesson1\index.html = Lesson1\index.html 13 | EndProjectSection 14 | EndProject 15 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Lesson 02", "Lesson 02", "{A8D87E5D-FE46-40B4-B0FD-95F7F2CD7485}" 16 | ProjectSection(SolutionItems) = preProject 17 | Lesson2\index.html = Lesson2\index.html 18 | EndProjectSection 19 | EndProject 20 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Lesson 03", "Lesson 03", "{6092CE3D-AB43-4273-8430-E7F8DA57A0C2}" 21 | ProjectSection(SolutionItems) = preProject 22 | Lesson3\index.html = Lesson3\index.html 23 | EndProjectSection 24 | EndProject 25 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Lesson 04", "Lesson 04", "{B4F3FF60-55CF-414E-A3A9-45DC03C2A0CB}" 26 | ProjectSection(SolutionItems) = preProject 27 | Lesson4\index.html = Lesson4\index.html 28 | EndProjectSection 29 | EndProject 30 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Lesson 05", "Lesson 05", "{9D733FFF-2C69-4BB1-A7A0-56305C878E4E}" 31 | ProjectSection(SolutionItems) = preProject 32 | Lesson5\index.html = Lesson5\index.html 33 | EndProjectSection 34 | EndProject 35 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Lesson 06", "Lesson 06", "{14B92EE4-99F5-4062-B288-97DB93C2EAE6}" 36 | ProjectSection(SolutionItems) = preProject 37 | Lesson6\index.html = Lesson6\index.html 38 | EndProjectSection 39 | EndProject 40 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Lesson 07", "Lesson 07", "{6316C012-ADAB-4195-9794-964E4E53C8AC}" 41 | ProjectSection(SolutionItems) = preProject 42 | Lesson7\index.html = Lesson7\index.html 43 | EndProjectSection 44 | EndProject 45 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Lesson 08", "Lesson 08", "{BF897705-8A03-450D-BAE7-E4F0E5C7F9EC}" 46 | ProjectSection(SolutionItems) = preProject 47 | Lesson8\index.html = Lesson8\index.html 48 | EndProjectSection 49 | EndProject 50 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Lesson 09", "Lesson 09", "{1E5C9B07-AADE-4C3E-B21B-A6A1E3539493}" 51 | ProjectSection(SolutionItems) = preProject 52 | Lesson9\index.html = Lesson9\index.html 53 | EndProjectSection 54 | EndProject 55 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Lesson 10", "Lesson 10", "{0726F6B9-5A9E-4572-BD9E-CED124AE3A96}" 56 | ProjectSection(SolutionItems) = preProject 57 | Lesson10\index.html = Lesson10\index.html 58 | EndProjectSection 59 | EndProject 60 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Lessons", "Lessons", "{94D2860E-6D47-4B10-9987-6A599F72F7C0}" 61 | EndProject 62 | Global 63 | GlobalSection(SolutionProperties) = preSolution 64 | HideSolutionNode = FALSE 65 | EndGlobalSection 66 | GlobalSection(NestedProjects) = preSolution 67 | {94D2860E-6D47-4B10-9987-6A599F72F7C0} = {BB467242-7E58-4A90-AE67-8DC2CBEB3E10} 68 | {1E5C9B07-AADE-4C3E-B21B-A6A1E3539493} = {94D2860E-6D47-4B10-9987-6A599F72F7C0} 69 | {E8B1EC7E-DEC6-412A-B032-C1241FDC1243} = {94D2860E-6D47-4B10-9987-6A599F72F7C0} 70 | {0726F6B9-5A9E-4572-BD9E-CED124AE3A96} = {94D2860E-6D47-4B10-9987-6A599F72F7C0} 71 | {A8D87E5D-FE46-40B4-B0FD-95F7F2CD7485} = {94D2860E-6D47-4B10-9987-6A599F72F7C0} 72 | {6092CE3D-AB43-4273-8430-E7F8DA57A0C2} = {94D2860E-6D47-4B10-9987-6A599F72F7C0} 73 | {B4F3FF60-55CF-414E-A3A9-45DC03C2A0CB} = {94D2860E-6D47-4B10-9987-6A599F72F7C0} 74 | {9D733FFF-2C69-4BB1-A7A0-56305C878E4E} = {94D2860E-6D47-4B10-9987-6A599F72F7C0} 75 | {14B92EE4-99F5-4062-B288-97DB93C2EAE6} = {94D2860E-6D47-4B10-9987-6A599F72F7C0} 76 | {6316C012-ADAB-4195-9794-964E4E53C8AC} = {94D2860E-6D47-4B10-9987-6A599F72F7C0} 77 | {BF897705-8A03-450D-BAE7-E4F0E5C7F9EC} = {94D2860E-6D47-4B10-9987-6A599F72F7C0} 78 | EndGlobalSection 79 | EndGlobal 80 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-slate -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WebGL Simple Lesson - by wysaid 5 | 9 | 10 | 11 |
12 |

WebGL Simple Lessons.

13 |

(from http://blog.wysaid.org/)

14 | 25 |
26 |
27 | 30 |
31 | 32 | 33 | --------------------------------------------------------------------------------