【问题标题】:Applying part of a texture (sprite sheet / texture map) to a point sprite in iOS OpenGL ES 2.0在 iOS OpenGL ES 2.0 中将部分纹理(精灵表/纹理贴图)应用于点精灵
【发布时间】:2012-03-21 21:04:12
【问题描述】:

看起来这应该很容易,但我在使用带有点精灵的纹理的一部分时遇到了很多困难。我已经广泛搜索并找到了各种答案,但没有一个涉及我遇到的具体问题。

到目前为止我学到了什么:

  1. Basics of point sprite drawing
  2. How to deal with point sprites rendering as solid squares
  3. How to alter orientation of a point sprite
  4. How to use multiple textures with a point sprite,这里越来越近了..
  5. 之前已经做过点精灵 + 精灵表,但只能在 OpenGL ES 2.0(不是 1.0)中实现

这是我正在努力实现的图表

我在哪里:

  • 我有一组工作点精灵都使用同一个正方形图像。例如:16x16 的圆形图像效果很好。
  • 我有一个 Objective-C 方法,它生成一个 600x600 的图像,其中包含一个带有多个图像的精灵表。我已经通过将整个精灵表图像应用到使用 GL_TRIANGLES 绘制的四边形来验证这是有效的。
  • 我已成功使用上述方法将精灵表的一部分绘制到四边形上。我只是无法让它与点精灵一起使用。
  • 目前,我正在生成纹理坐标,指向我要定位的精灵表上的精灵中心。例如:使用底部的图像;星级:0.166,0.5;云:0.5,0.5;心脏:0.833,0.5。

代码:

顶点着色器

uniform mat4 Projection;
uniform mat4 Modelview;
uniform float PointSize;

attribute vec4 Position;
attribute vec2 TextureCoordIn;

varying vec2 TextureCoord;

void main(void)
{
    gl_Position = Projection * Modelview * Position;
    TextureCoord = TextureCoordIn;
    gl_PointSize = PointSize;
}

片段着色器

varying mediump vec2 TextureCoord;
uniform sampler2D Sampler;

void main(void)
{
    // Using my TextureCoord just draws a grey square, so
    // I'm likely generating texture coords that texture2D doesn't like.
    gl_FragColor = texture2D(Sampler, TextureCoord);

    // Using gl_PointCoord just draws my whole sprite map
    // gl_FragColor = texture2D(Sampler, gl_PointCoord);
}

我的困惑:

  1. 我不明白如何在片段着色器中使用gl_PointCoord 变量。 gl_PointCoord 最初包含什么?为什么?它从哪里获取数据?
  2. 我不明白要传入什么纹理坐标。例如,点精灵如何根据纹理坐标选择要使用我的精灵表的哪个部分?我习惯于绘制实际上具有 4 组纹理坐标(每个顶点一组)的四边形,这有什么不同(显然是这样)?

【问题讨论】:

  • 还是卡住了,有人可以分享一个示例着色器吗?

标签: ios opengl-es glsl opengl-es-2.0


【解决方案1】:

我的一位同事帮助解答了这个问题。事实证明,诀窍是利用点的大小(以 OpenGL 为单位)和精灵的大小(以纹理为单位,(0..1))结合一点矢量数学来仅渲染部分sprite-sheet 到每个点上。

顶点着色器

uniform mat4 Projection;
uniform mat4 Modelview;
// The radius of the point in OpenGL units, eg: "20.0"
uniform float PointSize;
// The size of the sprite being rendered. My sprites are square
// so I'm just passing in a float.  For non-square sprites pass in
// the width and height as a vec2.
uniform float TextureCoordPointSize;

attribute vec4 Position;
attribute vec4 ObjectCenter;
// The top left corner of a given sprite in the sprite-sheet
attribute vec2 TextureCoordIn;

varying vec2 TextureCoord;
varying vec2 TextureSize;

void main(void)
{
    gl_Position = Projection * Modelview * Position;
    TextureCoord = TextureCoordIn;
    TextureSize = vec2(TextureCoordPointSize, TextureCoordPointSize);

    // This is optional, it is a quick and dirty way to make the points stay the same
    // size on the screen regardless of distance.
    gl_PointSize = PointSize / Position.w;
}

片段着色器

varying mediump vec2 TextureCoord;
varying mediump vec2 TextureSize;
uniform sampler2D Sampler;

void main(void)
{
    // This is where the magic happens.  Combine all three factors to render
    // just a portion of the sprite-sheet for this point
    mediump vec2 realTexCoord = TextureCoord + (gl_PointCoord * TextureSize);
    mediump vec4 fragColor = texture2D(Sampler, realTexCoord);

    // Optional, emulate GL_ALPHA_TEST to use transparent images with
    // point sprites without worrying about z-order.
    // see: http://stackoverflow.com/a/5985195/806988
    if(fragColor.a == 0.0){
        discard;
    }

    gl_FragColor = fragColor;
}

【讨论】:

  • 我遇到了一些麻烦,想知道是否有人可以帮助我。我的精灵表总大小为 640 X 640。有 5 个精灵 x 5 行。所以每个精灵都是 128 x 128。我的 PointSize 是 50。我只是将此代码添加到我的片段着色器中。 vec2 TextureCoord = vec2(0, 0); // 我猜这是精灵表的左上角 vec2 TextureSize = vec2(128, 128); // 我不确定这是 PointSize(50) 还是 sprite size(128) mediump vec2 realTexCoord = TextureCoord + (gl_PointCoord * TextureSize); vec4 rotateTexture = texture2D(texture, realTexCoord);
  • @JamesAndres 很棒的问答,正是我想要的。起初,鉴于 Khronos 官方文档,我不相信这是可能的!我能预见的唯一问题是点大小,在我的系统上似乎最大为 1024,因此不能用于任意大小的通用精灵块...例如全尺寸背景等。
  • @JamesAndres 遇到的另一个问题是旋转。实际的方形点精灵本身不会在屏幕上旋转。只有 uv 纹理坐标被旋转,所以不幸的是,任何旋转似乎都被剪裁到作为点精灵的静态正方形。解决这个问题的一种方法是确保我的精灵纹理以一个矩形为边界,在该矩形中它们可以旋转 360 度而不会超出它们的边界。
  • @JamesAndres 我注意到的最后一个警告是,如果你想要一个矩形精灵,例如100x50 那么你需要100x100 的精灵纹理,我们用25 透明像素填充精灵的顶部和底部。否则,100x50 纹理只会映射到 square 点精灵上,有效地拉伸它以适应正方形。我从您的回答中学到了很多关于 Point Sprites 的知识。由于这些原因,我认为我会坚持使用 Point Sprites 在我的游戏中对平铺背景进行闪电般的处理,并使用常规的 GL_TRIANGLES sprite batcher 来处理其余的事情! :-D
  • PS 也适用于任何感兴趣的人,我的实验表明,在(我的)Android 手机上,使用优化的GL_TRIANGLES 的 3000 个精灵比使用优化的 GL_POINTS 的 3000 个精灵快(带有片段旋转)。对于数量相对较少的精灵,我会选择GL_POINTS 方法,因为CPU 端代码更简洁,但为了提高效率,最好坚持使用GL_TRIANGLES,尽管正如我提到的,我认为GL_POINTS 可能对不需要旋转的平铺背景!
【解决方案2】:

点精灵由单个位置组成。因此,任何“变化”的值实际上都不会变化,因为它们之间没有任何插值。

gl_PointCoord 是一个vec2 值,其中 XY 值介于 [0, 1] 之间。它们代表该点上的位置。 (0, 0) 是点的左下角,(1, 1) 是右上角。

因此,您希望将 (0, 0) 映射到精灵的左下角,并将 (1, 1) 映射到右上角。要做到这一点,你需要知道一些事情:精灵的大小(假设它们都是相同的大小),纹理的大小(因为纹理获取函数采用标准化纹理坐标,而不是像素位置),以及 当前正在渲染哪个 sprite。

后者可以通过varying 设置。它可以只是作为每个顶点数据传递到顶点着色器中的varying 的值。

您使用它加上精灵的大小来确定要在纹理中的哪个位置为该精灵提取数据。一旦有了要使用的纹理坐标,就可以将它们除以纹理大小以生成归一化的纹理坐标。

在任何情况下,点精灵,尽管有这个名字,实际上并不意味着 sprite 渲染。为此使用四边形/三角形会更容易,因为您可以更准确地确定所有东西的位置。

【讨论】:

  • 感谢您的回复尼科尔。所以看来我快到了。 TextureCoordIn 基本上是一个 XY (0..1) 点,指示要渲染的精灵。我有精灵的大小,精灵表/纹理的大小等。然而,我感到困惑的是如何将它们放在一起。我在网上看到过一些例子,他们对gl_PointCoord 进行数学运算以确定要渲染纹理的哪个部分,例如:gl_FragColor = texture2D(Sampler, gl_PointCoord + SomeOtherVec2);。你能给出一个示例着色器来为我指明正确的方向吗?
  • 我还应该提到,我对 GLSL(和一般的 OpenGL)还很陌生。当你说“所以你想将 (0, 0) 映射到你的精灵的左下角”和“为这个精灵拉数据”时,你是什么意思?谢谢。
猜你喜欢
  • 2013-04-07
  • 2016-10-01
  • 2011-08-11
  • 2011-05-09
  • 2012-02-10
  • 1970-01-01
  • 1970-01-01
  • 2018-08-20
  • 1970-01-01
相关资源
最近更新 更多