4个版本
0.1.3 | 2021年12月15日 |
---|---|
0.1.2 | 2021年12月12日 |
0.1.1 | 2021年12月11日 |
0.1.0 | 2021年11月26日 |
#220 in 图形API
在 3 crate 中使用
61KB
1.5K SLoC
limelight
此crate在WebGL之上提供两层抽象。第一层是 阴影GPU 层,它抽象了WebGL的状态性,以提供一个更面向功能和数据的接口。第二层是 Renderer
API,它在上面的(未类型化的)阴影GPU之上提供类型化接口。
如果您不想使用自定义着色器,只想绘制很多形状,可以忽略下面的示例,查看 primitives crate 中的示例。
入门指南
查看 examples 目录中的可运行示例。
本教程假定您熟悉基本的WebGL术语,如顶点和片段着色器、统一变量和缓冲区。
绘制三角形
此示例演示了使用limelight生成图像的三个主要步骤
- 创建一个
Program
对象。limelight中的Program
包含顶点和片段着色器对(一个WebGLProgram
对象),还包含程序特定的状态。 - 创建一个
Renderer
。在我们初始化了所有程序并使用GL上下文后,我们将GL上下文的拥有权转移到Renderer
,然后它将负责所有GL端状态转换。 - 我们调用
renderer.render(program, buffer)
,这将导致绘制三角形。在这个示例中,我们没有附加顶点属性缓冲区,而是使用顶点着色器生成顶点。我们仍然需要告诉WebGL我们想要生成多少个顶点(3个),因此我们传递了一个大小为3
的DummyBuffer
。
use web_sys::WebGl2RenderingContext;
use limelight::{Program, Renderer, DummyBuffer, DrawMode};
fn render_triangle(gl: WebGl2RenderingContext) {
// limelight doesn't touch the DOM at all. Use your preferred
// framework to create a canvas and create a WebGL2 context
// from it.
// Create a shader program by passing in GLSL code as strings for
// the fragment and vertex shaders.
let mut program = Program::new(
include_str!("../../examples/01-triangle/shaders/shader.vert"),
include_str!("../../examples/01-triangle/shaders/shader.frag"),
DrawMode::Triangles
);
// Create a renderer. The renderer becomes the owner of the
// WebGl2RenderingContext, to ensure that its internal representation
// of the GPU state is always accureate.
let mut renderer = Renderer::new(gl);
// Run the program, rendering the results to the screen. We are
// not passing any vertex attribute data, so we use a `DummyBuffer`
// which renders three vertices: one for each corner of a triangle.
renderer.render(&mut program, &DummyBuffer::new(3)).unwrap();
}
使用缓冲区
缓冲区允许将任意顶点属性数据传递到着色器中。Limelight提供了一个过程宏(attribute
),用于从Rust端struct
映射到GPU端的顶点属性集。要使用此宏,您的crate还必须依赖bytemuck
及其derive
功能。
use web_sys::WebGl2RenderingContext;
use limelight::{Program, Renderer, Buffer, DrawMode, BufferUsageHint, attribute};
// This attribute macro derives a number of traits, including `VertexAttribute`, which
// is required for a type to be used in an `Buffer`.
#[attribute]
struct VertexDescription {
position: [f32; 2], // field names are mapped to variables in the shader.
}
impl VertexDescription {
pub fn new(x: f32, y: f32) -> Self {
VertexDescription { position: [x, y] }
}
}
fn render_triangles(gl: WebGl2RenderingContext) {
let mut program = Program::new(
include_str!("../../examples/02-buffer/shaders/shader.vert"),
include_str!("../../examples/02-buffer/shaders/shader.frag"),
DrawMode::Triangles
);
let mut renderer = Renderer::new(gl);
let data = vec![
// Lower-left triangle.
VertexDescription::new(-0.1, -0.1),
VertexDescription::new(-0.5, -0.1),
VertexDescription::new(-0.5, -0.5),
// Upper-right triangle.
VertexDescription::new(0.1, 0.1),
VertexDescription::new(0.5, 0.1),
VertexDescription::new(0.5, 0.5),
];
// Declare a buffer.
let mut buffer: Buffer<VertexDescription> =
Buffer::new(data, BufferUsageHint::StaticDraw);
renderer.render(&mut program, &buffer).unwrap();
}
统一变量
统一变量可以在着色器和片段程序中使用。它们可以在render
调用之间变化,但对于给定的渲染调用,每个统一变量在所有顶点和片段中都有一个常量值。
use limelight::{DrawMode, DummyBuffer, Program, Renderer, Uniform};
use web_sys::WebGl2RenderingContext;
fn render_triangles_with_uniform(gl: WebGl2RenderingContext) {
// This will correspond to "uniform float u_rotate" in the vertex shader.
let rotate_uniform = Uniform::new(std::f32::consts::PI / 3.4);
// This will correspond to "uniform vec2 u_scale" in the vertex shader.
let scale_uniform = Uniform::new([0.5, 0.8]);
// This will correspond to "uniform vec3 u_color" in the fragment shader.
let color_uniform = Uniform::new([0.9, 0.2, 0.3]);
let mut program = Program::new(
include_str!("../../examples/03-uniform/shaders/shader.vert"),
include_str!("../../examples/03-uniform/shaders/shader.frag"),
DrawMode::Triangles,
)
// We need to map the uniforms when we create the program.
// The GPU-side types are automatically inferred from the Rust types.
.with_uniform("u_rotate", rotate_uniform)
.with_uniform("u_scale", scale_uniform)
.with_uniform("u_color", color_uniform);
let mut renderer = Renderer::new(gl);
renderer.render(&mut program, &DummyBuffer::new(3)).unwrap();
}
动画
前面的示例已经渲染了静态图像,因此我们没有必要将设置初始数据结构的代码与更新GPU端数据并触发动画的代码分开。在这个示例中,我们将代码分为一个只调用一次的new()
方法和每个帧都会调用的render
方法。
limelight不是一个框架,为了与其他框架集成,它没有对如何组织您的代码有偏见。这个示例展示了您可以选择的一种为简单动画组织代码的方式(请参阅完整代码以了解如何与Yew网页框架集成)。
buffer.set_data
和uniform.set_data
是惰性的:它们不会在缓冲区在渲染调用中使用之前引起任何GPU活动。(见WebGL Insights第14.2节,延迟到绘制周期。)如果缓冲区或统一变量在渲染调用之间没有改变,则不会重新写入GPU。
use limelight::{Buffer, BufferUsageHint, DrawMode, Program, Renderer, Uniform, attribute};
use web_sys::WebGl2RenderingContext;
struct Animation {
program: Program<VertexDescription, ()>,
buffer: Buffer<VertexDescription>,
uniform: Uniform<[f32; 3]>,
}
impl Animation {
pub fn new(gl: &WebGl2RenderingContext) -> Self {
let buffer = Buffer::new(vec![], BufferUsageHint::DynamicDraw);
let uniform = Uniform::new([0., 0., 0.]);
let program = Program::new(
include_str!("../../examples/04-animate/shaders/shader.vert"),
include_str!("../../examples/04-animate/shaders/shader.frag"),
DrawMode::Triangles,
)
// Note that we clone uniform, so that we can retain a handle to it.
// Cloning a `Uniform` results in a reference-counted pointer to the
// same uniform.
.with_uniform("u_color", uniform.clone());
Animation {
buffer,
program,
uniform
}
}
pub fn render(&mut self, time: f64, renderer: &mut Renderer) {
let theta1 = time as f32 / 1000.;
let theta2 = theta1 + (std::f32::consts::TAU / 3.);
let theta3 = theta2 + (std::f32::consts::TAU / 3.);
self.buffer.set_data(vec![
VertexDescription::new(theta1.cos(), theta1.sin()),
VertexDescription::new(theta2.cos(), theta2.sin()),
VertexDescription::new(theta3.cos(), theta3.sin()),
]);
let r = (time as f32 / 3000.).sin() / 2. + 0.5;
let g = (time as f32 / 5000.).sin() / 2. + 0.5;
let b = (time as f32 / 7000.).sin() / 2. + 0.5;
self.uniform.set_value([r, g, b]);
renderer.render(&mut self.program, &self.buffer).unwrap();
}
}
#[attribute]
struct VertexDescription {
position: [f32; 2],
}
impl VertexDescription {
pub fn new(x: f32, y: f32) -> Self {
VertexDescription { position: [x, y] }
}
}
依赖项
~7–9MB
~175K SLoC