Using linear interpolation to try and build a surface in R3 is alot more work than you need to do. Instead you can implement a bezier surface equation as follows:
First you need to have a function to calculate the bernstein polynomial, which can be done as follows:
--[[
Gets the factorial of n
@param {integer} n: domain[0, 170], nth factorial to get
]]
local factorial_lookup = {}
local function factorial(n)
local f = factorial_lookup[n] -- increase speed of factorial method
if f then return f end
f = n > 1 and n*factorial(n-1) or 1
factorial_lookup[n] = f
return f
end
factorial(170) -- pre-allocate factorials up to 170, 170 is the IEEE-754 limit, anything after that is INF
--[[
Calculates the bernstein polynomial, only applicable for a 4-sided polygon.
If you want a larger polynomial, replace 1/... with factorial(m)/...
@see https://en.wikipedia.org/wiki/Bernstein_polynomial
]]
local function bernstein(m, i, u)
return 1/(factorial(i)*factorial(m-i)) * u^i * (1-u)^(m-i)
end
Next you should create a higher-order function to “give” you the equation you need for any given 4 points:
--[[
Returns the equation of a bezier surface in terms of u and v, which are degrees similar to an interpolation alpha
@param {Vector3} a: point 1
@param {Vector3} b: point 2
@param {Vector3} c: point 3
@param {Vector3} d: point 4
@returns {function}: bezier surface equation for the surface formed by abcd
]]
local function bezierSurfaceEquation(a, b, c, d)
--[[
Calculates the position in R3 of a point on the bezier surface created by abcd
@param {double} u: domain [0,1], determines one of two iteration degrees, similar to alpha in interpolation
@param {double} v: domain [0,1], determines one of two iteration degrees, similar to alpha in interpolation
@returns {Vector3}: point on the plane based on supplied degrees of iteration
]]
return function(u, v)
return bernstein(1,0,u)*bernstein(1,0,v)*a
+ bernstein(1,0,u)*bernstein(1,1,v)*b
+ bernstein(1,1,u)*bernstein(1,0,v)*c
+ bernstein(1,1,u)*bernstein(1,1,v)*d
end
end
You can also, optionally, defer the use of a higher-order function and use a similar method to your existing getPointOnPlane()
function as follows:
--[[
Calculates the position in R3 of a point on the bezier surface created by abcd
@param {Vector3} a: point 1
@param {Vector3} b: point 2
@param {Vector3} c: point 3
@param {Vector3} d: point 4
@param {double} u: domain [0,1], determines one of two iteration degrees, similar to alpha in interpolation
@param {double} v: domain [0,1], determines one of two iteration degrees, similar to alpha in interpolation
@returns {Vector3}: point on the plane based on supplied degrees of iteration
]]
local function getPointOnPlane(a, b, c, d, u, v)
return bernstein(1,0,u)*bernstein(1,0,v)*a
+ bernstein(1,0,u)*bernstein(1,1,v)*b
+ bernstein(1,1,u)*bernstein(1,0,v)*c
+ bernstein(1,1,u)*bernstein(1,1,v)*d
end
This will end up looking alot better than any linear interpolation you might do, and at the same time will let you build neat surfaces like:
I should note that I use some atypical language here, since I opted to use words you might be familiar with, as opposed to typically understood mathematical dialect, to explain some of the concepts here. Since there is so much to un-pack with this, and there are already great explanations online, I’ll link some below instead of trying to explain everything here myself from memory:
Explanations/Further reading:
Bernstein Polynomial (Wikipedia)
Bezier surface (Wikipedia)
Bezier surface (mtu.edu)
Bezier surface (math encyclopedia)
Bezier spline (math encyclopedia)