Skip to content

Commit 0349f92

Browse files
committed
Remaining sprite work
- UV bias - Single sprite rendering - Cache more stuff in context
1 parent 8ead822 commit 0349f92

5 files changed

Lines changed: 119 additions & 21 deletions

File tree

arcade/context.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
Contains pre-loaded programs
44
"""
55

6+
from array import array
67
from collections.abc import Iterable, Sequence
78
from pathlib import Path
89
from typing import Any
@@ -112,6 +113,7 @@ def __init__(
112113
self.sprite_list_program_no_geo["texture_id_data"] = 5
113114
self.sprite_list_program_no_geo["index_data"] = 6
114115

116+
# Geo shader single sprite program
115117
self.sprite_program_single = self.load_program(
116118
vertex_shader=":system:shaders/sprites/sprite_single_vs.glsl",
117119
geometry_shader=":system:shaders/sprites/sprite_list_geometry_no_cull_geo.glsl",
@@ -120,6 +122,34 @@ def __init__(
120122
self.sprite_program_single["sprite_texture"] = 0
121123
self.sprite_program_single["uv_texture"] = 1
122124
self.sprite_program_single["spritelist_color"] = 1.0, 1.0, 1.0, 1.0
125+
# Non-geometry shader single sprite program
126+
self.sprite_program_single_simple = self.load_program(
127+
vertex_shader=":system:shaders/sprites/sprite_single_simple_vs.glsl",
128+
fragment_shader=":system:shaders/sprites/sprite_list_simple_fs.glsl",
129+
)
130+
self.sprite_program_single_simple["sprite_texture"] = 0
131+
self.sprite_program_single_simple["uv_texture"] = 1
132+
self.sprite_program_single_simple["spritelist_color"] = 1.0, 1.0, 1.0, 1.0
133+
134+
# fmt: off
135+
self.spritelist_geometry_simple = self.geometry(
136+
[
137+
BufferDescription(
138+
self.buffer(
139+
data=array("f", [
140+
-0.5, +0.5, # Upper left
141+
-0.5, -0.5, # lower left
142+
+0.5, +0.5, # upper right
143+
+0.5, -0.5, # lower right
144+
])
145+
),
146+
"2f",
147+
["in_pos"]
148+
),
149+
],
150+
mode=self.TRIANGLE_STRIP,
151+
)
152+
# fmt: on
123153

124154
# Shapes
125155
self.shape_line_program: Program = self.load_program(

arcade/draw/rect.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def draw_texture_rect(
5454
ctx.disable(ctx.BLEND)
5555

5656
atlas = atlas or ctx.default_atlas
57-
program = ctx.sprite_program_single
57+
program = ctx.sprite_program_single_simple
5858

5959
texture_id, _ = atlas.add(texture)
6060
if pixelated:
@@ -68,14 +68,13 @@ def draw_texture_rect(
6868
atlas.use_uv_texture(unit=1)
6969

7070
geometry = ctx.geometry_empty
71-
program["pos"] = rect.x, rect.y, 0
71+
program["pos_rot"] = rect.x, rect.y, 0, angle
7272
program["color"] = color.normalized
7373
program["size"] = rect.width, rect.height
74-
program["angle"] = angle
75-
program["texture_id"] = float(texture_id)
74+
program["texture_id"] = texture_id
7675
program["spritelist_color"] = 1.0, 1.0, 1.0, alpha_normalized
7776

78-
geometry.render(program, mode=gl.POINTS, vertices=1)
77+
geometry.render(program, mode=gl.TRIANGLE_STRIP, vertices=4)
7978

8079
if blend:
8180
ctx.disable(ctx.BLEND)

arcade/resources/system/shaders/sprites/sprite_list_simple_vs.glsl

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,20 @@ void main() {
5252

5353
mat4 mvp = window.projection * window.view;
5454

55-
// TODO: Half pixel offset
55+
// Apply half pixel offset modified by bias.
56+
// What bias to set depends on the texture filtering mode.
57+
// Linear can have 1.0 bias while nearest should have 0.0 (unless scale is 1:1)
58+
// uvs (
59+
// 0.0, 0.0,
60+
// 1.0, 0.0,
61+
// 0.0, 1.0,
62+
// 1.0, 1.0
63+
// )
64+
vec2 hp = 0.5 / vec2(textureSize(sprite_texture, 0)) * uv_offset_bias;
65+
uv0 += hp;
66+
uv1 += vec2(-hp.x, hp.y);
67+
uv2 += vec2(hp.x, -hp.y);
68+
uv3 += -hp;
5669

5770
int vertex_id = gl_VertexID % 4;
5871
vec2 uvs[4] = vec2[4](uv0, uv2, uv1, uv3);
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#version 330
2+
// vert/frag only version of the sprite list shader
3+
4+
uniform WindowBlock {
5+
mat4 projection;
6+
mat4 view;
7+
} window;
8+
9+
// Texture atlas
10+
uniform sampler2D sprite_texture;
11+
// Texture containing UVs for the entire atlas
12+
uniform sampler2D uv_texture;
13+
14+
uniform vec4 pos_rot; // rect.x, rect.y, 0, angle
15+
uniform vec4 color; // color.normalized
16+
uniform vec2 size; // rect.width, rect.height
17+
uniform int texture_id;
18+
19+
// How much half-pixel offset to apply to the UVs.
20+
// 0.0 is no offset, 1.0 is half a pixel offset
21+
uniform float uv_offset_bias;
22+
23+
// Output to frag shader
24+
out vec2 v_uv;
25+
out vec4 v_color;
26+
27+
#include :system:shaders/lib/sprite.glsl
28+
29+
30+
const vec2 vertices[4] = vec2[4](
31+
vec2(-0.5, +0.5), // Upper left
32+
vec2(-0.5, -0.5), // lower left
33+
vec2(+0.5, +0.5), // upper right
34+
vec2(+0.5, -0.5) // lower right
35+
);
36+
37+
void main() {
38+
vec2 uv0, uv1, uv2, uv3;
39+
getSpriteUVs(uv_texture, texture_id, uv0, uv1, uv2, uv3);
40+
41+
vec3 center = pos_rot.xyz;
42+
float angle = radians(pos_rot.w);
43+
mat2 rot = mat2(
44+
cos(angle), -sin(angle),
45+
sin(angle), cos(angle)
46+
);
47+
48+
mat4 mvp = window.projection * window.view;
49+
50+
// Apply half pixel offset modified by bias.
51+
// What bias to set depends on the texture filtering mode.
52+
// Linear can have 1.0 bias while nearest should have 0.0 (unless scale is 1:1)
53+
// uvs (
54+
// 0.0, 0.0,
55+
// 1.0, 0.0,
56+
// 0.0, 1.0,
57+
// 1.0, 1.0
58+
// )
59+
vec2 hp = 0.5 / vec2(textureSize(sprite_texture, 0)) * uv_offset_bias;
60+
uv0 += hp;
61+
uv1 += vec2(-hp.x, hp.y);
62+
uv2 += vec2(hp.x, -hp.y);
63+
uv3 += -hp;
64+
65+
int vertex_id = gl_VertexID % 4;
66+
vec2 uvs[4] = vec2[4](uv0, uv2, uv1, uv3);
67+
v_color = color;
68+
gl_Position = mvp * vec4(rot * (vertices[vertex_id] * size) + center.xy, 0.0, 1.0);
69+
v_uv = uvs[vertex_id];
70+
}

arcade/sprite_list/sprite_list.py

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1721,21 +1721,7 @@ def __init__(self, ctx: ArcadeContext, capacity: int, atlas: TextureAtlasBase) -
17211721
# Program without geo shader
17221722
self.program = self.ctx.sprite_list_program_no_geo
17231723
self._atlas = atlas or self.ctx.default_atlas
1724-
1725-
# fmt: off
1726-
self._instance_buffer = self.ctx.buffer(
1727-
data=array("f", [
1728-
-0.5, +0.5, # Upper left
1729-
-0.5, -0.5, # lower left
1730-
+0.5, +0.5, # upper right
1731-
+0.5, -0.5, # lower right
1732-
]),
1733-
)
1734-
# fmt: on
1735-
self._geometry = self.ctx.geometry(
1736-
[BufferDescription(self._instance_buffer, "2f", ["in_pos"])],
1737-
mode=self.ctx.TRIANGLE_STRIP,
1738-
)
1724+
self._geometry = self.ctx.spritelist_geometry_simple
17391725

17401726
# Texture buffers for per-sprite data. These are looked up using gl_InstanceID
17411727
self._storage_pos_angle: Texture2D = self.ctx.texture(

0 commit comments

Comments
 (0)