原理不再赘述,请见wgsl shader实现。
当前示例源码github地址:
https://github.com/vilyLei/voxwebgpu/blob/feature/rendering/src/voxgpu/sample/BillboardEntityTest.ts
当前示例运行效果:
WGSL顶点shader:
@group(0) @binding(0) var<uniform> objMat : mat4x4<f32>;
@group(0) @binding(1) var<uniform> viewMat : mat4x4<f32>;
@group(0) @binding(2) var<uniform> projMat : mat4x4<f32>;
@group(0) @binding(3) var<uniform> billParam: vec4<f32>;
struct VertexOutput {
@builtin(position) Position : vec4<f32>,
@location(0) uv : vec2<f32>
}
@vertex
fn main(
@location(0) position : vec3<f32>,
@location(1) uv : vec2<f32>
) -> VertexOutput {
let cosv = cos(billParam.z);
let sinv = sin(billParam.z);
let vtx = position.xy * billParam.xy;
let vtx_pos = vec2<f32>(vtx.x * cosv - vtx.y * sinv, vtx.x * sinv + vtx.y * cosv);
var viewV = viewMat * objMat * vec4f(0.0,0.0,0.0,1.0);
viewV = vec4<f32>(viewV.xy + vtx_pos.xy, viewV.zw);
var projV = projMat * viewV;
projV.z = ((projV.z / projV.w) + billParam.w) * projV.w;
var output : VertexOutput;
output.Position = projV;
output.uv = uv;
return output;
}
WGSL片段shader:
@group(0) @binding(4) var<uniform> color: vec4f;
@group(0) @binding(5) var<uniform> uvScale: vec4f;
@group(0) @binding(6) var billSampler: sampler;
@group(0) @binding(7) var billTexture: texture_2d<f32>;
@fragment
fn main(
@location(0) uv: vec2f
) -> @location(0) vec4f {
var c4 = textureSample(billTexture, billSampler, uv * uvScale.xy + uvScale.zw) * color;
return c4;
}
此示例基于此渲染系统实现,当前示例TypeScript源码如下:
export class BillboardEntityTest {
private mRscene = new RendererScene();
initialize(): void {
this.mRscene.initialize();
this.initScene();
this.initEvent();
}
private initScene(): void {
this.initEntities();
}
private initEntities(): void {
let rc = this.mRscene;
let diffuseTex0 = { diffuse: { url: "static/assets/flare_core_02.jpg" } };
let entity = new FixScreenPlaneEntity({ extent: [-0.8, -0.8, 1.6, 1.6], textures: [diffuseTex0] });
entity.color = [0.1, 0.3, 0.5];
rc.addEntity(entity);
rc.addEntity(new AxisEntity());
for (let i = 0; i < 10; ++i) {
let billboard = new BillboardEntity({ textures: [diffuseTex0] });
billboard.color = [0.5, 0.5, 2];
billboard.scale = Math.random() * 2 + 1;
billboard.transform.setPosition([Math.random() * 1000 - 500, 0, 0]);
rc.addEntity(billboard);
let diffuseTex1 = { diffuse: { url: "static/assets/testEFT4_01.jpg", flipY: true } };
billboard = new BillboardEntity({ textures: [diffuseTex1] });
billboard.color = [1.8, 1.5, 0.5];
// billboard.color = [0.8, 0.5, 0.5];
billboard.scale = Math.random() * 2 + 1;
billboard.uvScale = [0.5, 0.5];
billboard.uvOffset = [1, 1];
// billboard.uvOffset = [0.5, 1];
billboard.transform.setPosition([0, Math.random() * 1000 - 500, 0]);
rc.addEntity(billboard);
}
}
private initEvent(): void {
const rc = this.mRscene;
rc.addEventListener(MouseEvent.MOUSE_DOWN, this.mouseDown);
new MouseInteraction().initialize(rc, 0, false).setAutoRunning(true);
}
private mouseDown = (evt: MouseEvent): void => {};
run(): void {
this.mRscene.run();
}
}