Применение части текстуры (лист спрайта/текстуры) для точечного спрайта в iOS OpenGL ES 2.0
Кажется, это должно быть легко, но я испытываю большие трудности, используя часть текстуры с помощью точечного спрайта. Я широко разошёл по всему миру и получил различные ответы, но ни одна из них не касается конкретной проблемы, которую я испытываю.
Что я узнал до сих пор:
Вот схема того, что я пытаюсь достичь
![Point sprite diagram]()
Где я нахожусь:
- У меня есть набор спрайтов рабочей точки, которые используют один и тот же квадратный образ. Например: изображение круга размером 16x16 отлично работает.
- У меня есть метод Objective-C, который генерирует изображение 600x600, содержащее спрайт-лист с несколькими изображениями. Я проверил, что это работает, применяя весь образ листа спрайта к квадру, нарисованному с помощью GL_TRIANGLES.
- Я использовал этот метод успешно, чтобы нарисовать части листа спрайтов на квадрациклах. Я просто не могу заставить его работать с точечными спрайтами.
- В настоящее время я создаю текстурные координаты, указывающие на центр спрайта на листе спрайта, на который я нацеливаюсь. Например: использование изображения внизу; звезда: 0,166,0,5; облако: 0,5,0,5; сердце: 0,833,0,5.
Код:
Vertex Shader
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);
}
Что я застрял:
- Я не понимаю, как использовать переменную
gl_PointCoord
в шейдере фрагментов. Что содержит gl_PointCoord
изначально? Зачем? Где он получает свои данные?
- Я не понимаю, какие текстурные координаты должны пройти. Например, как точечный спрайт выбирает, какую часть моего листа спрайта использовать на основе координат текстуры? Я привык рисовать квадроциклы, у которых есть фактически 4 набора координат текстуры (по одной для каждой вершины), как это отличается (очевидно, это так)?
Ответы
Ответ 1
Помощник моего помог ответить. Оказалось, что уловкой является использование как размера точки (в единицах OpenGL), так и размера спрайта (в единицах текстуры (0..1)) в сочетании с небольшой векторной математикой, чтобы отображать только часть sprite-sheet на каждую точку.
Vertex Shader
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;
}
Ответ 2
Точечные спрайты состоят из одной позиции. Поэтому любые "переменные" значения на самом деле не будут меняться, потому что между ними нечего интерполировать.
gl_PointCoord
- это значение vec2
, где значения XY находятся между [0, 1]. Они представляют собой местоположение на точке. (0, 0) - левая нижняя точка, а (1, 1) - верхняя правая.
Итак, вы хотите отобразить (0, 0) в левом нижнем углу спрайта и (1, 1) в верхнем правом углу. Для этого вам нужно знать определенные вещи: размер спрайтов (при условии, что они имеют одинаковый размер), размер текстуры (поскольку функции извлечения текстуры принимают нормализованные координаты текстуры, а не пиксельные местоположения) и которые sprite в настоящее время отображается.
Последнее может быть установлено через a varying
. Это может быть просто значение, которое передается в виде вершинных данных в varying
в вершинном шейдере.
Вы используете это плюс размер спрайтов, чтобы определить, где в текстуре вы хотите извлечь данные для этого спрайта. После того, как вы захотите использовать координаты текселя, вы разделите их на размер текстуры, чтобы получить нормализованные координаты текстуры.
В любом случае, точечные спрайты, несмотря на название, на самом деле не предназначены для рендеринга спрайтов. Было бы проще использовать для этого квадранты/треугольники, так как у вас может быть больше уверенности в том, какие позиции у всех есть.