Web Worker之Milo Yip的光线追踪
以上是milo yip写的《用JavaScript玩转计算机图形学(一)光线追踪入门》中代码的Web Worker版本。我将代码略作修改使其能够在Web Worker中运行,并且添加的uv坐标使得Checker材质能更通用,和折射(最后3个示例)。当然,我这篇文章只说Web Worker部分。
Web Worker简介
一个Web Worker对象就对应着一个操作系统的线程。使用如下方式创建:
var worker = new Worker("foo.js");
Worker构造函数的参数是个url,在新建的线程中就执行foo.js中的代码。但是在foo.js中的代码是有限制的,它不能直接访问在主线程也就是html页面中的任何对象,而只能使用消息来进行通信。
例如,html中的代码:
var worker = new Worker("foo.js"); worker.onmessage = function(e){ console.log(e.data); } worker.postMessage("world");
foo.js中的代码
this.onmessage = function(e){ postMessage("hello,"+e.data); }
在上述代码中,html中新建一个Worker对象,并监听message事件(也可以使用AddEventListener),最后发送一个"world"字符串给Worker。
在foo.js中,监听message时间,当收到来自于主线程的消息时,在消息前面加上"hello,"并回复。
在大多数情况下,都是html页面中的代码发送一个消息给Worker,Worker进行复杂的计算,并且将结果用消息发送给html页面。
不过,postMessage能发送的消息并不能是任意的对象,这个对象不能包含函数,任何dom对象,等等。幸运的是,可以是canvas中的ImageData对象,上面的示例传递的就是ImageData对象。(具体哪些可以,哪些不可以请参考:https://developer.mozilla.org/en/DOM/The_structured_clone_algorithm)
Inline Web Worker
Worker对象构造函数参数一定要是一个url,那如何才能将Web Worker代码和html写在同一个文件中呢? 代码如下:
function createBlobUrl(text) { var bb; if (window.BlobBuilder) bb = new BlobBuilder(); else if (window.WebKitBlobBuilder) bb = new WebKitBlobBuilder(); else if (window.MozBlobBuilder) bb = new MozBlobBuilder(); else if (window.Blob) bb = new Blob(); return null; bb.append(text); if (window.URL && URL.createObjectURL) { return window.URL.createObjectURL(bb.getBlob()); } else if (window.webkitURL && webkitURL.createObjectURL) { return window.webkitURL.createObjectURL(bb.getBlob()); } else return null; } function createInlineWebWorker(id) { var url = createBlobUrl(document.getElementById(id).textContent); if (!url) return null; return new Worker(url); }
CreateBlobUrl(text)会创建一个url,浏览器访问这个url就会返回text的内容(不过由于chrome一个该死的wontfix的bug,此函数对于保存的本地html不起作用)。 试试,复制下面的url,并在浏览器的新页面打开
是不是内容是abcdefg呢?
这样就可以这么写了:
<script id="worker1" type="webworker"> this.onmessage = function(e){ postMessage("hello,"+e.data); } </script> <script> var worker = createInlineWebWorker("worker1") </script>
注意哦,第一个script的type是worker,所以浏览器并不会执行其代码。而且由于使用script标签,所以中间的内容并不需要html escape也不会显示出来。
最后
我来说一下,上面光线追踪中Web Worker代码是怎么写的。 Web Worker代码如下:
self.onmessage = function(e) { var data = e.data; if (data.type=="eval") { var ret; var code = data.code; try { ret = eval(code); } catch(err) { postMessage({type:"log",msg:err.message+"\nline:"+err.lineNumber}); postMessage({type:"error",msg:err.message+"\nline:"+err.lineNumber}); return; } postMessage({type:"result",result:ret}); } }
很简单吧,就是在Web Worker中运行一下html页面上发过来的代码(不是函数,是代码的文本哦)。如果成功,则将返回值返回给页面;出错则将出错信息返回给页面。例如,你想在Web worker中计算1到100的和,则代码如下:
function calc(n) { var ret = 0; for (var i=1;i<=n;++i) ret += i; return ret; } worker.onmessage = function(e){ if (e.data.type == "result") { alert(e.data.result) } } worker.postMessage({ type:"eval", code:calc.toString()+";calc(100);" });
好啦,就写到这里啦。其实,我对Web Worker的了解就是参考了google "Web Worker"中第一页的几篇文章,尤其这篇比较通俗易懂:http://www.html5rocks.com/en/tutorials/workers/basics/希望大家能认真看一下。
最后的最后,希望下次我能写篇文章来说一下光线追踪的折射。