在 Threejs 平面上渲染 TMX 地图

2024-01-07

使用新代码更新了问题

我正在尝试编写一个 WebGL 着色器来绘制TMX层 https://github.com/bjorn/tiled/wiki/TMX-Map-Format(从 Tiled 编辑器导出)。我正在使用 THREE.js 创建一个Plane网格并让材料成为ShaderMaterial它将在其上绘制地图。

对于那些不知道由平铺编辑器 https://www.mapeditor.org/因为 json 会给出data每层的属性;它包含一个数值数组,每个数值都是图块集中的图块索引,如下所示:

"data": [5438, 5436, 5437, 5438, 5436, 5437, 5438, 5436, 5437, 5438, 845, ...]

由于我的图块地图为 256 x 256 个图块,因此该数组的长度为 65536 个元素。每个元素的值引用tilemap中的一个tile,其中索引的定义如下:

tile set
(source: melonjs.org http://www.melonjs.org/docs/symbols/spritesheet_grid.png)

所以图块地图的索引 0 指的是图块 5438,它们的计数方式如上所示。索引表示图块图中的哪个图块具有图块集中的该图块,它们以相同的方式进行计数。

以下是我创建材质、平面和网格的方法:

this.size = new THREE.Vector2(256, 256);
this.tileSize = new THREE.Vector2(16, 16);

this._material = new THREE.ShaderMaterial({
    uniforms: this._uniforms,
    vertexShader: this.vShader,
    fragmentShader: this.fShader,
    transparent: (this.opacity === 0)
});

this._plane = new THREE.PlaneGeometry(
    this.size.x * this.tileSize.x,
    this.size.y * this.tileSize.y
);

this._mesh = new THREE.Mesh(this._plane, this._material);

最后是制服和着色器。基本上我需要将数据元素映射到图块集中的实际图块并绘制它。为了得到data数组到着色器中我将其加载为THREE.DataTexture并将其视为纹理。

这是我的第二次尝试:

//Shaders
var vShader = [
    'varying vec2 pixelCoord;',
    'varying vec2 texCoord;',

    'uniform vec2 layerSize;',
    'uniform vec2 tilesetSize;',
    'uniform vec2 inverseTilesetSize;',

    'uniform vec2 tileSize;',
    'uniform vec2 inverseTileSize;',
    'uniform float scale;',

    'void main(void) {',
    '   pixelCoord = (uv * layerSize) * tileSize * scale;', //pixel we are at
    '   texCoord = pixelCoord * inverseTilesetSize * inverseTileSize;', //calculate the coord on this map
    '   gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);',
    '}'
].join('\n');

var fShader = [
    //"precision highp float;",

    'varying vec2 pixelCoord;',         
    'varying vec2 texCoord;',

    'uniform vec2 tilesetSize;',
    'uniform vec2 inverseTilesetSize;',

    'uniform vec2 tileSize;',
    'uniform vec2 inverseTileSize;',
    'uniform vec2 numTiles;',
    'uniform float scale;',

    'uniform sampler2D tileset;',
    'uniform sampler2D tileIds;',

    //I store the tile IDs as a texture (1 float value = rgba)
    //this will decode the rgba values back into a float ID
    'highp float decode32(highp vec4 rgba) {',
    '   const vec4 bit_shift = vec4(1.0/(256.0*256.0*256.0), 1.0/(256.0*256.0), 1.0/256.0, 1.0);',
    '   float depth = dot(rgba, bit_shift);',
    '   return depth;',
    '}',

    'void main(void) {',
    '   vec4 tileId = texture2D(tileIds, texCoord);', //grab this tileId from the layer data
    '   tileId.rgba = tileId.abgr;', //flip flop due to endianess
    //I find that this value is always `0 < tileValue < 1`, I think my `decode32` sucks...
    '   float tileValue = decode32(tileId);', //decode the vec4 into the float ID
    '   vec2 tileLoc = vec2(mod(tileValue, numTiles.y), floor(tileValue / numTiles.y));', //convert the ID into x, y coords
    '   vec2 coord = floor(tileLoc * 256.0) * tileSize;', //coord in the tileset
    '   vec2 offset = mod(pixelCoord, tileSize);', //how much to draw
    '   gl_FragColor = texture2D(tileset, (coord + offset) * inverseTilesetSize);', //grab tile from tilset
    '}'
].join('\n');

还有制服和数据纹理:

//tried making this 256 x 256 like it is conceptually,
//and also tried 65536 x 1 like the data structure
this.dataTex = new THREE.DataTexture(
                this.data,
                this.data.length, //width (65536)
                1, //height (1)
                THREE.RGBAFormat, //format
                THREE.UnsignedByteType, //type
                THREE.UVMapping, //mapping
                THREE.ClampToEdgeWrapping, //wrapS
                THREE.ClampToEdgeWrapping, //wrapT
                THREE.NearestFilter, //magFilter
                THREE.NearestMipMapNearestFilter //minFilter
            );
this.dataTex.needsUpdate = true;


this._uniforms = window._uniforms = {
    layerSize:          { type: 'v2', value: this.size },
    tilesetSize:        { type: 'v2', value: new THREE.Vector2(this.tileset.image.width, this.tileset.image.height) },
    inverseTilesetSize: { type: 'v2', value: new THREE.Vector2(1 / this.tileset.image.width, 1 / this.tileset.image.height) },

    tileSize:           { type: 'v2', value: this.tileSize },
    inverseTileSize:    { type: 'v2', value: new THREE.Vector2(1 / this.tileSize.x, 1 / this.tileSize.y) },
    numTiles:           { type: 'v2', value: new THREE.Vector2(this.tileset.image.width / this.tileSize.x, this.tileset.image.height / this.tileSize.y) },
    scale:              { type: 'f', value: 1 / this.scale },

    tileset:            { type: 't', value: this.tileset },
    tileIds:            { type: 't', value: this.dataTex },
    repeatTiles:        { type: 'i', value: this.repeat ? 1 : 0 }
};

因此,当渲染时,我只会一遍又一遍地重复瓷砖集的第一个图块:

不确定是什么原因造成的,但因为它处于位置0, 0我想我在某个地方对我的干扰为零。


终于有了一个可行的解决方案,我最大的问题是将浮点数数组打包到纹理中,然后进行解码。我忘记了,当从数据纹理加载字节时,它们是“标准化的”。这意味着每个值都除以 255 以将其限制为 [0, 1]。移位后,我需要乘以 255.0 来进行非规格化。

我还在某处读到 webgl 浮点数是 24 位而不是 32 位,因此我更改了编码和解码算法以仅使用 RGB 通道。

现在它成功地将整数(或浮点)数组打包到Uint8Array,将其作为纹理传递给 webgl 并正确使用它:

数组打包:

var array = [5438, 5436, 5437, 5438, 5436, 5437, ...],
    arrayBuff = new ArrayBuffer(array.length * 3),
    array8 = new Uint8Array(arrayBuff);

for(var i = 0, y = 0, il = array.length; i < il; ++i, y += 3) {
    var value = array[i];

    array8[y + 0] = (value & 0x000000ff);
    array8[y + 1] = (value & 0x0000ff00) >> 8;
    array8[y + 2] = (value & 0x00ff0000) >> 16;
}


dataTex = new THREE.DataTexture(
                            array8,
                            this.size.x, //width
                            this.size.y, //height
                            THREE.RGBFormat, //format
                            THREE.UnsignedByteType, //type
                            THREE.UVMapping, //mapping
                            THREE.ClampToEdgeWrapping, //wrapS
                            THREE.ClampToEdgeWrapping, //wrapT
                            THREE.NearestFilter, //magFilter
                            THREE.NearestMipMapNearestFilter //minFilter
                        );
dataTex.needsUpdate = true;

Shaders:

var vShader = [
    'varying vec2 pixelCoord;',
    'varying vec2 texCoord;',

    'uniform vec2 mapSize;',
    'uniform vec2 inverseLayerSize;',

    'uniform vec2 inverseTileSize;',

    'void main(void) {',
    '   pixelCoord = (uv * mapSize);',
    '   texCoord = pixelCoord * inverseLayerSize * inverseTileSize;',
    '   gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);', //hand this position to WebGL
    '}'
].join('\n');

var fShader = [
    'varying vec2 pixelCoord;',         
    'varying vec2 texCoord;',

    'uniform vec2 inverseTilesetSize;',

    'uniform vec2 tileSize;',
    'uniform vec2 numTiles;',

    'uniform sampler2D tileset;',
    'uniform sampler2D tileIds;',

    'float decode24(const in vec3 rgb) {',
    '   const vec3 bit_shift = vec3((256.0*256.0), 256.0, 1.0);',
    '   float fl = dot(rgb, bit_shift);', //shift the values appropriately
    '   return fl * 255.0;', //denormalize the value
    '}',

    'void main(void) {',
    '   vec3 tileId = texture2D(tileIds, texCoord).rgb;', //grab this tileId from the layer data
    '   tileId.rgb = tileId.bgr;', //flip flop due to endianess
    '   float tileValue = decode24(tileId);', //decode the normalized vec3 into the float ID
    '   vec2 tileLoc = vec2(mod(tileValue, numTiles.x) - 1.0, floor(tileValue / numTiles.x));', //convert the ID into x, y coords;
    '   tileLoc.y = numTiles.y - 1.0 - tileLoc.y;', //convert the coord from bottomleft to topleft

    '   vec2 offset = floor(tileLoc) * tileSize;', //offset in the tileset
    '   vec2 coord = mod(pixelCoord, tileSize);', //coord of the tile.

    '   gl_FragColor = texture2D(tileset, (offset + coord) * inverseTilesetSize);', //grab tile from tileset
    '}'
].join('\n');

Uniforms:

this._uniforms = window._uniforms = {
    mapSize:            { type: 'v2', value: new THREE.Vector2(this.size.x * this.tileSize.x, this.size.y * this.tileSize.y) },
    inverseLayerSize:   { type: 'v2', value: new THREE.Vector2(1 / this.size.x, 1 / this.size.y) },
    inverseTilesetSize: { type: 'v2', value: new THREE.Vector2(1 / this.tileset.image.width, 1 / this.tileset.image.height) },

    tileSize:           { type: 'v2', value: this.tileSize },
    inverseTileSize:    { type: 'v2', value: new THREE.Vector2(1 / this.tileSize.x, 1 / this.tileSize.y) },
    numTiles:           { type: 'v2', value: new THREE.Vector2(this.tileset.image.width / this.tileSize.x, this.tileset.image.height / this.tileSize.y) },

    tileset:            { type: 't', value: this.tileset },
    tileIds:            { type: 't', value: this.dataTex }
};
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Threejs 平面上渲染 TMX 地图 的相关文章

  • 亚马逊联属网络营销搜索广告脚本:未捕获类型错误:无法读取 null 属性“getBoundingClientRect”

    我在我的网站上嵌入了亚马逊联属原生搜索广告 当我在我的开发笔记本电脑上进行测试时 搜索广告 显示没有任何问题 但是 当部署到托管提供商时 遇到运行时错误并且亚马逊搜索广告未显示 请在这里帮忙 Error Uncaught TypeError
  • 有没有办法从 PhantomJS 的键盘读取用户输入?

    我使用 PhantomJS 登录网站 必须手动输入验证码 如何将验证码图像保存到磁盘 然后在 PhantomJS 控制台中手动输入验证码 我遇到了同样的问题 只需将系统模块与 page render 和一些传递给 page evaluate
  • 禁用外部点击时关闭模式

    我正在制作一些使用模式的博客物质化 但我的模态 onclick 外部和错误数据有问题 这是我的代码 main js function changepassword var user userlog val var content conte
  • 如何在 React JS 中根据键创建动态表?

    我正在尝试在 React JS 中创建一个动态表组件 该组件当前只有一个静态标头 其中包括最常见的结果键 有些结果还包含更多信息 例如电话号码 学位 如何根据键 值的存在动态地使用附加列扩展表 我应该与state并在存在时使其可见 或者我应
  • 无法将消息发布到服务工作人员,因为控制器值为空

    我正在尝试做一个website https secure depths 31934 herokuapp com 在 Service Worker 的帮助下可以离线使用 以缓存页面所需的文件 我试图让用户控制他希望缓存的图像 为此 我使用一个
  • 在 IE8 中使用 javascript __proto__

    你好 我在 javascript 中有这两个对象 var john firstname John lastname Smith var jane firstname Jane 这样做 jane proto john 我可以访问 Jane 的
  • 使用非常大的背景位置偏移是否存在性能问题?

    我正在构建一个进度条控件 并且正在研究它实际上并不显示进度 而只是旋转 正在发生某事 的指示器的情况 我的设计基本上是交替的对角条纹 本质上是一个像这样的理发杆 但是 旋转 由于希望将尽可能多的负载转移给渲染引擎 我想为此使用 CSS 过渡
  • 个人 Tumblr 帖子上的 Javascript

    我知道您可以编辑在 tumblr 博客上呈现所有帖子博客主页的 html AngularJS 但是 有什么办法可以添加自定义到各个帖子 我想在逐个帖子的基础上做一些 javascript 的东西 但似乎无法找到可以编辑代码的位置 或者 如果
  • 在 UIWebView 中禁用复制和粘贴

    几乎 我已经尝试了一切方法来禁用复制 粘贴UIWebView但对我来说没有任何作用 我正在加载我的UIWebView来自字符串 字符串数组 如下所示 webView loadHTMLString NSString stringWithFor
  • 对使用“new”创建的数组上“map”的行为感到困惑[重复]

    这个问题在这里已经有答案了 我对结果感到困惑mapping 使用创建的数组new function returnsFourteen return 14 var a new Array 4 gt undefined x 4 in Chrome
  • 我应该如何实现将状态保存到 localStorage?

    CODE var React require react var Recipe require Recipe jsx var AddRecipe require AddRecipe jsx var EditRecipe require Ed
  • 将文本字段限制为仅包含数字的最佳方法?

    I m using the following Javascript to restrict a text field on my website to only accept numerical input and no other le
  • 如何绕过Access-Control-Allow-Origin?

    我正在一个平台上对我自己的服务器进行ajax调用 他们设置了阻止这些ajax调用的平台 但我需要它从我的服务器获取数据以显示从我的服务器数据库检索到的数据 我的 ajax 脚本正在运行 它可以将数据发送到我的服务器的 php 脚本以允许其处
  • 检查浏览器空闲的替代方法

    我有一个简单的网络信息亭 在鼠标未使用 jQuery 移动 30 秒后显示用户操作提示 var i null body wrap mousemove function clearTimeout i overlay fadeOut i set
  • 此版本的 CLI 仅与 Angular 版本 5.0.0 或更高版本兼容错误

    我已经有 Angular 项目在 4 版本中运行 在安装新项目时 不幸的是我安装了 6 版本的 Angular cli 在以 4 版本运行的旧项目中运行 ngserve 命令时 这会引发错误 您的全局 Angular CLI 版本大于本地版
  • 什么时候可以使用Javascript,什么时候不可以?

    不使用太多 javascript jquery 是个好习惯吗 我们应该尽可能避免它 为了良好的可访问性 吗 什么时候可以使用 JavaScript 什么时候不能在网页设计和开发中使用 JavaScript 在什么场景 什么条件下 Updat
  • 在 Meteor 应用程序中实现 MongoDB 2.4 的全文搜索

    我正在考虑向 Meteor 应用程序添加全文搜索 我知道 MongoDB 现在支持此功能 但我对实现有一些疑问 启用文本搜索功能的最佳方法是什么 textSearchEnabled true 在 Meteor 应用程序中 有没有办法添加索引
  • highchart堆积柱每个类别的总数据

    我想获取每个类别的总数据 这point stackTotal只给出活动数据的总数 从我粘贴的代码示例中 我想知道每种水果的总消耗量 因此 即使我单击右上角图例上的乔的名字 这使得堆叠图表上的所有乔信息都处于非活动状态 我仍然可以知道约翰 简
  • Safari 扩展将消息发送到特定选项卡

    有没有办法从全局页面发送消息到特定选项卡 我目前正在做的是 在创建选项卡时 注入的脚本会创建一个唯一的 ID 并将包含该编号的消息发送到全局页面 并且全局页面会保存该编号 如果全局页面需要发送一些数据到一个tab 即 tab 3 然后全局页
  • 使用
    元素作为 JavaScript 代码的输入。这是最好的方法吗?

    各位 显然 我是编码新手 所以最近完成了一些有关 HTML 和 Javascript 的 Lynda 课程后 我的简单 HTML 页面遇到了困难 基本上 我想要的是使用 JavaScript 进行基本计算 让用户使用 HTML 输入两个数字

随机推荐

  • 使用 Python 创建 2 人游戏

    我的朋友向我提出挑战 要求我制作一个我们可以互相对战的流行游戏 战舰 的版本 如果你不了解这款游戏 那其实并不重要 因为对我来说 困难的部分不是制作游戏 而是弄清楚我们如何通过计算机相互对战 我之前制作过同样的游戏 只针对一名玩家 我使用的
  • 如何在rails 4中的单个rails应用程序中访问多个数据库?

    我是 Rails 新手 不知道如何在 Rails 单个应用程序中访问多个数据库 我会这样尝试 配置 数据库 yml default default adapter mysql2 encoding utf8 pool 5 username r
  • R:按字母数字对数据框进行排序

    我有一个数据框 它存储每个模型的计数值 型号名称是字母数字 然后 我使用 ggplot2 生成条形图 其中 x 轴为模型 y 轴为计数 我想订购我的 x 轴 x 轴在数据框中和图中的 x 轴中显示如下 我想对其进行正确排序 例如 M 1 M
  • 如何强制将一段文本设置为“direction rtl”段落内的“direction ltr”

    因此 电话号码始终是ltr 从左到右 在多语言网站上工作时 我需要在具有方向 rtl 的文本段落中插入电话号码 带有 前缀和用 分隔的数字 当然对于相关语言 所以我有这样的事情 ltr test direction ltr rtl test
  • cout 和 printf 适用于内置调试而非发布的 Dll

    我构建了一个 DLL 通常通过 SetWindowHookEx 注入到控制台应用程序中 DLL 将信息输出到控制台非常重要 我一直在使用 std cout 执行此操作 DLL 已接近完成 直到我尝试在发布模式下构建 DLL 这使得所有 co
  • 调试 C 代码

    有人可以告诉我我的代码有什么问题以及为什么它会产生这个输出 Code int main unsigned num char response do printf Please enter a positive integer greater
  • 禁用属性的正确值是多少?

    的正确值是多少disabled文本框或文本区域的属性 我以前见过使用以下内容
  • 如何根据环境实现cucumber场景不同的数据

    我在不同环境中执行 cucumber jvm 场景时遇到问题 场景的特征文件中包含的数据属于一个环境 为了在不同的环境中执行场景 我需要根据要执行的环境更新功能文件中的数据 例如 在以下场景中 我将搜索条件包含在功能文件中 搜索条件对于 Q
  • Objective-C - 弱属性 - getter autoreleases(自动引用计数)

    我有疑问weakARC 中的属性 自动引用计数 我的理解 如有错误请指正 weak属性的行为类似于assign属性 但当该属性指向的实例被销毁时 ivar 会指向 nil 问题 我只是觉得吸气剂weak属性保留和自动释放 它不是应该像 ge
  • 动态添加的表行不会触发单击事件

    我有一个空表 我使用以下方法通过 jQuery 添加行 table gt tbody last append tr td symbol Code1 td td symbol Code2 td td symbol Code3 td tr 一切
  • 将基于类的组件转换为钩子(gapi API)

    我有这个基于类的组件 使用gapi Google Auth API来呈现按钮并且它可以工作 import React from react class GoogleAuth extends React Component state isS
  • 正确关闭 ActiveMQ 和 Spring DefaultMessageListenerContainer

    当 Tomcat 管理器发出 停止 命令时 我们的系统不会关闭 我已确定与ActiveMQ Spring有关 我什至已经弄清楚如何让它关闭 但是我的解决方案是一个黑客 至少我希望这不是 正确 的方法 我想知道关闭 ActiveMQ 的正确方
  • 如何正确更改MaterialAlertDialog文本颜色?

    我尝试仅使用 Material Components 中的小部件 但在许多情况下 没有记录如何实现样式 让我们考虑一下MaterialAlertDialog 每次我想显示一个对话框时 我都会调用这部分代码 MaterialAlertDial
  • Scanf/Printf 双变量 C

    假设我有以下 C 代码 double var scanf lf var printf lf var printf f var 它从标准输入变量 var 读取 然后在标准输出 var 中打印两次 我知道这就是您从标准输入读取双变量的方式 但我
  • Tensorflow表查找int->float

    给定一个包含整数 代表类 的未知维度 的 2D 张量 我想获得一个相同形状的新张量 但用从查找表中获取的浮点数替换值 代表类权重 例如 inputs 1 3 3 2 4 2 lookup table 1 0 2 2 0 25 3 0 1 4
  • OS X 安装 Sphinx:找不到“sphinx-build”和“sphinx-quickstart”

    关于这个主题 有几个 未答复 的帖子 涉及找不到 sphinx build 在 Mac 操作系统中找不到 sphinx build h 命令 https stackoverflow com questions 24582037 locati
  • 包含流中包含 JSF 标签/组件的动态内容

    我正在开发一个应用程序 我想在其中包含来自流的动态 XHTML 内容 为了处理这个问题 我编写了一个标记处理程序扩展 它将动态 XHTML 内容转储到输出组件 如下所示 UIOutput htmlChild UIOutput ctx get
  • 优化图片的javascript预加载

    我想知道是否有人有任何通过 javascript 优化图像预加载的策略 我正在将 Flash 应用程序移植到 html css 中 尝试重新创建尽可能接近原始站点的 UI 它本质上是一个照片浏览器应用程序 当用户将鼠标悬停在链接上时会显示高
  • 按“后退”按钮时,未使用 setResult() 在 onPause() 中设置结果

    我有 2 项活动 AAA 和 BBB 我使用 AAA 从 AAA 呼叫 BBBstartActivityForResult 意图 int 完成 BBB 后 我按 后退 按钮返回 AAA 在 BBB 中 我重写 onPause 并使用以下方法
  • 在 Threejs 平面上渲染 TMX 地图

    使用新代码更新了问题 我正在尝试编写一个 WebGL 着色器来绘制TMX层 https github com bjorn tiled wiki TMX Map Format 从 Tiled 编辑器导出 我正在使用 THREE js 创建一个