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关照 

我只是把不容易理解的部分给讲一下,希望对你有帮助。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/733180.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【java】22:try-catch 异常处理

try-catch 方式处理异常说明 public static void main(String[] args) { int num1 10; int num2 0; try { int res num1 / num2; } catch (Exception e) { System.out.println(e.getMessage()); } } 注意事项 1)如果异常发生了&#xff0c;则异常发生后面的代码不会执行&…

前后端分离项目Docker部署指南(下)

目录 前言&#xff1a; 一.安装nginx 创建目录 上传nginx.conf至/data/nginx/conf文件夹中 运行启动容器 上传静态资源文件 ​编辑 访问结果 前言&#xff1a; 在上一篇博客中&#xff0c;我们深入探讨了如何使用Docker部署一个前后端分离的项目中的后端部分。我们构建…

基于qt的图书管理系统----05其他优化

参考b站&#xff1a;视频连接 源码github&#xff1a;github 目录 1 优化借阅记录显示2 时间显示为年月日3 注册接口 1 优化借阅记录显示 现在只能显示部分信息&#xff0c;把接的书名和人的信息全部显示 在sql语句里替换为这一句即可实现查询相关联的所有信息 QString str…

2024 年 AI 辅助研发发展与趋势研究

引言 这几年&#xff0c;人工智能&#xff08;AI&#xff09;技术火得不行&#xff0c;它渗透到了我们生活的方方面面。从帮助我们识别图片、理解语音&#xff0c;到推荐我们喜欢的内容&#xff0c;甚至自动驾驶汽车&#xff0c;AI都在大显身手。特别是在研发领域&#xff0c;…

蓝桥杯倒计时 36天-DFS练习

文章目录 飞机降落仙境诅咒小怂爱水洼串变换 飞机降落 思路&#xff1a;贪心暴搜。 #include<bits/stdc.h>using namespace std; const int N 10; int t,n; //这题 N 比较小&#xff0c;可以用暴力搜搜复杂度是 TN*N! struct plane{int t,d,l; }p[N]; bool vis[N];//用…

基于springboot实现大学外卖管理系统项目【项目源码+论文说明】

基于springboot实现大学外卖管理系统演示 摘要 如今&#xff0c;信息化不断的高速发展&#xff0c;社会也跟着不断进步&#xff0c;现今的社会&#xff0c;各种工作都离不开信息化技术&#xff0c;更离不开电脑的管理。信息化技术也越来越渗透到各小型的企业和公司中&#xff…

Java8 CompletableFuture异步编程-进阶篇

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Java全栈-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 前言 我们在前面文章讲解了CompletableFuture这个异步编程类的基本用法&#xff0c;…

Web Worker:JavaScript的后台任务解决方案

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

SpringBoot项目没有启动按键

问题一&#xff1a; pom文件正常&#xff0c;但是springboot包报红&#xff0c;同时Plugin ‘org.springframework.boot:spring-boot-maven-plugin:‘ not found报红 解决办法&#xff1a; 无法识别使用哪个版本的 spring-boot-maven-plugin 包 <build><plugins>&…

javase day01笔记

第一天课堂笔记 Java第三代高级语言中的面向对象的语言 b/s 浏览器/服务器c/s 客户端/服务端 1991年詹姆斯高斯林在sun公司开发的Java 常用的dos命令 磁盘操作系统&#xff1a;dos win &#xff0b; r -》 cmd dos命令 切换盘符&#xff1a;直接输入对应盘符目录操作&#x…

【C++进阶】哈希的应用 --- 布隆过滤器

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前学习C和算法 ✈️专栏&#xff1a;C航路 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac; 点赞&#x1…

面试准备不充分,被Java守护线程干懵了,面试官主打一个东西没用但你得会

写在开头 面试官&#xff1a;小伙子请聊一聊Java中的精灵线程&#xff1f; 我&#xff1a;什么&#xff1f;精灵线程&#xff1f;啥时候精灵线程&#xff1f; 面试官&#xff1a;精灵线程没听过&#xff1f;那守护线程呢&#xff1f; 我&#xff1a;守护线程知道&#xff0c;就…

计算机软件文档编制规范GB_T 8567-2006

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 计算机软件文档编制规范概述 计算机软件文档编制规范&#xff08;Specification for computer software documentation&#xff09; 由TC28&#xff08;全国信息技术标准化技…

《HTTPS协议》

文章目录 一、什么是HTTPS协议二、理解关键字三、为什么要加密四、常见的加密方式1.对称加密2.非对称加密 五、如何进行加密&#xff1f;1.只使用对称加密2.只使用非对称加密3.双方都使用非对称加密4.使用对称加密非对称加密5.对称加密非对称加密CA证书认证5.1数据摘要&#xf…

GPT-4-turbo还是大家心中第一!Claude 3竞技场人类投票成绩出炉:仅居第三

Claude 3的竞技场排名终于揭晓了&#xff1a; 在仅仅3天的时间里&#xff0c;20000张投票使得排名的流量达到了前所未有的高度。 最后&#xff0c;Claude 3的"大杯"模型Opus以1233的分数赢得了胜利&#xff0c;成为了第一个能和GPT-4-Turbo匹敌的选手。 "中杯…

QGridLayout网格布局和QVBoxLayout垂直布局有着非常大的差别

QGridLayout网格布局&#xff1a;1.把这块控件划分成一个个的 单元格 2.把你的控件填充进入 单元格 3.这些有关限制大小的函数接口统统失效 setMaximumWidth&#xff08;&#xff09; setMinimumWidth() setPolicySize()图示&#xff1a;我是用的网格布局&#xff0c;左边放QT…

C# WinForm AndtUI第三方库 Table控件使用记录

环境搭建 1.在NuGet中搜索AndtUI并下载至C# .NetFramework WinForm项目。 2.添加Table控件至窗体。 使用方法集合 1.单元格点击事件 获取被点击记录特定列内容 private void dgv_CellClick(object sender, MouseEventArgs args, object record, int rowIndex, int columnIn…

【Python】装饰器函数

专栏文章索引&#xff1a;Python 原文章&#xff1a;装饰器函数基础_装饰函数-CSDN博客 目录 1. 学习装饰器的基础 2.最简单的装饰器 3.闭包函数装饰器 4.装饰器将传入的函数中的值大写 5. 装饰器的好处 6. 多个装饰器的执行顺序 7. 装饰器传递参数 8. 结语 1. 学习装饰…

利用IDEA创建Java项目使用Servlet工具

【文件】-【项目结构】 【模块】-【依赖】-【】-【JAR】 找到Tomcat的安装路径打开【lib】找到【servlet.jar】点击【确定】 勾选上jar,然后【应用】-【确定】 此时新建文件可以发现多了一个Servlet&#xff0c;我们点击会自动创建一个继承好的Servlet类

STL容器之哈希的补充——其他哈希问题

1.其他哈希问题 ​ 减少了空间的消耗&#xff1b; 1.1位图 ​ 位图判断在不在的时间复杂度是O(1)&#xff0c;速度特别快; ​ 使用哈希函数直接定址法&#xff0c;1对1映射&#xff1b; ​ 对于海量的数据判断在不在的问题&#xff0c;使用之前的一些结构已经无法满足&…