canvas中模拟光照效果——绿的你发慌

简介: 前言可视化开发中,尤其是在2d视图下,看到一些非常的好玩的特效,五颜六色的光。好的本篇文章就带你去用canvas去模拟你自己想要的效果。涉及到一些数学知识,不过的都是基础的。我还是争取讲的更加通俗易懂一点。

前言


可视化开发中,尤其是在2d视图下,看到一些非常的好玩的特效,五颜六色的光。好的本篇文章就带你去用canvas去模拟你自己想要的效果。涉及到一些数学知识,不过的都是基础的。我还是争取讲的更加通俗易懂一点。


光照



我们能看到物体,是因为光照照射在物体上然后反射到我们的眼睛中,影响光照的因素非常多,位置,光的颜色,物体表面的颜色,材质和粗糙程度。本篇文章讨论一下光源, 光源又分为环境光, 点光源,平行光, 聚光灯。如下图显示:


640.png


平行光


平行光顾名思义光线平行,对于一个平面而言,平面不同区域接收到平行光的入射角一样。对于平行光而言,主要是确定光线的方向,光线方向设定好了,光线的与物体表面入射角就确定了,仅仅设置光线位置是不起作用的。


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


我们用一个垂直于平面的向量去描述平面的朝向,在图形学中,一般把这个向量称为“法向量”。法向量一般只有方向没有长度,下面有个normalize 就是单位长度的1的向量。


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


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


但是这个只是点乘的数学意义, 但是点乘更重要的是他的几何意义:


  1. 「用来判断两个向量是否在同一个方向」


  1. 「判断一个多边形是否正对摄像机」


  1. 「一个向量在另一个向量上的投影」


看图我给大家解释:



640 (1).png



因为点乘的结果是一个标量,所以决定大小的就是向量之间的夹角,cos的函数图像是0-90 是正的, 90-180 是负数嘛。所以点乘和光强的变化十分符合。这里我们计算的是三维向量,我们用数组来表示向量。然后实现一些方法。代码如下:


class Vector3 {
  constructor(x, y, z) {
    this.x = x || 0
    this.y = y || 0
    this.z = z || 0
  }
  //点乘
  dot(vec) {
    return this.x * vec.x + this.y * vec.y + this.z * vec.z
  }
  // 克隆
  clone() {
    return new this.constructor(this.x, this.y, this.z)
  }
  //求长度
  length() {
    return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z)
  }
  multiplyScalar(scalar) {
    this.x *= scalar
    this.y *= scalar
    this.z *= scalar
    return this
  }
  //向量相减
  sub(v) {
    this.x -= v.x
    this.y -= v.y
    this.z -= v.z
    return this
  }
  // 单位化
  normalize() {
    return this.multiplyScalar(1 / this.length())
  }
  // 取反
  negate() {
    this.x = -this.x
    this.y = -this.y
    this.z = -this.z
    return this
  }
}


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


const plane = {
  center: new Vector3(250, 250, 0), // 平面中心点坐标
  width: 500, // 宽
  height: 500, // 高
  normal: new Vector3(0, 0, 1), // 朝向,即法向量
  color: { r: 255, g: 0, b: 0 }, // 颜色为红色
}


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


const directionalLight = {
  direction: new Vector3(0, 0, -1), // 从屏幕外垂直照向屏幕
  color: { r: 255, g: 255, b: 255 }, // 颜色为纯白色
}


平行光的光线都是平行的,所以它照射到平面上各个位置的效果都是一样的,换言之,整个平面都应该是同一个颜色。根据上面的规则(光强等于光线反方向向量「点乘」平面法向量),我们可以计算出这个颜色:


const reverseLightDirection = directionalLight.direction.clone().negate() // 计算平行光的反方向向量
const intensity = reverseLightDirection.dot(plane.normal) // 计算两向量点乘
// 计算有光照时的颜色
const 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,
}


我写了例子去模拟下这个情况:

640.gif




代码例子在我的github上欢迎fork


点光源



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


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


const plane = {
    center: new Vector3(250,250,0),    // 平面中心点坐标
    width: 500,                 // 宽
    height: 500,                // 高
    normal: new Vector3(0,0,1),        // 朝向,即法向量
    color: { r: 0, g: 255, b: 0 }   // 颜色为绿色
}
const pointLight = {
    position: new Vector3(250,250,60),
    color: {
        r: 255,
        g: 255,
        b: 255
    }
}


初始值设置之后, 这里其实要知道canvas 的「createImageData」

「putImageData」 这个方法可以直接填入一个区域的像素颜色值来绘图。光照的效果原理主要是改变图片的每一个像素值, 达到光照的效果;


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


const imageData = ctx.createImageData( plane.width, plane.height );
  function render() {
    for ( let x = 0; x < imageData.width; x++ ) {
      for ( let y = 0; y < imageData.height; y++ ) {
        let index = y * imageData.width + x;
        // 每一个像素点
        let position = new Vector3(x,y,0);
        let normal = new Vector3(0,0,1);
        // 点光源与每个像素点 之间的方向就是 光线的方向
        let currentToLight = pointLight.position.clone().sub(position).normalize();
        let light = currentToLight.dot(normal);
        imageData.data[ index * 4 ] = Math.min( 255, ( pointLight.color.r + plane.color.r ) * light);
        imageData.data[ index * 4 + 1 ] =  Math.min( 255, ( pointLight.color.g + plane.color.g ) * light );
        imageData.data[ index * 4 + 2 ] =  Math.min( 255, ( pointLight.color.b + plane.color.b ) * light );
        imageData.data[ index * 4 + 3 ] = 255;
        }
      }
      ctx.putImageData( imageData, 100, 100 );
  }

效果图如下所示:

640 (1).gif


为了看起来更加炫酷, 我增加了move 和 wheel 事件, move 就是改变点光源的x, y 坐标。


document.addEventListener( 'mousemove', function( e ) {
      pointLight.position.x = e.clientX - 100
      pointLight.position.y = e.clientY - 100
      render()
  }, false )


效果如下:


640 (2).png


总结



本篇主要是简单的介绍了几种光照并在canvas 下的模拟实现, 主要是理解光强的计算方式:反向向量 和 平面的法向量 做点乘。本篇文章所有代码都在我的github上欢迎自己copy下来玩一玩。最后,文章写作不易,如果看完对你有帮助的话,你的点赞和关注是我持续更新的最大动力。如果你也喜欢图形,喜欢可视化,你可以点个关注,后面我会持续分享高质量的文章, 勿忘初心!

目录
打赏
0
0
0
0
10
分享
相关文章
kde
|
5天前
|
Docker镜像加速指南:手把手教你配置国内镜像源
配置国内镜像源可大幅提升 Docker 拉取速度,解决访问 Docker Hub 缓慢问题。本文详解 Linux、Docker Desktop 配置方法,并提供测速对比与常见问题解答,附最新可用镜像源列表,助力高效开发部署。
kde
3026 8
国内如何安装和使用 Claude Code镜像教程 - Windows 用户篇
国内如何安装和使用 Claude Code镜像教程 - Windows 用户篇
562 0
Dify MCP 保姆级教程来了!
大语言模型,例如 DeepSeek,如果不能联网、不能操作外部工具,只能是聊天机器人。除了聊天没什么可做的。
821 9
2025年最新版最细致Maven安装与配置指南(任何版本都可以依据本文章配置)
本文详细介绍了Maven的项目管理工具特性、安装步骤和配置方法。主要内容包括: Maven概述:解释Maven作为基于POM的构建工具,具备依赖管理、构建生命周期和仓库管理等功能。 安装步骤: 从官网下载最新版本 解压到指定目录 创建本地仓库文件夹 关键配置: 修改settings.xml文件 配置阿里云和清华大学镜像仓库以加速依赖下载 设置本地仓库路径 附加说明:包含详细的配置示例和截图指导,适用于各种操作系统环境。 本文提供了完整的Maven安装和配置
2025年最新版最细致Maven安装与配置指南(任何版本都可以依据本文章配置)
【保姆级图文详解】大模型、Spring AI编程调用大模型
【保姆级图文详解】大模型、Spring AI编程调用大模型
345 7
【保姆级图文详解】大模型、Spring AI编程调用大模型
Excel数据治理新思路:引入智能体实现自动纠错【Python+Agent】
本文介绍如何利用智能体与Python代码批量处理Excel中的脏数据,解决人工录入导致的格式混乱、逻辑错误等问题。通过构建具备数据校验、异常标记及自动修正功能的系统,将数小时的人工核查任务缩短至分钟级,大幅提升数据一致性和办公效率。
DeepSeek R1+Open WebUI实现本地知识库的搭建和局域网访问
本文介绍了使用 DeepSeek R1 和 Open WebUI 搭建本地知识库的详细步骤与注意事项,涵盖核心组件介绍、硬件与软件准备、模型部署、知识库构建及问答功能实现等内容,适用于本地文档存储、向量化与检索增强生成(RAG)场景的应用开发。
367 0
让AI时代的卓越架构触手可及,阿里云技术解决方案开放免费试用
阿里云推出基于场景的解决方案免费试用活动,新老用户均可领取100点试用点,完成部署还可再领最高100点,相当于一年可获得最高200元云资源。覆盖AI、大数据、互联网应用开发等多个领域,支持热门场景如DeepSeek部署、模型微调等,助力企业和开发者快速验证方案并上云。
303 22
让AI时代的卓越架构触手可及,阿里云技术解决方案开放免费试用
FLUX.1 Kontext 的全生态教程来啦!AIGC专区在线试玩!
Flux.1 Kontext [dev] 开源模型大家都用上了吗?小编汇总了3个使用教程,打包送上!
410 1

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问