Cocos Creator 2d光照

news2024/11/17 6:25:44

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) {
        // 更新uniform
        let 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:vert
      frag: light-fs:frag
      depthStencilState:
        depthTest: false
        depthWrite: false
      blendState:
        targets:
        - blend: true
          blendSrc: src_alpha
          blendDst: one_minus_src_alpha
          blendDstAlpha: one_minus_src_alpha
      rasterizerState:
        cullMode: none
      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 }
}%

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>
  #endif
  in 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_LOCAL
      pos = cc_matWorld * pos;
    #endif

    #if USE_PIXEL_ALIGNMENT
      pos = cc_matView * pos;
      pos.xyz = floor(pos.xyz);
      pos = cc_matProj * pos;
    #else
      pos = cc_matViewProj * pos;
    #endif

    uv0 = a_texCoord;
    #if SAMPLE_FROM_RT
      CC_HANDLE_RT_SAMPLE_FLIP(uv0);
    #endif
    color = 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_TEXTURE
    in vec2 uv0;
    #pragma builtin(local)
    layout(set = 2, binding = 11) uniform sampler2D cc_spriteTexture;
  #endif

  // 是否使用2d法线
  #if USE_2D_NORMAL
    uniform sampler2D light_normal;
  #endif

  #if USE_2D_LIGHT
    uniform 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);
      // 远处 远离光源的时候迅速减小到0
      float falloffTermFar  = clamp((offset - dist * dx_2), 0.0, 1.0);

      // 当dist > intercept 的时候 => 1  dist < intercept => 0
      float 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() {
    }
  #endif

  vec4 frag () {
    vec4 o = vec4(1, 1, 1, 1);

    #if USE_TEXTURE
      o *= CCSampleWithAlphaSeparated(cc_spriteTexture, uv0);
      #if IS_GRAY
        float gray  = 0.2126 * o.r + 0.7152 * o.g + 0.0722 * o.b;
        o.r = o.g = o.b = gray;
      #endif
    #endif

    o *= color;
    ALPHA_TEST(o);

  #if USE_2D_LIGHT
    return vec4(light_color(vec3(o)), o.a);
  #else
    return 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_2
      float offset = 0.5 + intercept * dx_2;

      // 近处 慢慢衰减 线性衰减
      float falloffTermNear = clamp((1.0 - dist * dx_1), 0.0, 1.0);
      // 远处 远离光源的时候迅速减小到0
      float falloffTermFar  = clamp((offset - dist * dx_2), 0.0, 1.0);

      // 当dist > intercept 的时候 => 1  dist < intercept => 0
      float falloffSelect = step(intercept, dist);

      // 计算光源对某一个点的照明贡献  距离衰减因子 dist < intercept => fallofftTermNear 近距离因子  dist > intercept => 远距离因子
      float falloffTerm = (1.0 - falloffSelect) * falloffTermNear + falloffSelect * falloffTermFar;

      return falloffTerm;
    }

讲解到这里希望你能够理解光源产生的过程。

下面贴下原文的链接:

cocos creator 2D关照 

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

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

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

相关文章

Spring Boot 中解决跨域的多种方式

Spring Boot 中解决跨域的多种方式 《踏过跨域障碍&#xff1a;Spring Boot 中解决跨域的多种方式》摘要引言正文何为跨域跨域问题出现特征方式一&#xff1a;使用 CrossOrigin 注解方式二&#xff1a;自定义 WebMvcConfigurer方式三&#xff1a;使用 Filter 进行跨域配置 结论…

java之lombok

Lombok是一个实用的java类库&#xff0c;能通过注解的形式自动生成构造器&#xff0c; getter setter equsls hashcode tostring等方法 并且可以自动化生成日志变量&#xff0c;简化java开发&#xff0c;提高效率 作用 导入 <dependency><groupId>org.projectlomb…

集合框架(一)Set系列集合

Set<E>是一个接口 特点 无序&#xff1a;添加数据的顺序和获取出的数据顺序不一致&#xff1b;不重复&#xff0c;无索引 注意&#xff1a;Set要用到的常用方法&#xff0c;基本上就是collection提供的!自己几乎没有额外新增一些常用功能! HashSet集合的底层原理 前置知…

我们的一生都是在挤火车。

哈喽&#xff0c;你好啊&#xff0c;我是雷工&#xff01; 昨天从燕郊坐火车回石家庄&#xff0c;由于赶上元旦假期&#xff0c;所有高铁票都售罄&#xff0c;一张普通火车票&#xff0c;还是一周前就买才买到的。 从燕郊站&#xff0c;到北京站&#xff0c;然后地铁去北京西站…

C++虚继承的一些细节

C虚继承的一些细节 何时使用虚继承普通继承的类对象布局虚继承类对象布局虚函数表指针虚函数表内容 何时使用虚继承 看代码&#xff0c;代码主要是菱形继承&#xff0c;base里面的成员变量会存在二义性 #include<iostream> using namespace std;class base { public:in…

【机器学习】从线性回归模型看一个简单的成本函数

&#x1f338;博主主页&#xff1a;釉色清风&#x1f338;文章专栏&#xff1a;机器学习&#x1f338;今日语录&#xff1a;事情不做&#xff0c;越想越难&#xff1b;事情做了&#xff0c;越做越容易。 从线性回归模型看一个简单的成本函数 &#x1f33c;引入&#xff1a;模型…

基于SpringBoo的火车订票管理系统(程序+文档+数据库)

** &#x1f345;点赞收藏关注 → 私信领取本源代码、数据库&#x1f345; 本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#xff0c;希望你能有所收获&#xff0c;少走一些弯路。&#x1f345;关注我不迷路&#x1f345;** 一、研究背景…

2024年【G2电站锅炉司炉】考试题及G2电站锅炉司炉证考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年【G2电站锅炉司炉】考试题及G2电站锅炉司炉证考试&#xff0c;包含G2电站锅炉司炉考试题答案和解析及G2电站锅炉司炉证考试练习。安全生产模拟考试一点通结合国家G2电站锅炉司炉考试最新大纲及G2电站锅炉司炉考…

LVS (Linux Virtual server)集群介绍

一 集群和分布式 &#xff08;一&#xff09;系统性能扩展方式&#xff1a; Scale UP&#xff1a;垂直扩展&#xff0c;向上扩展,增强&#xff0c;性能更强的计算机运行同样的服务 &#xff08;即升级单机的硬件设备&#xff09; Scale Out&#xff1a;水平扩展&#xff0…

Python 学会typing的类型注解面向泛型编程

文章目录 文章开篇typing模块简介typing模块作用基本类型注解1.不使用类型注解2.使用类型注解 泛型1.List2.Tuple3.Set4.Dict 特殊类型1.Any2. Union2. Optional4. Callable 类型别名1.TypeVar2. NewType 更多技巧1.NoReturn和None2.Sequence3. Generator 总结 文章开篇 Python…

VUE_自适应布局lib-flexible+postcss-pxtorem、lib-flexible + postcss-px2rem,nuxt页面自适配

lib-flexible postcss-pxtorem适配 我采用的是flexable.js和postcss-pxtorem。我一开始用的是postcss-px2rem后来发现和nuxt引入公共css的时候发生了冲突所以改用了postcss-pxtorem。 安装依赖 npm i lib-flexible -S npm install postcss-pxtorem --save 1、lib-flexible.…

怎么在学习强国网上发布文章,学习强国投稿发稿方法途径,附学习强国多少钱价格明细表

学习强国是一款受用户欢迎的学习软件&#xff0c;许多人希望在其平台上发布自己的文章&#xff0c;以分享和传播自己的学习成果和心得体会。那么&#xff0c;怎么在学习强国网上发布文章呢&#xff1f;接下来&#xff0c;我们将介绍一些投稿发稿的方法和途径。 首先&#xff0c…

蓝桥杯刷题5--GCD和LCM

目录 1. GCD 1.1 性质 1.2 代码实现 2. LCM 2.1 代码实现 3. 习题 3.1 等差数列 3.2 Hankson的趣味题 3.3 最大比例 3.4 GCD 1. GCD 整数a和b的最大公约数是能同时整除a和b的最大整数&#xff0c;记为gcd(a, b) 1.1 性质 GCD有关的题目一般会考核GCD的性质。   …

Java开发从入门到精通(一):Java的进阶语法知识

Java大数据开发和安全开发 Java的方法1.1 方法是什么1.1.1 方法的定义1.1.2 方法如何执行?1.1.3 方法定义时注意点1.1.4 使用方法的好处是? 1.2 方法的多种形式1.2.1 无参数 无返回值1.2.2 有参数 无返回值 1.3 方法使用时的常见问题1.4 方法的设计案例1.4.1 计算1-n的和1.4.…

代码随想录刷题笔记 DAY 42 | 最后一块石头的重量 II No.1049 | 目标和 No.494 | 一和零 No.474

文章目录 Day 4301. 最后一块石头的重量 II&#xff08;No. 1049&#xff09;<1> 题目<2> 笔记<3> 代码 02. 目标和&#xff08;No. 494&#xff09;<1> 题目<2> 笔记<3> 代码 03. 一和零&#xff08;No. 474&#xff09;<1> 题目&l…

知名比特币质押协议项目Babylon确认参加Hack.Summit()2024区块链开发者大会

Babylon项目已确认将派遣其项目代表出席2024年在香港数码港举办的Hack.Summit()2024区块链开发者大会。作为比特币生态的领军项目&#xff0c;Babylon积极参与全球区块链领域的交流与合作&#xff0c;此次出席大会将为其提供一个展示项目进展、交流技术与创新思路的重要平台。B…

ssm游戏社交网站-计算机毕业设计源码05667

目 录 摘要 1 绪论 1.1 研究意义 1.2研究方法 1.3ssm框架 1.4论文结构与章节安排 2 2 游戏社交网站系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1 数据增加流程 2.2.2 数据修改流程 2.2.3数据删除流程 2.3 系统功能分析 2.3.1功能性分析 2.3.2非功能性分析 2…

Intel® Extension for PyTorch*详细安装教程

最近在研究Intel的pytorch的加速拓展Intel Extension for PyTorch*,但是发现官网的文档全是英文的&#xff0c;不太好找安装教程。所以特此分享Intel Extension for PyTorch*的详细安装教程。 文章目录 一、安装所需系统要求1.1 硬件需求1.2 软件需求 二、准备2.1 安装驱动程序…

CVE-2024-27198 JetBrains TeamCity 身份验证绕过漏洞分析

漏洞简介 JetBrains TeamCity 是一款由 JetBrains 公司开发的持续集成和持续交付服务器。它提供了强大的功能和工具&#xff0c;旨在帮助开发团队构建、测试和部署他们的软件项目 JetBrains TeamCity发布新版本修复了两个高危漏洞JetBrains TeamCity 身份验证绕过漏洞(CVE-20…

【源码系列】基于SpringBoot的二手车交易系统的设计与实现

技术栈 用户端&#xff1a;uniapp&#xff08;vue语法&#xff09; 管理后台&#xff1a;vueelementUI 后台服务&#xff1a;SpringBootMyBatisPlusMySQL 版本支持&#xff1a;AndroidIOSH5小程序公众号 移动端 管理端