Skip to content

Instantly share code, notes, and snippets.

@Aeva
Created October 14, 2017 23:52
Show Gist options
  • Save Aeva/8927ceffe0af865cdda976976a356d93 to your computer and use it in GitHub Desktop.
Save Aeva/8927ceffe0af865cdda976976a356d93 to your computer and use it in GitHub Desktop.
python rendering API mockup
# "DataSource" classes are used to define assorted properties and
# functions that might be used by a shader. The properties etc are
# not specific to any particular shader stage. So, "vertex"
# properties will have an implied varrying variable if they're
# accessed from the fragment shader.
class BasicModel(DataSource):
position = vertex.float4(index=0)
normal = vertex.float3()
uv = vertex.float2()
world_matrix = uniform.matrix4()
@property
def world_space(self) -> float4:
return self.world_matrix @ position
# "DataSources" can be subclassed for polymorphic behavior. This is
# useful for things like vertex animation.
# When a DataSource references an outside variable or function (such
# as the call to "time.time()" below), the compiler will try to either
# inline the function into the shader if it is possible and reasonable
# to do, otherwise it will note what the shader wanted to access so
# that the result can be automatically produced cpu-side before
# starting the shader, and then the result would be passed in as an
# implicit uniform variable. Or it'll throw an error.
# The python inside of a DataSource class is not really python, but
# rather a subset of it. So, attempting to do something the compiler
# can't happen will throw an error of some kind.
class Rotatoe(BasicModel):
@property
def world_space(self) -> float4:
return matrix4.rotate_z(time.time()) @ self.world_matrix @ position
# DataSource classes don't need to contain any per-vertex data. You
# can use them to encapsulate things that might be specific to one
# shader stage or another. In the end, the shader programs are glue
# to mix the DataSources together to produce some useful outcome.
class Camera(DataSource):
perspective_matrix = uniform.matrix4()
view_matrix = uniform.matrix4()
@property
def camera_matrix(self) -> matrix4:
return self.perspective_matrix @ self.view_matrix
def project(self, point: float4) -> float4:
return self.camera_matrix @ point
class Material(DataSource):
texture = uniform.pixmap()
fnord = uniform.float()
def sample(self, uv: float2) -> float4:
return self.texture.sample(uv) * self.fnord
# RenderPasses objects define render targets and shader stages used by
# a render pass. Compute shaders might be made available as a special
# decorator.
class GBuffers(RenderPass):
color = buffer.RGBA(primary=1)
depth = buffer.FLOAT()
def vertex_shader(camera: Camera, model: BasicModel):
projected = camera.project(model.world_space)
self.material_uv = model.uv
self.depth = projected.z
return projected
def fragment_shader(material: Material):
self.color = material.sample(self.material_uv)
# Splat passes are like RenderPasses, but the vertex_shader may be
# omitted. They draw a full screen quad by default, and the implicit
# vertex shader provides the screen space uv. These are good for post
# processing effects.
class SomeEffect(SplatPass):
color = buffer.RGBA()
def fragment_shader(gbuffers: GBuffers):
some_color = gbuffers.color.sample(self.uv)
some_depth = gbuffers.depth.sample(self.uv)
a, b, c, d = some_color
inverse = float4(float3(1.0) - float3(a, b, c), 1.0)
self.color = lerp(some_color, inverse, some_depth)
# There are a few things missing still:
#
# - There is currently no mechanism for establishing what the legal
# shader permutations are, or running the compiler.
#
# - I'd like some notion of DataSource "collections". Eg, a model
# and a material would be used together in a collection, and the
# render passes could be written to consume those instead.
#
# - Some means of quering collections for objects that meet a
# specific criteria. These queries would basically reduce to
# commands to be executed by the backend, outside of python.
#
# - Some way of connecting this all together. Eg, funcitons who
# define collection queries and the flow of data between that and
# render passes. These also generate commands to be executed by
# the backend, outside of python.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment