Add depth buffer

This commit is contained in:
Alex Clink 2022-03-06 18:10:23 -05:00
parent cf6409250f
commit 329f57af0e
3 changed files with 43 additions and 14 deletions

24
src/3d/depth_buffer.cr Normal file
View file

@ -0,0 +1,24 @@
module PF
# A buffer of depth values for rending 3d scenes
class DepthBuffer
@values : Slice(Float64)
@width : Int32
@height : Int32
def initialize(@width, @height)
@values = Slice(Float64).new(@width * @height, 0.0)
end
def clear
@values.fill(0.0)
end
def [](x : Int, y : Int)
@values[y * @width + x]
end
def []=(x : Int, y : Int, value : Float64)
@values[y * @width + x] = value
end
end
end

View file

@ -55,7 +55,7 @@ module PF
end
# Project an array of Triangles into screen space
def project(tris : Array(Tri), camera = @camera)
def project(tris : Array(Tri), camera = @camera, sort : Bool = false)
mat_view = camera.view_matrix
# only draw triangles facing the camera
@ -96,9 +96,8 @@ module PF
end
end
# sort triangles
# TODO: Z-buffer
tris.sort! { |a, b| b.z <=> a.z }
# sort triangles, no need to do this if using a depth buffer
tris.sort! { |a, b| b.z <=> a.z } if sort
# Clip against the edges of the screen
{

View file

@ -61,7 +61,7 @@ module PF
end
# Draw a textured triangle
def fill_triangle(p1 : Vector2, p2 : Vector2, p3 : Vector2, t1 : Vector3, t2 : Vector3, t3 : Vector3, sprite : Sprite, color : Pixel = Pixel.white)
def fill_triangle(p1 : Vector2, p2 : Vector2, p3 : Vector2, t1 : Vector3, t2 : Vector3, t3 : Vector3, sprite : Sprite, buffer : DepthBuffer, color : Pixel = Pixel.white)
# Sort points from top to bottom
p1, p2, t1, t2 = p2, p1, t2, t1 if p2.y < p1.y
p1, p3, t1, t3 = p3, p1, t3, t1 if p3.y < p1.y
@ -151,10 +151,15 @@ module PF
t = scan_size == 0 ? 0.0 : (x - x_left) / scan_size
texture_point = texture_line.lerp(t)
if texture_point.z >= buffer[x, y + c]
buffer[x, y + c] = texture_point.z
# Get the x and y of the texture coords, divide by z for perspective, then
# multiply the point by the size of the sprite to get the final texture point
sample_point = Vector[texture_point.x / texture_point.z, texture_point.y / texture_point.z] * sprite.size
pixel = sprite.sample(sample_point.to_i)
sample_point = ((Vector[texture_point.x, texture_point.y] / texture_point.z) * sprite.size)
# Invert the y axis for the sprite
sample_point.y = sprite.height - sample_point.y
# sample_point = sample_point / texture_point.z if texture_point.z != 0
pixel = sprite.sample((sample_point + 0.5).to_i)
# Blend the pixel sample with the provided color
pixel.r = (pixel.r * (color.r / 255)).to_u8
@ -163,6 +168,7 @@ module PF
draw_point(x, y + c, pixel)
end
end
# Once we hit the point where a line changes, we need a new slope for that line
if y == mid