将部分纹理(精灵表/纹理贴图)应用于iOS OpenGL ES 2.0中的点精灵

时间:2012-03-07 21:34:26

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

看起来这应该很简单,但是使用点精灵的部分纹理我遇到了很多困难。我已经广泛搜索并提出了各种答案,但这些都没有解决我遇到的具体问题。

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

  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)中使用
  6. 以下是我正在努力实现的目标

    Point sprite diagram

    我在哪里:

    • 我有一组工作点精灵都使用相同的单个方形图像。例如:一个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组纹理坐标(每个顶点一个)的四边形,这有什么不同(显然是这样)?

2 个答案:

答案 0 :(得分:10)

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

顶点着色器

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;
}

答案 1 :(得分:4)

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

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

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

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

使用它加上精灵的大小来确定纹理中为此精灵提取数据的位置。获得要使用的纹素坐标后,将它们除以纹理大小以生成标准化纹理坐标。

在任何情况下,无论名称如何,精灵点都不适用于精灵渲染。使用四边形/三角形会更容易,因为您可以更确切地确定所有位置的确切位置。