Fix transparent blocks
This commit is contained in:
+125
-13
@@ -1,5 +1,6 @@
|
||||
import { Camera } from "../components/camera.ts";
|
||||
import { mat4 } from "gl-matrix";
|
||||
import type { RenderLayer } from "$/common/everything_registry.ts";
|
||||
|
||||
export let device: GPUDevice;
|
||||
export let canvas: HTMLCanvasElement;
|
||||
@@ -20,6 +21,10 @@ let canvas_format: GPUTextureFormat;
|
||||
|
||||
let pipeline_2d: GPURenderPipeline;
|
||||
let pipeline_3d: GPURenderPipeline;
|
||||
let terrain_pipelines: Record<RenderLayer, GPURenderPipeline>;
|
||||
// 0 1 2 0 2 3 for every quad, shared by all solid and cutout chunk meshes
|
||||
let quad_index_buffer: GPUBuffer | undefined;
|
||||
let quad_index_capacity = 0;
|
||||
let uniform_layout: GPUBindGroupLayout;
|
||||
let texture_layout: GPUBindGroupLayout;
|
||||
let sampler: GPUSampler;
|
||||
@@ -90,9 +95,30 @@ fn vs_main(
|
||||
return out;
|
||||
}
|
||||
|
||||
// blended draws. fully clear texels don't write depth, or they would hide what's drawn behind them later
|
||||
@fragment
|
||||
fn fs_main(in: VertexOut) -> @location(0) vec4<f32> {
|
||||
return textureSample(texture0, sampler0, in.tex_coord) * in.color;
|
||||
let color = textureSample(texture0, sampler0, in.tex_coord) * in.color;
|
||||
if (color.a < 0.01) {
|
||||
discard;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
// no discard, so the gpu can reject hidden fragments before running the shader
|
||||
@fragment
|
||||
fn fs_solid(in: VertexOut) -> @location(0) vec4<f32> {
|
||||
return vec4<f32>((textureSample(texture0, sampler0, in.tex_coord) * in.color).rgb, 1.0);
|
||||
}
|
||||
|
||||
// alpha tested instead of blended, so it doesn't need sorting
|
||||
@fragment
|
||||
fn fs_cutout(in: VertexOut) -> @location(0) vec4<f32> {
|
||||
let color = textureSample(texture0, sampler0, in.tex_coord) * in.color;
|
||||
if (color.a < 0.1) {
|
||||
discard;
|
||||
}
|
||||
return vec4<f32>(color.rgb, 1.0);
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -251,8 +277,33 @@ export function flush_batch() {
|
||||
vert_index = 0;
|
||||
}
|
||||
|
||||
export function flush_buffer(buffer: GPUBuffer, draw_count: number) {
|
||||
draw(buffer, 0, draw_count);
|
||||
// draws a chunk mesh made of quads (4 vertices each). without an index buffer the quads are drawn in order
|
||||
export function draw_terrain(
|
||||
layer: RenderLayer,
|
||||
vertex_buffer: GPUBuffer,
|
||||
quad_count: number,
|
||||
index_buffer?: GPUBuffer,
|
||||
) {
|
||||
if (!current_texture || quad_count === 0) {
|
||||
return;
|
||||
}
|
||||
if (!index_buffer) {
|
||||
ensure_quad_indices(quad_count);
|
||||
index_buffer = quad_index_buffer!;
|
||||
}
|
||||
|
||||
const render_pass = ensure_pass();
|
||||
const pipeline = terrain_pipelines[layer];
|
||||
if (current_pipeline !== pipeline) {
|
||||
render_pass.setPipeline(pipeline);
|
||||
current_pipeline = pipeline;
|
||||
}
|
||||
|
||||
render_pass.setBindGroup(0, uniform_bind_group, [uniform_slot * UNIFORM_SLOT_SIZE]);
|
||||
render_pass.setBindGroup(1, get_texture_bind_group(current_texture));
|
||||
render_pass.setVertexBuffer(0, vertex_buffer);
|
||||
render_pass.setIndexBuffer(index_buffer, "uint32");
|
||||
render_pass.drawIndexed(quad_count * 6);
|
||||
}
|
||||
|
||||
export function create_vertex_buffer(vertices: Float32Array): GPUBuffer {
|
||||
@@ -264,7 +315,16 @@ export function create_vertex_buffer(vertices: Float32Array): GPUBuffer {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
export function destroy_vertex_buffer(buffer: GPUBuffer) {
|
||||
export function create_index_buffer(indices: Uint32Array): GPUBuffer {
|
||||
const buffer = device.createBuffer({
|
||||
size: Math.max(4, indices.byteLength),
|
||||
usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
|
||||
});
|
||||
device.queue.writeBuffer(buffer, 0, indices);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
export function destroy_buffer(buffer: GPUBuffer) {
|
||||
// it might be used by a draw thats not submitted yet
|
||||
if (encoder) {
|
||||
pending_destroy.push(buffer);
|
||||
@@ -375,6 +435,28 @@ export function push_quad_vertices(
|
||||
|
||||
// internal
|
||||
|
||||
function ensure_quad_indices(quad_count: number) {
|
||||
if (quad_count <= quad_index_capacity) {
|
||||
return;
|
||||
}
|
||||
if (quad_index_buffer) {
|
||||
destroy_buffer(quad_index_buffer);
|
||||
}
|
||||
quad_index_capacity = Math.max(quad_count, quad_index_capacity * 2, 16384);
|
||||
const indices = new Uint32Array(quad_index_capacity * 6);
|
||||
for (let q = 0; q < quad_index_capacity; q++) {
|
||||
const v = q * 4;
|
||||
const i = q * 6;
|
||||
indices[i] = v;
|
||||
indices[i + 1] = v + 1;
|
||||
indices[i + 2] = v + 2;
|
||||
indices[i + 3] = v;
|
||||
indices[i + 4] = v + 2;
|
||||
indices[i + 5] = v + 3;
|
||||
}
|
||||
quad_index_buffer = create_index_buffer(indices);
|
||||
}
|
||||
|
||||
function draw(buffer: GPUBuffer, offset: number, vertex_count: number) {
|
||||
if (!current_texture || vertex_count === 0) {
|
||||
return;
|
||||
@@ -554,21 +636,51 @@ function create_pipelines() {
|
||||
depthStencil: { format: DEPTH_FORMAT, depthWriteEnabled: false, depthCompare: "always" },
|
||||
});
|
||||
|
||||
const primitive_3d: GPUPrimitiveState = { topology: "triangle-list", cullMode: "back", frontFace: "ccw" };
|
||||
const depth_3d: GPUDepthStencilState = {
|
||||
format: DEPTH_FORMAT,
|
||||
depthWriteEnabled: true,
|
||||
depthCompare: "less-equal",
|
||||
// same as the old polygonOffset(1, 1)
|
||||
depthBias: 1,
|
||||
depthBiasSlopeScale: 1,
|
||||
};
|
||||
|
||||
pipeline_3d = device.createRenderPipeline({
|
||||
layout,
|
||||
vertex,
|
||||
fragment,
|
||||
multisample,
|
||||
primitive: { topology: "triangle-list", cullMode: "back", frontFace: "ccw" },
|
||||
depthStencil: {
|
||||
format: DEPTH_FORMAT,
|
||||
depthWriteEnabled: true,
|
||||
depthCompare: "less-equal",
|
||||
// same as the old polygonOffset(1, 1)
|
||||
depthBias: 1,
|
||||
depthBiasSlopeScale: 1,
|
||||
},
|
||||
primitive: primitive_3d,
|
||||
depthStencil: depth_3d,
|
||||
});
|
||||
|
||||
// like sodium and vanilla: solid and cutout don't blend, translucent blends and still writes depth
|
||||
// since it's drawn sorted back to front
|
||||
const unblended = (entryPoint: string): GPUFragmentState => ({
|
||||
module,
|
||||
entryPoint,
|
||||
targets: [{ format: canvas_format }],
|
||||
});
|
||||
terrain_pipelines = {
|
||||
solid: device.createRenderPipeline({
|
||||
layout,
|
||||
vertex,
|
||||
fragment: unblended("fs_solid"),
|
||||
multisample,
|
||||
primitive: primitive_3d,
|
||||
depthStencil: depth_3d,
|
||||
}),
|
||||
cutout: device.createRenderPipeline({
|
||||
layout,
|
||||
vertex,
|
||||
fragment: unblended("fs_cutout"),
|
||||
multisample,
|
||||
primitive: primitive_3d,
|
||||
depthStencil: depth_3d,
|
||||
}),
|
||||
translucent: pipeline_3d,
|
||||
};
|
||||
}
|
||||
|
||||
function create_stream_buffer(size: number) {
|
||||
|
||||
Reference in New Issue
Block a user