光照

我们能看到物体,是因为光照射在物体上然后反射到我们的眼睛当中。其中的影响因素非常多:观察者的位置、光源的位置、光的颜色、物体表面的颜色、材质和粗糙程度等等。以后我们将会详细探究如何模拟物体的材质,在这篇文章中我们只讨论光源。

平行光源

太阳的尺度相对地球来说非常大,所以可以认为从太阳照射来的光线都是平行的,即太阳是一个平行光源。

模拟平行光源的光照非常简单,当光垂直照射到平面上,即光线方向和平面呈90度角时,这时光照是最强的。如果照射的角度不断变大(或者说光线和平面的夹角不断变小),光照也会随之变弱,当光线方向完全和平面平行时,这时没有光能照射到平面上,光强变成了0。

可以总结出,平行光的光照情况和两个方向有关:光线的方向和受光照平面的朝向。

我们用一个垂直于平面的向量去描述平面的朝向,在图形学中,一般把这个向量称为“法向量”。

我们可以用向量的“点乘”运算来计算光强变化。

点乘也叫数量积,是接受在实数R上的两个向量并返回一个实数值标量的二元运算。点乘运算规则非常简单,将两个向量对应坐标的乘积求和就行了。

这里我们计算的是三维向量,我们用数组来表示向量,写一个简单的方法来计算点乘:

/**

* 点乘运算

* @param {Array} v1 向量v1

* @param {Array} v2 向量v2

* @return {number} 点乘结果

*/

function dot( v1, v2 ) {

return v1[ 0 ] * v2[ 0 ] + v1[ 1 ] * v2[ 1 ] + v1[ 2 ] * v2[ 2 ];

}

还有几个重要的向量运算我们也会用到,在这里我们提前定义好,为减小篇幅,这里省略掉具体实现,代码可以看最后的实例源码。

/**

* 将向量转为单位向量

* @param {Array} v

* @return {Array} 单位向量

*/

function normalize( v ) { /* ... */ }

/**

* 两向量相减

* @param {Array} v1

* @param {Array} v2

* @return {Array}

*/

function sub( v1, v2 ) { /* ... */ }

/**

* 计算一个向量的反方向向量

* @param {Array} v

* @return {Array}

*/

function negate( v ) { /* ... */ }

我们假设页面的左上角为原点O,右方向为x轴正方向,下方向为y轴正方向,垂直屏幕向外的方向为z轴正方向。我们可以这样定义一个宽高都为500的平面:

var plane = {

center: [ 250, 250, 0 ], // 平面中心点坐标

width: 500, // 宽

height: 500, // 高

normal: [ 0, 0, 1 ], // 朝向,即法向量

color: { r: 255, g: 0, b: 0 } // 颜色为红色

}

对于平行光,只需要关心它的方向和颜色,我们可以这样来定义一个平行光源:

var directionalLight = {

direction: [ 0, 0, -1 ], // 从屏幕外垂直照向屏幕

color: { r: 255, g: 255, b: 255 } // 颜色为纯白色

}

平行光的光线都是平行的,所以它照射到平面上各个位置的效果都是一样的,换言之,整个平面都应该是同一个颜色。

根据上面的规则(光强等于光线反方向向量点乘平面法向量),我们可以计算出这个颜色:

// ...

var reverseLightDirection = negate( directionalLight.direction ); // 计算平行光的反方向向量

var intensity = dot( reverseLightDirection, plane.normal ); // 计算两向量点乘

// 计算有光照时的颜色

var color = {

r: intensity * plane.color.r + intensity * directionalLight.r,

g: intensity * plane.color.g + intensity * directionalLight.g,

b: intensity * plane.color.b + intensity * directionalLight.g,

}

var canvas = document.getElementById( 'canvas' );

var ctx = canvas.getElementById( '2d' );

ctx.rect( plane.center[ 0 ], plane.center[ 1 ], plane.width, plane.height );

ctx.fillStyle = 'rgb(' + color.r + ',' + color.g + ',' + color.b ')';

ctx.fill();

我写了一个示例,可以调整光线方向来观察不同方向下的光照效果。

在线运行示例

点光源

在日常生活中,点光源更加常见,白炽灯、台灯等都可以认为是点光源。

首先,我们先定义一个点光源,对于一个点光源来说,我们只需要关心它的位置和颜色:

var pointLight = {

position: [ 250, 250, 100 ], // 光源位于平面中心上方100处

color: { r: 255, g: 255, b: 255 } // 颜色为纯白色

}

光强的计算规则仍然不变:光强等于光线反方向向量点乘平面法向量。但是点光源的光是从一个点发射出来,它们照射到平面上时,所有光线的方向都不一样。所以,我们必须挨个计算平面上所有像素的光强。

这里需要用到canvas提供的putImageData,这个方法可以直接填入一个区域的像素颜色值来绘图。代码如下:

// ...

var imageData = ctx.createImageData( 500, 500 ); // 创建一个ImageData,用来保存像素数据

for ( var x = 0; x < imageData.width; x++ ) {

for ( var y = 0; y < imageData.height; y++ ) {

var index = y * imageData.width + x; // 当前计算的像素点的索引

var point = [ x, y, 0 ];

var normal = [ 0, 0, 1 ];

var reverseLightDirection = normalize( sub( pointLight.position, point ) ); // 光线方向的反方向向量

var light = dot( reverseLightDirection, normal );

imageData.data[ index * 4 ] = pointLight.color.r * intensity + plane.color.r * intensity;

imageData.data[ index * 4 + 1 ] = pointLight.color.g * intensity + plane.color.g * intensity;

imageData.data[ index * 4 + 2 ] = pointLight.color.b * intensity + plane.color.b * intensity;

imageData.data[ index * 4 + 3 ] = 255;

}

}

ctx.putImageData( imageData, 100, 100 );

这样就可以看到结果了:

我写了一个更复杂一点的例子,可以通过鼠标去移动光源,滑动滚轮来改变光源高度:

在线运行示例

动态图看起来有很多圈圈,实际上并没有,可以自己玩一下

WebGL的优势

对于一个500*500的平面,我们去计算它在点光源光照下的颜色,需要挨个计算平面上所有点,需要循环500*500=250000次,这其实是非常低效的。并且在做复杂场景的渲染时,不会只有一个光源,而且还会有投影等计算,计算量将会非常大。

从更底层的角度来说,这是因为每次计算都是由CPU完成的,而CPU只能串行计算,它只能完成一个计算以后才能开始下一次计算,所以非常缓慢。

这种复杂的渲染其实更适合用WebGL来做,因为每一次计算其实前后无关,WebGL可以利用GPU的并行计算能力,同时去计算所有点的光照强度。一个500*500的平面,理论上只需要花一次计算的时间,这个提升是非常大的。

这篇文章也是想通过这个简单的光照计算来引出WebGL,后面的文章我会用WebGL来重新实现这个效果。

WebGL渲染的光照效果

关于我的博客

这篇文章到这里就结束了。

我计划写一系列关于前端图形渲染的文章,将会涵盖常用的前端图形绘制技术:canvas、svg和WebGL。希望通过这一系列文章能让读者对前端的各种图形绘制接口以及图像处理、图形学的基础知识有所了解。希望在分享的同时,也能巩固和复习自己所学知识,和大家共同进步。

如果能帮助到你,欢迎star,这样也能及时追踪博客的更新。

模拟光源 html5,光照渲染——用canvas模拟光照效果相关推荐

  1. html5笔迹画图,html5绘图工具canvas模拟笔迹绘画特效

    特效描述:html5绘图工具 canvas 模拟笔迹 绘画特效.html5绘图工具使用鼠标在网页上进行写字,canvas绘画模拟笔迹特效 代码结构 1. HTML代码 sorry! //定义获取id ...

  2. html酷炫电子时钟效果,逼真的HTML5 canvas模拟时钟特效

    插件描述:thooClock是一款效果非常逼真的HTML5 canvas模拟时钟特效.该时钟特效使用jQuery和HTML5 Canvas API来制作,模拟现实生活中的时钟.并且它还具有定时闹钟的功 ...

  3. 在HTML5中如何利用Canvas处理并存储图片

    HTML5中增加的Canvas元素,配合JS灵活的语法,处理起图片变得异常简单,不需要在客户端用C/C 写一大堆代码,对于熟悉JS的程序员来说,只需要考虑处理图片的逻辑了. canvas中如果想要处理 ...

  4. PixiJS - 基于 WebGL 的超快 HTML5 2D 渲染引擎

    Pixi.js 是一个开源的HTML5 2D 渲染引擎,使用 WebGL 实现,不支持的浏览器会自动降低到 Canvas 实现.PixiJS 的目标是提供一个快速且轻量级的2D库,并能兼容所有设备.此 ...

  5. html5 canvas 椭圆,html5中怎么利用Canvas绘制椭圆

    html5中怎么利用Canvas绘制椭圆 发布时间:2021-07-08 16:32:10 来源:亿速云 阅读:58 作者:Leah html5中怎么利用Canvas绘制椭圆,针对这个问题,这篇文章详 ...

  6. html5 strongeaseinout,HTML5新特性 之canvas标签(Day1-4)(示例代码)

    canvas 1.什么是canvas? 1.1 是HTML5提供的一种新标签 1.2 canvas是一个矩形区域的画布,可以用JavaScript在上面绘画.控制其每一个像素 1.3 canvas 拥 ...

  7. 风一样的HTML5 2D渲染引擎Pixi.js——真正意义的跨平台开发工具

    摘要:Pixi.js是一款超快的开源HTML5 2D渲染引擎,由于其使用了带有Canvas回调功能的WebGL,开发者不需要了解WebGL便可以感受到硬件加速的强大力量.Pixi.js作为JavaSc ...

  8. MatCap模拟光照效果实现

    大家好,我是阿赵 之前介绍过各种光照模型的实现方法.那些光照模型的实现虽然有算法上的不同,但基本上都是灯光方向和法线方向的计算得出的明暗结果. 下面介绍一种叫做MatCap的模拟光照效果,这种方式计算 ...

  9. HTML5的图像系统Canvas与SVG

    在HTML5之前,没有标准的提供可供Javascript脚本调用的图像系统,以前的做法通常是通过Div来画点.线.矩形这种非常"笨"的办法来变相的绘图,虽然很"笨&quo ...

最新文章

  1. c语言220程序,《C语言程序实例大全》原代码220例
  2. webview与js交互
  3. xp sp3 安装不了ie8
  4. 指针运算符 * 说明
  5. Spring学习总结(11)——Spring JMS MessageConverter介绍
  6. 项目:识别Twitter用户性别
  7. linux huge模式设置,Linux HugePages 配置步骤
  8. TFP-161/100/6MM/6MM/MPU
  9. Python——全国二级等级考试
  10. N-Tiers開發方式(ASP/ASP.NET、VB6/VB.NET呼叫使用COM+元件)
  11. CAD-Cass小结(4)——Cass、CAD对图像校正与等高线矢量化
  12. bzoj1937: [Shoi2004]Mst 最小生成树
  13. 基于unity的2D横版游戏开发日志 小结
  14. 【专题5: 硬件设计】 之 【16.二极管/三极管的钳位功能】
  15. 【Node.js】解决中文乱码问题
  16. Windows安装Erlang
  17. php codeigniter安全,php – codeigniter中的安全性
  18. 2020年个人投资总结
  19. [转载][转帖]谈谈我对攻读计算机研究生的看法。。。大牛的文章,见解精深独到...
  20. 图像分类代码演练(一)

热门文章

  1. EMV规范(三)——应用初始化
  2. rk3568 | 瑞芯微平台GPIO引脚驱动编写
  3. 大数据的应用场景都有哪些(医疗篇)
  4. 巴士(DFS迭代加深 + 剪枝)
  5. linux 8g内存只认4g,为什么8g运行内存只有4g可用
  6. 分区助手4.0绿色中文版
  7. 响应式五金机械网站pbootcms模板,蓝色营销型五金配件网站源码下载
  8. Unknown error 1146的解决办法:
  9. 推挽输出、开漏输出和悬空输入等
  10. zookeepr 简介