查看: 6492|回复: 15

[交流讨论] 写了个auto shadow插件,但是效率很低

[复制链接]
梦石
2
星屑
5550
在线时间
2566 小时
注册时间
2012-2-9
回帖
947

开拓者

发表于 2017-10-14 02:31:02 | 显示全部楼层 |阅读模式

加入我们,或者,欢迎回来。

您需要 登录 才可以下载或查看,没有账号?注册会员

×
本帖最后由 garfeng 于 2017-11-10 21:44 编辑

QQ图片20171014021830.png QQ图片20171014021803.png

设置方法,用region id标记光源


QQ图片20171014022356.png


效果和生成的光图:
未标题-4.jpg test.png
两个黑点是用来测试标记光源位置的,实际没有。

大概就是这种效果。
现在处理的办法是:用另一个编译好的exe算光影图,算完后js会调ulds把图加载进来。
这种办法是第一次载入地图时,把图存起来,以后直接就是显示图片了,可以很快。
但是第一次载入会很慢很慢很慢。

计算方法大概是从每个点到每个光源做直线,然后计算这条线段上有无遮挡。遮挡因为只计算墙壁,所以都是规则的立方体。
不知道有没有其他的更快的算法……求教。
如果能解决好,大的游戏也可以带光影,只是在玩家机器上生成而已

不知道大家对解决这个问题有没有什么办法……

谢谢~







下面是使用方法和相关文件,因为楼下收到Khas Advanced Lighting这个插件的回复,所以本插件不再继续开发,现有的版本使用方法大致如下:


1. 添加下方的脚本:


[fold=js代码]
[pre lang="javascript"]/*: * @plugindesc Auto light.
* @author Garfeng
* @help 关于光源参数:
* 数组,里面每一个条目也是一个数组,
* 第一项是该光源的region id,
* 第二项是该光源的光晕大小,单位:像素
* 第三项是该光源衰减速度,越大,则光源照射范围越小,单位:像素
* 总之,第二项和第三项的乘积,请勿大于255。
* 超过这个光晕范围的,则慢慢衰减(公式忘记了)
* 你可以把生成的图片打包进游戏里,用ulds手动调用,
* 然后把本插件以及附带的exe文件删除。
*
*
* @param path
* @desc 设置图片存放位置,在img目录下,注意,该文件夹必须你自己创建
* @default auto_light
*
* @param opacity
* @desc 透明度:0~255
* @default 150
*
* @param config
* @desc 光源参数设置,
* @default [[1,10,2],[2,5,1],[3,20,3]]
*/

/**
* <autolight>{"wall_height":1}</autolight>
*/

(function() {

  var Helper = {
    t: 0,

    // Converts a coordinate on the map to the corresponding coordinate on the screen.
    rx: function(x, scrollRate) {
      if (scrollRate == null) {
        scrollRate = $gameMap.tileWidth();
      }

      if (scrollRate === 0) {
        return x;
      } else {
        return $gameMap.adjustX(x / scrollRate) * scrollRate;
      }
    },

    ry: function(y, scrollRate) {
      if (scrollRate == null) {
        scrollRate = $gameMap.tileHeight();
      }

      if (scrollRate === 0) {
        return y;
      } else {
        return $gameMap.adjustY(y / scrollRate) * scrollRate;
      }
    },
    rOpacity: function(num, offset, end) {
      if ($gameSwitches.value(20)) {
        if (num > 0) {
          if (this.opacity < end) {
            return Math.min(this.opacity + num, end);
          } else {
            return end;
          }
        } else {
          if (this.opacity > end) {
            return Math.max(this.opacity + num, end);
          } else {
            return end;
          }
        }

      } else {
        return offset;
      }
    },


    update: function() {
      ++this.t;
      this._updater(this.t, $gameSwitches, $gameVariables);
    },

    assignSettings: function(settings) {
      var code = '';
      for (var key in settings) {
        var value = settings[key];
        if (typeof(value) === 'string') {
          // this.x = (formula);
          // this.scale.x = (formula); // key is "scale.x"
          code += 'this.' + key + ' = (' + value + ');\n';
        } else {
          // if key is "scale.x"
          // keys is ["scale", "x"]
          var keys = key.split('.');
          // set key to "x"
          key = keys.pop();

          var target = this;
          keys.forEach(function(k) {
            if (typeof(target) !== 'object') {
              target[k] = {};
            }
            target = target[k];
          });

          target[key] = value;
        }
      }
      // You may log the code for debugging purpose.
      // console.log(code);
      this._updater = new Function('t', 's', 'v', code);
    }
  };



  var parameters = PluginManager.parameters("auto_light");
  var path = parameters["path"] || "auto_light";
  var opacity = parseInt(parameters["opacity"], 10) || 150;
  var lamp_config_parameter;
  if (typeof parameters["config"] == "undefined" || parameters["config"] == null) {
    lamp_config_parameter = [
      [1, 10, 2],
      [2, 5, 1],
      [3, 20, 3]
    ];
  } else {
    lamp_config_parameter = JSON.parse(parameters["config"]);
  }



  var note_regexp = /<autolight>([^]*?)<\/autolight>/ig;
  var lamp_config = [];

  for (var i in lamp_config_parameter) {
    var v = lamp_config_parameter;
    lamp_config[v[0]] = {
      halo_x: v[1],
      halo_k: v[2]
    };
  }

  console.log(lamp_config);

  var fs = require('fs');
  var ph = require('path');
  var childProcess = require('child_process');
  var base = ph.dirname(process.mainModule.filename);
  var complete_path = `${base}/img/${path}`;
  var light_cmd = `${base}/light.exe`;
  /*
    lamp_config[1] = {
      halo_x: 10,
      halo_k: 2
    }
    */

  var config = {
    path: complete_path,
    opacity: opacity,
    lamp_config: lamp_config
  };

  var DEFAULT_SETTINGS = {
    z: 10,
    path: path,
    smooth: true
  };

  // NOT a class constructor
  function ULDS(settings) {
    settings = Object.assign({}, DEFAULT_SETTINGS, settings);
    var spriteClass = ULDS.Sprite;
    var bitmap;

    bitmap = ImageManager.loadBitmap('img/' + settings.path + '/',
      settings.name, settings.hue, settings.smooth);

    var sprite = new spriteClass(bitmap);

    delete settings.path;
    delete settings.name;
    delete settings.loop;
    delete settings.hue;
    delete settings.smooth;
    delete settings.jpeg;

    sprite.assignSettings(settings);

    return sprite;
  }

  ULDS.Sprite = function(bitmap) {
    Sprite.call(this, bitmap);
  };

  ULDS.Sprite.prototype = Object.create(Sprite.prototype);
  ULDS.Sprite.prototype.constructor = ULDS.Sprite;
  Object.assign(ULDS.Sprite.prototype, Helper);

  Spriteset_Map.prototype.parseAutoLight = function() {
    var filename = `${complete_path}/light_${$dataMap.auto_light_config.mapId}.png`;

    var auto_light_settings = {
      name: `light_${$dataMap.auto_light_config.mapId}`,
      x: "this.rx(0)",
      y: "this.ry(0)",
      opacity: opacity,
      blendMode: 2
    };

    var config_file = `${complete_path}/light_${$dataMap.auto_light_config.mapId}.json`;

    if (fs.existsSync(filename)) {
      this._tilemap.addChild(ULDS(auto_light_settings));
    } else {
      var callback_for_parseReady = function(error, stdout, stderr) {
        if (error != null) {
          throw error;
        }
        this._tilemap.addChild(ULDS(auto_light_settings));
      }.bind(this);
      var callback_for_writeConfig = function(err) {
        if (err != null) {
          throw err;
        }
        childProcess.exec(`${light_cmd} ${config_file}`, null, callback_for_parseReady);
      }

      var config_all = Object.assign({}, $dataMap.auto_light_config);

      var tilemap_config = [];
      var width = $gameMap.width();
      var height = $gameMap.height();
      config_all.width = width;
      config_all.height = height;
      //var tilemap = this._tilemap;
      for (var j = 0; j < height; j++) {
        for (var i = 0; i < width; i++) {
          var tileId0 = $gameMap.tileId(i, j, 0);
          var tileId1 = $gameMap.tileId(i, j, 1);
          var regionId = $gameMap.regionId(i, j);
          var index = j * width + i;

          var id = 0;

          if (Tilemap.isWallSideTile(tileId0) || Tilemap.isWallSideTile(tileId1)) {
            id = 1;
          } else if (Tilemap.isGroundTile(tileId0) || Tilemap.isGroundTile(tileId1)) {
            id = 2;
          } else {
            id = 0;
          }
          if (typeof lamp_config[regionId] != "undefined" && lamp_config[regionId] != null) {
            id |= (regionId << 2);
          }

          tilemap_config[index] = id;
        }
      }

      config_all.tilemap = tilemap_config;
      var str = JSON.stringify(config_all);
      fs.writeFile(config_file, str, {
        flag: "w"
      }, callback_for_writeConfig);
    }
  }

  var alias_Spriteset_Map_createTilemap = Spriteset_Map.prototype.createTilemap;
  Spriteset_Map.prototype.createTilemap = function() {
    alias_Spriteset_Map_createTilemap.call(this);
    var match;
    $dataMap.auto_light_config = Object.assign({}, config);
    $dataMap.auto_light_config.mapId = $gameMap.mapId();
    console.log($dataMap.note);
    var note = $dataMap.note;
    note_regexp.lastIndex = 0;
    if ((match = note_regexp.exec(note)) != null) {
      try {
        var settings = JSON.parse(match[1]);
        if (typeof settings !== "object") {
          throw "Auto light settings should be object";
        }
        $dataMap.auto_light_config = Object.assign($dataMap.auto_light_config, settings);
        this.parseAutoLight();
      } catch (e) {
        throw e;
      }
    }


  }

})();[/pre]

[/fold]


2. 下载下方附件解压,将得到的exe文件放在游戏根目录,且在游戏的img目录下创建一个auto_light文件夹:

light.zip (663.65 KB, 下载次数: 142)

QQ图片20171110212553.png


QQ图片20171110212929.png

3. 在rmmv编辑器里添加光源,在地图note里添加墙壁高度:


QQ图片20171110212722.png


这里的region id 1,3是我在插件参数里设置好的光源。

在地图note里设置墙壁高度:

[pre lang="html"]<autolight>{"wall_height":1}</autolight>[/pre]

4. 运行游戏:

开始后,会检测auto_light目录下的光图片,没有的话就会生成:


QQ图片20171110211943.png


生成的光影图:

light_1.png


你可以对它进行一些其他的优化,比如修改色调。
QQ图片20171110213215.png




评分

参与人数 4星屑 +50 赞 +4 收起 理由
yueshiguili112 + 1 我很赞同
89444640 + 1 效果真不錯
king + 1 求JS插件
余烬之中 + 50 + 1 有意义的讨论主题

查看全部评分

梦石
0
星屑
1816
在线时间
2527 小时
注册时间
2010-10-12
回帖
1336

开拓者

发表于 2017-10-14 06:27:30 | 显示全部楼层
本帖最后由 刺夜之枪 于 2017-10-13 14:29 编辑

有意思的问题,我回头有空研究一下。马克一下。

先查了些资料:


Intersection of Linear and Circular Components in 2D


line segment to circle collision/intersection detection


how to create 2D visibility/shadow effects for your game


2d Visibility from Red Blob Games


Field of View and Line of Sight in 2D


基本上还是 通过 射线 , FoV算法 。


用PIXI写,效率是不会有问题的。

评分

参与人数 3星屑 +10 赞 +2 收起 理由
余烬之中 + 10 认真讨论回帖
hyrious + 1 塞糖
garfeng + 1 谢谢~了解下

查看全部评分


回复 1 0

使用道具 举报

老黄鸡

梦石
4
星屑
45699
在线时间
7948 小时
注册时间
2009-7-6
回帖
13337

MZ评测员RM创作大赛01组委会开拓者贵宾

发表于 2017-10-14 10:09:05 | 显示全部楼层
射线检测感觉效率不会太低?
http://ncase.me/sight-and-light/

这里也有算法,我在极短12里就参考这个算法实现了动态光照。
丝毫不会卡顿。

点评

这个范例速度好快!  发表于 2017-10-14 10:48

评分

参与人数 2星屑 +10 赞 +1 收起 理由
余烬之中 + 10 认真讨论回帖
garfeng + 1 谢谢~

查看全部评分

RGDirect - DirectX驱动的RGSS,点我了解.
(排满,暂停)RM全系列成套系统定制请联系QQ1213237796
不接受对其他插件维护的委托
回复

使用道具 举报

梦石
0
星屑
22879
在线时间
8598 小时
注册时间
2011-12-31
回帖
3270
发表于 2017-10-14 12:52:54 | 显示全部楼层
本帖最后由 tseyik 于 2017-10-14 12:56 编辑

VA有名的光影脚本的作者寫的MV插件
Khas Advanced Lighting​
https://forums.rpgmakerweb.com/index.php?threads/khas-advanced-lighting-v4-2-ultra-now-with-real-time-shadows.77679/
You can download the Master Demo or all my plugins here (click me!).
[RMMV] Advanced Lighting Tutorial (with real-time shadows!)
https://www.youtube.com/watch?time_continue=856&v=ghcX7H8ijjs




点评

mega也被禁了……能发个百度网盘吗……TOT  发表于 2017-10-14 14:34
谢谢~  发表于 2017-10-14 14:28
https://mega.nz/#F!ZcYUkC7S!hyCQej47BxowGyC7xBtPYA  发表于 2017-10-14 13:25
谢谢~求搬运脚本或工程=-=近期因为开会,梯子挂了……TOT  发表于 2017-10-14 13:03

评分

参与人数 2星屑 +10 赞 +1 收起 理由
余烬之中 + 10 认真讨论回帖
garfeng + 1 塞糖

查看全部评分

回复

使用道具 举报

梦石
0
星屑
34
在线时间
22 小时
注册时间
2017-10-8
回帖
2
发表于 2017-10-15 00:14:30 | 显示全部楼层
Graphics插件需要1.1版本才支持
本插件版本为4.2,支持动态光影,对性能要求较高

KhasUltraLighting.rar

12.02 KB, 下载次数: 104

KhasCore.rar

2.15 KB, 下载次数: 98

KhasGraphics.rar

2.22 KB, 下载次数: 85

评分

参与人数 1赞 +1 收起 理由
garfeng + 1 谢谢~谢谢!

查看全部评分

回复

使用道具 举报

梦石
0
星屑
34
在线时间
22 小时
注册时间
2017-10-8
回帖
2
发表于 2017-10-15 01:06:34 | 显示全部楼层
lhy9443 发表于 2017-10-15 00:14
Graphics插件需要1.1版本才支持
本插件版本为4.2,支持动态光影,对性能要求较高
...

还要注意一下:
这插件需要Pixi.js v3或以上的版本
RMMV 1.01版本提供的Pixi.js是低于v3的版本
所以 需要更高的RMMV版本才能使用
至于符合要求的最低版本 我没一个一个去试
反正我用RMMV 1.3.4是没有问题的 提供的Pixi.js是v4.0.3
话说有谁会用的?发个教程呗

点评

是21M才對  发表于 2017-10-15 18:46
他在mega中有範例(3,0和4.X二種),約2.3M大小  发表于 2017-10-15 18:44
arcthunder.blogspot.com/p/tutorial-advanced-lighting.html,这是他的帮助文档。另外,该脚本需要素材,素材大概应会在他的帮助页面有下载?我这里爬不出去  发表于 2017-10-15 13:09

评分

参与人数 1星屑 +5 收起 理由
余烬之中 + 5 认真讨论回帖-连贴

查看全部评分

回复

使用道具 举报

梦石
0
星屑
16261
在线时间
4014 小时
注册时间
2015-9-14
回帖
1322

开拓者

发表于 2017-10-15 15:10:58 | 显示全部楼层
樓主是否這些lights素材: lights.rar (162.66 KB, 下载次数: 112)

评分

参与人数 2星屑 +10 赞 +1 收起 理由
余烬之中 + 10 认真讨论回帖
garfeng + 1 哇~谢谢!

查看全部评分

https://rpg.blue/data/attachment/album/202003/28/021323lltgrsvfytfwuya5.gif
回复

使用道具 举报

梦石
0
星屑
69
在线时间
0 小时
注册时间
2018-11-30
回帖
32
发表于 2018-11-30 05:07:34 | 显示全部楼层
感谢楼主分享
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

Powered by Discuz! X5.0 © 2001-2026 Discuz! Team.

在本版发帖返回顶部