如何在片段着色器(SpriteKit)中绘制纹理的特定部分
How to draw specific part of texture in fragment shader (SpriteKit)
我的节点大小为 64x32,纹理大小为 192x192,我试图在第一个节点绘制该纹理的第一部分,在第二个节点绘制第二部分...
片段着色器(贴图贴图大小为64x32的SKSpriteNode)
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
- function to convert a value from [0,1] to, for example, [0, 0.33]
但是结果好像不对:
带有附加纹理的 SKSpriteNode
没有纹理的SKSpriteNode(我想用纹理实现的)
当纹理在 altas 中时,它不再由从 (0,0)
到 (1,1)
的坐标寻址。图集实际上是一个在幕后组装的大型纹理。当您在普通精灵中使用图集中的特定命名图像时,SpriteKit 会在有关图集如何组装的信息中查找该图像名称,然后告诉 GPU 类似“使用 bigAtlasTexture
绘制此精灵,坐标 (0.1632,0.8814)
到 (0.1778, 0.9143)
”。如果您打算使用相同的纹理编写自定义着色器,则需要有关它在图集内的位置的信息,您可以从 textureRect
:
https://developer.apple.com/documentation/spritekit/sktexture/1519707-texturerect
所以你的纹理实际上不是一个图像,而是由大量纹理打包的大图像中的一个位置 textureRect()
定义的。我发现用 (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
我的节点大小为 64x32,纹理大小为 192x192,我试图在第一个节点绘制该纹理的第一部分,在第二个节点绘制第二部分...
片段着色器(贴图贴图大小为64x32的SKSpriteNode)
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- function to convert a value from [0,1] to, for example, [0, 0.33]
但是结果好像不对:
带有附加纹理的 SKSpriteNode
没有纹理的SKSpriteNode(我想用纹理实现的)
当纹理在 altas 中时,它不再由从 (0,0)
到 (1,1)
的坐标寻址。图集实际上是一个在幕后组装的大型纹理。当您在普通精灵中使用图集中的特定命名图像时,SpriteKit 会在有关图集如何组装的信息中查找该图像名称,然后告诉 GPU 类似“使用 bigAtlasTexture
绘制此精灵,坐标 (0.1632,0.8814)
到 (0.1778, 0.9143)
”。如果您打算使用相同的纹理编写自定义着色器,则需要有关它在图集内的位置的信息,您可以从 textureRect
:
https://developer.apple.com/documentation/spritekit/sktexture/1519707-texturerect
所以你的纹理实际上不是一个图像,而是由大量纹理打包的大图像中的一个位置 textureRect()
定义的。我发现用 (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