1 output an image

写在前面:
之前一直在想,是不是可以使用简单的方式作为切入点学习渲染?然而在寻找答案这条路上走了很多弯路:
考虑过Ogre,然而不喜欢那些接口。
考虑过U3D,然而只考虑了一瞬间哈哈。
考虑过UE4,但是面对可视化材质编辑器和纷繁复杂,一日一变的usf,又感觉无从下手
考虑过Creator3D,然而看了一眼论坛的公测日志,我又犹豫了
考虑过shader toy,使用pixel shader,直觉告诉我这不是我想要的
考虑过DX12,可是复杂的资源管理让我感到在戴着枷锁跳舞

在这之前看了不少书:《全局光照技术》,《Game Engine Architecture 3th》,《Real Time Rendering 4th》,都是很棒的书,看得似懂非懂,看的过程中不乏有时激动得很想实践一番,奈何不知道从哪里开始。

昨晚辗转反侧之后,突然想到之前@MiloYip在知乎的回答:用JavaScript玩转计算机图形学(一)光线追踪入门
今天来尝试了一番之后,我相信这就是我想要的。@MiloYip在更新了两章用js玩光追的内容就鸽了,可能是发现了更有挑战的领域了吧。但是对我来说,一切从简是一个很好的开始。

正文
从这里开始,就是《ray tracing in one weekend》的冒险之旅了。
所有的内容,都将使用javascript实践。

光线追踪,简单的理解,我们不断的去跟踪进入眼球的光线,查看每条光线的辐照度,辐照度可以RGBA表示,至于RGBA到一个能看到的画面的映射,我们暂时不去深究,让我们立足于RGBA这一边界。
抽象来看,每条进入眼球的光线,相当于都带了一个RGBA值,这些光线在进入眼球的过程中,会先打向一个方形的平面(并将光线的RGBA值打在交点上(叠加),于是平面上那个点就有了颜色),这个平面就是我们最终看到的画面。

好了,问题来了,最基础的,我们如何去显示一个画面?
最简单的方式,一个方形的画面,我们可以用数组来存储这些信息(RGBA值)。通过修改这个数组的信息,理论上我们就相当于在修改画面的表现了。
刚刚好,canvas就有这样的接口。

获取像素数组

1
2
3
4
5
let ctx = canvas.getContext('2d')
let width = canvas.attributes.width.value
let height = canvas.attributes.height.value
let imgdata = ctx.getImageData(0,0,width,height)
let pixels = imgdata.data // 像素数组(一维)

使用像素数组绘制画面

1
ctx.putImageData(imgdata, 0, 0)

这是一个很好的开始,剩下的就只要从pixels里面做文章了。

回到标题,让我们输出一个画面,将数组坐标绘制成颜色!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<html>
<canvas width="256" height="256" id="screen"></canvas>
<script>
function render(canvas)
{
// 获取像素数组
let ctx = canvas.getContext("2d")
let w = canvas.attributes.width.value
let h = canvas.attributes.height.value
ctx.fillStyle = "rgb(0,0,0)"
ctx.fillRect(0, 0, w, h)
let imgdata = ctx.getImageData(0, 0, w, h)
let pixels = imgdata.data

//将数组坐标(通过除以数组长度映射到[0,1])当作颜色绘制
let i = 0
for(let y = 0; y < h; y++) {
let sy = 1 - y / h
for(let x = 0; x < w; x++)
{
let sx = x / w
pixels[i ] = sx * 255
pixels[i + 1] = sy * 255
pixels[i + 2] = 0.2 * 255
pixels[i + 3] = 1 * 255
i+=4
}
}
// 使用像素数组绘制画面
ctx.putImageData(imgdata, 0, 0)
}
let canvas = document.getElementById('screen')
render(canvas)
</script>
</html>

最终画面如下: