Correct for perspective in texturing

This commit is contained in:
Alex Clink 2022-03-06 14:26:35 -05:00
parent 0e599fbe94
commit bab69ad5f5
7 changed files with 63 additions and 17 deletions

7
spec/line_spec.cr Normal file
View file

@ -0,0 +1,7 @@
require "./spec_helper"
require "../src/line"
include PF
describe Line do
end

View file

@ -70,6 +70,11 @@ describe Vector do
v1 = Vector[5, 5]
(v1 // 2).should eq(Vector[2, 2])
end
it "applies exponents" do
v = Vector[2, 2] ** 5
v.should eq(Vector[32, 32])
end
end
describe "#-" do
@ -80,6 +85,13 @@ describe Vector do
end
end
describe "#sum" do
it "returns all components added together" do
v = Vector[1, 2, 3]
v.sum.should eq(6)
end
end
describe "#abs" do
it "returns the absolute value" do
v = Vector[-1, -1]

View file

@ -23,12 +23,17 @@ module PF
parts = line.split(/\s+/)
case parts[0]
when "o"
puts "Object: #{parts[1]}"
# TODO: This is when a new mesh starts
when "mtllib"
# TODO This is where we need to load texture and material information
when "v"
w = parts[4]?.try { |n| n.to_f64 }
verticies << Vector3.new(x: parts[1].to_f64, y: parts[2].to_f64, z: parts[3].to_f64)
when "vt"
v = parts[2]?.try { |n| n.to_f64 } || 0.0
w = parts[3]?.try { |n| n.to_f64 } || 0.0
w = parts[3]?.try { |n| n.to_f64 } || 1.0
texture_verticies << Vector3.new(parts[1].to_f64, v, w)
when "vn"
if use_normals
@ -67,9 +72,13 @@ module PF
tris << tri
# Split a square into triangles
# TODO: Handle texture points
if face_verts.size > 3
tri = Tri.new(face_verts[0], face_verts[2], face_verts[3], normal: normal)
unless face_tex.empty?
tri.t1 = face_tex[0]
tri.t2 = face_tex[2]
tri.t3 = face_tex[3]
end
tris << tri
end
end

View file

@ -48,15 +48,19 @@ module PF
# Multiply all points by a *Matrix*, returning a new *Tri*
def *(mat : Matrix)
# The transform function returns a w, which is the 4th component of the vertex
# this is the perspective information that we can use to apply textures
pp1, w1 = Transform3d.apply(@p1, mat)
pp2, w2 = Transform3d.apply(@p2, mat)
pp3, w3 = Transform3d.apply(@p3, mat)
pt1 = @t1 / w1
pt2 = @t2 / w2
pt3 = @t3 / w3
Tri.new(
pp1, pp2, pp3,
@t1,
@t2,
@t3,
pt1, pt2, pt3,
@color
)
end

View file

@ -2,9 +2,9 @@ require "./vector"
module PF
struct Line(T)
property p1 : Vector2(T), p2 : Vector2(T)
property p1 : T, p2 : T
def initialize(@p1 : Vector2(T), @p2 : Vector2(T))
def initialize(@p1 : T, @p2 : T)
end
def rise
@ -66,16 +66,18 @@ module PF
(@p2 - @p1) * t + @p1
end
# Return the length of the line
def length
Math.sqrt((run.abs * 2) + (rise.abs * 2))
Math.sqrt(((@p2 - @p1) ** 2).sum)
end
def /(n : (Float | Int))
Line.new(@p1 / n, @p2 / n)
end
def to_point
Vector[run, rise]
# Convert this line into a normalized vector
def to_vector
(@p2 - @p1).normalized
end
# Find the normal axis to this line

View file

@ -61,7 +61,7 @@ module PF
end
# Draw a textured triangle
def fill_triangle(p1 : Vector2, p2 : Vector2, p3 : Vector2, t1 : Vector2, t2 : Vector2, t3 : Vector2, sprite : Sprite, color : Pixel = Pixel.white)
def fill_triangle(p1 : Vector2, p2 : Vector2, p3 : Vector2, t1 : Vector3, t2 : Vector3, t3 : Vector3, sprite : Sprite, 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
@ -114,6 +114,7 @@ module PF
# Get the normalized t value for this height level
ty = height > 0 ? y / height : 0.0
# LERP both texture edges at the y position to create a new line
tyl =
if switch_left
@ -148,10 +149,14 @@ module PF
x_left.upto(x_right) do |x|
# LERP the line between the texture edges
t = scan_size == 0 ? 0.0 : (x - x_left) / scan_size
# Multiply the point by the size of the sprite to get the final texture point
sample_point = texture_line.lerp(t) * sprite.size
pixel = sprite.sample((sample_point + 0.5).to_i)
texture_point = texture_line.lerp(t)
# 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)
# Blend the pixel sample with the provided color
pixel.r = (pixel.r * (color.r / 255)).to_u8
pixel.g = (pixel.g * (color.g / 255)).to_u8
pixel.b = (pixel.b * (color.b / 255)).to_u8

View file

@ -1,7 +1,7 @@
require "./matrix"
module PF
module Vector
abstract struct Vector
# Creates a new `Vector` with the given *args*
#
# ```
@ -18,7 +18,7 @@ module PF
{% for i in 2..4 %}
{% vars = %w[x y z w] %}
struct Vector{{i}}(T)
struct Vector{{i}}(T) < Vector
{% for arg in 0...i %}
property {{vars[arg].id}} : T
{% end %}
@ -64,7 +64,7 @@ module PF
end
{% end %}
{% for op in %w[* / // + - %] %}
{% for op in %w[* / // + - % **] %}
# Applies `{{op.id}}` to all component of this vector with the corresponding component of *other*
def {{ op.id }}(other : Vector{{i}})
Vector[{% for arg in 0...i %} @{{vars[arg].id}} {{op.id}} other.{{vars[arg].id}}, {% end %}]
@ -76,6 +76,13 @@ module PF
end
{% end %}
# Add all components together
def sum
{% for arg in 0...i %}
@{{vars[arg].id}} {% if arg != i - 1 %} + {% end %}
{% end %}
end
# The length or magnitude of the vector calculated by the Pythagorean theorem
def magnitude
Math.sqrt({% for arg in 0...i %} @{{vars[arg].id}} ** 2 {% if arg != i - 1 %} + {% end %}{% end %})