Convert planets to use shaders.

This commit is contained in:
Quantum 2018-08-26 03:38:26 -04:00
parent 66c54a9dbb
commit fbcdb4d9b0
7 changed files with 205 additions and 20 deletions

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, SimpleSphere
from punyverse.glgeom import compile, glRestore, belt, Sphere, Disk, OrbitVBO, Matrix4f, SimpleSphere, TangentSphere
from punyverse.model import load_model, WavefrontVBO
from punyverse.orbit import KeplerOrbit
from punyverse.texture import get_best_texture, load_clouds
@ -24,11 +24,16 @@ class Entity(object):
self.rotation = rotation
self.direction = direction
@cached_property
def model_matrix(self):
return Matrix4f.from_angles(self.location, self.rotation)
@cached_property
def mv_matrix(self):
return self.world.view_matrix() * Matrix4f.from_angles(self.location, self.rotation)
return self.world.view_matrix() * self.model_matrix
def update(self):
self.model_matrix = None
self.mv_matrix = None
x, y, z = self.location
dx, dy, dz = self.direction
@ -266,15 +271,27 @@ class Body(Entity):
class SphericalBody(Body):
_sphere_cache = {}
@classmethod
def _get_sphere(cls, division):
if division in cls._sphere_cache:
return cls._sphere_cache[division]
cls._sphere_cache[division] = sphere = TangentSphere(division, division)
return sphere
def __init__(self, name, world, info, parent=None):
super(SphericalBody, self).__init__(name, world, info, parent)
self.radius = world.evaluate(info.get('radius', world.length)) / world.length
division = info.get('division', max(min(int(self.radius / 8), 60), 10))
self.light_source = info.get('light_source', False)
self.texture = get_best_texture(info['texture'])
self.sphere = Sphere(self.radius, division, division)
self.light_source = info.get('light_source', False)
self.shininess = info.get('shininess', 0)
self.type = info.get('type', 'planet')
self.diffuse_texture = get_best_texture(info['texture'])
self.sphere = self._get_sphere(division)
self.atmosphere = None
self.clouds = None
@ -306,24 +323,47 @@ class SphericalBody(Body):
self.ring_texture = get_best_texture(info['ring'].get('texture', None), clamp=True)
self.ring = Disk(distance, distance + size, 30)
def _draw_sphere(self, fv4=GLfloat * 4):
with glRestore(GL_LIGHTING_BIT | GL_ENABLE_BIT | GL_TEXTURE_BIT):
glLoadMatrixf(self.mv_matrix.as_gl())
glEnable(GL_CULL_FACE)
glCullFace(GL_BACK)
def _draw_planet(self):
shader = self.world.activate_shader('planet')
shader.uniform_float('u_radius', self.radius)
shader.uniform_mat4('u_modelMatrix', self.model_matrix)
shader.uniform_mat4('u_mvMatrix', self.mv_matrix)
shader.uniform_mat4('u_mvpMatrix', self.world.vp_matrix * self.model_matrix)
glEnable(GL_TEXTURE_2D)
glBindTexture(GL_TEXTURE_2D, self.texture)
glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D, self.diffuse_texture)
shader.uniform_bool('u_planet.hasDiffuse', True)
shader.uniform_texture('u_planet.diffuseMap', 0)
if self.light_source:
glDisable(GL_LIGHTING)
else:
glDisable(GL_BLEND)
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, fv4(1, 1, 1, 0))
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, fv4(1, 1, 1, 0))
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 125)
shader.uniform_bool('u_planet.hasNormal', False)
shader.uniform_bool('u_planet.hasSpecular', False)
shader.uniform_bool('u_planet.hasEmission', False)
self.sphere.draw()
shader.uniform_vec3('u_planet.ambient', 1, 1, 1)
shader.uniform_vec3('u_planet.diffuse', 1, 1, 1)
shader.uniform_vec3('u_planet.specular', 0, 0, 0)
shader.uniform_vec3('u_planet.emission', 0, 0, 0)
shader.uniform_float('u_planet.shininess', 0)
glBindBuffer(GL_ARRAY_BUFFER, self.sphere.vbo)
shader.vertex_attribute('a_normal', self.sphere.direction_size, self.sphere.type, GL_FALSE,
self.sphere.stride, self.sphere.direction_offset)
shader.vertex_attribute('a_tangent', self.sphere.tangent_size, self.sphere.type, GL_FALSE,
self.sphere.stride, self.sphere.tangent_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)
glActiveTexture(GL_TEXTURE0)
def _draw_sphere(self):
if self.type == 'planet':
self._draw_planet()
def _draw_atmosphere(self):
with glRestore(GL_ENABLE_BIT | GL_CURRENT_BIT | GL_TEXTURE_BIT):

View file

@ -236,6 +236,45 @@ class SimpleSphere(object):
self.vbo = array_to_gl_buffer(buffer)
class TangentSphere(object):
type = GL_FLOAT
stride = 7 * 4
direction_offset = 0
direction_size = 3
tangent_offset = direction_size * 4
tangent_size = 2
uv_offset = tangent_offset + tangent_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 * 8 * [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
sphi2, cphi2 = sin(phi2), cos(phi2)
sphi1, cphi1 = sin(phi1), cos(phi1)
buffer[index:index + 14] = [
sine * cphi2, sine * sphi2, dz, sine * -sphi2, sine * cphi2, phi2 / tau, t,
sine * cphi1, sine * sphi1, dz, sine * -sphi1, sine * cphi1, phi1 / tau, t,
]
index += 14
reverse ^= True
self.vbo = array_to_gl_buffer(buffer)
class Sphere(object):
def __init__(self, r, lats, longs):
tau = pi * 2

View file

@ -90,6 +90,15 @@ class Program(object):
def uniform_texture(self, name, index):
glUniform1i(self.uniforms[name], index)
def uniform_float(self, name, value):
glUniform1f(self.uniforms[name], value)
def uniform_bool(self, name, value):
glUniform1i(self.uniforms[name], bool(value))
def uniform_vec3(self, name, a, b, c):
glUniform3f(self.uniforms[name], a, b, c)
def _variable_locations(self, count_type, get_func, loc_func):
variables = {}
count = GLint()

View file

@ -0,0 +1,57 @@
#version 130
in vec2 v_uv;
in vec3 v_normal;
in vec3 v_position;
in vec3 v_camDirection;
in mat3 v_TBN;
struct Surface {
bool hasDiffuse;
sampler2D diffuseMap;
bool hasNormal;
sampler2D normalMap;
bool hasSpecular;
sampler2D specularMap;
bool hasEmission;
sampler2D emissionMap;
vec3 ambient;
vec3 diffuse;
vec3 specular;
vec3 emission;
float shininess;
};
struct Sun {
vec3 ambient;
vec3 diffuse;
vec3 specular;
vec3 position;
float intensity;
};
uniform Sun u_sun;
uniform Surface u_planet;
void main() {
vec3 normal = u_planet.hasNormal ? normalize(v_TBN * texture2D(u_planet.normalMap, v_uv).rgb * 2 - 1) : v_normal;
vec3 diffuse = u_planet.hasDiffuse ? texture2D(u_planet.diffuseMap, v_uv).rgb : vec3(1);
vec3 specular = u_planet.hasSpecular ? texture2D(u_planet.specularMap, v_uv).rgb : vec3(1);
vec3 emission = u_planet.hasEmission ? texture2D(u_planet.emissionMap, v_uv).rgb : vec3(1);
vec3 incident = normalize(u_sun.position - v_position);
vec3 reflected = normalize(-reflect(incident, normal));
float diffuseIntensity = max(dot(normal, incident), 0.0);
float shininess = pow(clamp(dot(normalize(v_camDirection), reflected), 0.0, 1.0), u_planet.shininess);
vec3 ambient = u_planet.ambient * u_sun.ambient * diffuse;
diffuse *= u_planet.diffuse * u_sun.diffuse * diffuseIntensity;
emission *= u_planet.emission * (1 - min(diffuseIntensity * 2, 1));
if (u_planet.shininess > 0)
specular *= u_planet.specular * u_sun.specular * clamp(shininess, 0, 1);
else
specular = vec3(0);
gl_FragColor = vec4((ambient + diffuse + emission + specular) * u_sun.intensity, 1);
}

View file

@ -0,0 +1,30 @@
#version 130
in vec3 a_normal;
in vec2 a_tangent;
in vec2 a_uv;
out vec2 v_uv;
out vec3 v_normal;
out vec3 v_position;
out vec3 v_camDirection;
out mat3 v_TBN;
uniform float u_radius;
uniform mat4 u_mvpMatrix;
uniform mat4 u_mvMatrix;
uniform mat4 u_modelMatrix;
void main() {
vec3 position = u_radius * a_normal;
gl_Position = u_mvpMatrix * vec4(position, 1);
v_normal = normalize(vec3(u_modelMatrix * vec4(a_normal, 0)));
v_uv = a_uv;
v_position = (u_modelMatrix * vec4(position, 1)).xyz;
v_camDirection = (u_mvMatrix * vec4(position, 1)).xyz;
vec3 tangent = normalize((u_modelMatrix * vec4(a_tangent, a_normal.z, 0)).xyz);
v_TBN = mat3(tangent, cross(tangent, v_normal), v_normal);
}

View file

@ -22,6 +22,7 @@
"mass": 1.9891e+30,
"rotation": 2164320,
"light_source": true,
"type": "star",
"atmosphere": {
"diffuse_texture": "sun_diffuse.png",
"diffuse_size": 300

View file

@ -19,6 +19,7 @@ def load_world(file, callback=lambda message, completion: None):
class World(object):
PROGRAMS = {
'sky': ('sky.vertex.glsl', 'sky.fragment.glsl'),
'planet': ('sphere.vertex.glsl', 'sphere.planet.fragment.glsl'),
}
def __init__(self, file, callback):
@ -44,6 +45,14 @@ class World(object):
for entity in self.tracker:
entity.update()
shader = self.activate_shader('planet')
shader.uniform_vec3('u_sun.ambient', 0.1, 0.1, 0.1)
shader.uniform_vec3('u_sun.diffuse', 1, 1, 1)
shader.uniform_vec3('u_sun.specular', 0.5, 0.5, 0.5)
shader.uniform_vec3('u_sun.position', 0, 0, 0)
shader.uniform_float('u_sun.intensity', 1)
self.activate_shader(None)
def _load_programs(self):
programs = {}
count = len(self.PROGRAMS)