Cocos Creator 2d光照
godot游戏引擎是有2d光照的,用起来感觉还是很强大的,不知道他是怎么搞的,有时间看看他们怎么实现的。
之前一直以为cocos社区里面没有2d光照的实现,偶然看到2d实现的具体逻辑,现在整理如下,
一:实现原理
这里实现的2d光源是类似聚光灯的效果,是有一个衰减过程的,具体怎么个衰减法,还得用到我们学的数学知识就是线性衰减 y = -x + b,就是用它来模拟光照的衰减效果的,在光照半径范围内衰减的时候是一个值(根据距离进行衰减,该值的意义是对光照效果的贡献值),大于光照半径范围 光照贡献值急速的变为0.
1:需要哪些参数
模拟光照的参数,需要一个光源照射的半径范围r,光源锥形的角度r1,光源的强度indensity,光源的颜色 color,光源世界坐标,用来计算物体距离光源大小,以此来计算光照效果。
properties:alphaThreshold: { value: 0.5 }light_normal: { value: white }light_worldpos: { value: [255, 255, 255, 255], editor: { type: vec4 } } light_ambientColor: { value: [127, 127, 127, 127], editor: { type: color } }light_lightColor: { value: [255, 255, 255, 255], editor: { type: color } } light_radius: { value: 10.0 }light_halfRadius: { value: 0.5 }light_brightness: { value: 1.0 }
uniform Constant {// 环境光模拟白天和黑夜vec4 light_ambientColor;// 光源颜色vec4 light_lightColor;// 光源世界坐标vec4 light_worldpos;// 光源半径float light_radius;// 光源角度半径 决定了光源锥形区域的宽度float light_halfRadius;// 光源的亮度float light_brightness;float light_unused;};
光源的世界坐标可以通过外部脚本传入,定义一个节点挂在Light脚本来控制光源的世界坐标:
import { _decorator, Component, Node, Sprite, math, UITransform, Label, Vec2, Vec3, Vec4, Camera, view, Material, Texture2D, renderer, color, Color } from 'cc';
import { EDITOR } from 'cc/env';
const { ccclass, property, executeInEditMode } = _decorator;@ccclass('Light')
@executeInEditMode
export class Light extends Component {@property([Node])bodys_normal: Node[] = [];@property([Node])bodys: Node[] = [];@property(Material)eff: Material = null!;@property(Material)eff_normal: Material = null!;onLoad() {}start() {this.updateLight();}update() {this.updateLight();}getwpos(node2d: Node) {return node2d.worldPosition;}updateBody(target, lightPos) {// 更新uniformlet spr = target.getComponent(Sprite);// 灯光位置spr.getSharedMaterial(0).setProperty('light_worldpos', new Vec4(lightPos.x, lightPos.y, lightPos.z, 1));}updateLight() {// 光源位置let lightPos = this.getwpos(this.node)for (var idx in this.bodys_normal) {let node = this.bodys_normal[idx];if (null == node) return;this.updateBody(node, lightPos);}for (var idx in this.bodys) {let node = this.bodys[idx];if (null == node) return;this.updateBody(node, lightPos);}}
}
2:具体的一些细节
想要让光源产生效果,需要单独的给每个图片加上一个单独的材质,这样才能控制颜色的输出,这里使用时最新版本(3.8.x)的shader的结构:
// Copyright (c) 2017-2020 Xiamen Yaji Software Co., Ltd.
CCEffect %{techniques:- passes:- vert: light-vs:vertfrag: light-fs:fragdepthStencilState:depthTest: falsedepthWrite: falseblendState:targets:- blend: trueblendSrc: src_alphablendDst: one_minus_src_alphablendDstAlpha: one_minus_src_alpharasterizerState:cullMode: noneproperties:alphaThreshold: { value: 0.5 }light_normal: { value: white }light_worldpos: { value: [255, 255, 255, 255], editor: { type: vec4 } } light_ambientColor: { value: [127, 127, 127, 127], editor: { type: color } }light_lightColor: { value: [255, 255, 255, 255], editor: { type: color } } light_radius: { value: 10.0 }light_halfRadius: { value: 0.5 }light_brightness: { value: 1.0 }
}%CCProgram light-vs %{precision highp float;#include <builtin/uniforms/cc-global>#if USE_LOCAL#include <builtin/uniforms/cc-local>#endif#if SAMPLE_FROM_RT#include <common/common-define>#endifin vec3 a_position;in vec2 a_texCoord;in vec4 a_color;out vec4 color;out vec2 uv0;out vec4 object_position;vec4 vert () {vec4 pos = vec4(a_position, 1);// 不适用mvp矩阵计算成世界坐标,因为如果屏幕是横屏的时候,转成世界坐标后,x轴会出现拉伸。// 这里使用的是UI的坐标系,参考light.ts获取世界坐标的代码。object_position = pos;#if USE_LOCALpos = cc_matWorld * pos;#endif#if USE_PIXEL_ALIGNMENTpos = cc_matView * pos;pos.xyz = floor(pos.xyz);pos = cc_matProj * pos;#elsepos = cc_matViewProj * pos;#endifuv0 = a_texCoord;#if SAMPLE_FROM_RTCC_HANDLE_RT_SAMPLE_FLIP(uv0);#endifcolor = a_color;return pos;}
}%CCProgram light-fs %{precision highp float;#include <builtin/internal/embedded-alpha>#include <builtin/internal/alpha-test>in vec4 color;in vec4 object_position;#if USE_TEXTUREin vec2 uv0;#pragma builtin(local)layout(set = 2, binding = 11) uniform sampler2D cc_spriteTexture;#endif// 是否使用2d法线#if USE_2D_NORMALuniform sampler2D light_normal;#endif#if USE_2D_LIGHTuniform Constant {// 环境光模拟白天和黑夜vec4 light_ambientColor;// 光源颜色vec4 light_lightColor;// 光源世界坐标vec4 light_worldpos;// 光源半径float light_radius;// 光源角度半径 决定了光源锥形区域的宽度float light_halfRadius;// 光源的亮度float light_brightness;float light_unused;};/*** 亮度计算, 按照距离远近衰减, 采取内外光圈叠加方式, 按照世界坐标计算 (0.0 ~ 1.0)* @param dist 距离 (0.0 ~ 1.0)* @param cutoff_r 外光圈半径 (> 0.0) 光源的截止半径 超过这个半径区域不再受到关照的影响* @param half_r 内光圈半径, 使用cutoffRadius的半径占比 (0.0 ~ 1.0) 光源的角度半径,决定了光源锥形区域的宽度*/float light_bright(float dist, float cutoff_r, float half_r) {// 截距float intercept = cutoff_r * half_r;// dx_1 = 1 / (2 * intercept) => y = 1 / 2x; 双曲线 近处float dx_1 = 0.5 / intercept;// dx_2曲线和dx_1曲线对称,对称中心是(cutoff_r / 2,1 / cutoff_r) 远处float dx_2 = 0.5 / (cutoff_r - intercept);float offset = 0.5 + intercept * dx_2;// 近处 慢慢衰减float falloffTermNear = clamp((1.0 - dist * dx_1), 0.0, 1.0);// 远处 远离光源的时候迅速减小到0float falloffTermFar = clamp((offset - dist * dx_2), 0.0, 1.0);// 当dist > intercept 的时候 => 1 dist < intercept => 0float falloffSelect = step(intercept, dist);// 计算光源对某一个点的照明贡献 距离衰减因子 dist < intercept => fallofftTermNear 近距离因子 dist > intercept => 远距离因子float falloffTerm = (1.0 - falloffSelect) * falloffTermNear + falloffSelect * falloffTermFar;return falloffTerm;}/*** 计算灯光的颜色值* @param dist 物体距离光源的距离, 世界单位 (> 0.0)* @param radius 光源半径,世界单位 (> 0.0)*/vec3 light_diffuse (float dist, float radius) { // 计算像素点所在光圈位置的亮度float falloffTerm = light_bright(dist, radius, light_halfRadius);// falloffTerm 为亮度值, light_lightColor 为灯光颜色return falloffTerm * vec3(light_lightColor);}/*** 计算光照颜色* @param object_position 物体坐标, 世界坐标* @param object_vertex_normal 顶点的法线向量, 归一化*/vec3 light_color(vec3 col) {// 计算光线方向, 这个方式不能直接用,打个比方纹理是正方形的,而世界坐标可能是长方形的(GL的坐标固定在-1.0到1.0之间, 而屏幕不一定是正方形)vec4 object_direction = object_position - light_worldpos;// 计算物体与灯光的距离float object_dist = length(vec3(object_direction));// 开启这个可以测试// object_dist = length(uv0 - 0.5);// 计算物体与灯光的的距离, 占用直径的百分比float object_dist_normal = object_dist / (light_radius * 2.0);// 获取灯光漫反射颜色vec3 diffuse = light_diffuse(object_dist_normal, light_radius);#if USE_2D_NORMAL// 获取法向量vec3 normal = texture(light_normal, uv0).rgb;normal = normal * 2.0 - 1.0;// 计算光照反射系数,向量点积float normalDot = max(0.0, dot(normal, -normalize(vec3(object_direction.x, object_direction.y, -60))));// 反射光 * 法向量衰减 + 环境光 return col * (diffuse * light_brightness * normalDot + vec3(light_ambientColor));#else// 反射光 * 法向量衰减 + 环境光 (没有法线的情况下需要 0.5 衰减)return col * (diffuse * light_brightness + vec3(light_ambientColor));#endif }/*** 计算光照颜色* @param object_position 物体坐标, 世界坐标* @param object_vertex_normal 顶点的法线向量, 归一化*/vec4 light_dist() {}#endifvec4 frag () {vec4 o = vec4(1, 1, 1, 1);#if USE_TEXTUREo *= CCSampleWithAlphaSeparated(cc_spriteTexture, uv0);#if IS_GRAYfloat gray = 0.2126 * o.r + 0.7152 * o.g + 0.0722 * o.b;o.r = o.g = o.b = gray;#endif#endifo *= color;ALPHA_TEST(o);#if USE_2D_LIGHTreturn vec4(light_color(vec3(o)), o.a);#elsereturn o;#endif}
}%
主要的核心方法:light_bright,这个方法是用来计算光照贡献值的。
其中intercept = cutoff_r * half_r 是设定了一个阙值,当dist < intercept的时候用什么样的衰减方式,当dist > intercept的时候用什么样的衰减方式,
那么 float dx_1 = 0.5 / intercept 该怎么理解呢,这个得结合falloffTermNear,falloffTermFar来理解,就好比v = s / t,总的衰减总数是1,前半部分(dist < intercept)占了0.5,那么后半部分就是1 - 0.5,我还是画一张图来理解吧,每衰减 1需要消耗的值就是 dx_1,这个dx_1可以理解为衰减的速率,也就是下面代码中出现的斜率,也就是衰减速度。那么dx_1讲清楚了,自然而然dx_2你也是理解的

这里还有一个点就是offset什么意思,offset指的就是当dist = intercept的时候怎么保证两个衰减过程衔接的非常自然呢,咱们可以列一个公式看看
1 - dist * dx_1 = offset - dist * dx_2,很容易我们解方程就能够知道 offset = 0.5 + intercept * dx_2;
最后根据dist来计算对光照的影响程度就可以了,
/*** 亮度计算, 按照距离远近衰减, 采取内外光圈叠加方式, 按照世界坐标计算 (0.0 ~ 1.0)* @param dist 距离 (0.0 ~ 1.0)* @param cutoff_r 外光圈半径 (> 0.0) 光源的截止半径 超过这个半径区域不再受到关照的影响* @param half_r 内光圈半径, 使用cutoffRadius的半径占比 (0.0 ~ 1.0) 光源的角度半径,决定了光源锥形区域的宽度*/float light_bright(float dist, float cutoff_r, float half_r) {// 截距float intercept = cutoff_r * half_r;// dx_1 = 1 / (2 * intercept) => y = 1 / 2x; 双曲线 近处float dx_1 = 0.5 / intercept;// dx_2曲线和dx_1曲线对称,对称中心是(cutoff_r / 2,1 / cutoff_r) 远处float dx_2 = 0.5 / (cutoff_r - intercept);// 用在两种衰减过程中的阙值出的矫正保证颜色渐变的连贯 计算过程是 1 - dist * dx_1 = offset - dist * dx_2 可以反算出来 offset = 0.5 + intercept * dx_2float offset = 0.5 + intercept * dx_2;// 近处 慢慢衰减 线性衰减float falloffTermNear = clamp((1.0 - dist * dx_1), 0.0, 1.0);// 远处 远离光源的时候迅速减小到0float falloffTermFar = clamp((offset - dist * dx_2), 0.0, 1.0);// 当dist > intercept 的时候 => 1 dist < intercept => 0float falloffSelect = step(intercept, dist);// 计算光源对某一个点的照明贡献 距离衰减因子 dist < intercept => fallofftTermNear 近距离因子 dist > intercept => 远距离因子float falloffTerm = (1.0 - falloffSelect) * falloffTermNear + falloffSelect * falloffTermFar;return falloffTerm;}
讲解到这里希望你能够理解光源产生的过程。
下面贴下原文的链接:
cocos creator 2D关照
我只是把不容易理解的部分给讲一下,希望对你有帮助。
相关文章:
Cocos Creator 2d光照
godot游戏引擎是有2d光照的,用起来感觉还是很强大的,不知道他是怎么搞的,有时间看看他们怎么实现的。 之前一直以为cocos社区里面没有2d光照的实现,偶然看到2d实现的具体逻辑,现在整理如下, 一࿱…...
5款好用的AI办公软件,一键轻松制作PPT、视频,提升工作效率!
众所周知,AI 人工智能技术已渗透到生活的方方面面,无论是很多人早已用上的智能音箱、语音助手,还是新近诞生的各种 AI 软件工具,背后都离不开 AI 人工智能技术的加持。 对于各类新生的 AI 软件工具,人们很容易「选边站…...
【MyBatis面试题】
目录 前言 1.MyBatis执行流程。 2.Mybatis是否支持延迟加载? 3.延迟加载的底层原理知道吗? 4.Mybatis的一级、二级缓存用过吗? 5.Mybatis的二级缓存什么时候会清理缓存中的数据? 总结 前言 本文主要介绍了MyBatis面试题相…...
编程界的圣经:从Scheme到JavaScript构建你的计算思维
文章目录 适读人群目 录 《计算机程序的构造和解释》(Structure and Interpretation of Computer Programs,简记为SICP)是MIT的基础课教材,出版后引起计算机教育界的广泛关注,对推动全世界大学计算机科学技术教育的发…...
智慧城市与智慧乡村:共创城乡一体化新局面
一、引言 随着科技的不断进步和城乡发展的日益融合,智慧城市与智慧乡村的建设已成为推动城乡一体化发展的新引擎。智慧城市利用物联网、大数据、云计算等先进技术,实现城市治理、公共服务、产业发展等领域的智能化;而智慧乡村则借助现代科技…...
蓝桥杯——web(ECharts)
ECharts 初体验 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><script src"echarts.js">&l…...
MySQL数据库在Windows和Linux中由于大小写默认规则不同,出现大小写问题如何解决?
Windows和Linux差异:在Windows上,lower_case_table_names默认为1,而在Linux上,默认值通常为0。因此,在Linux上更改这个设置更常见,以确保与Windows环境的兼容性或实现特定的大小写敏感性需求。 操作系统的大…...
新雀优化算法NOA求解机器人栅格地图最短路径规划,可以自定义地图(提供MATLAB代码)
一、星雀优化算法 星雀优化算法(Nutcracker optimizer algorithm,NOA)由Mohamed Abdel-Basset等人于2023年提出,该算法模拟星雀的两种行为,即:在夏秋季节收集并储存食物,在春冬季节搜索食物的存储位置。CEC2005:星雀优化算法(Nut…...
重塑语言智能未来:掌握Transformer,驱动AI与NLP创新实战
Transformer模型 Transformer是自然语言理解(Natural Language Understanding,NLU)的游戏规则改变者,NLU 是自然语言处理(Natural Language Processing,NLP)的一个子集。NLU已成为全球数字经济中AI 的支柱之一。 Transformer 模型标志着AI 新…...
【Windows】Windows 11无法连接共享打印机
Windows 11无法连接共享打印机 1.在电脑点击winr 键然后输入gpedit.msc进行回车进入本地本地组策略编辑器2.打开本地组策略-管理模板>打印机->找到配置RPC连接设置,打开3.选择“已启用”,将下面连接协议改成“命名管道上的RPC”,搞定。…...
Window10数据库崩溃启动失败,MySQL8.0.30通过data文件夹恢复数据库到Docker
背景: 昨天关机前还在使用mysql,一切正常,但今天打开电脑,发现mysql启动不起来了,老是提示端口占用,但是系统也没有新安装什么软件,而且通过查询nat命令也没发现3306端口占用。而且修改成3307等…...
【树】-Lc101-对称二叉树(一棵树是否是另一棵树的子树的变形)
写在前面 最近想复习一下数据结构与算法相关的内容,找一些题来做一做。如有更好思路,欢迎指正。 目录 写在前面一、场景描述二、具体步骤1.环境说明2.代码 写在后面 一、场景描述 对称二叉树。给给定一个二叉树,检查它是否是镜像对称的。 例…...
在Jupyter Notebook中安装第三方库
pip vs. conda pip 可以在所有环境下安装python包。conda 可以在conda环境下安装所有包。 如果你已经安装了python,那么这个选择对你来说是非常容易的: 如果你是用Anaconda或者Miniconda安装的python,那么请使用conda命令来安装python包。如…...
「AI工程师」数据处理与分析-工作指导
工作指导书 一、工作职责 负责数据的收集、清洗、整合和处理,确保数据质量和准确性。进行数据分析和挖掘,提取有价值的信息,为业务决策提供支持。构建和维护数据处理和分析的流程和工具,提高数据处理效率。与其他团队成员合作,共同解决数据处理和分析过程中遇到的问题。二…...
Rust:Mutex 的示例代码
在Rust中,你可以使用std::sync::Mutex来创建一个互斥锁,从而保护共享资源。下面是一个使用Mutex的简单示例: use std::sync::Mutex; use std::thread; use std::time::Duration; fn main() { // 创建一个包含整数的Mutex let counter…...
在 Docker 环境下安装 OpenWrt
在 Docker 环境下安装 OpenWrt 是一种方便且易于管理的方式,它允许您在不需要物理设备的情况下运行 OpenWrt。以下是在 Docker 中安装 OpenWrt 的步骤: 首先,您需要安装 Docker。具体安装方法可以参考 Docker 官方文档。在安装完成后…...
stl的基本知识学习
1.vector: 2.set: 3.map: 4.栈: 5.队列: 6. unordered_map与unordered_set: 7. 位运算: 8.cctype: 导图:...
Python从0到100(三):Python中的变量介绍
前言: 零基础学Python:Python从0到100最新最全教程。 想做这件事情很久了,这次我更新了自己所写过的所有博客,汇集成了Python从0到100,共一百节课,帮助大家一个月时间里从零基础到学习Python基础语法、Pyth…...
污水处理厂重金属废水深度处理CH-90树脂处理系统
项目名称 广东某工业污水处理厂重金属废水深度处理工程项目 工艺选择 科海思重金属深度处理工艺 工艺原理 离子交换吸附 项目背景 随着环保要求不断提高,工业废水处理已成为众多企业的必修课。然而在工业生产中,如何有效处理含有重金属的废水成为…...
WordPress供求插件API文档:用户登录
该文档为WordPress供求插件文档,详情请查看 WordPress供求插件:一款专注于同城生活信息发布的插件-CSDN博客文章浏览阅读67次。WordPress供求插件:sliver-urban-life 是一款专注于提供同城生活信息发布与查看的插件,该插件可以实…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...
解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错
出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上,所以报错,到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本,cu、torch、cp 的版本一定要对…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...
基于Springboot+Vue的办公管理系统
角色: 管理员、员工 技术: 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能: 该办公管理系统是一个综合性的企业内部管理平台,旨在提升企业运营效率和员工管理水…...
PostgreSQL——环境搭建
一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在࿰…...
深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏
一、引言 在深度学习中,我们训练出的神经网络往往非常庞大(比如像 ResNet、YOLOv8、Vision Transformer),虽然精度很高,但“太重”了,运行起来很慢,占用内存大,不适合部署到手机、摄…...
6️⃣Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙
Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙 一、前言:离区块链还有多远? 区块链听起来可能遥不可及,似乎是只有密码学专家和资深工程师才能涉足的领域。但事实上,构建一个区块链的核心并不复杂,尤其当你已经掌握了一门系统编程语言,比如 Go。 要真正理解区…...
ArcGIS Pro+ArcGIS给你的地图加上北回归线!
今天来看ArcGIS Pro和ArcGIS中如何给制作的中国地图或者其他大范围地图加上北回归线。 我们将在ArcGIS Pro和ArcGIS中一同介绍。 1 ArcGIS Pro中设置北回归线 1、在ArcGIS Pro中初步设置好经纬格网等,设置经线、纬线都以10间隔显示。 2、需要插入背会归线…...
C++中vector类型的介绍和使用
文章目录 一、vector 类型的简介1.1 基本介绍1.2 常见用法示例1.3 常见成员函数简表 二、vector 数据的插入2.1 push_back() —— 在尾部插入一个元素2.2 emplace_back() —— 在尾部“就地”构造对象2.3 insert() —— 在任意位置插入一个或多个元素2.4 emplace() —— 在任意…...
