Update clipping

This commit is contained in:
Alex Clink 2023-06-11 20:01:18 -04:00
parent 926a345c9b
commit 0eb0eb2449
3 changed files with 77 additions and 36 deletions

View file

@ -111,22 +111,37 @@ class ThreeDee < PF::Game
@depth_buffer, @depth_buffer,
tri.color tri.color
) )
if tri.clipped
p1 = PF::Vector[tri.p1.x.to_i, tri.p1.y.to_i]
p2 = PF::Vector[tri.p2.x.to_i, tri.p2.y.to_i]
p3 = PF::Vector[tri.p3.x.to_i, tri.p3.y.to_i]
draw_triangle(p1, p2, p3, PF::Pixel.new(255, 255, 0))
end
end end
tris = @projector.project(@model.tris) tris = @projector.project(@model.tris, sort: true)
tris.each do |tri| tris.each do |tri|
# Rasterize all triangles # Rasterize all triangles
p1 = PF::Vector[tri.p1.x.to_i, tri.p1.y.to_i]
p2 = PF::Vector[tri.p2.x.to_i, tri.p2.y.to_i]
p3 = PF::Vector[tri.p3.x.to_i, tri.p3.y.to_i]
fill_triangle( fill_triangle(
PF::Vector[tri.p1.x.to_i, tri.p1.y.to_i], p1,
PF::Vector[tri.p2.x.to_i, tri.p2.y.to_i], p2,
PF::Vector[tri.p3.x.to_i, tri.p3.y.to_i], p3,
pixel: tri.color # buffer: @depth_buffer pixel: tri.color # buffer: @depth_buffer
) )
if tri.clipped
draw_triangle(p1, p2, p3, PF::Pixel.new(255, 0, 0))
end
end end
string = String.build do |io| string = String.build do |io|
io << "Triangles: " << tris.size + cube_tris.size io << "Triangles: " << cube_tris.size + tris.size
io << "\nPosition: " io << "\nPosition: "
io << "x: " << @camera.position.x.round(2) io << "x: " << @camera.position.x.round(2)
io << "y: " << @camera.position.y.round(2) io << "y: " << @camera.position.y.round(2)

View file

@ -6,18 +6,20 @@ module PF
getter width : Int32 | Float64 getter width : Int32 | Float64
getter height : Int32 | Float64 getter height : Int32 | Float64
property near = 0.1 property near = 0.1
property far = 1000.0 property far = 50.0
getter fov = 90.0 getter fov = 70.0
property aspect_ratio : Float64? property aspect_ratio : Float64?
property camera : Camera property camera : Camera
property light : Vector3(Float64) = Vector3.new(0.0, 0.0, -1.0).normalized property light : Vector3(Float64) = Vector3.new(0.0, 0.0, -1.0).normalized
property mat_proj : Matrix(Float64, 16)? property mat_proj : Matrix(Float64, 16)?
property clipping_plane_near : Vector3(Float64) property clipping_plane_near : Vector3(Float64)
property near_plane_normal : Vector3(Float64) = Vector3.new(0.0, 0.0, 1.0) property clipping_plane_far : Vector3(Float64)
@fov_rad : Float64? @fov_rad : Float64?
def initialize(@width, @height, @camera = Camera.new) def initialize(@width, @height, @camera = Camera.new)
@clipping_plane_near = Vector3.new(0.0, 0.0, @near) @clipping_plane_near = Vector3.new(0.0, 0.0, @near)
@clipping_plane_far = Vector3.new(0.0, 0.0, @far)
end end
def mat_proj def mat_proj
@ -58,43 +60,51 @@ module PF
def project(tris : Array(Tri), camera = @camera, sort : Bool = false) def project(tris : Array(Tri), camera = @camera, sort : Bool = false)
mat_view = camera.view_matrix mat_view = camera.view_matrix
# only draw triangles facing the camera # Only draw triangles facing the camera
tris = tris.select do |tri| tris = tris.select do |tri|
tri.normal.dot(tri.p1 - camera.position) < 0.0 tri.normal.dot(tri.p1 - camera.position) < 0.0
end end
# Iterate tris to transform into view, project, and clip # Iterate tris to transform into
0.upto(tris.size - 1) do tris = tris.map do |tri|
tri = tris.pop
shade = (tri.normal.dot(light) + 1.0) / 2 # light should be normalized shade = (tri.normal.dot(light) + 1.0) / 2 # light should be normalized
tri.color = tri.color * shade tri.color = tri.color * shade
tri * mat_view
end
tri *= mat_view # Clip tris
{
# Clip against the near plane {clipping_plane_near, Vector[0.0, 0.0, 1.0]},
tri.clip(plane: clipping_plane_near, plane_normal: near_plane_normal).each do |tri| {clipping_plane_far, Vector[0.0, 0.0, -1.0]},
tri *= mat_proj }.each do |clip|
0.upto(tris.size - 1) do
# Invert the y axis tri = tris.pop
tri.p1.y = tri.p1.y * -1.0 tri.clip(plane: clip[0], plane_normal: clip[1]).each do |tri|
tri.p2.y = tri.p2.y * -1.0
tri.p3.y = tri.p3.y * -1.0
# scale into screen space
tri.p1 += 1.0
tri.p2 += 1.0
tri.p3 += 1.0
tri.p1.x = tri.p1.x * 0.5 * width
tri.p1.y = tri.p1.y * 0.5 * height
tri.p2.x = tri.p2.x * 0.5 * width
tri.p2.y = tri.p2.y * 0.5 * height
tri.p3.x = tri.p3.x * 0.5 * width
tri.p3.y = tri.p3.y * 0.5 * height
tris.unshift(tri) tris.unshift(tri)
end end
end end
end
# Project the triangles
tris = tris.map do |tri|
z = tri.z
tri *= mat_proj
tri.z = z
tri.map_points do |point|
# Invert the y axis
point.y *= -1.0
# scale into screen space
point += 1.0
point.x *= 0.5 * width
point.y *= 0.5 * height
point
end
tri
end
# sort triangles, no need to do this if using a depth buffer # sort triangles, no need to do this if using a depth buffer
tris.sort! { |a, b| b.z <=> a.z } if sort tris.sort! { |a, b| b.z <=> a.z } if sort

View file

@ -12,14 +12,15 @@ module PF
property t2 : Vector3(Float64) = Vector[0.0, 0.0, 0.0] property t2 : Vector3(Float64) = Vector[0.0, 0.0, 0.0]
property t3 : Vector3(Float64) = Vector[0.0, 0.0, 0.0] property t3 : Vector3(Float64) = Vector[0.0, 0.0, 0.0]
property color : PF::Pixel property color : Pixel
getter clipped : Bool = false
setter normal : Vector3(Float64)? setter normal : Vector3(Float64)?
def initialize(@p1 : Vector3(Float64), @p2 : Vector3(Float64), @p3 : Vector3(Float64), @color = PF::Pixel::White, @normal = nil) def initialize(@p1 : Vector3(Float64), @p2 : Vector3(Float64), @p3 : Vector3(Float64), @color = PF::Pixel::White, @normal = nil, @clipped = false)
end end
def initialize(@p1, @p2, @p3, @t1, @t2, @t3, @color = PF::Pixel::White) def initialize(@p1, @p2, @p3, @t1, @t2, @t3, @color = PF::Pixel::White, @clipped = false)
end end
# Return the normal assuming clockwise pointing winding # Return the normal assuming clockwise pointing winding
@ -46,6 +47,18 @@ module PF
(@p1.z + @p2.z + @p3.z) / 3.0 (@p1.z + @p2.z + @p3.z) / 3.0
end end
def z=(value : Float64)
@p1.z = value
@p2.z = value
@p3.z = value
end
def map_points
@p1 = yield @p1
@p2 = yield @p2
@p3 = yield @p3
end
# Multiply all points by a *Matrix*, returning a new *Tri* # Multiply all points by a *Matrix*, returning a new *Tri*
def *(mat : Matrix) def *(mat : Matrix)
# The transform function returns a w, which is the 4th component of the vertex # The transform function returns a w, which is the 4th component of the vertex
@ -119,7 +132,8 @@ module PF
Tri.new( Tri.new(
inside_points[0], clip_p1, clip_p2, inside_points[0], clip_p1, clip_p2,
inside_texts[0], int_t1, int_t2, inside_texts[0], int_t1, int_t2,
color: @color color: @color,
clipped: true
), ),
} }
end end
@ -141,14 +155,16 @@ module PF
Tri.new( Tri.new(
inside_points[0], inside_points[1], clip_p1, inside_points[0], inside_points[1], clip_p1,
inside_texts[0], inside_texts[1], int_t1, inside_texts[0], inside_texts[1], int_t1,
color: @color color: @color,
clipped: true
), ),
# The second triangle will have the second inside point, the second intersection, then the first intersection # The second triangle will have the second inside point, the second intersection, then the first intersection
# This order preserves clockwise winding # This order preserves clockwise winding
Tri.new( Tri.new(
inside_points[1], clip_p2, clip_p1, inside_points[1], clip_p2, clip_p1,
inside_texts[1], int_t2, int_t1, inside_texts[1], int_t2, int_t1,
color: @color color: @color,
clipped: true
), ),
} }
end end