Use geometry shaders to (hopefully) lighten CPU workload
Also tweak color palette for better contrast
This commit is contained in:
parent
c7a280191d
commit
94ecb1b119
16
axuy/misc.py
16
axuy/misc.py
|
@ -23,21 +23,11 @@ from random import choices, shuffle
|
|||
import numpy
|
||||
from pkg_resources import resource_filename
|
||||
|
||||
TANGO = {'Butter': ('fce94f', 'edd400', 'c4a000'),
|
||||
'Orange': ('fcaf3e', 'f57900', 'ce5c00'),
|
||||
'Chocolate': ('e9b96e', 'c17d11', '8f5902'),
|
||||
'Chameleon': ('8ae234', '73d216', '4e9a06'),
|
||||
'Sky Blue': ('729fcf', '3465a4', '204a87'),
|
||||
'Plum': ('ad7fa8', '75507b', '5c3566'),
|
||||
'Scarlet Red': ('ef2929', 'cc0000', 'a40000'),
|
||||
'Aluminium': ('eeeeec', 'd3d7cf', 'babdb6',
|
||||
'888a85', '555753', '2e3436')}
|
||||
COLOR_NAMES = ['Butter', 'Orange', 'Chocolate', 'Chameleon',
|
||||
'Sky Blue', 'Plum', 'Scarlet Red']
|
||||
NEIGHBORS = set(chain.from_iterable(
|
||||
map(permutations, combinations_with_replacement((-1, 0, 1), 3))))
|
||||
# map.npy is generated by ../tools/mapgen
|
||||
SPACE = numpy.load(resource_filename('axuy', 'map.npy'))
|
||||
COLORS = tuple(numpy.float32(color) for color in permutations((1.0, 0.5, 0.0)))
|
||||
|
||||
|
||||
def abspath(resource_name):
|
||||
|
@ -45,9 +35,9 @@ def abspath(resource_name):
|
|||
return resource_filename('axuy', resource_name)
|
||||
|
||||
|
||||
def color(name, idx=0):
|
||||
def color(code, value):
|
||||
"""Return NumPy float32 array of RGB colors from color name."""
|
||||
return numpy.float32([i / 255 for i in bytes.fromhex(TANGO[name][idx])])
|
||||
return COLORS[code] * (value + 1) * 0.5
|
||||
|
||||
|
||||
def mapidgen(replacement=False):
|
||||
|
|
|
@ -16,7 +16,7 @@ vec2 fringe(vec2 vert, float delta)
|
|||
void main(void)
|
||||
{
|
||||
vec2 vert = in_text * 2.0 - 1.0;
|
||||
f_color = texture(la, in_text) * 0.42 + vec4(
|
||||
f_color = texture(la, in_text) + vec4(
|
||||
texture(tex, fringe(vert, -invfov)).r,
|
||||
texture(tex, fringe(vert, invfov)).g,
|
||||
texture(tex, in_text).b, 1.0);
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
#version 330 core
|
||||
layout (lines) in;
|
||||
layout (line_strip, max_vertices = 54) out;
|
||||
|
||||
uniform float visibility;
|
||||
uniform vec3 camera;
|
||||
uniform mat4 vp;
|
||||
|
||||
out float intensity;
|
||||
|
||||
void translate(inout vec4 delta)
|
||||
{
|
||||
vec4 vert;
|
||||
float dist;
|
||||
for (int n = 0; n < 2; ++n) {
|
||||
vert = gl_in[n].gl_Position + delta;
|
||||
dist = distance(camera, vec3(vert));
|
||||
intensity = 1 / (1 + dist * dist / visibility);
|
||||
gl_Position = vp * vert;
|
||||
EmitVertex();
|
||||
}
|
||||
EndPrimitive();
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
float i, j, k;
|
||||
for (i = -12.0; i < 12.3; i += 12.0)
|
||||
for (j = -12.0; j < 12.3; j += 12.0)
|
||||
for (k = -9.0; k < 12.3; k += 9.0)
|
||||
translate(vec4(i, j, k, 0.0));
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
#version 330
|
||||
|
||||
uniform vec3 color;
|
||||
|
||||
in float intensity;
|
||||
out vec4 f_color;
|
||||
|
||||
void main()
|
||||
{
|
||||
f_color = vec4(color, intensity);
|
||||
}
|
|
@ -1,16 +1,11 @@
|
|||
#version 330
|
||||
|
||||
uniform mat4 vp;
|
||||
uniform mat4 model;
|
||||
uniform vec3 camera;
|
||||
uniform float visibility;
|
||||
uniform vec3 pos;
|
||||
uniform mat4 rot;
|
||||
|
||||
in vec3 in_vert;
|
||||
out float intensity;
|
||||
in vec4 in_vert;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 vert = model * vec4(in_vert, 1.0);
|
||||
gl_Position = vp * vert;
|
||||
intensity = 1 / (1 + pow(distance(camera, vec3(vert)), 2) / visibility);
|
||||
gl_Position = rot * in_vert + vec4(pos, 0.0);
|
||||
}
|
||||
|
|
|
@ -7,9 +7,7 @@ out vec4 f_color;
|
|||
|
||||
void main(void)
|
||||
{
|
||||
f_color = texture(tex, in_text);
|
||||
f_color = texture(tex, in_text) * 0.5;
|
||||
float r = f_color.r, g = f_color.g, b = f_color.b;
|
||||
float c = r + g + b;
|
||||
float p = sqrt(r * (r - g) + g * (g - b) + b * (b - r)) * 2.0;
|
||||
f_color *= sign(floor(p / (c + p) * 6.9));
|
||||
f_color *= abs(r - g) + abs(g - b) + abs(b - r);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
#version 330 core
|
||||
layout (triangles) in;
|
||||
layout (triangle_strip, max_vertices = 81) out;
|
||||
|
||||
uniform float visibility;
|
||||
uniform vec3 camera;
|
||||
uniform mat4 vp;
|
||||
|
||||
out float intensity;
|
||||
|
||||
void translate(inout vec4 delta)
|
||||
{
|
||||
vec4 vert;
|
||||
float dist;
|
||||
for (int n = 0; n < 3; ++n) {
|
||||
vert = gl_in[n].gl_Position + delta;
|
||||
dist = distance(camera, vec3(vert));
|
||||
intensity = 1 / (1 + dist * dist / visibility);
|
||||
gl_Position = vp * vert;
|
||||
EmitVertex();
|
||||
}
|
||||
EndPrimitive();
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
float i, j, k;
|
||||
for (i = -12.0; i < 12.3; i += 12.0)
|
||||
for (j = -12.0; j < 12.3; j += 12.0)
|
||||
for (k = -9.0; k < 12.3; k += 9.0)
|
||||
translate(vec4(i, j, k, 0.0));
|
||||
}
|
84
axuy/view.py
84
axuy/view.py
|
@ -20,7 +20,7 @@ __doc__ = 'Axuy module for map class'
|
|||
|
||||
from itertools import product
|
||||
from math import sqrt
|
||||
from random import choice
|
||||
from random import randint
|
||||
|
||||
import glfw
|
||||
import moderngl
|
||||
|
@ -28,14 +28,15 @@ import numpy as np
|
|||
from PIL import Image
|
||||
from pyrr import Matrix44
|
||||
|
||||
from .pico import Picobot
|
||||
from .misc import COLOR_NAMES, abspath, color, neighbors, sign
|
||||
from .pico import SHARD_LIFE, Picobot
|
||||
from .misc import abspath, color, neighbors, sign
|
||||
|
||||
FOV_MIN = 30
|
||||
FOV_MAX = 120
|
||||
FOV_INIT = (FOV_MIN+FOV_MAX) / 2
|
||||
FOV_INIT = (FOV_MIN+FOV_MAX) // 2
|
||||
CONWAY = 1.303577269034
|
||||
MOUSE_SPEED = 1/8
|
||||
EDGE_BRIGHTNESS = 0.5
|
||||
|
||||
QUAD = np.float32([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]).tobytes()
|
||||
OXY = np.float32([[0, 0, 0], [1, 0, 0], [1, 1, 0],
|
||||
|
@ -55,9 +56,10 @@ OCTOINDECIES = np.int32([0, 1, 2, 0, 1, 4, 3, 0, 2, 3, 0, 4,
|
|||
2, 1, 5, 4, 1, 5, 2, 5, 3, 4, 5, 3])
|
||||
|
||||
with open(abspath('shaders/map.vert')) as f: MAP_VERTEX = f.read()
|
||||
with open(abspath('shaders/map.frag')) as f: MAP_FRAGMENT = f.read()
|
||||
with open(abspath('shaders/world.frag')) as f: WORLD_FRAGMENT = f.read()
|
||||
with open(abspath('shaders/pico.vert')) as f: PICO_VERTEX = f.read()
|
||||
with open(abspath('shaders/pico.frag')) as f: PICO_FRAGMENT = f.read()
|
||||
with open(abspath('shaders/line.geom')) as f: LINE_GEOMETRY = f.read()
|
||||
with open(abspath('shaders/triangle.geom')) as f: TRIANGLE_GEOMETRY = f.read()
|
||||
|
||||
with open(abspath('shaders/tex.vert')) as f: TEX_VERTEX = f.read()
|
||||
with open(abspath('shaders/sat.frag')) as f: SAT_FRAGMENT = f.read()
|
||||
|
@ -114,12 +116,12 @@ class View:
|
|||
Processed executable code in GLSL for map rendering.
|
||||
mapva : moderngl.VertexArray
|
||||
Vertex data of the map.
|
||||
prog : moderngl.Program
|
||||
prog2, prog3 : moderngl.Program
|
||||
Processed executable code in GLSL
|
||||
for rendering picobots and their shards.
|
||||
pva : moderngl.VertexArray
|
||||
pva2, pva3 : moderngl.VertexArray
|
||||
Vertex data of picobots.
|
||||
sva : moderngl.VertexArray
|
||||
sva2, sva3 : moderngl.VertexArray
|
||||
Vertex data of shards.
|
||||
pfilter : moderngl.VertexArray
|
||||
Vertex data for filtering highly saturated colors.
|
||||
|
@ -156,7 +158,7 @@ class View:
|
|||
# Attributes for event-handling
|
||||
self.camera = camera
|
||||
self.picos = {address: camera}
|
||||
self.colors = {address: choice(COLOR_NAMES)}
|
||||
self.colors = {address: randint(0, 5)}
|
||||
self.last_time = glfw.get_time()
|
||||
self.lock = lock
|
||||
|
||||
|
@ -174,7 +176,7 @@ class View:
|
|||
|
||||
# Create OpenGL context
|
||||
self.context = context = moderngl.create_context()
|
||||
context.enable_only(moderngl.BLEND | moderngl.DEPTH_TEST)
|
||||
context.enable_only(moderngl.DEPTH_TEST)
|
||||
|
||||
# Map creation
|
||||
self.space, vertices = space, []
|
||||
|
@ -188,20 +190,27 @@ class View:
|
|||
|
||||
# GLSL program and vertex array for map rendering
|
||||
self.maprog = context.program(vertex_shader=MAP_VERTEX,
|
||||
fragment_shader=MAP_FRAGMENT)
|
||||
self.maprog['color'].write(color('Aluminium').tobytes())
|
||||
fragment_shader=WORLD_FRAGMENT)
|
||||
self.maprog['color'].write(bytes((0, 0, 128, 63) * 3))
|
||||
mapvb = context.buffer(np.stack(vertices).astype(np.float32).tobytes())
|
||||
self.mapva = context.simple_vertex_array(self.maprog, mapvb, 'in_vert')
|
||||
|
||||
# GLSL programs and vertex arrays for picos and shards rendering
|
||||
self.prog = context.program(vertex_shader=PICO_VERTEX,
|
||||
fragment_shader=PICO_FRAGMENT)
|
||||
pvb = [(context.buffer(TETRAVERTICES.tobytes()), '3f', 'in_vert')]
|
||||
pib = context.buffer(TETRAINDECIES.tobytes())
|
||||
self.pva = context.vertex_array(self.prog, pvb, pib)
|
||||
svb = [(context.buffer(OCTOVERTICES.tobytes()), '3f', 'in_vert')]
|
||||
sib = context.buffer(OCTOINDECIES.tobytes())
|
||||
self.sva = context.vertex_array(self.prog, svb, sib)
|
||||
|
||||
self.prog2 = context.program(vertex_shader=PICO_VERTEX,
|
||||
geometry_shader=LINE_GEOMETRY,
|
||||
fragment_shader=WORLD_FRAGMENT)
|
||||
self.pva2 = context.vertex_array(self.prog2, pvb, pib)
|
||||
self.sva2 = context.vertex_array(self.prog2, svb, sib)
|
||||
self.prog3 = context.program(vertex_shader=PICO_VERTEX,
|
||||
geometry_shader=TRIANGLE_GEOMETRY,
|
||||
fragment_shader=WORLD_FRAGMENT)
|
||||
self.pva3 = context.vertex_array(self.prog3, pvb, pib)
|
||||
self.sva3 = context.vertex_array(self.prog3, svb, sib)
|
||||
|
||||
self.pfilter = context.simple_vertex_array(
|
||||
context.program(vertex_shader=TEX_VERTEX,
|
||||
|
@ -311,38 +320,38 @@ class View:
|
|||
@fps.setter
|
||||
def fps(self, fps):
|
||||
self.camera.fps = fps
|
||||
#print(fps)
|
||||
|
||||
def is_pressed(self, *keys) -> bool:
|
||||
"""Return whether given keys are pressed."""
|
||||
return any(glfw.get_key(self.window, k) == glfw.PRESS for k in keys)
|
||||
|
||||
def prender(self, obj, va, col, bright=0):
|
||||
def prender(self, obj, va2, va3, col, bright=1.0):
|
||||
"""Render the obj and its images in bounded 3D space."""
|
||||
vsqr = self.visibility ** 2
|
||||
rotation = Matrix44.from_matrix33(obj.rot)
|
||||
i, j, k = map(sign, self.pos - obj.pos)
|
||||
for position in product(*zip(obj.pos, obj.pos + [i*12, j*12, k*9])):
|
||||
if sum((self.pos-position) ** 2) > vsqr: continue
|
||||
model = rotation @ Matrix44.from_translation(position)
|
||||
self.prog['model'].write(model.astype(np.float32).tobytes())
|
||||
self.prog['color'].write(color('Aluminium', -1).tobytes())
|
||||
self.prog['color'].write(color(col, -1).tobytes())
|
||||
va.render(moderngl.LINES)
|
||||
self.prog['color'].write(color(col, bright).tobytes())
|
||||
va.render(moderngl.TRIANGLES)
|
||||
rotation = Matrix44.from_matrix33(obj.rot).astype(np.float32).tobytes()
|
||||
position = obj.pos.astype(np.float32).tobytes()
|
||||
self.prog2['rot'].write(rotation)
|
||||
self.prog3['rot'].write(rotation)
|
||||
self.prog2['pos'].write(position)
|
||||
self.prog3['pos'].write(position)
|
||||
self.prog2['color'].write(color(col, bright*EDGE_BRIGHTNESS).tobytes())
|
||||
self.prog3['color'].write(color(col, bright).tobytes())
|
||||
va2.render(moderngl.LINES)
|
||||
va3.render(moderngl.TRIANGLES)
|
||||
|
||||
def render_pico(self, pico):
|
||||
"""Render pico and its images in bounded 3D space."""
|
||||
self.prender(pico, self.pva, self.colors[pico.addr])
|
||||
self.prender(pico, self.pva2, self.pva3, self.colors[pico.addr])
|
||||
|
||||
def render_shard(self, shard):
|
||||
"""Render shard and its images in bounded 3D space."""
|
||||
self.prender(shard, self.sva, self.colors[shard.addr], -shard.power)
|
||||
self.prender(shard, self.sva2, self.sva3,
|
||||
self.colors[shard.addr], shard.power/SHARD_LIFE)
|
||||
|
||||
def add_pico(self, address, position, rotation):
|
||||
"""Add picobot from addr at pos with rot."""
|
||||
self.picos[address] = Pico(address, self.space, position, rotation)
|
||||
self.colors[address] = choice(COLOR_NAMES)
|
||||
self.colors[address] = randint(0, 5)
|
||||
|
||||
def render(self):
|
||||
"""Render the scene before post-processing."""
|
||||
|
@ -359,9 +368,12 @@ class View:
|
|||
self.mapva.render(moderngl.TRIANGLES)
|
||||
|
||||
# Render picos and shards
|
||||
self.prog['visibility'].value = visibility
|
||||
self.prog['camera'].write(self.pos.tobytes())
|
||||
self.prog['vp'].write(vp)
|
||||
self.prog2['visibility'].value = visibility
|
||||
self.prog3['visibility'].value = visibility
|
||||
self.prog2['camera'].write(self.pos.tobytes())
|
||||
self.prog3['camera'].write(self.pos.tobytes())
|
||||
self.prog2['vp'].write(vp)
|
||||
self.prog3['vp'].write(vp)
|
||||
|
||||
self.lock.acquire(blocking=False)
|
||||
for pico in self.picos.values():
|
||||
|
|
Loading…
Reference in New Issue