ios - 如何在片段着色器(SpriteKit)中绘制纹理的特定部分
问题描述
我的节点大小为 64x32,纹理大小为 192x192,我试图在第一个节点处绘制该纹理的第一部分,在第二个节点处绘制第二部分......
片段着色器(附加到 SKSpriteNode,纹理大小为 64x32)
void main() {
float bX = 64.0 / 192.0 * (offset.x + 1);
float aX = 64.0 / 192.0 * (offset.x );
float bY = 32.0 / 192.0 * (offset.y + 1);
float aY = 32.0 / 192.0 * (offset.y);
float normalizedX = (bX - aX) * v_tex_coord.x + aX;
float normalizedY = (bY - aY) * v_tex_coord.y + aY;
gl_FragColor = texture2D(u_temp, vec2(normalizedX, normalizedY));
}
offset.x
- [0, 2]offset.y
- [0, 5]u_temp
- 纹理大小为 192x192- 将值从 [0,1] 转换为例如 [0, 0.33] 的函数
但结果似乎是错误的:
带有附加纹理的 SKSpriteNode
SKSpriteNode 没有纹理(我想用纹理实现)
解决方案
当纹理在 altas 中时,它不再由坐标 from (0,0)
to处理(1,1)
。地图集真的是一个在幕后组装的大纹理。当您在普通精灵中使用来自图集的特定命名图像时,SpriteKit 会在有关图集如何组装的信息中查找该图像名称,然后告诉 GPU 类似“使用bigAtlasTexture
, 坐标(0.1632,0.8814)
通过绘制此精灵(0.1778, 0.9143)
”之类的信息。如果您要使用相同的纹理编写自定义着色器,则需要有关它在图集中的位置的信息,您可以从以下位置获得textureRect
:
https://developer.apple.com/documentation/spritekit/sktexture/1519707-texturerect
因此,您的纹理实际上不是一个图像,而是由textureRect()
大量纹理的大型打包图像中的位置定义的。我发现用 to 来思考是最容易的(0,0)
,(1,1)
所以在编写着色器时,我通常会textureRect
=> 减去和缩放以获取(0,0)-(1,1)
=> 计算所需的修改坐标 => 缩放并添加以textureRect
再次获取 =>texture2D
查找。
由于您的着色器需要知道textureRect
但您不能从着色器代码中调用它,因此您有两种选择:
- 制作一个属性或统一来保存该信息,从外部填充它,然后让着色器引用它。
- 如果着色器仅用于特定纹理或少数纹理,那么您可以生成专用于所需的着色器代码
textureRect
,即它只是在纹理代码中包含一些常量。
这是使用方法 #2 的示例的一部分:
func myShader(forTexture texture: SKTexture) -> SKShader {
// Be careful not to assume that the texture has v_tex_coord ranging in (0, 0) to
// (1, 1)! If the texture is part of a texture atlas, this is not true. I could
// make another attribute or uniform to pass in the textureRect info, but since I
// only use this with a particular texture, I just pass in the texture and compile
// in the required v_tex_coord transformations for that texture.
let rect = texture.textureRect()
let shaderSource = """
void main() {
// Normalize coordinates to (0,0)-(1,1)
v_tex_coord -= vec2(\(rect.origin.x), \(rect.origin.y));
v_tex_coord *= vec2(\(1 / rect.size.width), \(1 / rect.size.height));
// Update the coordinates in whatever way here...
// v_tex_coord = desired_transform(v_tex_coord)
// And then go back to the actual coordinates for the real texture
v_tex_coord *= vec2(\(rect.size.width), \(rect.size.height));
v_tex_coord += vec2(\(rect.origin.x), \(rect.origin.y));
gl_FragColor = texture2D(u_texture, v_tex_coord);
}
"""
let shader = SKShader(source: shaderSource)
return shader
}
这是这里一些具体示例的精简版: https ://github.com/bg2b/RockRats/blob/master/Asteroids/Hyperspace.swift
推荐阅读
- r - R中列表中矩阵列表的减法
- sql - 需要在Oracle中添加字符串
- ruby-on-rails - 如何在 Rails 6 站点中实现 plyr.js?
- mysql - Linux Ubuntu 18.04 - 加载 MySQLdb 模块时出错。你安装了mysqlclient吗?
- javascript - p5 中的多个画布
- nginx - Nginx 重定向而不是使用 proxy_pass 进行反向代理
- python - 我已经通过 for 循环提供了嵌套列表访问,但我无法获得结果?
- excel - VBA:以 XML 格式保存工作簿但具有相同的工作簿名称
- c++ - 如何在运行时选择迭代器方向
- python - matplotlib 有没有办法使用公制更改轴值的比例?