Move sky to shaders.

This commit is contained in:
Quantum 2018-08-25 20:53:21 -04:00
parent 999dc78410
commit 66c54a9dbb
8 changed files with 215 additions and 13 deletions

View file

@ -16,7 +16,7 @@ class Camera(object):
self.fov = radians(45)
self.aspect = 1
self.znear = 1
self.zfar = 50000000
self.zfar = 3000000
self.speed = 0
self.roll_left = False

View file

@ -5,7 +5,7 @@ from pyglet.gl import *
# noinspection PyUnresolvedReferences
from six.moves import range
from punyverse.glgeom import compile, glRestore, belt, Sphere, Disk, OrbitVBO, Matrix4f
from punyverse.glgeom import compile, glRestore, belt, Sphere, Disk, OrbitVBO, Matrix4f, SimpleSphere
from punyverse.model import load_model, WavefrontVBO
from punyverse.orbit import KeplerOrbit
from punyverse.texture import get_best_texture, load_clouds
@ -120,20 +120,30 @@ class Sky(Entity):
self.texture = get_best_texture(info['texture'])
division = info.get('division', 30)
self.sphere = Sphere(info.get('radius', 1000000), division, division)
self.sphere = SimpleSphere(division, division)
def draw(self, options):
cam = self.world.cam
with glRestore(GL_TEXTURE_BIT | GL_ENABLE_BIT):
matrix = self.world.view_matrix() * Matrix4f.from_angles((-cam.x, -cam.y, -cam.z), self.rotation)
glLoadMatrixf(matrix.as_gl())
glEnable(GL_CULL_FACE)
glEnable(GL_TEXTURE_2D)
glDisable(GL_LIGHTING)
shader = self.world.activate_shader('sky')
shader.uniform_mat4('u_mvpMatrix', self.world.projection_matrix() *
Matrix4f.from_angles(rotation=(cam.pitch, cam.yaw, cam.roll)) *
Matrix4f.from_angles(rotation=self.rotation))
glCullFace(GL_FRONT)
glBindTexture(GL_TEXTURE_2D, self.texture)
self.sphere.draw()
glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D, self.texture)
shader.uniform_texture('u_skysphere', 0)
glBindBuffer(GL_ARRAY_BUFFER, self.sphere.vbo)
shader.vertex_attribute('a_direction', self.sphere.direction_size, self.sphere.type, GL_FALSE,
self.sphere.stride, self.sphere.direction_offset)
shader.vertex_attribute('a_uv', self.sphere.uv_size, self.sphere.type, GL_FALSE,
self.sphere.stride, self.sphere.uv_offset)
glDrawArrays(GL_TRIANGLE_STRIP, 0, self.sphere.vertex_count)
shader.deactivate_attributes()
glBindBuffer(GL_ARRAY_BUFFER, 0)
self.world.activate_shader(None)
class Body(Entity):

View file

@ -202,6 +202,40 @@ class Disk(object):
glBindBuffer(GL_ARRAY_BUFFER, 0)
class SimpleSphere(object):
type = GL_FLOAT
stride = 5 * 4
direction_offset = 0
direction_size = 3
uv_offset = direction_size * 4
uv_size = 2
def __init__(self, lats, longs):
tau = pi * 2
phi_div = tau / longs
theta_div = pi / lats
self.vertex_count = (lats + 1) * (longs + 1) * 2
buffer = self.vertex_count * 5 * [0]
index = 0
reverse = False
for i in range(longs + 1):
phi1, phi2 = i * phi_div, (i + 1) * phi_div
for j in range(lats + 1):
theta = j * theta_div
if reverse:
theta = pi - theta
sine = sin(theta)
dz = cos(theta)
t = 1 - theta / pi
buffer[index:index + 10] = [sine * cos(phi2), sine * sin(phi2), dz, phi2 / tau, t,
sine * cos(phi1), sine * sin(phi1), dz, phi1 / tau, t]
index += 10
reverse ^= True
self.vbo = array_to_gl_buffer(buffer)
class Sphere(object):
def __init__(self, r, lats, longs):
tau = pi * 2

104
punyverse/shader.py Normal file
View file

@ -0,0 +1,104 @@
import os
from ctypes import pointer, byref, create_string_buffer, POINTER, cast
from pyglet.gl import *
# noinspection PyUnresolvedReferences
from six.moves import range
SHADERS_DIR = os.path.join(os.path.dirname(__file__), 'shaders')
class CompileError(ValueError):
pass
class glShader(object):
def __init__(self, type):
self.type = type
def __enter__(self):
self.shader = glCreateShader(self.type)
return self.shader
def __exit__(self, exc_type, exc_val, exc_tb):
glDeleteShader(self.shader)
class Program(object):
@classmethod
def load_file(cls, file):
with open(os.path.join(SHADERS_DIR, file), 'rb') as f:
return f.read()
@classmethod
def compile_shader(cls, shader, source):
buffer = create_string_buffer(source)
glShaderSource(shader, 1, cast(pointer(pointer(buffer)), POINTER(POINTER(GLchar))), None)
glCompileShader(shader)
succeeded = GLint()
log_length = GLint()
glGetShaderiv(shader, GL_COMPILE_STATUS, byref(succeeded))
if not succeeded:
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, byref(log_length))
buffer = create_string_buffer(log_length.value + 1)
glGetShaderInfoLog(shader, log_length.value, None, buffer)
raise CompileError(buffer.value)
def __init__(self, vertex_file, fragment_file):
with glShader(GL_VERTEX_SHADER) as vertex_shader, glShader(GL_FRAGMENT_SHADER) as fragment_shader:
self.compile_shader(vertex_shader, self.load_file(vertex_file))
self.compile_shader(fragment_shader, self.load_file(fragment_file))
program = glCreateProgram()
glAttachShader(program, vertex_shader)
glAttachShader(program, fragment_shader)
glLinkProgram(program)
succeeded = GLint()
log_length = GLint()
glGetProgramiv(program, GL_LINK_STATUS, byref(succeeded))
if not succeeded:
glGetProgramiv(program, GL_INFO_LOG_LENGTH, byref(log_length))
buffer = create_string_buffer(log_length.value + 1)
glGetProgramInfoLog(program, log_length.value, None, buffer)
glDeleteProgram(program)
raise CompileError(buffer.value)
glDetachShader(program, vertex_shader)
glDetachShader(program, fragment_shader)
self.program = program
self.attributes = self._variable_locations(GL_ACTIVE_ATTRIBUTES, glGetActiveAttrib, glGetAttribLocation)
self.uniforms = self._variable_locations(GL_ACTIVE_UNIFORMS, glGetActiveUniform, glGetUniformLocation)
self.active_attributes = set()
def vertex_attribute(self, name, size, type, normalized, stride, offset):
location = self.attributes[name]
glVertexAttribPointer(location, size, type, normalized, stride, offset)
glEnableVertexAttribArray(location)
self.active_attributes.add(location)
def deactivate_attributes(self):
for location in self.active_attributes:
glDisableVertexAttribArray(location)
self.active_attributes.clear()
def uniform_mat4(self, name, matrix):
glUniformMatrix4fv(self.uniforms[name], 1, GL_FALSE, matrix.as_gl())
def uniform_texture(self, name, index):
glUniform1i(self.uniforms[name], index)
def _variable_locations(self, count_type, get_func, loc_func):
variables = {}
count = GLint()
glGetProgramiv(self.program, count_type, byref(count))
buffer = create_string_buffer(256)
size = GLint()
type = GLenum()
for index in range(count.value):
get_func(self.program, index, 256, None, byref(size), byref(type), buffer)
variables[buffer.value.decode('ascii')] = loc_func(self.program, buffer)
return variables

View file

@ -0,0 +1,8 @@
#version 130
in vec2 v_uv;
uniform sampler2D u_skysphere;
void main() {
gl_FragColor = vec4(texture(u_skysphere, v_uv).rgb, 1);
}

View file

@ -0,0 +1,11 @@
#version 130
in vec3 a_direction;
in vec2 a_uv;
out vec2 v_uv;
uniform mat4 u_mvpMatrix;
void main() {
gl_Position = (u_mvpMatrix * vec4(a_direction, 1)).xyww;
v_uv = a_uv;
}

View file

@ -298,7 +298,6 @@
"sky": {
"texture": "sky.jpg",
"rotation": 0,
"radius": 305000000,
"division": 30,
"pitch": 90,
"yaw": 30,

View file

@ -9,6 +9,7 @@ import six
from punyverse import texture
from punyverse.camera import Camera
from punyverse.entity import *
from punyverse.shader import Program
def load_world(file, callback=lambda message, completion: None):
@ -16,6 +17,10 @@ def load_world(file, callback=lambda message, completion: None):
class World(object):
PROGRAMS = {
'sky': ('sky.vertex.glsl', 'sky.fragment.glsl'),
}
def __init__(self, file, callback):
self.tracker = []
self.x = None
@ -28,6 +33,9 @@ class World(object):
self.callback = callback
self._parse(file)
self._program = None
self.programs = self._load_programs()
del self.callback # So it can't be used after loading finishes
self._time_accumulate = 0
@ -36,6 +44,15 @@ class World(object):
for entity in self.tracker:
entity.update()
def _load_programs(self):
programs = {}
count = len(self.PROGRAMS)
for i, (name, (vertex, fragment)) in enumerate(six.iteritems(self.PROGRAMS)):
self.callback('Loading shaders (%d of %d)...' % (i, count),
'Loading shader "%s" (%s, %s).' % (name, vertex, fragment), i / count)
programs[name] = Program(vertex, fragment)
return programs
def evaluate(self, value):
return eval(str(value), {'__builtins__': None}, self._context)
@ -128,6 +145,7 @@ class World(object):
def update(self, dt, move, tick):
c = self.cam
c.update(dt, move)
self.vp_matrix = None
if tick:
delta = self.tick_length * dt
@ -151,6 +169,24 @@ class World(object):
def projection_matrix(self):
return self._projection_matrix
@cached_property
def vp_matrix(self):
return self._projection_matrix * self.cam.view_matrix
def resize(self, width, height):
self.cam.aspect = width / max(height, 1)
self._projection_matrix = self.cam.projection_matrix()
self.vp_matrix = None
def activate_shader(self, name):
program = None
if self._program != name:
if name is None:
glUseProgram(0)
else:
program = self.programs[name]
glUseProgram(program.program)
self._program = name
elif self._program is not None:
program = self.programs[self._program]
return program