Render Pass
You can define your own render passes that extends the renderer, currently there are render passes only for scenes, so no custom post-effects (this is planned to be improved in Fyrox 0.28). Render pass has full access to graphics framework (which is a thin wrapper around OpenGL) so it can utilize full power of it to implement various graphical effects.
Creating a render pass
Render pass is a complex thing, that requires relatively deep knowledge in computer graphics. It is intended to be used by experienced graphics programmers. Here’s the simplest render pass that renders unit quad without any textures.
#![allow(unused)]
fn main() {
const SHADER: &'static str = r##"
(
name: "Overlay",
resources: [
(
name: "properties",
kind: PropertyGroup([
(name: "worldViewProjectionMatrix", kind: Matrix4()),
]),
binding: 0
),
],
passes: [
(
name: "Primary",
draw_parameters: DrawParameters(
cull_face: None,
color_write: ColorMask(
red: true,
green: true,
blue: true,
alpha: true,
),
depth_write: false,
stencil_test: None,
depth_test: None,
blend: Some(BlendParameters(
func: BlendFunc(
sfactor: SrcAlpha,
dfactor: OneMinusSrcAlpha,
alpha_sfactor: SrcAlpha,
alpha_dfactor: OneMinusSrcAlpha,
),
equation: BlendEquation(
rgb: Add,
alpha: Add
)
)),
stencil_op: StencilOp(
fail: Keep,
zfail: Keep,
zpass: Keep,
write_mask: 0xFFFF_FFFF,
),
scissor_box: None
),
vertex_shader:
r#"
layout(location = 0) in vec3 vertexPosition;
void main()
{
gl_Position = properties.worldViewProjectionMatrix * vec4(vertexPosition, 1.0);
}
"#,
fragment_shader:
r#"
out vec4 FragColor;
void main()
{
FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
"#,
)
]
)
"##;
pub struct MyRenderPass {
quad: GpuGeometryBuffer,
shader: RenderPassContainer,
pub scene_handle: Handle<Scene>,
}
impl MyRenderPass {
pub fn new(server: &dyn GraphicsServer) -> Rc<RefCell<Self>> {
Rc::new(RefCell::new(Self {
quad: GpuGeometryBuffer::from_surface_data(
"Quad",
&SurfaceData::make_unit_xy_quad(),
BufferUsage::StaticDraw,
server,
)
.unwrap(),
shader: RenderPassContainer::from_str(server, SHADER).unwrap(),
scene_handle: Default::default(),
}))
}
}
impl SceneRenderPass for MyRenderPass {
fn on_hdr_render(
&mut self,
ctx: SceneRenderPassContext,
) -> Result<RenderPassStatistics, FrameworkError> {
let mut stats = RenderPassStatistics::default();
if ctx.scene_handle != self.scene_handle {
return Ok(stats);
}
let view_projection = ctx.observer.position.view_projection_matrix;
let properties =
PropertyGroup::from([property("worldViewProjectionMatrix", &view_projection)]);
let material = RenderMaterial::from([binding("properties", &properties)]);
stats += self.shader.run_pass(
1,
&ImmutableString::new("Primary"),
ctx.framebuffer,
&self.quad,
ctx.observer.viewport,
&material,
ctx.uniform_buffer_cache,
Default::default(),
None,
)?;
Ok(stats)
}
fn source_type_id(&self) -> TypeId {
TypeId::of::<MyPlugin>()
}
}
}
The code snippet shows how to create a shader, find its uniforms, and finally how to actually render something in target frame buffer.
Registering a render pass
Every render pass must be registered in the renderer, otherwise it won’t be used. You can register a render pass using
add_render_pass method of the Renderer:
#![allow(unused)]
fn main() {
fn usage_example(renderer: &mut Renderer, render_pass: MyRenderPass) {
let shared_pass = Rc::new(RefCell::new(render_pass));
// You can share the pass across multiple places to be able to control it.
renderer.add_render_pass(shared_pass);
}
}
Please notice that we’ve wrapped render pass in Rc<RefCell<..>>, this means that you can share it across multiple places
and modify its data from the code of your game.