年轻人的第一次Three.js

在华工的软件学院,到了大三是要专业分流的 —— 数字媒体、人工智能、移动开发、嵌入式。

一年的专业课,能学的东西其实也有限。不如说主要还是靠自己折腾,就象以前那样。

于是最后我选了数字媒体,做点图形学的实验,应该比较有意思吧(虽然他们都说是火坑

第一节课

老师上课打哈哈,我在下面打哈欠。哗啦哗啦地(←根本没买书)翻课本,看到一个Sierpiński triangle的生成算法,随手用Love2D实现了一个。

tri = {{110, 100}, {10, 500}, {700, 300}}

points = {}

p = {200, 300}

function love.update()
    for i = 1, 1000 do
        local t = tri[math.random(1, 3)]
        local q = {(t[1] + p[1]) / 2, (t[2] + p[2]) / 2, 1, 0, 255, 255}
        table.insert(points, q)
        p = q
    end
end

function love.draw()
    love.graphics.polygon('fill', tri[1][1], tri[1][2], tri[2][1], tri[2][2], tri[3][1], tri[3][2])
    love.graphics.points(points)
end

第二节课

雨实在太大了(下略

没想到上节课上糊的分形就是这节课的作业,只是这回要求「program with OpenGL Shader or WebGL with JavaScript three.js」。

OpenGL也不是第一次打交道了,画个三角形再画些点,想必一两下就弄完了吧(虽然看旁边的Bobby用OpenGL做,好像也不是想象中的那么白给)。

因为不想再配置环境了,干脆就用WebGL做吧,函数大概也差个八九不离十……?

一看Three.js的文档,这长得完全不一样嘛。虽说Three.js是WebGL的封装,WebGL由OpenGL ES派生,OpenGL ES是OpenGL的子集,但是乍一看也没看到2D绘图函数,就有点脑壳疼了。是不是还有个Two.js啊(

不过都打开了VS Code,装好了Power Mode插件,甚至新建了html文件(完全不充分的理由),那就这么往下写吧。半个小时应该就能写完了吧。(←最后写了四个小时)

在重复了若干次<阅读文档→抄文档代码→把不能跑的部分拖出来换成StackOverflow上的代码再试试→搞清楚为什么代码不能跑>的过程之后,我终于得到了一个能跑的版本。总体上来讲,JS的开发/调试还是比较轻松的(毕竟有F12)。不过相比C++,它就没能很好的提示函数的参数和描述(也有可能是配置不合理);在DevTools里的Console写代码倒也是满愉快的(x

用Three.js,首先要初始化场景、渲染器和摄像机,有了这仨就能开始画东西。

renderer.render(scene, camera);

接着创建我们需要的Geometry对象,然后往里面填充属性。除此之外,还需要Material,之后就可以用Geometry和Material来创建对象。把创建出来的东西塞进scene里面,就可以渲染了。

主要遇到的坑是Geometry不能动态加点,要实现动画效果需要用BufferGeometry。虽然作业也没要求动画就是了。

追记

回去又审了一遍题,OpenGL要shader,WebGL就不要shader感觉好像也不是很对(万一它写漏了呢

要用上shader并不是什么难事,大体上只需要:

  1. 把对应的material换成ShaderMaterial或者RawShaderMaterial(区别在于前者会引入一些Three.js的私货,比如position之类的,可以让Shader程序简单一些);
  2. 用BufferGeometry替换Geometry。

在定义ShaderMaterial时需要传入shader的代码,例如:

<script id="vertexShader" type="x-shader/x-vertex">
	void main()	{
		gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
	}
</script>

<script id="fragmentShader" type="x-shader/x-fragment">
	void main()	{
		gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
	}
</script>
var material = new THREE.ShaderMaterial({
	vertexShader: document.getElementById('vertexShader').textContent,
	fragmentShader: document.getElementById('fragmentShader').textContent,
	uniforms: uniforms
});

其中,uniforms是外部传入给shader的参数,通过它我们可以实现随时间变化的动画效果

再往下一次的作业是线性代数题,等下一个experiment出了再续吧(真的会有续篇吗

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注