Convert planets to use shaders.

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

View file

@ -5,7 +5,7 @@ from pyglet.gl import *
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences
from six.moves import range 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.model import load_model, WavefrontVBO
from punyverse.orbit import KeplerOrbit from punyverse.orbit import KeplerOrbit
from punyverse.texture import get_best_texture, load_clouds from punyverse.texture import get_best_texture, load_clouds
@ -24,11 +24,16 @@ class Entity(object):
self.rotation = rotation self.rotation = rotation
self.direction = direction self.direction = direction
@cached_property
def model_matrix(self):
return Matrix4f.from_angles(self.location, self.rotation)
@cached_property @cached_property
def mv_matrix(self): 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): def update(self):
self.model_matrix = None
self.mv_matrix = None self.mv_matrix = None
x, y, z = self.location x, y, z = self.location
dx, dy, dz = self.direction dx, dy, dz = self.direction
@ -266,15 +271,27 @@ class Body(Entity):
class SphericalBody(Body): 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): def __init__(self, name, world, info, parent=None):
super(SphericalBody, self).__init__(name, world, info, parent) super(SphericalBody, self).__init__(name, world, info, parent)
self.radius = world.evaluate(info.get('radius', world.length)) / world.length self.radius = world.evaluate(info.get('radius', world.length)) / world.length
division = info.get('division', max(min(int(self.radius / 8), 60), 10)) 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.light_source = info.get('light_source', False)
self.sphere = Sphere(self.radius, division, division) 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.atmosphere = None
self.clouds = 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_texture = get_best_texture(info['ring'].get('texture', None), clamp=True)
self.ring = Disk(distance, distance + size, 30) self.ring = Disk(distance, distance + size, 30)
def _draw_sphere(self, fv4=GLfloat * 4): def _draw_planet(self):
with glRestore(GL_LIGHTING_BIT | GL_ENABLE_BIT | GL_TEXTURE_BIT): shader = self.world.activate_shader('planet')
glLoadMatrixf(self.mv_matrix.as_gl()) shader.uniform_float('u_radius', self.radius)
glEnable(GL_CULL_FACE) shader.uniform_mat4('u_modelMatrix', self.model_matrix)
glCullFace(GL_BACK) shader.uniform_mat4('u_mvMatrix', self.mv_matrix)
shader.uniform_mat4('u_mvpMatrix', self.world.vp_matrix * self.model_matrix)
glEnable(GL_TEXTURE_2D) glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D, self.texture) 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: shader.uniform_bool('u_planet.hasNormal', False)
glDisable(GL_LIGHTING) shader.uniform_bool('u_planet.hasSpecular', False)
else: shader.uniform_bool('u_planet.hasEmission', False)
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)
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): def _draw_atmosphere(self):
with glRestore(GL_ENABLE_BIT | GL_CURRENT_BIT | GL_TEXTURE_BIT): 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) 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): class Sphere(object):
def __init__(self, r, lats, longs): def __init__(self, r, lats, longs):
tau = pi * 2 tau = pi * 2

View file

@ -90,6 +90,15 @@ class Program(object):
def uniform_texture(self, name, index): def uniform_texture(self, name, index):
glUniform1i(self.uniforms[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): def _variable_locations(self, count_type, get_func, loc_func):
variables = {} variables = {}
count = GLint() 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, "mass": 1.9891e+30,
"rotation": 2164320, "rotation": 2164320,
"light_source": true, "light_source": true,
"type": "star",
"atmosphere": { "atmosphere": {
"diffuse_texture": "sun_diffuse.png", "diffuse_texture": "sun_diffuse.png",
"diffuse_size": 300 "diffuse_size": 300

View file

@ -19,6 +19,7 @@ def load_world(file, callback=lambda message, completion: None):
class World(object): class World(object):
PROGRAMS = { PROGRAMS = {
'sky': ('sky.vertex.glsl', 'sky.fragment.glsl'), 'sky': ('sky.vertex.glsl', 'sky.fragment.glsl'),
'planet': ('sphere.vertex.glsl', 'sphere.planet.fragment.glsl'),
} }
def __init__(self, file, callback): def __init__(self, file, callback):
@ -44,6 +45,14 @@ class World(object):
for entity in self.tracker: for entity in self.tracker:
entity.update() 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): def _load_programs(self):
programs = {} programs = {}
count = len(self.PROGRAMS) count = len(self.PROGRAMS)